/* =============================================================================
   PharmaPilulier — Système de design
   Thème « pharmacie » : vert clinique, blanc stérile, accents cyan.
   Aucune dépendance externe : tout est local, fonctionne hors ligne.
   ============================================================================= */

/* -----------------------------------------------------------------------------
   1. Jetons de couleur
   -------------------------------------------------------------------------- */
:root {
    color-scheme: light dark;

    /* Marque — bleu clinique */
    --pp-brand-50:  #EBF4FF;
    --pp-brand-100: #D5E7FF;
    --pp-brand-200: #B0D0FF;
    --pp-brand-300: #7FB2FC;
    --pp-brand-400: #4A90F0;
    --pp-brand-500: #2372DE;
    --pp-brand-600: #145CBC;
    --pp-brand-700: #124A97;
    --pp-brand-800: #133D79;
    --pp-brand-900: #0C2A54;

    /* Accents */
    --pp-cyan-400: #22D3EE;
    --pp-cyan-500: #06B6D4;
    --pp-cyan-600: #0891B2;

    /* Signalétique */
    --pp-danger:   #E11D48;
    --pp-danger-bg:#FFF1F3;
    --pp-warn:     #D97706;
    --pp-warn-bg:  #FFFBEB;
    --pp-info:     #0284C7;
    --pp-info-bg:  #F0F9FF;
    --pp-ok:       #059669;
    --pp-ok-bg:    #ECFDF5;

    /* Rayons, ombres, rythme */
    --pp-r-xs: 6px;
    --pp-r-sm: 9px;
    --pp-r-md: 13px;
    --pp-r-lg: 18px;
    --pp-r-xl: 26px;
    --pp-r-full: 999px;

    --pp-ombre-1: 0 1px 2px rgb(10 30 60 / .06), 0 1px 1px rgb(10 30 60 / .04);
    --pp-ombre-2: 0 4px 12px rgb(10 30 60 / .08), 0 1px 3px rgb(10 30 60 / .05);
    --pp-ombre-3: 0 18px 48px rgb(10 30 60 / .14), 0 4px 12px rgb(10 30 60 / .07);
    --pp-ombre-brand: 0 8px 22px rgb(35 114 222 / .32);

    --pp-transition: 160ms cubic-bezier(.22, .61, .36, 1);

    /* ---------------------------------------------------------------------
       Ambiance : « --pp-nuit » va de 0 (plein jour) à 1 (nuit complète).
       Les surfaces ne sont pas deux jeux de couleurs qui se remplacent, mais un
       mélange continu entre deux extrémités : le curseur de l'en-tête ne fait que
       déplacer ce nombre, et toute l'interface suit d'un bloc.
       Le mélange se fait en oklab — un dégradé perceptuellement régulier, sans le
       passage boueux qu'un mélange en sRGB produirait entre un bleu très clair et
       un bleu très sombre.
       --------------------------------------------------------------------- */
    --pp-nuit: 0;

    /* Extrémité claire */
    --jour-bg:            #F2F6FB;
    --jour-bg-motif:      #E4EBF4;
    --jour-surface:       #FFFFFF;
    --jour-surface-2:     #F4F8FC;
    --jour-surface-3:     #E9F0F8;
    --jour-bordure:       #E0E8F2;
    --jour-bordure-forte: #C6D5E6;
    --jour-texte:         #0C1A2B;
    --jour-texte-2:       #4A5A70;
    --jour-texte-3:       #65758C;   /* 4.7:1 sur blanc — conforme AA pour du texte normal */

    /* Extrémité sombre — volontairement moins profonde qu'un noir bleuté :
       une salle de préparation reste éclairée, l'écran n'a pas à faire un trou. */
    --nuit-bg:            #111D2E;
    --nuit-bg-motif:      #17243A;
    --nuit-surface:       #182637;
    --nuit-surface-2:     #1F2E43;
    --nuit-surface-3:     #273950;
    --nuit-bordure:       #2B3C55;
    --nuit-bordure-forte: #3B506E;
    --nuit-texte:         #E9F0FA;
    --nuit-texte-2:       #B0BFD2;
    --nuit-texte-3:       #93A7BE;

    --pp-bg:            color-mix(in oklab, var(--jour-bg),            var(--nuit-bg)            calc(var(--pp-nuit) * 100%));
    --pp-bg-motif:      color-mix(in oklab, var(--jour-bg-motif),      var(--nuit-bg-motif)      calc(var(--pp-nuit) * 100%));
    --pp-surface:       color-mix(in oklab, var(--jour-surface),       var(--nuit-surface)       calc(var(--pp-nuit) * 100%));
    --pp-surface-2:     color-mix(in oklab, var(--jour-surface-2),     var(--nuit-surface-2)     calc(var(--pp-nuit) * 100%));
    --pp-surface-3:     color-mix(in oklab, var(--jour-surface-3),     var(--nuit-surface-3)     calc(var(--pp-nuit) * 100%));
    --pp-bordure:       color-mix(in oklab, var(--jour-bordure),       var(--nuit-bordure)       calc(var(--pp-nuit) * 100%));
    --pp-bordure-forte: color-mix(in oklab, var(--jour-bordure-forte), var(--nuit-bordure-forte) calc(var(--pp-nuit) * 100%));
    --pp-texte:         color-mix(in oklab, var(--jour-texte),         var(--nuit-texte)         calc(var(--pp-nuit) * 100%));
    --pp-texte-2:       color-mix(in oklab, var(--jour-texte-2),       var(--nuit-texte-2)       calc(var(--pp-nuit) * 100%));
    --pp-texte-3:       color-mix(in oklab, var(--jour-texte-3),       var(--nuit-texte-3)       calc(var(--pp-nuit) * 100%));

    --pp-focus:         var(--pp-brand-500);
    --pp-verre:         color-mix(in srgb, var(--pp-surface) 74%, transparent);
}

/* =====================================================================================
   COMPTAGE DES PRESCRIPTIONS — poste portrait 1080×1920, caméra 1920×1080.
   ===================================================================================== */
.comptage-page { display: grid; gap: 2rem; }
.comptage-accueil {
    width: min(100%, 760px); margin: 0 auto; text-align: center; padding: clamp(2rem, 5vw, 3rem);
    border-color: color-mix(in srgb, var(--pp-brand-500) 18%, var(--pp-bordure));
    box-shadow: 0 18px 46px rgb(20 63 115 / .10);
}
.comptage-accueil__icone {
    width: 76px; height: 76px; margin: 0 auto 1rem; border-radius: 24px;
    display: grid; place-items: center; color: var(--pp-brand-500);
    background: color-mix(in srgb, var(--pp-brand-500) 12%, transparent);
}
.comptage-accueil__icone .icone { width: 38px; height: 38px; }
.comptage-accueil h2 { margin: 0; font-size: clamp(1.35rem, 3vw, 1.75rem); }
.comptage-accueil form { display: grid; gap: 1rem; margin: 2rem auto 1rem; max-width: 540px; text-align: left; }
.comptage-accueil__machine { width: 100%; padding: .38rem .75rem; text-align: left; }
.comptage-accueil__demarrer { width: 100%; justify-content: center; box-shadow: var(--pp-ombre-brand); }
.comptage-accueil__aide { margin: 1.15rem 0 0; }
.comptage-choix {
    width: min(100%, 760px); margin: 0 auto; padding: 2.2rem; text-align: center;
    background: linear-gradient(145deg, var(--pp-surface), color-mix(in srgb, var(--pp-brand-50) 38%, var(--pp-surface)));
}
.comptage-choix h2 { margin: 0 0 .45rem; }
.comptage-choix p { margin: 0; color: var(--pp-texte-2); }
.comptage-selecteur {
    width: min(100%, 1120px); margin: 0 auto; padding: 1.35rem;
    border: 1px solid var(--pp-bordure); border-radius: var(--pp-r-xl);
    background: color-mix(in srgb, var(--pp-surface) 92%, transparent);
}
.comptage-selecteur__entete { margin: 0 0 1rem; }
.comptage-selecteur__entete > span {
    color: var(--pp-brand-500); font-size: .74rem; font-weight: 800;
    letter-spacing: .09em; text-transform: uppercase;
}
.comptage-selecteur__entete h2 { margin: .15rem 0 0; font-size: 1.2rem; }
.comptage-modes { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .8rem; }
.comptage-mode {
    position: relative; min-height: 92px; padding: 1rem; display: flex; align-items: center; gap: .85rem;
    color: var(--pp-texte); text-decoration: none; overflow: hidden;
    border: 1px solid var(--pp-bordure); border-radius: 17px; background: var(--pp-surface);
    box-shadow: var(--pp-ombre-1); transition: transform .16s ease, border-color .16s ease, box-shadow .16s ease;
}
.comptage-mode:hover { transform: translateY(-2px); border-color: var(--pp-brand-400); box-shadow: var(--pp-ombre-2); }
.comptage-mode:focus-visible { outline: 3px solid color-mix(in srgb, var(--pp-focus) 35%, transparent); outline-offset: 2px; }
.comptage-mode__icone {
    flex: none; width: 52px; height: 52px; border-radius: 16px; display: grid; place-items: center;
    background: color-mix(in srgb, var(--pp-brand-500) 11%, var(--pp-surface-2)); color: var(--pp-brand-500);
}
.comptage-mode__icone .icone { width: 25px; height: 25px; }
.comptage-mode__texte { min-width: 0; display: grid; gap: .18rem; }
.comptage-mode__texte strong { font-size: 1rem; }
.comptage-mode__texte small { color: var(--pp-texte-3); }
.comptage-mode__fleche { margin-left: auto; color: var(--pp-brand-500); font-size: 1.35rem; transition: transform .16s ease; }
.comptage-mode:hover .comptage-mode__fleche { transform: translateX(3px); }
.comptage-mode--actif {
    color: #fff; border-color: transparent;
    background: linear-gradient(135deg, var(--pp-brand-500), var(--pp-brand-700));
    box-shadow: var(--pp-ombre-brand);
}
.comptage-mode--actif .comptage-mode__icone { color: #fff; background: rgb(255 255 255 / .16); }
.comptage-mode--actif .comptage-mode__texte small,
.comptage-mode--actif .comptage-mode__fleche { color: rgb(255 255 255 / .82); }
.comptage-produit { border: 0; padding: 0; margin: 0; min-width: 0; }
.comptage-accueil__lecture .champ__saisie { min-height: 64px; font-size: 1.45rem; letter-spacing: .06em; }
.comptage-rx-apercu {
    display: grid; gap: .75rem; padding: 1rem; text-align: left;
    border: 1px solid color-mix(in srgb, var(--pp-brand-500) 25%, var(--pp-bordure));
    border-radius: 15px; background: color-mix(in srgb, var(--pp-brand-50) 45%, var(--pp-surface));
    animation: comptage-apercu-entree .18s ease-out;
}
.comptage-rx-apercu[hidden],
.comptage-rx-apercu [hidden] { display: none; }
.comptage-rx-apercu__chargement { min-height: 62px; display: flex; align-items: center; justify-content: center; gap: .65rem; color: var(--pp-texte-2); }
.comptage-rx-apercu__erreur {
    padding: .75rem; border-radius: var(--pp-r-sm); color: var(--pp-danger);
    background: var(--pp-danger-bg); font-weight: 700;
}
.comptage-rx-apercu__contenu { display: grid; gap: .7rem; }
.comptage-rx-apercu__contenu header { display: flex; align-items: center; gap: .7rem; }
.comptage-rx-apercu__contenu header > div { min-width: 0; flex: 1; }
.comptage-rx-apercu__contenu header small { color: var(--pp-texte-3); font-size: .72rem; font-weight: 750; text-transform: uppercase; letter-spacing: .06em; }
.comptage-rx-apercu__contenu h3 { margin: .08rem 0 0; font-size: 1.05rem; }
.comptage-rx-apercu__icone {
    flex: none; width: 42px; height: 42px; display: grid; place-items: center;
    border-radius: 13px; color: var(--pp-brand-600); background: var(--pp-brand-100);
}
.comptage-rx-apercu__produit { padding-top: .65rem; border-top: 1px solid var(--pp-bordure); }
.comptage-rx-apercu dl { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .55rem; margin: 0; }
.comptage-rx-apercu dl > div { min-width: 0; padding: .55rem .65rem; border-radius: 10px; background: var(--pp-surface); }
.comptage-rx-apercu dt { color: var(--pp-texte-3); font-size: .68rem; font-weight: 750; text-transform: uppercase; letter-spacing: .04em; }
.comptage-rx-apercu dd { margin: .18rem 0 0; overflow-wrap: anywhere; font-size: .83rem; font-weight: 650; }
.comptage-rx-apercu__contenu > p { margin: 0; padding: .6rem .7rem; border-left: 3px solid var(--pp-brand-500); color: var(--pp-texte-2); background: var(--pp-surface); }
@keyframes comptage-apercu-entree { from { opacity: 0; transform: translateY(-5px); } }
.comptage-reprise__actions {
    display: grid !important; justify-items: center; gap: .65rem;
    width: min(100%, 360px); margin: 1.5rem auto 1rem !important; padding-top: 1.25rem;
    border-top: 1px solid var(--pp-bordure);
}
.comptage-reprise__actions .btn { width: min(100%, 290px); justify-content: center; }
.btn--grand { min-height: 52px; padding: .8rem 1.15rem; font-size: 1rem; }

.comptage-poste { width: min(100%, 1180px); margin: 0 auto; display: grid; gap: .85rem; }
/* Les deux enveloppes du poste n'existent qu'en paysage (voir plus bas) : ailleurs
   elles s'effacent et leurs enfants restent des cases de la grille du poste. */
.comptage-poste__haut, .comptage-poste__bas { display: contents; }
.comptage-poste__entete {
    display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 1rem;
    align-items: stretch; padding: 1rem 1.15rem; border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-lg); background: var(--pp-surface);
}
.comptage-poste__identite { display: flex; flex-direction: column; gap: .28rem; min-width: 0; }
.comptage-poste__identite h1 { margin: .1rem 0; font-size: clamp(1.2rem, 2.5vw, 1.75rem); }
.comptage-poste__identite strong { overflow-wrap: anywhere; }
.comptage-poste__identite .etiquette { width: max-content; }
.comptage-poste__rx {
    width: max-content; max-width: 100%; display: inline-flex; align-items: baseline; gap: .55rem;
    padding: .42rem .75rem; border: 1px solid color-mix(in srgb, var(--pp-brand-500) 35%, var(--pp-bordure));
    border-radius: 12px; color: var(--pp-brand-700);
    background: color-mix(in srgb, var(--pp-brand-500) 10%, var(--pp-surface));
}
.comptage-poste__rx small { font-size: .78rem; font-weight: 750; text-transform: uppercase; letter-spacing: .045em; }
.comptage-poste__rx strong { color: inherit; font-size: 1.18rem; line-height: 1; }
.comptage-poste__identifiants { font-size: 1rem; font-weight: 750; overflow-wrap: anywhere; }
.comptage-poste__caracteristiques { color: var(--pp-texte-2); font-size: 1rem; font-weight: 700; }
.comptage-poste__identite h1,
.comptage-poste__produit,
.comptage-poste__identifiants,
.comptage-poste__caracteristiques,
.comptage-poste__posologie {
    display: block; width: 100%; max-width: 100%; min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    /* « overflow: hidden » ne rogne pas que la droite : il coupe aussi en HAUTEUR.
       La boîte de texte de Segoe UI fait 1,33 em ; en dessous, la ligne est plus basse
       que la fonte et le « g » de Bourget perd sa queue (mesuré : 6 px coupés à 1,05,
       encore 1 px à 1,25). 1,35 passe au-dessus de la boîte : aucun jambage n'est
       rogné. Valeur de MÉCANIQUE, pas d'esthétique — ces lignes n'en fixent plus
       d'autre, pour qu'il n'y ait qu'un seul endroit où elle se règle. */
    line-height: 1.35;
}
.comptage-poste__total {
    min-width: 180px; display: grid; place-items: center; align-content: center;
    padding: .75rem 1.3rem; border-radius: 16px; color: #fff; text-align: center;
    transition: background .18s ease, box-shadow .18s ease;
}
.comptage-poste__total--incorrect { background: linear-gradient(135deg, #e63f5e, #b71335); box-shadow: 0 8px 26px rgba(190,18,60,.23); }
.comptage-poste__total--exact { background: linear-gradient(135deg, #20b970, #087f4b); box-shadow: 0 8px 26px rgba(8,127,75,.24); }
.comptage-poste__total > span { font-size: .8rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; }
.comptage-poste__total strong {
    display: grid; justify-items: center; align-content: center; gap: .08em;
    font-size: clamp(2.25rem, 6vw, 4rem); line-height: 1; font-variant-numeric: tabular-nums;
}
.comptage-poste__total strong > [data-comptage-valeur] { font: inherit; }
.comptage-poste__cible {
    padding: .32em .68em; border: 1px solid rgb(255 255 255 / .35);
    border-radius: 999px; background: rgb(255 255 255 / .14);
    font-size: .22em; font-weight: 800; line-height: 1.1; white-space: nowrap;
    text-transform: uppercase; letter-spacing: .035em;
}
.comptage-poste__total small { margin-top: .3rem; font-weight: 700; }
.comptage-poste__etat,
.comptage-poste__message { min-height: 28px; padding: .35rem .75rem; font-size: .9rem; color: var(--pp-texte-2); }
.comptage-poste__etat--ok { color: #087f4b; }
.comptage-poste__etat:empty { display: none; }
.comptage-poste__message:empty { display: none; }
.comptage-poste__etat--erreur,
.comptage-poste__message--erreur { color: #be123c; font-weight: 700; }

.comptage-camera { border: 1px solid var(--pp-bordure); border-radius: var(--pp-r-lg); background: #07101c; overflow: hidden; }
/* Le flux remplit le cadre par recadrage central, jamais par étirement. ONNX et la
   photo reçoivent exactement ce même cadrage via cadrage-itrack.js. Le calque est
   déjà en 16:9, dans les coordonnées de la photo (et non du flux natif portrait). */
.comptage-camera__cadre { position: relative; width: 100%; aspect-ratio: 16 / 9; background: #02060b; overflow: hidden; }
.comptage-camera video,
.comptage-camera canvas { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
.comptage-camera video { background: #000; object-fit: cover; object-position: center; }
.comptage-camera canvas { z-index: 2; pointer-events: none; touch-action: none; }
/* La caméra du poste filme tête en bas : l'image est remise à l'endroit ici. Le calque
   des repères tourne AVEC le flux — les points restent donc sur leurs pilules sans
   qu'aucune coordonnée ne soit recalculée. La photo enregistrée reçoit exactement le
   même demi-tour (capturerPhoto, dans comptage.js) : ce qu'on voit est ce qu'on garde. */
.comptage-camera video,
.comptage-camera canvas { transform: rotate(180deg); }
.comptage-camera__overlay--edition { pointer-events: auto !important; cursor: crosshair; }
.comptage-camera__chargement {
    position: absolute; z-index: 3; inset: 0; display: grid; place-content: center;
    justify-items: center; gap: .75rem; color: #fff; background: rgba(2,6,11,.82);
}
.comptage-camera__chargement[hidden] { display: none; }
.comptage-camera__outils {
    min-height: 54px; padding: .55rem .75rem; display: flex; align-items: center;
    gap: .55rem; flex-wrap: wrap; color: #dbe9f8; background: #0b1726;
}
.comptage-camera__outils .attenue { color: #b7c6d8; margin-left: auto; }
.comptage-camera__outils .etiquette { color: #dbe9f8; border-color: #344a63; }
.comptage-recentes {
    padding: .85rem; border: 1px solid var(--pp-bordure); border-radius: var(--pp-r-lg);
    background: var(--pp-surface);
}
.comptage-recentes[hidden] { display: none; }
.comptage-recentes__entete {
    display: flex; align-items: baseline; justify-content: space-between; gap: .75rem;
    margin-bottom: .65rem; font-size: .87rem;
}
.comptage-carrousel {
    display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center; gap: .65rem; min-width: 0;
}
.comptage-carrousel__fenetre {
    min-width: 0; overflow-x: auto; overflow-y: hidden; scroll-behavior: smooth;
    scroll-snap-type: x mandatory; scrollbar-width: none; overscroll-behavior-inline: contain;
}
.comptage-carrousel__fenetre::-webkit-scrollbar { display: none; }
.comptage-carrousel__bouton {
    width: 48px; height: 64px; padding: 0; display: grid; place-items: center;
    border: 1px solid var(--pp-bordure-forte); border-radius: 14px;
    color: var(--pp-brand-600); background: var(--pp-surface-2);
    font: inherit; font-size: 2.15rem; font-weight: 500; line-height: 1; cursor: pointer;
}
.comptage-carrousel__bouton:hover { background: var(--pp-brand-100); }
.comptage-carrousel__bouton:disabled { opacity: .3; cursor: default; }
.comptage-recentes__liste { display: flex; gap: .65rem; min-width: 0; }
.comptage-recente {
    position: relative; flex: 0 0 calc((100% - 1.3rem) / 3); min-width: 0;
    margin: 0; overflow: hidden; scroll-snap-align: start;
    border: 1px solid var(--pp-bordure); border-radius: 12px; background: #07101c;
}
.comptage-recente img { display: block; width: 100%; aspect-ratio: 16 / 9; object-fit: contain; }
.comptage-recente figcaption {
    position: absolute; left: .45rem; bottom: .4rem; display: flex; align-items: center; gap: .35rem;
    padding: .25rem .45rem; border-radius: 999px; color: #fff; background: rgb(3 12 23 / .78);
    font-size: .72rem; backdrop-filter: blur(5px);
}
.comptage-recente figcaption strong {
    min-width: 1.35rem; height: 1.35rem; padding: 0 .28rem; display: grid; place-items: center;
    border-radius: 999px; background: var(--pp-brand-500); font-size: .72rem;
}
.comptage-poste__actions {
    position: sticky; bottom: .5rem; z-index: 4; padding: .75rem;
    display: flex; gap: .65rem; justify-content: flex-end; flex-wrap: wrap;
    border: 1px solid var(--pp-bordure); border-radius: var(--pp-r-lg);
    background: color-mix(in srgb, var(--pp-surface) 94%, transparent);
    box-shadow: 0 12px 35px rgba(11,26,43,.14); backdrop-filter: blur(10px);
}
.comptage-poste__actions .btn:first-child { margin-right: auto; }
.comptage-annulation__photos {
    display: flex; align-items: flex-start; gap: .55rem; margin: 0; padding: .75rem;
    border-radius: var(--pp-r-sm); color: var(--pp-danger); background: var(--pp-danger-bg);
    font-size: .9rem; font-weight: 650; line-height: 1.4;
}
.comptage-annulation__photos .icone { flex: none; margin-top: .05rem; }
.texte-succes { color: #087f4b; }
.texte-danger { color: #be123c; }

/* La grille est posée à même la carte : elle porte donc elle-même le rembourrage
   du corps de carte, sinon la première photo touche le bord. */
.comptage-photos {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: 1rem; padding: 1.1rem 1.35rem 1.25rem;
}
.comptage-photo { margin: 0; overflow: hidden; border: 1px solid var(--pp-bordure); border-radius: 14px; background: var(--pp-surface-2); }
.comptage-photo a { display: block; position: relative; aspect-ratio: 16 / 9; background: #07101c; }
.comptage-photo img { width: 100%; height: 100%; object-fit: contain; }
.comptage-photo figcaption { padding: .8rem; display: grid; gap: .18rem; }
.comptage-photo a { cursor: zoom-in; }
.comptage-photo__lien.est-en-chargement::before,
.comptage-visionneuse__cadre.est-en-chargement::before {
    content: ''; position: absolute; z-index: 4; width: 42px; height: 42px; top: 50%; left: 50%;
    margin: -21px 0 0 -21px; border: 4px solid rgba(255,255,255,.28); border-top-color: #fff;
    border-radius: 50%; animation: rotation-photo .8s linear infinite;
}
.comptage-photo__lien.est-en-chargement::after {
    content: ''; position: absolute; inset: 0; z-index: 3;
    background: linear-gradient(105deg, #07101c 25%, #14243a 42%, #07101c 60%);
    background-size: 220% 100%; animation: chargement-photo 1.35s ease-in-out infinite;
}
.comptage-photo__lien.est-en-erreur::after,
.comptage-visionneuse__cadre.est-en-erreur::after {
    content: 'Photo indisponible'; position: absolute; z-index: 4; inset: 0;
    display: grid; place-items: center; color: #fff; background: #07101c;
}
@keyframes chargement-photo { to { background-position: -220% 0; } }
@keyframes rotation-photo { to { transform: rotate(360deg); } }
.dialogue.comptage-visionneuse {
    /* La photo est 16:9 : la largeur est limitée par la hauteur disponible afin
       d'occuper presque tout l'écran paysage sans produire de grandes bandes noires. */
    width: min(94vw, 100rem, calc(160dvh - 15rem));
    max-width: min(94vw, 100rem, calc(160dvh - 15rem));
    height: auto; max-height: 92dvh;
    overflow: hidden; border-radius: 20px;
}
.comptage-visionneuse__corps {
    height: auto; min-height: 0; padding: 1rem; display: grid;
    grid-template-rows: auto auto auto; gap: .75rem;
}
.comptage-visionneuse__entete {
    display: flex; align-items: center; justify-content: space-between; gap: 1rem; min-width: 0;
}
.comptage-visionneuse__entete h2 {
    min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.comptage-visionneuse__outils { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.comptage-visionneuse__outils [data-comptage-zoom-valeur] { min-width: 4.5rem; text-align: center; }
.comptage-visionneuse__cadre {
    position: relative; width: 100%; min-width: 0; min-height: 0; aspect-ratio: 16 / 9; overflow: hidden;
    border: 1px solid #26374d; border-radius: 14px; background: #02060b;
    cursor: grab; touch-action: none; user-select: none;
}
.comptage-visionneuse__cadre.est-deplacee { cursor: grabbing; }
.comptage-visionneuse__cadre img {
    position: absolute; top: 50%; left: 50%; display: block;
    max-width: 100%; max-height: 100%; object-fit: contain; pointer-events: none;
    transform: translate(-50%, -50%) translate3d(var(--photo-x, 0px), var(--photo-y, 0px), 0)
               scale(var(--photo-zoom, 1));
    transform-origin: center; will-change: transform;
}
.comptage-visionneuse__navigation {
    position: absolute; z-index: 6; top: 50%; transform: translateY(-50%);
    width: 58px; height: 72px; border: 1px solid rgba(255,255,255,.28); border-radius: 16px;
    background: rgba(5,14,26,.78); color: #fff; font-size: 2.8rem; line-height: 1;
    cursor: pointer; box-shadow: 0 8px 25px rgba(0,0,0,.28);
}
.comptage-visionneuse__navigation:hover:not(:disabled) { background: var(--pp-primaire); }
.comptage-visionneuse__navigation:disabled { opacity: .22; cursor: default; }
.comptage-visionneuse__navigation--precedente { left: 1rem; }
.comptage-visionneuse__navigation--suivante { right: 1rem; }
.comptage-visionneuse__position {
    position: absolute; z-index: 6; left: 50%; bottom: .8rem; transform: translateX(-50%);
    min-width: 5rem; padding: .35rem .7rem; border-radius: 999px; text-align: center;
    color: #fff; background: rgba(5,14,26,.8); pointer-events: none;
}
.comptage-visionneuse__aide { margin: 0; text-align: center; font-size: .88rem; }
@media (orientation: portrait) {
    .dialogue.comptage-visionneuse {
        width: 96vw; max-width: 96vw; height: auto; max-height: 72dvh;
    }
}
@media (max-width: 720px) {
    .dialogue.comptage-visionneuse { width: 96vw; max-width: 96vw; height: auto; max-height: 90dvh; }
    .comptage-visionneuse__corps { padding: .65rem; }
    .comptage-visionneuse__entete { align-items: flex-start; flex-direction: column; }
    .comptage-visionneuse__outils { width: 100%; }
    .comptage-visionneuse__outils .btn { min-height: 48px; }
}
.modele-comptage { max-width: 900px; }
.configuration-site__horaires { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 210px), 1fr)); gap: 1rem; margin: 1rem 0; }
.configuration-site__horaires .champ { margin-top: 0; }
.modele-comptage__hash { overflow-wrap: anywhere; }
.modele-comptage__form { display: grid; gap: 1rem; margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid var(--pp-bordure); }
.modele-comptage .carte__corps .modele-comptage__form { margin-top: 0; }
.modele-comptage__form .btn { justify-self: start; white-space: normal; }
/* --- Cadrage de la zone de détection d'un I-Track ---
   Le cadre occupait toute la largeur de la carte : un rectangle noir de plus de mille
   pixels sur six cents, vide tant qu'on n'a pas ouvert la caméra, et les trois boutons
   alignés dessous sur une ligne entière. Le cadre garde une taille où l'on trace
   encore juste, et ses commandes passent à côté plutôt qu'en dessous. */
.itrack-zone {
    display: grid;
    grid-template-columns: minmax(0, 38rem) minmax(0, 1fr);
    grid-template-areas: "cadre actions" "cadre etat";
    align-content: start;
    gap: .8rem 1.3rem;
    width: 100%;
}
.itrack-zone__cadre { grid-area: cadre; position: relative; width: 100%; aspect-ratio: 16 / 9; overflow: hidden; border-radius: 14px; background: #02060b; }

/* Les commandes du cadrage ne closent pas un formulaire : ni trait de séparation ni
   renfoncement. Empilées, elles tiennent dans la colonne restée libre à côté du cadre. */
.itrack-zone .barre-actions {
    grid-area: actions;
    flex-direction: column;
    align-items: stretch;
    justify-content: flex-start;
    margin-top: 0;
    padding-top: 0;
    border-top: 0;
    gap: .45rem;
}

/* Un bouton étiré sur toute la colonne se lit comme une bannière : on les borne. */
.itrack-zone .barre-actions .btn { max-width: 20rem; }
.itrack-zone [data-itrack-zone-etat] { grid-area: etat; margin: 0; align-self: start; }

/* Trop étroit pour deux colonnes : le cadre reprend la largeur et les boutons se
   remettent en ligne sous lui. */
@media (max-width: 720px) {
    .itrack-zone { grid-template-columns: minmax(0, 1fr); grid-template-areas: "cadre" "actions" "etat"; }
    .itrack-zone .barre-actions { flex-direction: row; }
}
.itrack-zone__cadre video, .itrack-zone__cadre canvas { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
.itrack-zone__cadre canvas { z-index: 2; pointer-events: none; touch-action: none; }
.itrack-zone__cadre .itrack-zone__canvas--interactive { pointer-events: auto; }
.itrack-zone__cadre .itrack-zone__canvas--creation { cursor: crosshair !important; }
.stat__valeur--texte { font-size: 1.05rem; }

@media (orientation: portrait) and (min-height: 1200px) {
    .comptage-poste { width: 100%; gap: .7rem; }
    .comptage-poste__entete { min-height: 150px; padding: 1.15rem 1.3rem; }
    .comptage-camera__cadre { aspect-ratio: 16 / 9; }
    .comptage-camera__outils { min-height: 68px; }
    .comptage-poste__actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .comptage-poste__actions .btn { width: 100%; min-height: 64px; margin: 0; }
    .comptage-poste__actions .btn:first-child { grid-column: 1 / -1; }
}

@media (max-width: 720px) {
    .comptage-accueil { padding: 1.5rem 1rem; }
    .comptage-modes { grid-template-columns: 1fr; }
    .comptage-selecteur { padding: 1rem; }
    .comptage-rx-apercu dl { grid-template-columns: 1fr; }
    .comptage-poste__entete { grid-template-columns: 1fr; }
    .comptage-poste__total { min-height: 110px; }
    .comptage-camera__outils .attenue { margin-left: 0; width: 100%; }
    .comptage-recentes__entete { display: grid; gap: .15rem; }
    .comptage-recentes__liste { gap: .45rem; }
    .comptage-recente { flex-basis: calc((100% - .45rem) / 2); }
    .comptage-carrousel { gap: .35rem; }
    .comptage-carrousel__bouton { width: 40px; height: 58px; }
    .comptage-recente figcaption { position: static; border-radius: 0; }
    .comptage-poste__actions { position: static; display: grid; grid-template-columns: 1fr; }
    .comptage-photos { grid-template-columns: 1fr; }
}

/* Le poste iTrack est utilisé sur un écran de 11 pouces en portrait. Les valeurs
   essentielles doivent rester lisibles à distance et les cibles tactiles généreuses. */
@media (orientation: portrait) {
    .comptage-poste {
        width: 100%; max-width: 100%; min-width: 0; gap: 1rem;
    }
    .comptage-poste > *,
    .comptage-poste__haut > *,
    .comptage-poste__bas > * { min-width: 0; max-width: 100%; }
    .comptage-poste__entete {
        min-height: 190px; gap: 1.25rem; padding: 1.35rem 1.5rem;
    }
    .comptage-poste__identite { gap: .5rem; }
    .comptage-poste__identite .etiquette {
        padding: .4rem .7rem; font-size: 1rem;
    }
    .comptage-poste__rx { gap: .75rem; padding: .55rem .9rem; }
    .comptage-poste__rx small { font-size: 1rem; }
    .comptage-poste__rx strong { font-size: clamp(1.5rem, 3.5vw, 2rem); }
    /* Ces quatre lignes ne fixent QUE leur taille : l'interligne vient du plancher
       commun (voir .comptage-poste__identite h1, … plus haut), qui garantit que rien
       n'est rogné en hauteur. */
    .comptage-poste__identite h1 {
        margin: .15rem 0; font-size: clamp(2rem, 5.5vw, 3.2rem);
    }
    .comptage-poste__produit { font-size: clamp(1.35rem, 3.4vw, 2rem); }
    .comptage-poste__identifiants,
    .comptage-poste__caracteristiques { font-size: clamp(1.12rem, 2.65vw, 1.5rem); }
    .comptage-poste__posologie { font-size: clamp(1.18rem, 2.7vw, 1.55rem); }
    .comptage-poste__machine { font-size: clamp(1rem, 2.2vw, 1.25rem); }
    .comptage-poste__total {
        min-width: 250px; min-height: 175px; padding: 1rem 1.5rem; border-radius: 22px;
    }
    .comptage-poste__total > span { font-size: 1.1rem; }
    .comptage-poste__total strong {
        font-size: clamp(4.8rem, 13vw, 7.5rem); line-height: .95;
    }
    .comptage-poste__total small { font-size: 1.2rem; }
    .comptage-poste__etat,
    .comptage-poste__message {
        min-height: 38px; padding: .5rem .8rem; font-size: 1.08rem;
    }
    .comptage-camera__outils { min-height: 70px; padding: .7rem .9rem; font-size: 1.05rem; }
    .comptage-recentes__entete { font-size: 1.05rem; }
    .comptage-recente figcaption,
    .comptage-recente figcaption strong { font-size: .95rem; }
    .comptage-poste__actions {
        bottom: .65rem; gap: .85rem; padding: .9rem;
    }
    .comptage-poste__actions .btn {
        min-width: 0; min-height: 78px; padding: 1rem 1.35rem;
        font-size: 1.25rem; font-weight: 750; white-space: normal; text-align: center;
    }
    .comptage-poste__actions .btn .icone { width: 1.6rem; height: 1.6rem; }
    .comptage-poste__annuler {
        position: static; grid-column: 1 / -1; width: 100% !important;
        min-height: 82px !important; margin: 0 !important; transform: none;
        font-size: 1.3rem !important; box-shadow: 0 8px 24px rgba(190,18,60,.2);
    }
    .dialogue .barre-actions .btn {
        min-height: 68px; padding: .9rem 1.15rem; font-size: 1.12rem;
    }
}

@media (orientation: portrait) and (max-width: 720px) {
    .comptage-poste__entete { grid-template-columns: 1fr; }
    .comptage-poste__total { min-height: 160px; }
    .comptage-poste__actions { position: sticky; bottom: .65rem; }
}

/* Le même poste sur un écran couché (1920 × 1080). En pile verticale, la caméra seule
   mange les deux tiers de la hauteur et les commandes tombent sous le bord : il
   faudrait faire défiler la page une fiole à la main. On tourne donc la mise en page —
   caméra à gauche sur toute la hauteur, identité, compte, images et commandes à
   droite — et l'écran entier tient dans la hauteur visible, sans ascenseur.

   La hauteur vient de la fenêtre et non d'un calcul : l'application devient une
   colonne souple, la zone de contenu partage ce qui reste, et un bandeau d'alerte
   éventuel prend sa part sans rien pousser dehors. */
@media (orientation: landscape) and (min-width: 1100px) and (min-height: 640px) {
    body.page-comptage-poste .app { height: 100dvh; }
    body.page-comptage-poste .app__principal { min-height: 0; }
    body.page-comptage-poste .app__contenu {
        display: flex; flex-direction: column; min-height: 0; padding: 1rem 1.25rem;
    }
    body.page-comptage-poste .app__contenu > * + * { margin-top: .75rem; }
    body.page-comptage-poste .app__contenu > .comptage-poste { flex: 1 1 auto; min-height: 0; }

    .comptage-poste {
        width: 100%; max-width: none; gap: .8rem;
        grid-template-columns: minmax(0, 1fr) clamp(20rem, 26%, 28rem);
        grid-template-rows: auto minmax(0, 1fr);
    }
    .comptage-poste__haut,
    .comptage-poste__bas {
        grid-column: 2; display: flex; flex-direction: column; min-width: 0;
    }
    .comptage-poste__haut { grid-row: 1; gap: .5rem; }
    .comptage-poste__bas { grid-row: 2; gap: .6rem; min-height: 0; justify-content: flex-end; }

    /* La caméra tient la colonne de gauche entière et se mesure à CE cadre : la plus
       petite des deux contraintes — largeur offerte, hauteur offerte — décide, le
       16/9 n'est jamais déformé et l'image ne déborde jamais par le bas. */
    .comptage-camera {
        grid-column: 1; grid-row: 1 / span 2;
        container-type: size; display: grid; place-items: center;
    }
    .comptage-camera__cadre { width: min(100cqw, calc(100cqh * 16 / 9)); }

    .comptage-poste__entete { grid-template-columns: 1fr; gap: .7rem; padding: .85rem 1rem; }
    .comptage-poste__identite h1 { font-size: clamp(1.15rem, 1.5vw, 1.5rem); }
    .comptage-poste__total { min-width: 0; padding: .55rem 1rem; border-radius: 14px; }
    .comptage-poste__total strong { font-size: clamp(2.4rem, 4.5vw, 3.6rem); }
    .comptage-poste__total small { margin-top: .15rem; }

    /* Le carrousel est la seule pièce élastique : c'est lui qui rend de la hauteur
       quand l'écran est juste, jamais le compte ni les boutons. */
    .comptage-recentes { flex: 0 1 auto; min-height: 0; overflow: hidden; padding: .7rem; }
    .comptage-recentes__entete { margin-bottom: .45rem; }
    .comptage-recente { flex-basis: calc((100% - .65rem) / 2); }
    .comptage-carrousel__bouton { width: 40px; height: 56px; font-size: 1.85rem; }

    .comptage-poste__actions {
        position: static; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: .6rem; padding: .7rem;
    }
    .comptage-poste__actions .btn { width: 100%; min-height: 58px; margin: 0; }
    .comptage-poste__actions .btn:first-child { grid-column: 1 / -1; margin-right: 0; }
    /* « Devons » absent : « Terminer » reste seul au milieu et prend la ligne entière,
       plutôt que la moitié gauche avec un vide à droite. */
    .comptage-poste__actions .btn:nth-child(2):nth-last-child(2) { grid-column: 1 / -1; }
    .comptage-poste__annuler { grid-column: 1 / -1; }
}

/* Couché mais peu haut — un portable en 1366 × 768. Tout tient encore, mais de justesse :
   on resserre ce qui ne coûte rien (rembourrages, taille des boutons) pour que le
   carrousel garde une vignette ENTIÈRE plutôt qu'un bandeau coupé. Aucune information
   n'est retirée, seulement de l'air. */
@media (orientation: landscape) and (min-width: 1100px) and (min-height: 640px) and (max-height: 900px) {
    .comptage-poste { gap: .6rem; }
    .comptage-poste__entete { gap: .5rem; padding: .7rem .9rem; }
    .comptage-poste__identite { gap: .15rem; }
    .comptage-poste__total { padding: .45rem .8rem; }
    .comptage-poste__total > span { font-size: .72rem; }
    .comptage-poste__total strong { font-size: 2.4rem; }
    .comptage-poste__etat,
    .comptage-poste__message { min-height: 0; padding: .15rem .5rem; }
    .comptage-recentes { padding: .55rem; }
    .comptage-recentes__entete { margin-bottom: .35rem; font-size: .8rem; }
    .comptage-carrousel { gap: .4rem; }
    .comptage-carrousel__bouton { width: 34px; height: 48px; font-size: 1.6rem; }
    .comptage-poste__actions { gap: .5rem; padding: .55rem; }
    .comptage-poste__actions .btn { min-height: 48px; }
}

/* Aucun élément du poste ne doit agrandir la page au-delà de la largeur visible.
   Les longues descriptions et les libellés de boutons reviennent à la ligne. */
html.page-comptage-poste,
body.page-comptage-poste { max-width: 100%; overflow-x: hidden; }
body.page-comptage-poste .app,
body.page-comptage-poste .app__principal,
body.page-comptage-poste .app__contenu {
    min-width: 0; max-width: 100%; overflow-x: clip;
}
body.page-comptage-poste .app__entete { min-width: 0; max-width: 100%; overflow: hidden; }

/* Passé la moitié du curseur, la signalétique et les ombres basculent d'un coup :
   un rouge d'alerte doit rester lisible, il ne se dégrade pas en douceur. */
:root[data-theme="dark"] {
    --pp-nuit: 1;

    --pp-danger-bg: #331723;
    --pp-warn-bg:   #33270F;
    --pp-info-bg:   #0D2937;
    --pp-ok-bg:     #0B2E24;
    --pp-danger:    #FB7185;
    --pp-warn:      #FBBF24;
    --pp-info:      #38BDF8;
    --pp-ok:        #34D399;

    --pp-ombre-1: 0 1px 2px rgb(0 0 0 / .42);
    --pp-ombre-2: 0 4px 14px rgb(0 0 0 / .38);
    --pp-ombre-3: 0 20px 50px rgb(0 0 0 / .46);
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --pp-nuit: 1;

        --pp-danger-bg: #331723;
        --pp-warn-bg:   #33270F;
        --pp-info-bg:   #0D2937;
        --pp-ok-bg:     #0B2E24;
        --pp-danger:    #FB7185;
        --pp-warn:      #FBBF24;
        --pp-info:      #38BDF8;
        --pp-ok:        #34D399;

        --pp-ombre-1: 0 1px 2px rgb(0 0 0 / .42);
        --pp-ombre-2: 0 4px 14px rgb(0 0 0 / .38);
        --pp-ombre-3: 0 20px 50px rgb(0 0 0 / .46);
    }
}

/* -----------------------------------------------------------------------------
   2. Base
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html {
    width: 100%; max-width: 100%; overflow-x: hidden; overflow-x: clip;
    -webkit-text-size-adjust: 100%;
}

body {
    margin: 0;
    width: 100%; max-width: 100%; overflow-x: hidden; overflow-x: clip;
    min-height: 100dvh;
    background: var(--pp-bg);
    color: var(--pp-texte);
    font-family: "Segoe UI Variable Text", "Segoe UI", system-ui, -apple-system,
                 "Inter", "Helvetica Neue", Arial, sans-serif;
    font-size: 15px;
    line-height: 1.55;
    letter-spacing: -0.005em;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

h1, h2, h3, h4 {
    margin: 0;
    font-weight: 650;
    letter-spacing: -0.022em;
    line-height: 1.2;
    color: var(--pp-texte);
}

h1 { font-size: 1.75rem; }
h2 { font-size: 1.25rem; }
h3 { font-size: 1.0625rem; }

p { margin: 0; }

a {
    color: var(--pp-brand-600);
    text-decoration: none;
    transition: color var(--pp-transition);
}
a:hover { color: var(--pp-brand-500); text-decoration: underline; }

:where(button, a, input, select, textarea):focus-visible {
    outline: 2px solid var(--pp-focus);
    outline-offset: 2px;
    border-radius: var(--pp-r-xs);
}

.chiffre { font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; }

.visuellement-cache {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* « hidden » veut dire CACHÉ, et rien ne doit pouvoir le contredire.

   Le navigateur ne lui donne qu'un « display: none » de feuille par défaut :
   la moindre règle qui pose un display — « .machin { display: flex } » — le
   rend visible de nouveau, sans que rien ne le signale. Le piège s'est déjà
   refermé deux fois dans ce fichier, et une troisième sur la boîte du
   générique de remplacement, où le champ de lecture s'affichait avant qu'on
   ait choisi de remplacer quoi que ce soit. */
[hidden] { display: none !important; }

/* -----------------------------------------------------------------------------
   3. Boutons
   -------------------------------------------------------------------------- */
.btn {
    --btn-bg: var(--pp-surface);
    --btn-fg: var(--pp-texte);
    --btn-bd: var(--pp-bordure-forte);

    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .5rem;
    padding: .625rem 1.05rem;
    min-height: 42px;
    border: 1px solid var(--btn-bd);
    border-radius: var(--pp-r-sm);
    background: var(--btn-bg);
    color: var(--btn-fg);
    font: inherit;
    font-weight: 600;
    font-size: .9375rem;
    letter-spacing: -0.01em;
    cursor: pointer;
    white-space: nowrap;
    transition: background var(--pp-transition), border-color var(--pp-transition),
                transform var(--pp-transition), box-shadow var(--pp-transition), opacity var(--pp-transition);
}

.btn:hover { background: var(--pp-surface-2); }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: .55; cursor: not-allowed; transform: none; }

.btn--primaire {
    --btn-bd: transparent;
    --btn-fg: #fff;
    background: linear-gradient(135deg, var(--pp-brand-500), var(--pp-brand-700));
    box-shadow: var(--pp-ombre-brand);
}
.btn--primaire:hover {
    background: linear-gradient(135deg, var(--pp-brand-400), var(--pp-brand-600));
}

.btn--bloc { width: 100%; }
.btn--grand { min-height: 50px; font-size: 1rem; padding-inline: 1.4rem; }

.btn--fantome {
    --btn-bg: transparent;
    --btn-bd: transparent;
    --btn-fg: var(--pp-texte-2);
}
.btn--fantome:hover { background: var(--pp-surface-3); color: var(--pp-texte); }

.btn--icone {
    padding: 0;
    width: 40px;
    min-height: 40px;
    border-radius: var(--pp-r-sm);
}

.btn .spinner { display: none; }
.btn.est-occupe .spinner { display: inline-block; }
.btn.est-occupe .btn__libelle { opacity: .75; }

.spinner {
    width: 16px; height: 16px;
    border: 2px solid rgb(255 255 255 / .35);
    border-top-color: #fff;
    border-radius: var(--pp-r-full);
    animation: pp-tourne .7s linear infinite;
}

@keyframes pp-tourne { to { transform: rotate(360deg); } }

/* -----------------------------------------------------------------------------
   4. Champs de formulaire
   -------------------------------------------------------------------------- */
.champ { display: flex; flex-direction: column; gap: .4rem; }
.champ + .champ { margin-top: 1.05rem; }

.champ__etiquette {
    font-size: .8125rem;
    font-weight: 600;
    letter-spacing: .01em;
    color: var(--pp-texte-2);
}

.champ__boite {
    position: relative;
    display: flex;
    align-items: center;
    background: var(--pp-surface);
    border: 1px solid var(--pp-bordure-forte);
    border-radius: var(--pp-r-sm);
    transition: border-color var(--pp-transition), box-shadow var(--pp-transition), background var(--pp-transition);
}

.champ__boite:focus-within {
    border-color: var(--pp-brand-500);
    box-shadow: 0 0 0 3.5px rgb(35 114 222 / .18);
}

.champ__boite:has(input[aria-invalid="true"]) { border-color: var(--pp-danger); }
.champ__boite:has(input[aria-invalid="true"]):focus-within {
    box-shadow: 0 0 0 3.5px rgb(225 29 72 / .16);
}

.champ__icone {
    display: grid;
    place-items: center;
    width: 42px;
    flex: 0 0 42px;
    color: var(--pp-texte-3);
    pointer-events: none;
}
.champ__boite:focus-within .champ__icone { color: var(--pp-brand-500); }

.champ__saisie {
    flex: 1 1 auto;
    min-width: 0;
    padding: .75rem .9rem .75rem 0;
    border: 0;
    background: transparent;
    color: var(--pp-texte);
    font: inherit;
    font-size: .95rem;
}
.champ__saisie::placeholder { color: var(--pp-texte-3); }
.champ__saisie:focus { outline: none; }

/* masque les flèches des champs numériques */
.champ__saisie[type="number"] { -moz-appearance: textfield; appearance: textfield; }
.champ__saisie[type="number"]::-webkit-outer-spin-button,
.champ__saisie[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* Zones de texte : la boîte s'étire, la poignée reste verticale. */
.champ__boite:has(textarea) { align-items: stretch; }

textarea.champ__saisie {
    padding: .75rem .9rem;
    min-height: 5.75rem;
    line-height: 1.5;
    resize: vertical;
}

/* Commentaire d'en-tête du pilulier : une note brève, pas un pavé. Au plus court par
   défaut — une seule ligne, padding resserré — et étirable à la main si besoin. */
textarea.champ__saisie--compact {
    min-height: 0;
    padding-block: .45rem;
}

.champ__action {
    display: grid;
    place-items: center;
    width: 42px;
    flex: 0 0 42px;
    border: 0;
    background: transparent;
    color: var(--pp-texte-3);
    cursor: pointer;
    border-radius: var(--pp-r-sm);
    transition: color var(--pp-transition);
}
.champ__action:hover { color: var(--pp-brand-600); }

.champ__erreur {
    font-size: .8125rem;
    color: var(--pp-danger);
    min-height: 1.2em;
}
.champ__erreur:empty { display: none; }

.champ__aide { font-size: .8125rem; color: var(--pp-texte-3); }
.champ__aide:empty { display: none; }
.champ__aide--ok { color: var(--pp-ok); }
.champ__aide--ecart { color: var(--pp-warn); }

.champ__aide--memoire {
    display: flex;
    align-items: center;
    gap: .45rem;
    flex-wrap: wrap;
}

/* Action secondaire présentée comme un lien, sans quitter le bouton sémantique. */
.lien-discret {
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    font-size: inherit;
    color: var(--pp-brand-600);
    text-decoration: underline;
    text-underline-offset: 2px;
    cursor: pointer;
}
.lien-discret:hover { color: var(--pp-brand-500); }
:root[data-theme="dark"] .lien-discret { color: var(--pp-brand-300); }

/* Case à cocher */
.case {
    display: inline-flex;
    align-items: center;
    gap: .55rem;
    cursor: pointer;
    font-size: .875rem;
    color: var(--pp-texte-2);
    user-select: none;
}

.case input {
    appearance: none;
    width: 18px; height: 18px;
    margin: 0;
    border: 1.5px solid var(--pp-bordure-forte);
    border-radius: var(--pp-r-xs);
    background: var(--pp-surface);
    display: grid;
    place-content: center;
    cursor: pointer;
    transition: background var(--pp-transition), border-color var(--pp-transition);
}

.case input::after {
    content: "";
    width: 10px; height: 10px;
    transform: scale(0);
    transition: transform var(--pp-transition);
    background: #fff;
    clip-path: polygon(14% 44%, 0 65%, 40% 100%, 100% 16%, 84% 0%, 38% 68%);
}

.case input:checked {
    background: var(--pp-brand-500);
    border-color: var(--pp-brand-500);
}
.case input:checked::after { transform: scale(1); }

/* -----------------------------------------------------------------------------
   5. Alertes, étiquettes, cartes
   -------------------------------------------------------------------------- */
.alerte {
    display: flex;
    align-items: flex-start;
    gap: .7rem;
    padding: .8rem .95rem;
    border-radius: var(--pp-r-sm);
    border: 1px solid transparent;
    font-size: .875rem;
    line-height: 1.45;
}

.alerte svg { flex: 0 0 auto; margin-top: 1px; }

.alerte--erreur  { background: var(--pp-danger-bg); border-color: color-mix(in srgb, var(--pp-danger) 32%, transparent); color: var(--pp-danger); }
.alerte--info    { background: var(--pp-info-bg);   border-color: color-mix(in srgb, var(--pp-info) 32%, transparent);   color: var(--pp-info); }
.alerte--succes  { background: var(--pp-ok-bg);     border-color: color-mix(in srgb, var(--pp-ok) 32%, transparent);     color: var(--pp-ok); }
.alerte--attention{background: var(--pp-warn-bg);   border-color: color-mix(in srgb, var(--pp-warn) 32%, transparent);   color: var(--pp-warn); }

/* Note du pilulier : une alerte resserrée, sur une seule ligne, qui ne mange plus
   le haut de la page. Le libellé et le texte se suivent au lieu de s'empiler. */
.alerte--note { align-items: center; gap: .5rem; padding: .5rem .8rem; font-size: .8125rem; }
.alerte--note strong { font-weight: 650; }
.alerte--note svg { width: 18px; height: 18px; }

.etiquette {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .2rem .55rem;
    border-radius: var(--pp-r-full);
    font-size: .75rem;
    font-weight: 650;
    letter-spacing: .02em;
    background: var(--pp-surface-3);
    color: var(--pp-texte-2);
    border: 1px solid var(--pp-bordure);
    white-space: nowrap;
}

.etiquette--marque {
    background: color-mix(in srgb, var(--pp-brand-500) 14%, transparent);
    border-color: color-mix(in srgb, var(--pp-brand-500) 30%, transparent);
    color: var(--pp-brand-600);
}
:root[data-theme="dark"] .etiquette--marque { color: var(--pp-brand-300); }

.carte {
    background: var(--pp-surface);
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-lg);
    box-shadow: var(--pp-ombre-1);
}

.carte__entete {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 1.1rem 1.35rem;
    border-bottom: 1px solid var(--pp-bordure);
}

/* Le rembourrage vertical est plus serré que l'horizontal : sur un poste de
   préparation, la hauteur d'écran est la ressource rare. */
.carte__corps { padding: .85rem 1.35rem 1rem; }

/* -----------------------------------------------------------------------------
   6. Marque : logo
   -------------------------------------------------------------------------- */
.marque { display: inline-flex; align-items: center; gap: .7rem; text-decoration: none; }
.marque:hover { text-decoration: none; }

.marque__logo { flex: 0 0 auto; display: block; }
.marque__texte { display: flex; flex-direction: column; line-height: 1.15; }

.marque__nom {
    font-weight: 700;
    font-size: 1.05rem;
    letter-spacing: -0.025em;
    color: var(--pp-texte);
}
.marque__nom span { color: var(--pp-brand-500); }

.marque__sous { font-size: .6875rem; letter-spacing: .09em; text-transform: uppercase; color: var(--pp-texte-3); }

/* =============================================================================
   7. Page de connexion
   ============================================================================= */
.auth {
    min-height: 100dvh;
    display: grid;
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
}

.auth__panneau {
    position: relative;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    padding: 3rem 3.5rem;
    color: #EBF4FF;
    background:
        radial-gradient(120% 90% at 12% 8%,  rgb(74 144 240 / .34) 0%, transparent 55%),
        radial-gradient(90% 80% at 92% 22%,  rgb(6 182 212 / .30)  0%, transparent 52%),
        radial-gradient(120% 110% at 70% 100%, rgb(20 92 188 / .42) 0%, transparent 58%),
        linear-gradient(155deg, #061A33 0%, #08213F 45%, #04142A 100%);
}

/* voile de grain fin */
.auth__panneau::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    opacity: .5;
    background-image:
        linear-gradient(to right, rgb(255 255 255 / .028) 1px, transparent 1px),
        linear-gradient(to bottom, rgb(255 255 255 / .028) 1px, transparent 1px);
    background-size: 34px 34px;
    mask-image: radial-gradient(120% 100% at 50% 30%, #000 30%, transparent 78%);
}

.auth__panneau > * { position: relative; z-index: 1; }

.auth__panneau .marque__nom { color: #fff; }
.auth__panneau .marque__nom span { color: var(--pp-brand-300); }
.auth__panneau .marque__sous { color: rgb(213 231 255 / .62); }

.auth__accroche { max-width: 30ch; }

.auth__accroche h1 {
    color: #fff;
    font-size: clamp(1.85rem, 2.6vw, 2.6rem);
    letter-spacing: -0.035em;
    line-height: 1.1;
}

.auth__accroche h1 em {
    font-style: normal;
    background: linear-gradient(100deg, var(--pp-brand-300), var(--pp-cyan-400));
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

.auth__accroche p {
    margin-top: .9rem;
    color: rgb(213 231 255 / .74);
    font-size: 1.0125rem;
    max-width: 40ch;
}

.auth__atouts {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    margin-top: 1.6rem;
}

.auth__atout {
    display: inline-flex;
    align-items: center;
    gap: .45rem;
    padding: .35rem .75rem;
    border-radius: var(--pp-r-full);
    font-size: .8125rem;
    font-weight: 550;
    color: rgb(235 244 255 / .9);
    background: rgb(255 255 255 / .07);
    border: 1px solid rgb(255 255 255 / .12);
    backdrop-filter: blur(6px);
}

.auth__pied {
    font-size: .75rem;
    color: rgb(213 231 255 / .45);
    display: flex;
    gap: 1rem;
    flex-wrap: wrap;
}

/* --- Le pilulier décoratif (7 jours × 4 moments) --- */
.pilulier {
    margin: 2.25rem 0;
    display: inline-grid;
    grid-template-columns: auto 1fr;
    gap: .55rem .75rem;
    align-items: center;
    padding: 1.15rem 1.3rem;
    border-radius: var(--pp-r-lg);
    background: rgb(255 255 255 / .045);
    border: 1px solid rgb(255 255 255 / .1);
    backdrop-filter: blur(8px);
    box-shadow: 0 20px 45px rgb(0 0 0 / .3);
}

.pilulier__coin { }

.pilulier__jours {
    display: grid;
    grid-template-columns: repeat(7, 30px);
    gap: .4rem;
    font-size: .6875rem;
    font-weight: 700;
    letter-spacing: .06em;
    color: rgb(213 231 255 / .5);
    text-align: center;
}

.pilulier__moments {
    display: grid;
    grid-template-rows: repeat(4, 30px);
    gap: .4rem;
    align-items: center;
    font-size: .625rem;
    letter-spacing: .05em;
    text-transform: uppercase;
    color: rgb(213 231 255 / .45);
    text-align: right;
}

.pilulier__grille {
    display: grid;
    grid-template-columns: repeat(7, 30px);
    grid-auto-rows: 30px;
    gap: .4rem;
}

.pilulier__case {
    border-radius: 8px;
    background: rgb(255 255 255 / .05);
    border: 1px solid rgb(255 255 255 / .09);
    display: grid;
    place-items: center;
    animation: pp-apparait .5s both;
}

.pilulier__case--plein {
    background: linear-gradient(150deg, rgb(74 144 240 / .3), rgb(6 182 212 / .2));
    border-color: rgb(127 178 252 / .45);
}

.capsule {
    width: 15px;
    height: 8px;
    border-radius: var(--pp-r-full);
    background: linear-gradient(90deg, var(--pp-brand-300) 0 50%, #EAF3FF 50% 100%);
    box-shadow: 0 0 10px rgb(127 178 252 / .55);
    transform: rotate(-32deg);
    animation: pp-respire 3.4s ease-in-out infinite;
}

.capsule--cyan { background: linear-gradient(90deg, var(--pp-cyan-400) 0 50%, #EAFDFF 50% 100%); }

@keyframes pp-apparait {
    from { opacity: 0; transform: translateY(6px) scale(.9); }
    to   { opacity: 1; transform: none; }
}

@keyframes pp-respire {
    0%, 100% { opacity: .85; transform: rotate(-32deg) scale(1); }
    50%      { opacity: 1;   transform: rotate(-32deg) scale(1.1); }
}

.capsule--cyan { box-shadow: 0 0 10px rgb(34 211 238 / .5); }

/* --- Colonne du formulaire --- */
.auth__formulaire {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 2.5rem 1.75rem;
    background:
        radial-gradient(80% 60% at 100% 0%, color-mix(in srgb, var(--pp-brand-500) 8%, transparent) 0%, transparent 60%),
        var(--pp-bg);
}

.auth__boite { width: 100%; max-width: 27rem; }

/* Barre au-dessus du formulaire : logo (mobile) + langue et thème. */
.auth__barre {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-bottom: 1.6rem;
}

.auth__barre-outils {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin-left: auto;
}

.auth__marque-mobile { display: none; }

.auth__soutien {
    margin-top: 1.15rem;
    text-align: center;
    font-size: .8125rem;
    color: var(--pp-texte-3);
}

/* Sélecteur de langue */
/* =============================================================================
   Module Piluliers
   ============================================================================= */
.pilulier-disposition {
    display: grid;
    grid-template-columns: minmax(210px, 260px) 1fr;
    gap: 1.4rem;
    align-items: start;
}

@media (max-width: 900px) {
    .pilulier-disposition { grid-template-columns: 1fr; }
}

.pilulier-message {
    display: none;
    margin-bottom: 1rem;
    padding: .7rem 1rem;
    border-radius: var(--pp-r-sm);
    font-size: .875rem;
    background: var(--pp-ok-bg);
    color: var(--pp-ok);
    border: 1px solid color-mix(in srgb, var(--pp-ok) 32%, transparent);
}
.pilulier-message.est-visible { display: block; }
.pilulier-message.est-erreur {
    background: var(--pp-danger-bg);
    color: var(--pp-danger);
    border-color: color-mix(in srgb, var(--pp-danger) 32%, transparent);
}

/* --- Colonne des piluliers --- */
.pilulier-liste { display: grid; gap: .5rem; align-content: start; }

.pilulier-liste__entete {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    margin-bottom: .2rem;
}
.pilulier-liste__entete h2 { font-size: .95rem; }

.pilulier-onglet {
    display: grid;
    gap: .35rem;
    width: 100%;
    padding: .75rem .9rem;
    text-align: left;
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-md);
    background: var(--pp-surface);
    color: var(--pp-texte);
    font: inherit;
    cursor: pointer;
    transition: border-color var(--pp-transition), background var(--pp-transition);
}
.pilulier-onglet:hover { border-color: var(--pp-brand-500); }

.pilulier-onglet.est-actif {
    border-color: var(--pp-brand-500);
    background: color-mix(in srgb, var(--pp-brand-500) 8%, var(--pp-surface));
    box-shadow: inset 3px 0 0 var(--pp-brand-500);
}

.pilulier-onglet__nom { font-weight: 600; font-size: .9rem; }
.pilulier-onglet__meta { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; font-size: .75rem; }

.pilulier-detail { display: grid; gap: 1.4rem; align-content: start; }

.carte__outils { display: flex; gap: .4rem; flex-wrap: wrap; }

/* Mention d'état par défaut (import de catalogue, etc.) : un texte discret qui
   pousse les boutons de sa barre vers la droite. */
.pilulier-etat { margin-right: auto; font-size: .8125rem; color: var(--pp-texte-3); align-self: center; }
.pilulier-etat.est-valide { color: var(--pp-ok); font-weight: 600; }
.pilulier-etat.est-modifie { color: var(--pp-warn); font-weight: 600; }

/* Sous le nom du patient, la même mention devient une pastille compacte : un seul
   mot (Validé, Brouillon, Modifié), peu de place. Le détail « validé par … le … »
   se lit dans l'infobulle (title). */
.entete-pilulier__etat .pilulier-etat {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    margin-right: 0;
    padding: .12rem .55rem;
    border-radius: var(--pp-r-full);
    font-size: .75rem;
    font-weight: 650;
    letter-spacing: .02em;
    border: 1px solid transparent;
    white-space: nowrap;
}
.entete-pilulier__etat .pilulier-etat.est-valide {
    background: var(--pp-ok-bg);
    border-color: color-mix(in srgb, var(--pp-ok) 32%, transparent);
    color: var(--pp-ok);
}
.entete-pilulier__etat .pilulier-etat.est-valide::before { content: "\2713"; font-weight: 700; }
.entete-pilulier__etat .pilulier-etat.est-modifie {
    background: var(--pp-warn-bg);
    border-color: color-mix(in srgb, var(--pp-warn) 32%, transparent);
    color: var(--pp-warn);
}
.entete-pilulier__etat .pilulier-etat.est-brouillon {
    background: var(--pp-surface-3);
    border-color: var(--pp-bordure);
    color: var(--pp-texte-2);
}

/* --- Une molécule du pilulier --- */
.pilulier-ligne {
    padding: .5rem 0;
    border-bottom: 1px solid var(--pp-bordure);
}
.pilulier-ligne:last-of-type { border-bottom: 0; }

/* Molécule désactivée : présente au dossier, hors préparation. */
.pilulier-ligne.est-inactive .pilulier-ligne__tete { opacity: .45; }
.pilulier-ligne.est-inactive .pilulier-ligne__actions { opacity: 1; }

/* Numéro de journée d'une carte à prise unique : une pastille, lisible d'un coup d'œil. */
.pilulier-grille__no {
    display: inline-grid;
    place-items: center;
    min-width: 1.7rem;
    height: 1.7rem;
    margin-bottom: .35rem;
    padding: 0 .35rem;
    border-radius: 999px;
    background: var(--pp-brand-600);
    color: #fff;
    font-size: .8125rem;
    font-weight: 700;
    line-height: 1;
    box-shadow: var(--pp-ombre-brand);
}

/* Quatre colonnes tenues : photo, nom, quantités, actions. Le nom est BORNÉ — laissé
   libre de grandir, il avalait tout l'espace disponible et creusait un vide au centre
   de la rangée, entre un nom court et les quantités. C'est la colonne des quantités qui
   prend désormais le mou : la place va là où l'on saisit. Les colonnes étant fixes,
   elles s'alignent d'une rangée à l'autre. */
.pilulier-ligne__tete {
    display: grid;
    grid-template-columns: auto minmax(9rem, 12rem) minmax(0, 1fr) auto;
    align-items: center;
    gap: .35rem .7rem;
}

.pilulier-ligne__tete > .pilulier-vignette      { grid-column: 1; grid-row: 1; }
.pilulier-ligne__tete > .pilulier-ligne__identite { grid-column: 2; grid-row: 1; }
.pilulier-ligne__tete > .pilulier-prises        { grid-column: 3; grid-row: 1; }
.pilulier-ligne__tete > .pilulier-ligne__actions { grid-column: 4; grid-row: 1; }

/* Rangée resserrée : la vignette s'aligne sur la hauteur du nom plutôt que de
   commander la hauteur de la ligne. Réduite ici seulement, ailleurs elle garde sa
   taille. */
.pilulier-ligne__tete .pilulier-vignette { width: 44px; height: 44px; }

.pilulier-ligne__identite { display: flex; flex-direction: column; line-height: 1.3; min-width: 0; }

.pilulier-ligne__identite .attenue { font-size: .78125rem; }

.pilulier-ligne__actions { display: flex; gap: .3rem; flex-wrap: wrap; margin-left: auto; }
.pilulier-ligne__actions .est-actif { border-color: var(--pp-brand-500); color: var(--pp-brand-600); }

/* Boutons d'action à icône seule : carrés et compacts. */
.pilulier-ligne__actions .btn--carre {
    width: 32px;
    min-height: 32px;
    padding-inline: 0;
}
.pilulier-ligne__actions .btn--carre svg { width: 18px; height: 18px; }

/* Flèches de tri et croix de retrait : gardées en caractère, mais plus grosses. */
.pilulier-ligne__actions .btn--glyphe {
    width: 32px;
    min-height: 32px;
    padding-inline: 0;
    font-size: 1.2rem;
    font-weight: 600;
    line-height: 1;
}

.pilulier-vignette {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 54px; height: 54px;
    padding: 3px;
    overflow: hidden;
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-sm);
    background: var(--pp-surface-2);
    cursor: pointer;
}
/* « contain » et non « cover » : une vignette rognée cache la forme du comprimé,
   qui est justement ce qui permet de le reconnaître. */
.pilulier-vignette img { width: 100%; height: 100%; object-fit: contain; }
.pilulier-vignette__vide { color: var(--pp-texte-3); font-size: .875rem; }
.pilulier-vignette--petite { width: 38px; height: 38px; padding: 2px; }
.pilulier-vignette:disabled { cursor: default; }

/* --- Quantités par temps de prise --- */
.pilulier-prises { display: flex; gap: .4rem; flex-wrap: wrap; }

/* Les quantités occupent toute la colonne qui leur revient : les quatre cases se
   partagent la largeur à parts égales — c'est là que va l'espace libre de la rangée,
   au lieu de rester en trou au centre. */
.pilulier-ligne__tete .pilulier-prises { gap: .5rem; flex-wrap: nowrap; }
.pilulier-ligne__tete .pilulier-prise { flex: 1 1 0; min-width: 3.4rem; }
.pilulier-ligne__tete .pilulier-prise__saisie { width: 100%; }

/* Écran étroit : quatre colonnes ne tiennent plus de front — on retombe sur un
   enroulement, le commentaire prenant sa propre ligne. Placé APRÈS les règles de la
   grille : à spécificité égale, c'est le dernier écrit qui l'emporte. */
@media (max-width: 900px) {
    .pilulier-ligne__tete { display: flex; flex-wrap: wrap; gap: .55rem; }
    .pilulier-ligne__identite { flex: 1 1 11rem; }
    .pilulier-ligne__comm,
    .pilulier-ligne__comm-edit { flex: 1 1 100%; }
    .pilulier-ligne__tete .pilulier-prises { flex-wrap: wrap; }
    .pilulier-ligne__tete .pilulier-prise { flex: 0 0 auto; min-width: 0; }
    .pilulier-ligne__tete .pilulier-prise__saisie { width: 3.6rem; }
}

.pilulier-prise { display: grid; gap: .2rem; justify-items: center; }
.pilulier-prise__libelle {
    font-size: .6875rem;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--pp-texte-3);
}
.pilulier-prises__note { font-size: .78125rem; align-self: center; }

.pilulier-prise__saisie {
    width: 3.6rem;
    padding: .38rem .4rem;
    text-align: center;
    border: 1px solid var(--pp-bordure-forte);
    border-radius: var(--pp-r-sm);
    background: var(--pp-surface);
    color: var(--pp-texte);
    font: inherit;
    font-size: .875rem;
    -moz-appearance: textfield;
    appearance: textfield;
}
.pilulier-prise__saisie::-webkit-outer-spin-button,
.pilulier-prise__saisie::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.pilulier-prise__saisie:focus-visible {
    outline: none;
    border-color: var(--pp-brand-500);
    box-shadow: 0 0 0 3px rgb(35 114 222 / .18);
}

/* --- Panneau calendrier d'une molécule --- */
.pilulier-panneau {
    margin-top: .85rem;
    padding: .95rem 1rem 1rem;
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-md);
    background: var(--pp-surface-2);
}
.pilulier-panneau > * + * { margin-top: .9rem; }

.pilulier-cycle { width: 100%; border-collapse: collapse; font-size: .8125rem; }
.pilulier-cycle th {
    padding: .35rem .5rem;
    text-align: left;
    font-size: .6875rem;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--pp-texte-3);
}
.pilulier-cycle td { padding: .2rem .3rem; }

/* --- Sélecteur de produit --- */
.pilulier-ajout { margin-top: 1.1rem; padding-top: 1.1rem; border-top: 1px dashed var(--pp-bordure); }

.pilulier-suggestions { display: grid; gap: .3rem; margin-top: .5rem; }

.pilulier-suggestion {
    display: flex;
    align-items: center;
    gap: .7rem;
    width: 100%;
    padding: .5rem .7rem;
    text-align: left;
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-sm);
    background: var(--pp-surface);
    color: var(--pp-texte);
    font: inherit;
    cursor: pointer;
}
.pilulier-suggestion:hover,
.pilulier-suggestion:focus-visible {
    border-color: var(--pp-brand-500);
    background: var(--pp-surface-2);
    outline: none;
    box-shadow: 0 0 0 3px rgb(35 114 222 / .14);
}
.pilulier-suggestion__texte { display: flex; flex-direction: column; line-height: 1.3; font-size: .875rem; }
.pilulier-suggestion__texte .attenue { font-size: .75rem; }

/* --- Commentaire d'une molécule ---
   Un champ nu, avec pour seul indice un filigrane qui s'efface à la première frappe,
   se confond avec les autres saisies de la ligne. Celui-ci porte son nom et occupe un
   encadré qui lui appartient : un commentaire de molécule se lit à voix haute pendant
   la préparation, il doit se repérer sans être cherché. */
/* Commentaire collé au nom : seconde rangée de la grille, calée sur le bord gauche du
   nom et courant jusqu'au bout de la ligne. En lecture, une note discrète précédée de
   la bulle ; en saisie, un champ ouvert par la bulle d'action. Il ne rétrécit donc
   jamais la colonne du nom, et la rangée du haut reste entière pour les quantités. */
.pilulier-ligne__comm,
.pilulier-ligne__comm-edit { grid-column: 2 / -1; grid-row: 2; }

.pilulier-ligne__comm {
    display: flex;
    align-items: flex-start;
    gap: .3rem;
    font-size: .8125rem;
    line-height: 1.3;
    color: var(--pp-brand-600);
}
:root[data-theme="dark"] .pilulier-ligne__comm { color: var(--pp-brand-300); }
.pilulier-ligne__comm svg { flex: 0 0 auto; width: 14px; height: 14px; margin-top: .1rem; opacity: .85; }

.pilulier-ligne__comm-saisie {
    width: 100%;
    padding: .3rem .5rem;
    font-size: .8125rem;
    border: 1px solid var(--pp-bordure-forte);
    border-left: 3px solid var(--pp-brand-500);
    border-radius: var(--pp-r-sm);
    background: var(--pp-surface);
}
.pilulier-ligne__comm-saisie:focus { border-color: var(--pp-brand-500); }

/* La bulle d'action prend la couleur de la marque quand un commentaire existe déjà. */
.pilulier-ligne__actions .pilulier-com-plein { color: var(--pp-brand-600); }
:root[data-theme="dark"] .pilulier-ligne__actions .pilulier-com-plein { color: var(--pp-brand-300); }

/* De même, l'icône calendrier prend la couleur de la marque quand un calendrier est
   défini sur la ligne (période, durée ou cycle). Même repère que le commentaire. */
.pilulier-ligne__actions .pilulier-cal-plein { color: var(--pp-brand-600); }
:root[data-theme="dark"] .pilulier-ligne__actions .pilulier-cal-plein { color: var(--pp-brand-300); }

/* Hors pilulier : à ne pas confondre avec « désactivée ». La molécule fait toujours
   partie du traitement — elle se sert simplement à côté du carton. */
.etiquette--hors {
    background: color-mix(in srgb, var(--pp-warn) 16%, transparent);
    color: var(--pp-warn);
    border-color: color-mix(in srgb, var(--pp-warn) 35%, transparent);
}

/* Périmé : le document ne reflète plus la carte. À ne surtout pas imprimer — d'où le
   rouge, non l'orange d'un simple avertissement. */
.etiquette--alerte {
    background: color-mix(in srgb, var(--pp-danger) 14%, transparent);
    color: var(--pp-danger);
    border-color: color-mix(in srgb, var(--pp-danger) 35%, transparent);
}

/* Pastille à icône seule : signale l'état sans manger la largeur de la ligne. */
.etiquette--icone { padding: .15rem .3rem; gap: 0; }
.etiquette--icone svg { width: 14px; height: 14px; }

/* Dans la colonne du nom (flex vertical), un badge s'étirait sur toute la largeur —
   une barre orange pleine ligne. Il se réduit maintenant à son contenu. */
.pilulier-ligne__identite .etiquette { align-self: flex-start; }

/* Visé sur l'action, et non sur « .est-actif » : le bouton du calendrier porte la même
   classe et doit garder le bleu de la marque. */
.pilulier-ligne__actions [data-action="hors-pilulier"].est-actif {
    border-color: var(--pp-warn);
    color: var(--pp-warn);
}

/* --- Grille du pilulier : le jour 1 en bas --- */
.pilulier-grille { white-space: normal; }

.pilulier-grille__jour {
    position: static;
    white-space: nowrap;
    background: var(--pp-surface-2);
    border-right: 2px solid var(--pp-bordure-forte);
}

/* Séparateurs d'alvéoles : la carte se lit colonne par colonne. */
.pilulier-grille thead th + th,
.pilulier-grille tbody td + td { border-left: 1px solid var(--pp-bordure-forte); }

.pilulier-grille tbody td { border-bottom: 1px solid var(--pp-bordure); }
.pilulier-grille tbody tr:hover { background: transparent; }

.pilulier-grille__case {
    vertical-align: top;
    min-width: 8rem;
    padding: .55rem .7rem;
}

.pilulier-puce {
    display: flex;
    align-items: baseline;
    gap: .35rem;
    padding: .12rem 0;
    font-size: .78125rem;
}
.pilulier-puce__qte {
    flex: 0 0 auto;
    min-width: 1.6rem;
    padding: .05rem .3rem;
    text-align: center;
    border-radius: 999px;
    background: color-mix(in srgb, var(--pp-brand-500) 15%, transparent);
    color: var(--pp-brand-600);
    font-weight: 700;
}

/* La pastille du comprimé, à hauteur d'une ligne de texte : de quoi reconnaître le
   produit d'un coup d'œil dans l'alvéole, sans que la case s'alourdisse.

   La LARGEUR est fixe, la hauteur vaut une ligne de texte, et l'image tient à
   l'intérieur sans être déformée (object-fit: contain — le ratio d'origine est
   conservé). C'est cette largeur fixe qui fait le travail demandé : les noms des
   comprimés s'alignent tous à la même verticale d'une pastille à l'autre, alors même
   que les photos n'ont pas la même forme — sans qu'on ait à découper la case en
   colonnes. L'image est calée à DROITE de son cadre : elle touche donc le nom, et le
   jeu éventuel se loge du côté de la quantité, où il ne saute pas aux yeux.

   Alignée sur la première ligne — le nom — même quand la précision passe dessous. */
.pilulier-puce__photo {
    flex: 0 0 auto;
    align-self: flex-start;
    width: 2.25em;
    height: 1.25em;
    margin-top: .05em;
    object-fit: contain;
    object-position: right center;
    border-radius: 3px;
}

/* --- Photo agrandie --- */
.pilulier-apercu {
    position: fixed;
    inset: 0;
    z-index: 50;
    display: grid;
    place-items: center;
    padding: 1.5rem;
    background: rgb(7 15 26 / .62);
}

.pilulier-apercu__boite {
    display: grid;
    gap: 1rem;
    justify-items: stretch;
    width: min(52rem, 100%);
    /* Plus haut que l'écran, le panneau défile au lieu d'être rogné. */
    max-height: calc(100vh - 3rem);
    overflow: auto;
    padding: 1.4rem;
    border-radius: var(--pp-r-lg);
    background: var(--pp-surface);
    box-shadow: var(--pp-ombre-2, 0 20px 50px rgb(0 0 0 / .35));
}

.pilulier-apercu__titre {
    margin: 0;
    text-align: center;
    font-size: 1rem;
    font-weight: 700;
}

.pilulier-apercu__boite .btn { justify-self: center; }

/* Le comprimé se voit en entier : cadre plus généreux, image jamais rognée. */
.pilulier-apercu .photo-face__cadre { aspect-ratio: 1; padding: 1.1rem; }

/* Sélecteur de contexte de l'en-tête : liste compacte, alignée sur les autres outils. */
.selecteur-contexte { display: flex; align-items: center; gap: .4rem; }

.selecteur-contexte__choix {
    appearance: none;
    max-width: 15rem;
    padding: .42rem 2rem .42rem .7rem;
    border: 1px solid var(--pp-bordure-forte);
    border-radius: var(--pp-r-sm);
    background: var(--pp-surface);
    color: var(--pp-texte);
    font: inherit;
    font-size: .8125rem;
    font-weight: 600;
    cursor: pointer;
    text-overflow: ellipsis;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2365758C' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right .55rem center;
    transition: border-color var(--pp-transition), box-shadow var(--pp-transition);
}

.selecteur-contexte__choix:hover { border-color: var(--pp-brand-500); }

.selecteur-contexte__choix:focus-visible {
    outline: none;
    border-color: var(--pp-brand-500);
    box-shadow: 0 0 0 3.5px rgb(35 114 222 / .18);
}

.selecteur-contexte__choix option,
.selecteur-contexte__choix optgroup { background: var(--pp-surface); color: var(--pp-texte); }

@media (max-width: 720px) {
    .selecteur-contexte__choix { max-width: 9rem; }
}

.selecteur-langue {
    display: inline-flex;
    gap: 2px;
    padding: 2px;
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-full);
    background: var(--pp-surface-2);
}

.selecteur-langue__choix {
    padding: .28rem .62rem;
    border: 0;
    border-radius: var(--pp-r-full);
    background: transparent;
    color: var(--pp-texte-3);
    font: inherit;
    font-size: .71875rem;
    font-weight: 700;
    letter-spacing: .05em;
    cursor: pointer;
    transition: background var(--pp-transition), color var(--pp-transition);
}

.selecteur-langue__choix:hover { color: var(--pp-texte); }

.selecteur-langue__choix.est-actif {
    background: var(--pp-surface);
    color: var(--pp-texte);
    box-shadow: var(--pp-ombre-1);
    cursor: default;
}

.auth__titre { margin-bottom: 1.75rem; }
.auth__titre h2 { font-size: 1.5rem; letter-spacing: -0.03em; }
.auth__titre p { margin-top: .35rem; color: var(--pp-texte-2); font-size: .9375rem; }

.auth__carte {
    background: var(--pp-surface);
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-xl);
    box-shadow: var(--pp-ombre-3);
    padding: 1.85rem;
}

.auth__separateur {
    display: flex;
    align-items: center;
    gap: .8rem;
    margin: 1.35rem 0 1.15rem;
    color: var(--pp-texte-3);
    font-size: .6875rem;
    letter-spacing: .1em;
    text-transform: uppercase;
}
.auth__separateur::before,
.auth__separateur::after {
    content: "";
    height: 1px;
    flex: 1;
    background: var(--pp-bordure);
}

.auth__options {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin: 1.1rem 0 1.4rem;
    flex-wrap: wrap;
}

.auth__note {
    margin-top: 1.4rem;
    display: flex;
    gap: .6rem;
    align-items: flex-start;
    padding: .8rem .9rem;
    border-radius: var(--pp-r-sm);
    background: var(--pp-surface-2);
    border: 1px solid var(--pp-bordure);
    color: var(--pp-texte-3);
    font-size: .78125rem;
    line-height: 1.5;
}
.auth__note svg { flex: 0 0 auto; color: var(--pp-brand-500); margin-top: 1px; }

.auth__exigences {
    margin-top: 1.1rem;
    padding: .85rem .95rem;
    border-radius: var(--pp-r-sm);
    background: var(--pp-surface-2);
    border: 1px dashed var(--pp-bordure-forte);
}
.auth__exigences h3 {
    font-size: .6875rem;
    text-transform: uppercase;
    letter-spacing: .09em;
    color: var(--pp-texte-3);
    margin-bottom: .5rem;
}
.auth__exigences ul {
    margin: 0; padding: 0; list-style: none;
    display: grid; gap: .3rem;
    font-size: .8125rem; color: var(--pp-texte-2);
}
.auth__exigences li { display: flex; gap: .45rem; align-items: center; }
.auth__exigences li::before {
    content: "";
    width: 5px; height: 5px; border-radius: 50%;
    background: var(--pp-brand-400);
    flex: 0 0 auto;
}

@media (max-width: 960px) {
    .auth { grid-template-columns: 1fr; }
    .auth__panneau { display: none; }
    .auth__marque-mobile { display: block; }
    .auth__formulaire { align-items: flex-start; padding-top: 3rem; }
}

/* =============================================================================
   8. Coquille applicative (barre latérale + entête)
   ============================================================================= */
.app {
    display: grid;
    grid-template-columns: 264px minmax(0, 1fr);
    width: 100%; max-width: 100%; min-width: 0;
    min-height: 100dvh;
    overflow-x: hidden; overflow-x: clip;
}

.app__lateral {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
    padding: 1.35rem 1rem;
    background: var(--pp-surface);
    border-right: 1px solid var(--pp-bordure);
    position: sticky;
    top: 0;
    height: 100dvh;
    overflow-y: hidden;
}

.app__marque { padding: .35rem .5rem 0; }

.nav { display: flex; flex-direction: column; gap: .15rem; flex: 1 1 auto; }
@media (min-width: 901px) {
    .app__lateral { gap: .65rem; padding-block: .8rem; }
    .app__lateral > .app__marque, .app__lateral > .nav-bascule { flex-shrink: 0; }
    .app__lateral > .nav { min-height: 0; overflow-y: auto; overflow-x: hidden; scrollbar-width: thin; }
    .app__lateral > .nav > * { flex-shrink: 0; }
}

/* Un bloc de navigation calé tout en bas de la barre latérale.
   Sélecteur à deux classes : passe devant le raccourci « margin » de .nav__groupe,
   défini plus bas, quel que soit l'ordre source. */
.nav .nav__groupe--bas { margin-top: auto; }

/* Dépliant natif (<details>/<summary>) — aucun script, conforme à la CSP.
   La tête se comporte comme un lien ; un chevron dessiné en CSS pivote à l'ouverture. */
.nav__repli { display: flex; flex-direction: column; }
.nav__repli__tete { cursor: pointer; list-style: none; user-select: none; }
.nav__repli__tete::-webkit-details-marker { display: none; }
.nav__repli__tete::after {
    content: "";
    flex: 0 0 auto;
    margin-left: auto;
    width: .45rem;
    height: .45rem;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(45deg);
    transition: transform var(--pp-transition);
    opacity: .55;
}
.nav__repli[open] > .nav__repli__tete::after { transform: rotate(-135deg); }
.nav__repli__corps { display: flex; flex-direction: column; gap: .15rem; margin-top: .15rem; }
.nav__repli__corps .nav__lien { padding-left: 2.2rem; }

.nav__groupe {
    margin: 1.1rem 0 .35rem;
    padding-inline: .75rem;
    font-size: .6875rem;
    font-weight: 700;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--pp-texte-3);
}

.nav__lien {
    display: flex;
    align-items: center;
    gap: .7rem;
    padding: .55rem .75rem;
    position: relative;
    border-radius: var(--pp-r-sm);
    color: var(--pp-texte-2);
    font-size: .9rem;
    font-weight: 550;
    transition: background var(--pp-transition), color var(--pp-transition);
}
.nav__lien:hover { background: var(--pp-surface-3); color: var(--pp-texte); text-decoration: none; }
.nav__lien svg { flex: 0 0 auto; opacity: .8; }

.nav__lien--actif {
    background: color-mix(in srgb, var(--pp-brand-500) 13%, transparent);
    color: var(--pp-brand-700);
}
:root[data-theme="dark"] .nav__lien--actif { color: var(--pp-brand-300); }
.nav__lien--actif svg { opacity: 1; }

.nav__lien--inactif { opacity: .42; pointer-events: none; }

.nav__badge {
    margin-left: auto;
    font-size: .625rem;
    font-weight: 700;
    letter-spacing: .04em;
    padding: .1rem .4rem;
    border-radius: var(--pp-r-full);
    background: var(--pp-surface-3);
    color: var(--pp-texte-3);
    border: 1px solid var(--pp-bordure);
}

/* Pastille d'alerte : décisions en attente du réseau (approbations, signalements).
   Contrairement à nav__badge, elle reste visible quand la barre est réduite. */
.nav__pastille {
    margin-left: auto;
    min-width: 1.2rem;
    padding: .05rem .38rem;
    font-size: .68rem;
    font-weight: 800;
    line-height: 1.3;
    text-align: center;
    border-radius: var(--pp-r-full);
    background: var(--pp-danger);
    color: #fff;
}
:root[data-nav="reduit"]:not([data-nav-ouvert]) .app__lateral .nav__pastille {
    position: absolute;
    top: .25rem;
    right: .25rem;
    margin: 0;
    min-width: 0;
    padding: .02rem .3rem;
    font-size: .6rem;
}

.app__principal { display: flex; flex-direction: column; width: 100%; max-width: 100%; min-width: 0; }

.app__entete {
    position: sticky;
    top: 0;
    z-index: 10;
    display: flex;
    align-items: center;
    width: 100%; max-width: 100%; min-width: 0;
    gap: 1rem;
    padding: .85rem 1.75rem;
    background: var(--pp-verre);
    backdrop-filter: blur(14px) saturate(1.4);
    border-bottom: 1px solid var(--pp-bordure);
}

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

.app__pharmacie { display: flex; flex-direction: column; line-height: 1.2; min-width: 0; }
.app__pharmacie strong { font-size: .9375rem; font-weight: 650; }
.app__pharmacie span { font-size: .75rem; color: var(--pp-texte-3); }

.app__outils { display: flex; align-items: center; gap: .55rem; min-width: 0; max-width: 100%; }

.avatar {
    width: 36px; height: 36px;
    border-radius: var(--pp-r-full);
    display: grid;
    place-items: center;
    font-size: .8125rem;
    font-weight: 700;
    color: #fff;
    background: linear-gradient(135deg, var(--pp-brand-500), var(--pp-cyan-600));
    box-shadow: var(--pp-ombre-1);
    flex: 0 0 auto;
}

.app__utilisateur { display: flex; align-items: center; gap: .6rem; min-width: 0; max-width: 100%; }
.app__utilisateur .infos { display: flex; flex-direction: column; min-width: 0; line-height: 1.2; }
.app__utilisateur .infos strong {
    max-width: 16rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    font-size: .875rem; font-weight: 650;
}
.app__utilisateur .infos span { font-size: .71875rem; color: var(--pp-texte-3); }
.app__deconnexion { flex: 0 0 auto; margin: 0; }

.app__contenu { width: 100%; max-width: 100%; min-width: 0; padding: 1.75rem; flex: 1; }
.app__contenu > * { max-width: 100%; min-width: 0; }
.app__contenu > * + * { margin-top: 1.4rem; }

/* Les tablettes de pharmacie sont physiquement en portrait, mais exposent encore
   souvent 1080 px CSS et n'activent donc pas les règles « mobile ». L'en-tête peut
   prendre deux lignes : aucune commande ne pousse l'utilisateur ou la sortie hors écran. */
@media (orientation: portrait) {
    .app__entete {
        flex-wrap: wrap; align-content: center;
        column-gap: .75rem; row-gap: .55rem; padding: .7rem 1rem;
    }
    .app__contexte { flex: 1 1 12rem; max-width: 100%; overflow: hidden; }
    .app__pharmacie strong,
    .app__pharmacie span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .app__entete .app__centre { order: 3; flex: 1 1 100%; margin-inline: 0; }
    .app__outils {
        flex: 1 1 28rem; flex-wrap: wrap; justify-content: flex-end;
        margin-left: auto;
    }
    .selecteur-contexte { min-width: 0; max-width: 100%; }
    .selecteur-contexte__choix { max-width: min(12rem, 100%); }
    .app__outils .ambiance__curseur { width: 64px; }
    .app__utilisateur { flex: 0 1 auto; }
    .app__utilisateur .infos strong { max-width: 13rem; }
}

@media (max-width: 900px) {
    .app { grid-template-columns: 1fr; }
    .app__lateral {
        position: static; height: auto;
        flex-direction: row; align-items: center; gap: 1rem;
        overflow-x: auto; border-right: 0; border-bottom: 1px solid var(--pp-bordure);
    }
    .nav { flex-direction: row; }
    .nav__groupe { display: none; }
    .app__utilisateur .infos { display: none; }
    .app__contenu { padding: 1.1rem; }
}

/* -----------------------------------------------------------------------------
   9. Tableau de bord
   -------------------------------------------------------------------------- */
.entete-page { display: flex; align-items: flex-end; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.entete-page p { color: var(--pp-texte-2); margin-top: .3rem; }
/* Plusieurs poignées en tête de page (Annuler + Enregistrer) : elles restent groupées. */
.entete-page__actions { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }

.grille-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
    gap: 1rem;
}

.stat {
    position: relative;
    overflow: hidden;
    padding: 1.15rem 1.25rem;
    background: var(--pp-surface);
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-lg);
    box-shadow: var(--pp-ombre-1);
    transition: transform var(--pp-transition), box-shadow var(--pp-transition);
}
.stat:hover { transform: translateY(-2px); box-shadow: var(--pp-ombre-2); }

/* Une carte de statistique qui mène quelque part : elle se comporte comme un lien —
   couleur héritée, pas de soulignement — et le curseur l'annonce. */
.stat--lien { display: block; text-decoration: none; color: inherit; cursor: pointer; }
.stat--lien:focus-visible { outline: 2px solid var(--pp-brand-500); outline-offset: 2px; }

.stat::before {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    width: 3px;
    background: linear-gradient(180deg, var(--pp-brand-400), var(--pp-cyan-500));
}

.stat--alerte::before { background: linear-gradient(180deg, var(--pp-warn), var(--pp-danger)); }

.stat__libelle {
    font-size: .75rem;
    font-weight: 650;
    letter-spacing: .05em;
    text-transform: uppercase;
    color: var(--pp-texte-3);
}

.stat__valeur {
    margin-top: .35rem;
    font-size: 2rem;
    font-weight: 700;
    letter-spacing: -0.04em;
    line-height: 1;
}

.stat__note { margin-top: .4rem; font-size: .78125rem; color: var(--pp-texte-3); }

.grille-2 {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
    gap: 1.4rem;
    align-items: start;
}

.liste-acces { display: grid; gap: 1.1rem; }

.liste-acces__module h3 {
    font-size: .6875rem;
    text-transform: uppercase;
    letter-spacing: .09em;
    color: var(--pp-texte-3);
    margin-bottom: .5rem;
}

.liste-acces__puces { display: flex; flex-wrap: wrap; gap: .35rem; }

.paire {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    padding: .55rem 0;
    border-bottom: 1px dashed var(--pp-bordure);
    font-size: .875rem;
}
.paire:last-child { border-bottom: 0; }
.paire dt { color: var(--pp-texte-3); }
.paire dd { margin: 0; font-weight: 600; }

dl.paires { margin: 0; }

.vide {
    padding: 2rem 1rem;
    text-align: center;
    color: var(--pp-texte-3);
    font-size: .875rem;
}

/* -----------------------------------------------------------------------------
   10. Page pleine (erreur, accès refusé)
   -------------------------------------------------------------------------- */
.page-centree {
    min-height: 100dvh;
    display: grid;
    place-items: center;
    padding: 2rem 1.5rem;
    text-align: center;
}
.page-centree .contenu { max-width: 32rem; display: grid; gap: 1rem; justify-items: center; }
.page-centree p { color: var(--pp-texte-2); }

/* Le code d'erreur, en grand et en filigrane : il situe l'incident d'un coup d'œil
   sans crier. Dégradé de la marque, replié sur le texte s'il n'est pas rendu. */
.code-erreur {
    margin: .25rem 0 -.5rem;
    font-size: clamp(3.5rem, 12vw, 5.5rem);
    font-weight: 800;
    line-height: 1;
    letter-spacing: -.02em;
    color: var(--pp-brand-500);
    background: linear-gradient(135deg, var(--pp-brand-400), var(--pp-brand-700));
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
}

/* =============================================================================
   11. Console d'administration : amorce, tableaux, formulaires, accès
   ============================================================================= */

/* --- Carte d'amorce (premier démarrage) --- */
.amorce {
    display: flex;
    gap: 1.1rem;
    align-items: flex-start;
    padding: 1.4rem 1.5rem;
    border-radius: var(--pp-r-lg);
    border: 1px solid color-mix(in srgb, var(--pp-brand-500) 28%, transparent);
    background:
        radial-gradient(90% 120% at 0% 0%, color-mix(in srgb, var(--pp-brand-500) 11%, transparent) 0%, transparent 60%),
        var(--pp-surface);
    box-shadow: var(--pp-ombre-1);
}
.amorce h2 { font-size: 1.0625rem; }
.amorce p { margin: .45rem 0 .9rem; color: var(--pp-texte-2); max-width: 68ch; font-size: .9375rem; }

.amorce__icone {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 42px; height: 42px;
    border-radius: var(--pp-r-md);
    color: #fff;
    background: linear-gradient(135deg, var(--pp-brand-500), var(--pp-cyan-600));
    box-shadow: var(--pp-ombre-brand);
}

/* --- Tableaux --- */
.tableau-defilant { overflow-x: auto; }

/* La fiche d'un foyer peuplé ne doit pas s'étirer avec ses patients : la liste
   défile dans sa carte, la page entière reste courte. L'en-tête du tableau,
   déjà « position: sticky », suit le défilement sans rien ajouter de plus. */
.foyer-patients__defilement { max-height: min(56vh, 34rem); overflow-y: auto; }

.tableau {
    width: 100%;
    border-collapse: collapse;
    font-size: .875rem;
    white-space: nowrap;
}

.tableau th {
    text-align: left;
    padding: .7rem 1.35rem;
    font-size: .6875rem;
    font-weight: 700;
    letter-spacing: .07em;
    text-transform: uppercase;
    color: var(--pp-texte-3);
    background: var(--pp-surface-2);
    border-bottom: 1px solid var(--pp-bordure);
    position: sticky;
    top: 0;
}

.tableau td {
    padding: .7rem 1.35rem;
    border-bottom: 1px solid var(--pp-bordure);
    vertical-align: middle;
}

.tableau tbody tr:last-child td { border-bottom: 0; }
.tableau tbody tr:hover { background: var(--pp-surface-2); }

.col-num { text-align: right; }
.attenue { color: var(--pp-texte-3); }

.cellule-user { display: inline-flex; align-items: center; gap: .6rem; }
.cellule-user > span:last-child { display: flex; flex-direction: column; line-height: 1.25; }
.cellule-user .attenue { font-size: .78125rem; }

.avatar--petit { width: 28px; height: 28px; font-size: .6875rem; }

/* --- Formulaires en grille --- */
.section-titre {
    margin: 1.9rem 0 .9rem;
    padding-bottom: .5rem;
    border-bottom: 1px solid var(--pp-bordure);
    font-size: .75rem;
    font-weight: 700;
    letter-spacing: .09em;
    text-transform: uppercase;
    color: var(--pp-texte-3);
}
.section-titre:first-child { margin-top: 0; }

/* Le dossier patient tient sur un seul écran : moins d'air entre les sections
   et les rangées que la mise en page courante, pour que rien n'exige de défiler. */
.patient-formulaire .section-titre { margin: 1.1rem 0 .7rem; }
.patient-formulaire .grille-champs { gap: .75rem; }

.grille-champs {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    gap: 1.05rem;
}
.grille-champs .champ + .champ { margin-top: 0; }
.champ--large { grid-column: 1 / -1; }

/* Une colonne pleine largeur : les adresses d'interface d'une machine dépassent la
   centaine de caractères et ne se relisent pas dans une case étroite. */
.grille-champs--large { grid-template-columns: minmax(0, 1fr); }

/* --- Clichés du pilulier terminé ---
   Assez grands pour qu'on distingue les comprimés d'une alvéole sans ouvrir l'image ;
   assez petits pour que trois constats tiennent côte à côte. */
.cliches {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: 1.1rem;
}

.cliche { margin: 0; }

.cliche__image {
    display: block;
    width: 100%;
    max-height: 22rem;
    object-fit: contain;
    border-radius: var(--pp-r-sm);
    border: 1px solid var(--pp-bordure);
    background: var(--pp-surface-2);
}

.cliche__legende {
    margin-top: .45rem;
    font-size: .8125rem;
    line-height: 1.45;
}

.cliche__note { margin-top: .2rem; font-style: italic; }

/* --- Banc d'essai de l'interface des appareils ---
   Une réponse d'API se lit en largeur : le bloc défile plutôt que de replier des
   adresses de deux cents caractères sur six lignes. */
.essai-api__echange + .essai-api__echange {
    margin-top: 1.6rem;
    padding-top: 1.4rem;
    border-top: 1px dashed var(--pp-bordure);
}

.essai-api__ligne {
    display: flex;
    align-items: center;
    gap: .55rem;
    flex-wrap: wrap;
}

.essai-api__url,
.essai-api__code {
    font-family: ui-monospace, "Cascadia Mono", Menlo, Consolas, monospace;
    font-size: .8125rem;
}

.essai-api__url { word-break: break-all; }

.essai-api__bloc {
    margin: 0 0 .4rem;
    padding: .75rem .9rem;
    max-height: 26rem;
    overflow: auto;
    border-radius: var(--pp-r-sm);
    border: 1px solid var(--pp-bordure);
    background: var(--pp-surface-2);
    font-family: ui-monospace, "Cascadia Mono", Menlo, Consolas, monospace;
    font-size: .78125rem;
    line-height: 1.5;
    white-space: pre;
    tab-size: 2;
}

/* --- Comptes externes d'une pharmacie (Cubi, Telus) ---
   Une carte par compte, et les cartes en GRILLE : une pharmacie qui porte deux codes les
   lit côte à côte au lieu de dérouler l'écran. Même compaction que les signalements —
   corps à .8rem, marges resserrées — parce que ces cartes sont des formulaires qu'on
   relit, pas des pages qu'on lit. Le bouton Enregistrer et le bouton Retirer partagent
   une seule barre de pied : chacun vise son formulaire par l'attribut « form », ce qui
   évite de nicher deux formulaires.

   Les comptes Telus reprennent exactement ce gabarit : on élargit les sélecteurs plutôt
   que de recopier les déclarations, pour que les deux écrans ne divergent jamais. */
.cubi-liste, .telus-liste {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 22rem), 1fr));
    align-items: start;
    gap: .75rem;
    margin-bottom: 1rem;
}

.cubi-compte, .telus-compte {
    min-width: 0;
    padding: .7rem .85rem .6rem;
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-md);
    background: var(--pp-surface-2);
    font-size: .8rem;
}
.cubi-compte .champ__etiquette, .telus-compte .champ__etiquette,
.cubi-ajout .champ__etiquette, .telus-ajout .champ__etiquette { font-size: .65rem; }
.cubi-compte .champ__saisie, .telus-compte .champ__saisie,
.cubi-ajout .champ__saisie, .telus-ajout .champ__saisie { padding: .34rem .55rem; font-size: .78rem; }
.cubi-compte .champ__aide, .telus-compte .champ__aide,
.cubi-ajout .champ__aide, .telus-ajout .champ__aide { font-size: .65rem; margin-top: .15rem; }

.cubi-compte__tete, .telus-compte__tete {
    display: flex;
    align-items: flex-end;
    gap: .45rem .7rem;
    flex-wrap: wrap;
}
.cubi-compte__tete .champ, .telus-compte__tete .champ { flex: 1 1 9rem; min-width: 0; }
.cubi-compte__actif, .telus-compte__actif { flex: 0 0 auto; padding-bottom: .3rem; font-size: .72rem; }

.cubi-compte__url, .telus-compte__url { margin-top: .5rem; }

/* Le délai d'attente porte son propre libellé clair et son aide : il occupe sa ligne,
   mais la boîte de saisie reste étroite — c'est un nombre de secondes, pas une phrase. */
.telus-compte__attente { margin-top: .6rem; }
.telus-compte__attente .champ__boite { max-width: 10rem; }

/* L'encadré des identifiants : teinté marque, il se détache du reste de la carte. */
.cubi-identite, .telus-identite {
    margin-top: .55rem;
    padding: .5rem .6rem .6rem;
    border: 1px solid color-mix(in srgb, var(--pp-brand-500) 30%, transparent);
    border-radius: var(--pp-r-xs);
    background: color-mix(in srgb, var(--pp-brand-500) 7%, var(--pp-surface));
}
/* Deux colonnes dans l'encadré : un code Telus et sa cible se lisent côte à côte ; le
   secret partagé, lui, garde la ligne entière (.champ--large dans la vue). */
.cubi-identite .grille-champs, .telus-identite .grille-champs {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: .5rem .65rem;
}
.cubi-identite__tete, .telus-identite__tete {
    display: flex;
    align-items: center;
    gap: .3rem;
    margin-bottom: .45rem;
    color: var(--pp-brand-600);
}
.cubi-identite__tete svg, .telus-identite__tete svg { width: 14px; height: 14px; }
.cubi-identite__titre, .telus-identite__titre {
    font-size: .62rem;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
}
:root[data-theme="dark"] .cubi-identite__tete, :root[data-theme="dark"] .telus-identite__tete { color: var(--pp-brand-300); }

.cubi-compte__pied, .telus-compte__pied {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .45rem .7rem;
    flex-wrap: wrap;
    margin-top: .6rem;
    padding-top: .55rem;
    border-top: 1px solid var(--pp-bordure);
}
.cubi-compte__pied .etiquette, .telus-compte__pied .etiquette { font-size: .62rem; }
.cubi-compte__pied-actions, .telus-compte__pied-actions { display: flex; gap: .4rem; margin-left: auto; }

/* Le formulaire d'ajout garde les mêmes gabarits, sur une seule colonne : on y saisit,
   on ne compare pas. Il est REPLIÉ par défaut — on vient bien plus souvent relire un
   compte existant qu'en créer un — et son bouton d'envoi vit dans le repli, ce qui
   empêche un champ obligatoire caché de bloquer la validation du navigateur. */
.cubi-ajout, .telus-ajout { max-width: 34rem; }
.compte-ajout__titre {
    width: fit-content;
    padding: .2rem 0;
    margin-top: .3rem;
    color: var(--pp-brand-600);
    cursor: pointer;
    font-weight: 700;
    font-size: .72rem;
    letter-spacing: .08em;
    text-transform: uppercase;
}
.compte-ajout__titre:focus-visible { outline: 2px solid var(--pp-brand-500); outline-offset: 3px; border-radius: 3px; }
.compte-ajout[open] .compte-ajout__titre { margin-bottom: .55rem; }
:root[data-theme="dark"] .compte-ajout__titre { color: var(--pp-brand-300); }

/* Ajout et test : mêmes gabarits d'en-tête que les cartes, sans l'encadré. */
.cubi-essai__ligne {
    display: flex;
    align-items: flex-end;
    gap: .9rem;
    flex-wrap: wrap;
}
.cubi-essai__ligne .champ { flex: 0 0 12rem; }

/* Import en masse : avancement en direct, un numéro à la fois — la liste peut
   dépasser l'écran, elle défile plutôt que d'étirer la boîte. */
.cubi-import__progres { display: grid; gap: .5rem; margin: .75rem 0; }
.cubi-import__progres progress { width: 100%; height: .7rem; accent-color: var(--pp-ok); }
.cubi-import__resultats {
    list-style: none;
    margin: 0;
    padding: 0;
    max-height: 14rem;
    overflow-y: auto;
    display: grid;
    gap: .3rem;
}
.cubi-import__resultats li {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: .5rem;
    padding: .35rem .55rem;
    border: 1px solid var(--pp-bordure);
    border-left: 3px solid var(--pp-bordure);
    border-radius: var(--pp-r-sm);
    font-size: .86rem;
}
.cubi-import__resultats li.est-ok { border-left-color: var(--pp-ok); }
.cubi-import__resultats li.est-erreur { border-left-color: var(--pp-danger); }
.cubi-import__resultats .attenue { font-size: .78rem; text-align: right; }

/* --- Fiche d'une machine : ajout et configuration ---
   Les deux formulaires ouvrent sur la même coupe. Empilés en bandes pleine largeur,
   leurs champs étiraient chaque case de saisie sur toute la carte et laissaient la
   moitié droite vide. Deux blocs côte à côte — l'identité de la machine, puis
   l'ordinateur qui la conduit — rangent le haut de la fiche en rangées de hauteur
   égale, qui s'alignent d'un bloc à l'autre. */
.machine-fiche {
    display: grid;
    grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
    align-items: start;
}

.machine-fiche__bloc { min-width: 0; padding-right: 2rem; }

/* Le trait sépare ce qui est la machine de ce qui est le poste qui la conduit. */
.machine-fiche__bloc--poste {
    padding: 0 0 0 2rem;
    border-left: 1px solid var(--pp-bordure);
}

/* Deux colonnes à gauche, une seule à droite : les cases tiennent alors sur des
   rangées qui se répondent d'un bloc à l'autre. */
.machine-fiche__bloc .grille-champs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.machine-fiche__bloc--poste .grille-champs { grid-template-columns: minmax(0, 1fr); }

/* La case « en service » occupe une cellule de la grille comme un champ : elle se
   centre sur la hauteur d'une case de saisie plutôt que de coller à son étiquette. */
.machine-fiche__bascule .case { min-height: 50px; }

/* L'avertissement suit les deux adresses, dans le bloc du poste. */
.machine-fiche__mot { margin: .9rem 0 0; font-size: .8125rem; line-height: 1.5; }

/* Trop étroit pour deux blocs : ils s'empilent, et le trait de séparation s'efface. */
@media (max-width: 900px) {
    .machine-fiche { grid-template-columns: minmax(0, 1fr); }
    .machine-fiche__bloc { padding-right: 0; }
    .machine-fiche__bloc--poste { padding-left: 0; border-left: 0; }
    .machine-fiche__bloc--poste .grille-champs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    /* Empilé, le second bloc n'est plus « premier enfant » à l'œil : son titre reprend
       l'air que la mise côte à côte lui enlevait. */
    .machine-fiche__bloc--poste .section-titre { margin-top: 1.9rem; }
}

@media (max-width: 560px) {
    .machine-fiche__bloc .grille-champs,
    .machine-fiche__bloc--poste .grille-champs { grid-template-columns: minmax(0, 1fr); }
}

/* --- Accès d'un appareil à l'interface --- */
.machine-acces {
    margin-top: 1.9rem;
    padding-top: 1.4rem;
    border-top: 1px dashed var(--pp-bordure);
}

/* Un mot de passe montré une seule fois se lit caractère par caractère, et se
   sélectionne d'un geste : il va être collé dans la configuration d'un appareil. */
.machine-acces__secret {
    margin: .5rem 0;
    padding: .6rem .8rem;
    border-radius: var(--pp-r-sm);
    border: 1px solid var(--pp-bordure-forte);
    background: var(--pp-surface);
    font-family: ui-monospace, "Cascadia Mono", Menlo, Consolas, monospace;
    font-size: .9375rem;
    letter-spacing: .04em;
    word-break: break-all;
    user-select: all;
}

.machine-acces__etat {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: .35rem 1rem;
    margin: 0 0 1rem;
    font-size: .875rem;
}

.machine-acces__etat dt { color: var(--pp-texte-3); }
.machine-acces__etat dd { margin: 0; }

.machine-acces .barre-actions { justify-content: flex-start; }

/* Un identifiant d'appareil tient en soixante caractères, un login Amco+ guère plus :
   leurs cases n'ont pas à traverser la carte pour être lisibles. */
.machine-acces .grille-champs { grid-template-columns: repeat(auto-fit, minmax(0, 24rem)); }

/* --- Fiche de raccordement d'une machine ---
   Les blocs des modèles non retenus sont « hidden » ET « disabled » : cachés pour
   l'œil, absents de l'envoi. */
.machine-reglages { margin: 1.9rem 0 0; padding: 0; border: 0; min-width: 0; }
.machine-reglages[hidden] { display: none; }

/* Les réglages déclarés tenaient sur UNE colonne pleine largeur, à cause des adresses
   d'interface d'un Farmadosis. Mais un I-Vision ne déclare que quatre heures de prise
   et deux choix : une colonne leur donnait six lignes de page pour six valeurs courtes.

   Rangée fluide plutôt que grille à cases fixes : la dernière ligne s'étale sur ce qui
   reste au lieu de laisser une case morte. Un I-Track n'a que deux réglages courts —
   en grille de trois, la troisième case restait vide à chaque visite. Les champs longs
   prennent la ligne entière, par « champ--large » posé dans la vue. */
.machine-reglages__grille { display: flex; flex-wrap: wrap; }
.machine-reglages__grille > .champ { flex: 1 1 16rem; min-width: 0; }
.machine-reglages__grille > .champ--large { flex-basis: 100%; }

/* Une adresse ou un identifiant se relit caractère par caractère : un 1 ne doit pas
   se confondre avec un l. */
.machine-reglages__saisie {
    font-family: ui-monospace, "Cascadia Mono", Menlo, Consolas, monospace;
    font-size: .8125rem;
}

/* Champ sans icône : la saisie occupe toute la boîte. */
.champ__saisie--seul { padding-left: .9rem; }

/* Listes déroulantes */
select.champ__saisie {
    appearance: none;
    cursor: pointer;
    padding-right: 2.3rem;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2365758C' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right .8rem center;
}
select.champ__saisie option,
select.champ__saisie optgroup { background: var(--pp-surface); color: var(--pp-texte); }
select.champ__saisie:disabled { opacity: .55; cursor: not-allowed; }

/* --- Un réglage qui se juge à l'œil : le curseur ---
   « accent-color » plutôt qu'une glissière redessinée de toutes pièces : le
   navigateur garde alors sa piste remplie, sa poignée, ses tailles de doigt et
   son comportement au clavier — flèches, Origine, Fin. Les redessiner à la main
   ne rendrait rien de tout cela, et cacherait la moitié du geste. */
.curseur {
    display: flex;
    align-items: center;
    gap: .85rem;
    min-width: 0;

    /* « .champ__boite » est une rangée flex : sans cette part, le curseur se
       replierait sur la largeur de sa poignée et n'offrirait qu'un centimètre
       de course. Un réglage qu'on pousse à l'œil a besoin de toute la boîte —
       les autres champs de la grille la prennent aussi. */
    flex: 1 1 auto;
    width: 100%;
}

.curseur__glissiere {
    flex: 1 1 auto;
    min-width: 0;
    height: 1.6rem;
    margin: 0;
    accent-color: var(--pp-brand-500);
    cursor: pointer;
}

.curseur__glissiere:disabled { opacity: .55; cursor: not-allowed; }

/* Le chiffre ne sert pas à viser — il sert à redire son réglage à quelqu'un.
   Largeur figée : sans elle, la glissière saute d'un cran à chaque dizaine. */
.curseur__valeur {
    flex: 0 0 auto;
    min-width: 3.4ch;
    padding: .18rem .5rem;
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-xs);
    background: var(--pp-surface-2);
    color: var(--pp-texte);
    font-family: ui-monospace, "Cascadia Mono", Menlo, Consolas, monospace;
    font-size: .8125rem;
    font-variant-numeric: tabular-nums;
    text-align: center;
}

/* --- Le banc d'essai du plateau lumineux ---
   Il éclaire, il n'enregistre pas : d'où le trait pointillé, qui le distingue
   des champs au-dessus. Ce qu'on y fait ne part pas avec le formulaire. */
.plateau-essai {
    margin-top: 1.4rem;
    padding: .95rem 1.1rem 1rem;
    border: 1px dashed var(--pp-bordure);
    border-radius: var(--pp-r-md);
    background: var(--pp-surface-2);
}

.plateau-essai__ligne {
    display: flex;
    flex-wrap: wrap;
    gap: .6rem;
}

.plateau-essai__mot {
    margin: .75rem 0 0;
    font-size: .8125rem;
    line-height: 1.45;
}

/* Un câble parti, un port refusé : cela se dit, sans prendre le ton d'une
   panne du site. Le plateau est un confort — le réglage s'inscrit sans lui. */
.plateau-essai__mot--alerte { color: var(--pp-warn); }


.barre-actions {
    display: flex;
    justify-content: flex-end;
    gap: .6rem;
    margin-top: 1.75rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--pp-bordure);
    flex-wrap: wrap;
}

/* Ligne mise en évidence dans un tableau — la session du poste courant, par exemple. */
.ligne-active > td { background: color-mix(in srgb, var(--pp-brand-500) 9%, transparent); }

/* La chaîne d'agent (navigateur) peut être longue : on la borne, le titre porte le reste. */
.cellule-agent { max-width: 22rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* --- Liste des accès --- */
.acces-modules {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 1rem;
}

.acces-modules[data-acces-fige] { opacity: .55; pointer-events: none; }

.acces-module {
    margin: 0;
    padding: .9rem 1rem 1rem;
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-md);
    background: var(--pp-surface-2);
}

.acces-module legend {
    padding: 0 .4rem;
    font-size: .6875rem;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--pp-texte-3);
}

.acces-item {
    display: flex;
    align-items: flex-start;
    gap: .55rem;
    padding: .3rem 0;
    color: var(--pp-texte);
}
.acces-item + .acces-item { border-top: 1px dashed var(--pp-bordure); }
.acces-item input { margin-top: 2px; flex: 0 0 auto; }
.acces-item > span { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; line-height: 1.35; }

/* Accès qu'on ne possède pas soi-même : visible mais verrouillé. */
.acces-item--bloque { opacity: .45; cursor: not-allowed; }
.acces-item--bloque input { cursor: not-allowed; }

.acces-marques { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }

.acces-note {
    font-size: .625rem;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    padding: .05rem .35rem;
    border-radius: var(--pp-r-full);
    background: var(--pp-surface-3);
    color: var(--pp-texte-3);
}

.acces-note--derogation {
    background: color-mix(in srgb, var(--pp-warn) 16%, transparent);
    color: var(--pp-warn);
}

/* Boutons d'action groupés dans une cellule de tableau. */
.actions-ligne {
    display: flex;
    align-items: center;
    gap: .3rem;
    flex-wrap: nowrap;
}

code {
    font-family: ui-monospace, "Cascadia Mono", "SF Mono", Menlo, Consolas, monospace;
    font-size: .875em;
    padding: .1em .35em;
    border-radius: var(--pp-r-xs);
    background: var(--pp-surface-3);
}

/* =============================================================================
   12. Changement de mot de passe
   ============================================================================= */
.page-mdp {
    min-height: 100dvh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 2.5rem 1.5rem;
    background:
        radial-gradient(70% 50% at 50% 0%, color-mix(in srgb, var(--pp-brand-500) 10%, transparent) 0%, transparent 65%),
        var(--pp-bg);
}

.page-mdp__boite { width: 100%; max-width: 29rem; }

/* Jauge de robustesse */
.jauge {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: .3rem;
    margin: .85rem 0 .8rem;
}

.jauge span {
    height: 4px;
    border-radius: var(--pp-r-full);
    background: var(--pp-surface-3);
    transition: background var(--pp-transition);
}

.jauge[data-niveau="1"] span:nth-child(-n+1) { background: var(--pp-danger); }
.jauge[data-niveau="2"] span:nth-child(-n+2) { background: var(--pp-warn); }
.jauge[data-niveau="3"] span:nth-child(-n+3) { background: var(--pp-cyan-500); }
.jauge[data-niveau="4"] span                 { background: var(--pp-brand-500); }

/* Liste des exigences, cochées en direct */
.exigences {
    margin: 0;
    padding: .85rem .95rem;
    list-style: none;
    display: grid;
    gap: .35rem;
    border-radius: var(--pp-r-sm);
    background: var(--pp-surface-2);
    border: 1px dashed var(--pp-bordure-forte);
    font-size: .8125rem;
    color: var(--pp-texte-3);
}

.exigences li {
    display: flex;
    align-items: center;
    gap: .5rem;
    transition: color var(--pp-transition);
}

.exigences li::before {
    content: "";
    flex: 0 0 auto;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    border: 1.5px solid var(--pp-bordure-forte);
    background: transparent;
    transition: background var(--pp-transition), border-color var(--pp-transition);
}

.exigences li.est-ok { color: var(--pp-ok); }

.exigences li.est-ok::before {
    background: var(--pp-ok) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E") center / 9px 9px no-repeat;
    border-color: var(--pp-ok);
}

/* =============================================================================
   13. Médicaments : vignettes, photos, approbations
   ============================================================================= */

.btn--danger {
    --btn-bd: transparent;
    --btn-fg: #fff;
    background: linear-gradient(135deg, var(--pp-danger), #9F1239);
    box-shadow: 0 8px 22px rgb(225 29 72 / .26);
}
.btn--danger:hover { background: linear-gradient(135deg, #F43F5E, #BE123C); }

.btn--petit {
    min-height: 32px;
    padding: .25rem .7rem;
    font-size: .8125rem;
}

/* Annuler : un vrai bouton, pas un lien effacé. Neutre au repos — ce n'est pas une
   action dangereuse en soi — mais au survol il annonce ce qu'il fait vraiment,
   abandonner la saisie. À côté d'un « Enregistrer » plein, il doit rester second
   sans devenir invisible. */
.btn--annuler {
    --btn-bg: var(--pp-surface);
    --btn-bd: var(--pp-bordure-forte);
    --btn-fg: var(--pp-texte-2);
}

.btn--annuler:hover {
    --btn-fg: var(--pp-danger);
    --btn-bg: var(--pp-danger-bg);
    --btn-bd: color-mix(in srgb, var(--pp-danger) 40%, transparent);
}

/* Les mêmes actions, mais dans l'en-tête de page : ni trait de séparation ni marge
   haute, elles s'alignent sur le titre. */
.barre-actions--entete {
    margin-top: 0;
    padding-top: 0;
    border-top: 0;
    align-items: center;
}

.etiquette--attente {
    background: color-mix(in srgb, var(--pp-warn) 16%, transparent);
    border-color: color-mix(in srgb, var(--pp-warn) 34%, transparent);
    color: var(--pp-warn);
}

.etiquette--manque {
    background: color-mix(in srgb, var(--pp-danger) 13%, transparent);
    border-color: color-mix(in srgb, var(--pp-danger) 30%, transparent);
    color: var(--pp-danger);
}

/* Préparé d'avance par un ChronoPac : distinct du vert « scanné » (bleu, pas une autre
   nuance de réglé), pour qu'on lise « d'où ça vient » d'un coup d'œil. */
.etiquette--chronopac {
    background: color-mix(in srgb, var(--pp-info) 14%, transparent);
    border-color: color-mix(in srgb, var(--pp-info) 32%, transparent);
    color: var(--pp-info);
}

.etiquette--farmadosis {
    background: color-mix(in srgb, var(--pp-brand-500) 14%, transparent);
    border-color: color-mix(in srgb, var(--pp-brand-500) 32%, transparent);
    color: var(--pp-brand-700);
}

.ligne-inactive { opacity: .5; }

/* --- Vignette de liste --- */
.col-photo { width: 62px; }

.vignette {
    display: block;
    width: 46px;
    height: 46px;
    border-radius: var(--pp-r-sm);
    object-fit: contain;
    background: var(--pp-surface-3);
    border: 1px solid var(--pp-bordure);
    padding: 2px;
}

.vignette--vide {
    display: grid;
    place-items: center;
    color: var(--pp-texte-3);
    border-style: dashed;
}

.vignette[data-photo-agrandir] { cursor: zoom-in; }

.cellule-produit { display: flex; flex-direction: column; line-height: 1.3; }
.cellule-produit .attenue { font-size: .78125rem; }

/* --- Photos de la fiche produit --- */
.photos-produit {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1rem;
}

.photo-face { margin: 0; }

.photo-face__cadre {
    display: grid;
    place-items: center;
    aspect-ratio: 4 / 3;
    padding: .9rem;
    border-radius: var(--pp-r-md);
    border: 1px solid var(--pp-bordure);
    background:
        repeating-conic-gradient(var(--pp-surface-2) 0% 25%, var(--pp-surface-3) 0% 50%)
        50% / 18px 18px;
    overflow: hidden;
}

.photo-face__cadre img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
    filter: drop-shadow(0 6px 14px rgb(0 0 0 / .25));
}

.photo-face__vide { color: var(--pp-texte-3); opacity: .55; }

.photo-face figcaption {
    margin-top: .5rem;
    text-align: center;
    font-size: .6875rem;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--pp-texte-3);
}

.photos-produit--compact .photo-face__cadre { aspect-ratio: 16 / 10; padding: .6rem; }

/* La file d'approbation doit laisser lire plusieurs produits à la fois. Deux petites
   vignettes suffisent; un clic peut toujours ouvrir la photo à sa taille utile. */
.carte-approbation .photos-produit--compact {
    grid-template-columns: repeat(2, 68px);
    justify-content: start;
    gap: .4rem;
}
.carte-approbation .photos-produit--compact .photo-face__cadre {
    width: 68px;
    height: 54px;
    aspect-ratio: auto;
    padding: .25rem;
}
.carte-approbation .photos-produit--compact img { cursor: zoom-in; }

.photos-produit--signalement {
    grid-template-columns: repeat(2, 68px);
    justify-content: start;
    gap: .4rem;
}
.photos-produit--signalement .photo-face__cadre { width: 68px; height: 54px; aspect-ratio: auto; padding: .2rem; font-size: .65rem; text-align: center; }
.photos-produit--signalement .photo-face figcaption { margin-top: .2rem; font-size: .6rem; }
.photos-produit--signalement img { cursor: zoom-in; }

/* Signalements réseau : images et corrections visibles, identité complète à la demande. */
.signalements-entete h1 { font-size: 1.4rem; }
.signalements-entete p { font-size: .85rem; }
.app__contenu > .signalements, .app__contenu > .grille-signalements { margin-top: .75rem; }
.signalements__barre { padding: 0; border: 0; flex-wrap: wrap; gap: .5rem; }
.signalements__barre h2 { font-size: .95rem; }
.signalements__barre .champ__saisie { padding-block: .4rem; }
.grille-signalements {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 350px), 1fr));
    align-items: start;
    gap: .75rem;
}
.signalement { min-width: 0; border-radius: var(--pp-r-md); }
.signalement__entete { display: flex; align-items: start; justify-content: space-between; gap: .5rem; padding: .65rem .85rem; border-bottom: 1px solid var(--pp-bordure); }
.signalement__entete h2 { margin: 0; font-size: 1rem; overflow-wrap: anywhere; }
.signalement__entete .etiquette { flex-shrink: 0; font-size: .65rem; }
.signalement__corps { display: grid; gap: .55rem; padding: .7rem .85rem; font-size: .8rem; }
.signalement__produit { display: flex; gap: .7rem; align-items: center; min-width: 0; }
.signalement__produit .photos-produit { flex: 0 0 auto; grid-template-columns: repeat(2, 68px); }
.signalement__reperes { display: grid; gap: .1rem; min-width: 0; overflow-wrap: anywhere; color: var(--pp-texte-2); font-size: .75rem; }
.signalement__reperes strong { color: var(--pp-texte); }
.signalement__auteur { display: flex; flex-wrap: wrap; justify-content: space-between; column-gap: .5rem; font-size: .7rem; }
.signalement__auteur span { overflow-wrap: anywhere; }
.signalement__auteur time { white-space: nowrap; }
.signalement__corps p { margin: 0; }
.signalement__fiche summary { width: fit-content; padding: .15rem 0; color: var(--pp-brand-600); cursor: pointer; font-weight: 600; font-size: .75rem; }
.signalement__fiche summary:focus-visible { outline: 2px solid var(--pp-brand-500); outline-offset: 3px; border-radius: 3px; }
:root[data-theme="dark"] .signalement__fiche summary { color: var(--pp-brand-300); }
.signalement-identite { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .4rem .8rem; padding-block: .5rem; }
.signalement-identite .paire { display: block; padding: 0; border: 0; font-size: .75rem; overflow-wrap: anywhere; }
.signalement-identite dt { font-size: .65rem; }
.signalement__motif { padding: .45rem .6rem; background: var(--pp-info-bg); border-left: 3px solid var(--pp-info); border-radius: var(--pp-r-xs); white-space: pre-wrap; overflow-wrap: anywhere; }
.signalement__comparaison { table-layout: fixed; width: 100%; font-size: .75rem; }
.signalement__comparaison th, .signalement__comparaison td { padding: .35rem .4rem; white-space: normal; overflow-wrap: anywhere; }
.signalement__comparaison th { font-size: .65rem; }
.signalement__comparaison th:first-child { width: 28%; }
.signalement__note { font-size: .75rem; overflow-wrap: anywhere; }
.signalement__decision { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; gap: .45rem; padding-top: .55rem; border-top: 1px solid var(--pp-bordure); }
.signalement__accepter { display: contents; }
.signalement__accepter .champ__boite { grid-column: 1; min-width: 0; }
.signalement__accepter .champ__saisie { padding: .4rem .6rem; font-size: .75rem; }
.signalement__accepter .btn { grid-column: 2; }
.signalement__decision > form:last-child { grid-column: 3; }

/* Cadres carrés, comme dans le module Piluliers : un comprimé n'est jamais rogné. */
.photos-produit--carrees .photo-face__cadre { aspect-ratio: 1; padding: 1.1rem; }
.photos-produit--carrees .photo-face__cadre img { cursor: zoom-in; }

/* Photo agrandie, partagée par les fiches produit. */
.photo-loupe {
    position: fixed;
    inset: 0;
    z-index: 70;
    display: grid;
    place-items: center;
    padding: 1.5rem;
    background: rgb(7 15 26 / .62);
}

.photo-loupe__boite {
    display: grid;
    gap: .8rem;
    justify-items: center;
    max-width: min(46rem, 100%);
    max-height: calc(100vh - 3rem);
    overflow: auto;
    padding: 1.2rem;
    border-radius: var(--pp-r-lg);
    background: var(--pp-surface);
    box-shadow: var(--pp-ombre-2, 0 20px 50px rgb(0 0 0 / .35));
}

.photo-loupe__titre { margin: 0; font-size: 1rem; text-align: center; }

.photo-loupe img {
    max-width: 100%;
    max-height: 70vh;
    object-fit: contain;
    cursor: default;
}

/* --- En-tête compacte de la page Piluliers ---
   Le titre et son bloc d'identité tiennent à gauche ; toutes les commandes se
   rassemblent à droite, sur une seule ligne qui s'enroule. On gagne les rangées
   que prenaient auparavant l'état étalé et les liens de navigation isolés. */
.entete-pilulier { align-items: flex-start; row-gap: .55rem; }
.entete-pilulier .entete-pilulier__titre { min-width: 0; }
.entete-pilulier h1 { font-size: 1.45rem; line-height: 1.15; }
.entete-pilulier .entete-pilulier__titre p { margin-top: .2rem; }
.entete-pilulier__etat { margin-top: .4rem; }
.entete-pilulier__etat:empty { display: none; }

/* Le groupe unique des commandes : navigation + actions du pilulier. */
.entete-pilulier__actions {
    display: flex;
    align-items: center;
    gap: .4rem;
    flex-wrap: wrap;
    justify-content: flex-end;
    margin-left: auto;
    min-width: 0;
}
/* Des boutons resserrés dans l'en-tête : on en aligne davantage par rangée. */
.entete-pilulier__actions .btn {
    min-height: 34px;
    padding: .32rem .72rem;
    font-size: .85rem;
    gap: .35rem;
}
.entete-pilulier__actions .btn svg { width: 18px; height: 18px; }

/* --- Barre d'actions (piluliers, et réutilisée par l'import de catalogue) ---
   Une ligne souple où la mention d'état (« .pilulier-etat », margin-right:auto)
   repousse les boutons à droite. Vide, elle ne prend aucune place. */
.pilulier-actions {
    display: flex;
    align-items: center;
    gap: .5rem;
    flex-wrap: wrap;
}

.pilulier-actions:empty { display: none; }

/* Dans l'en-tête des piluliers, elle se fond dans le groupe des commandes (ni cadre
   ni fond) et resserre son écart pour s'accorder au reste de la rangée. */
.entete-pilulier__actions .pilulier-actions { gap: .4rem; }

/* Sous les écrans étroits, l'en-tête passe en colonnes : le groupe des commandes
   reprend toute la largeur et s'aligne à gauche plutôt que de comprimer le titre. */
@media (max-width: 62rem) {
    .entete-pilulier__actions { flex-basis: 100%; justify-content: flex-start; margin-left: 0; }
}

/* --- Commentaire porté par la ligne du médicament --- */
.pilulier-ligne__note { margin-top: .45rem; }
.pilulier-ligne__note .champ__saisie { font-size: .8125rem; padding-block: .35rem; }

/* --- Puce d'alvéole : le produit y est nommé en entier --- */
.pilulier-puce__texte { display: grid; line-height: 1.25; min-width: 0; }
.pilulier-puce__texte .attenue { font-size: .6875rem; }

/* --- Boîte de renommage des temps de prise --- */
.modale__boite--etroite { width: min(34rem, 100%); }

.renommage-apercu { margin-top: 1rem; }
.renommage-apercu__liste { display: flex; gap: .4rem; flex-wrap: wrap; margin-top: .35rem; }

/* --- Curseur d'ambiance, du clair au sombre --- */
.ambiance {
    display: flex;
    align-items: center;
    gap: .35rem;
    padding: .25rem .45rem;
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-full);
    background: var(--pp-surface);
}

.ambiance__bout {
    display: grid;
    place-items: center;
    width: 26px; height: 26px;
    padding: 0;
    border: 0;
    border-radius: var(--pp-r-full);
    background: transparent;
    color: var(--pp-texte-3);
    cursor: pointer;
}
.ambiance__bout:hover { color: var(--pp-texte); background: var(--pp-surface-2); }
.ambiance__bout svg { width: 15px; height: 15px; }

.ambiance__curseur {
    appearance: none;
    width: 82px;
    height: 18px;
    margin: 0;
    background: transparent;
    cursor: pointer;
}

/* La piste montre le trajet : le jour à gauche, la nuit à droite. */
.ambiance__curseur::-webkit-slider-runnable-track {
    height: 6px;
    border-radius: var(--pp-r-full);
    background: linear-gradient(to right, var(--jour-surface-3), var(--nuit-surface));
    border: 1px solid var(--pp-bordure);
}
.ambiance__curseur::-moz-range-track {
    height: 6px;
    border-radius: var(--pp-r-full);
    background: linear-gradient(to right, var(--jour-surface-3), var(--nuit-surface));
    border: 1px solid var(--pp-bordure);
}

.ambiance__curseur::-webkit-slider-thumb {
    appearance: none;
    width: 14px; height: 14px;
    margin-top: -5px;
    border-radius: var(--pp-r-full);
    background: var(--pp-brand-500);
    border: 2px solid var(--pp-surface);
    box-shadow: var(--pp-ombre-1);
}
.ambiance__curseur::-moz-range-thumb {
    width: 14px; height: 14px;
    border-radius: var(--pp-r-full);
    background: var(--pp-brand-500);
    border: 2px solid var(--pp-surface);
}

.ambiance__curseur:focus-visible { outline: 2px solid var(--pp-focus); outline-offset: 3px; border-radius: var(--pp-r-full); }

@media (max-width: 720px) {
    .ambiance__curseur { width: 56px; }
}

/* --- Ligne de tableau menant à une page de travail --- */
.ligne-cliquable { cursor: pointer; }
.ligne-cliquable:hover { background: var(--pp-surface-2); }
.ligne-cliquable:focus-visible {
    outline: 2px solid var(--pp-brand-500);
    outline-offset: -2px;
}

/* --- Téléversement --- */
.apercu-photo {
    display: flex;
    align-items: center;
    gap: .8rem;
    padding: .6rem;
    margin-bottom: .5rem;
    border-radius: var(--pp-r-sm);
    background: var(--pp-surface-2);
    border: 1px solid var(--pp-bordure);
}

.apercu-photo img {
    width: 64px;
    height: 48px;
    object-fit: contain;
    border-radius: var(--pp-r-xs);
    background: var(--pp-surface-3);
}

.fichier {
    width: 100%;
    padding: .55rem .7rem;
    font: inherit;
    font-size: .875rem;
    color: var(--pp-texte-2);
    background: var(--pp-surface);
    border: 1px dashed var(--pp-bordure-forte);
    border-radius: var(--pp-r-sm);
    cursor: pointer;
}

.fichier::file-selector-button {
    margin-right: .7rem;
    padding: .35rem .8rem;
    font: inherit;
    font-size: .8125rem;
    font-weight: 600;
    color: var(--pp-texte);
    background: var(--pp-surface-3);
    border: 1px solid var(--pp-bordure-forte);
    border-radius: var(--pp-r-xs);
    cursor: pointer;
}
.fichier::file-selector-button:hover { background: var(--pp-bordure); }

/* --- Recherche --- */
.barre-recherche {
    display: flex;
    gap: .5rem;
    align-items: center;
    flex-wrap: wrap;
}
.barre-recherche .champ__boite { min-width: 15rem; flex: 1 1 15rem; }
.barre-recherche select.champ__saisie { min-width: 11rem; }

/* --- Approbations --- */
.grille-approbations {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 350px), 1fr));
    gap: .75rem;
    align-items: start;
}

.approbations-entete h1 { font-size: 1.4rem; }
.approbations-entete h2 { font-size: 1.15rem; }
.approbations-entete p { font-size: .85rem; }
.app__contenu > .grille-approbations { margin-top: .75rem; }
.carte-approbation { min-width: 0; border-radius: var(--pp-r-md); }
.carte-approbation .carte__entete { padding: .65rem .85rem; align-items: start; gap: .5rem; }
.carte-approbation .carte__entete h2 { font-size: 1rem; overflow-wrap: anywhere; }
.carte-approbation .etiquette { font-size: .65rem; white-space: normal; }
.carte-approbation .carte__corps { display: grid; gap: .55rem; padding: .7rem .85rem; font-size: .8rem; }
.carte-approbation .carte__corps p { margin: 0; overflow-wrap: anywhere; }
.carte-approbation .photo-face figcaption { margin-top: .2rem; font-size: .6rem; }
.carte-approbation .alerte { margin: 0; padding: .5rem; gap: .4rem; font-size: .75rem; }
.approbation__apercu { display: flex; gap: .7rem; align-items: center; }
.approbation__apercu .photos-produit { flex-shrink: 0; }
.approbation__reperes { display: grid; gap: .1rem; min-width: 0; overflow-wrap: anywhere; color: var(--pp-texte-2); font-size: .75rem; }
.approbation__reperes strong { color: var(--pp-texte); }
.approbation__auteur { display: flex; flex-wrap: wrap; column-gap: .5rem; justify-content: space-between; font-size: .7rem; }
.approbation__fiche summary { width: fit-content; padding: .15rem 0; color: var(--pp-brand-600); cursor: pointer; font-weight: 600; font-size: .75rem; }
.approbation__fiche summary:focus-visible { outline: 2px solid var(--pp-brand-500); outline-offset: 3px; border-radius: 3px; }
:root[data-theme="dark"] .approbation__fiche summary { color: var(--pp-brand-300); }
.approbation__identite { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .4rem .8rem; padding-block: .5rem; }
.approbation__identite .paire { display: block; padding: 0; border: 0; font-size: .75rem; overflow-wrap: anywhere; }
.approbation__identite dt { font-size: .65rem; }
.approbation__motif { padding: .45rem .6rem; background: var(--pp-info-bg); border-left: 3px solid var(--pp-info); border-radius: var(--pp-r-xs); white-space: pre-wrap; }
.approbation__decision { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; gap: .45rem; align-items: center; padding-top: .55rem; border-top: 1px solid var(--pp-bordure); }
.approbation__decision form { display: contents; }
.approbation__decision .champ__boite { grid-column: 1; grid-row: 1; min-width: 0; }
.approbation__decision .champ__saisie { padding: .4rem .6rem; font-size: .75rem; }
.approbation__accepter .btn { grid-column: 2; grid-row: 1; }
.approbation__refuser .btn { grid-column: 3; grid-row: 1; }
.approbation__decision .btn svg { width: 14px; height: 14px; }
.carte-approbation .paire-equivalence { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .5rem; }
.carte-approbation .paire-equivalence__face { padding: .5rem; border-radius: var(--pp-r-sm); }
.carte-approbation .paire-equivalence__face > * + * { margin-top: .5rem; }
.carte-approbation .paire-equivalence__titre { font-size: .6rem; gap: .3rem; letter-spacing: .02em; }
.carte-approbation .paire-equivalence__titre svg { flex-shrink: 0; width: .9rem; height: .9rem; }
.carte-approbation .paire-equivalence .photos-produit { grid-template-columns: repeat(2, minmax(0, 68px)); }
.carte-approbation .paire-equivalence .photo-face__cadre { width: 100%; }
.paire-equivalence .approbation__identite { grid-template-columns: 1fr; }

.equivalence-din__actions { display: flex; justify-content: flex-end; gap: .45rem; padding-top: .55rem; border-top: 1px solid var(--pp-bordure); }

.journal-decisions .carte__entete { flex-wrap: wrap; padding: .7rem 1rem; }
.journal-decisions .carte__entete h2 { font-size: 1rem; }
.journal-decisions__filtres { display: flex; flex-wrap: wrap; gap: .5rem; align-items: end; }
.journal-decisions__filtres .champ { gap: .15rem; }
.journal-decisions__filtres .champ__etiquette { font-size: .7rem; margin: 0; }
.journal-decisions__filtres .champ__saisie { font-size: .8rem; padding: .35rem .5rem; }
.journal-decisions .tableau th, .journal-decisions .tableau td { padding: .45rem .7rem; }
.journal-decisions .tableau td { white-space: normal; overflow-wrap: break-word; max-width: 24rem; font-size: .8rem; }
/* « anywhere » couperait un DIN ou un nom en deux dès que la fenêtre se resserre : on laisse
   plutôt le tableau défiler, comme partout ailleurs, et les dates rester entières. */
.journal-decisions .tableau td.chiffre { white-space: nowrap; }
.journal-decisions__etat { margin: .5rem 1rem; font-size: .8rem; }
.journal-decisions [aria-busy="true"] { opacity: .55; }
.journal-decisions .pagination { gap: .5rem; flex-wrap: wrap; }
.journal-decisions .pagination__boutons { flex-wrap: wrap; gap: .4rem; }

.decision {
    display: flex;
    flex-wrap: wrap;
    gap: .6rem;
    align-items: center;
    padding-top: 1rem;
    border-top: 1px solid var(--pp-bordure);
}

.decision__form { display: flex; gap: .5rem; align-items: center; flex: 1 1 auto; }
.decision__form .champ__boite { flex: 1 1 10rem; }

/* Une seconde file dans la même page : un titre de section se distingue du
   titre de page sans prétendre au même poids. */
.entete-page--section { margin-top: 2rem; }
.entete-page--section h2 { margin: 0; }

/* Une équivalence proposée, c'est une PAIRE : les deux fiches côte à côte,
   description et photos, pour décider sur pièces et non sur deux nombres. */
.paire-equivalence {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    gap: 1rem;
}

.paire-equivalence__face {
    min-width: 0;
    padding: .8rem;
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-md);
    background: var(--pp-surface-2);
}
.paire-equivalence__face > * + * { margin-top: .7rem; }

.paire-equivalence__titre {
    display: flex;
    gap: .45rem;
    align-items: center;
    margin: 0;
    font-size: .85rem;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--pp-texte-3);
}
.paire-equivalence__titre svg { width: 1.05rem; height: 1.05rem; }

@media (max-width: 560px) {
    .photos-produit { grid-template-columns: 1fr; }
    .decision__form { flex: 1 1 100%; }
    .paire-equivalence { grid-template-columns: 1fr; }
}

/* -----------------------------------------------------------------------------
   13 bis. Impression des piluliers — boîte modale, calendrier, avancement
   -------------------------------------------------------------------------- */
.modale {
    position: fixed;
    inset: 0;
    z-index: 60;
    display: grid;
    place-items: center;
    padding: 1.5rem;
    background: rgb(7 15 26 / .62);
}

.modale__boite {
    display: flex;
    flex-direction: column;
    width: min(46rem, 100%);
    max-height: calc(100vh - 3rem);
    border-radius: var(--pp-r-lg);
    background: var(--pp-surface);
    box-shadow: var(--pp-ombre-2, 0 20px 50px rgb(0 0 0 / .35));
}

.modale__entete {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 1rem 1.3rem;
    border-bottom: 1px solid var(--pp-bordure);
}
.modale__entete h2 { margin: 0; font-size: 1.05rem; }

/* Le contenu défile dans la boîte : jamais la page derrière. Il prend la place
   restante entre l'en-tête et le pied (flex + min-height:0), et défile seul. */
.modale__corps { flex: 1 1 auto; min-height: 0; padding: 1.2rem 1.3rem 1.4rem; overflow: auto; }

/* Pied fixe : la barre d'actions y est déplacée pour rester visible sans défiler. */
.modale__pied {
    flex: 0 0 auto;
    padding: .85rem 1.3rem;
    border-top: 1px solid var(--pp-bordure);
    background: var(--pp-surface);
    border-bottom-left-radius: var(--pp-r-lg);
    border-bottom-right-radius: var(--pp-r-lg);
}
.modale__pied .barre-actions {
    margin-top: 0;
    padding-top: 0;
    border-top: none;
}

.impression > * + * { margin-top: 1.1rem; }

/* --- Calendrier maison --- */
.calendrier {
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-md);
    padding: .7rem;
    background: var(--pp-surface-2);
}

.calendrier__barre {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    margin-bottom: .6rem;
}
.calendrier__titre { font-weight: 700; text-transform: capitalize; }

.calendrier__grille {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 3px;
}

.calendrier__jour {
    padding: .3rem 0;
    text-align: center;
    font-size: .6875rem;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--pp-texte-3);
    border-radius: var(--pp-r-sm);
}

.calendrier__vide { min-height: 2rem; }

.calendrier__case {
    min-height: 2rem;
    padding: 0;
    border: 1px solid transparent;
    border-radius: var(--pp-r-sm);
    background: transparent;
    color: var(--pp-texte-3);
    font: inherit;
    font-size: .8125rem;
    cursor: default;
}

/* La colonne du jour de préparation du foyer se distingue de tout le mois :
   c'est la seule sur laquelle un pilulier peut commencer. */
.calendrier__jour.est-jour-foyer {
    background: color-mix(in srgb, var(--pp-brand-500) 22%, transparent);
    color: var(--pp-brand-600);
}

.calendrier__case.est-jour-foyer {
    background: color-mix(in srgb, var(--pp-brand-500) 14%, transparent);
    border-color: color-mix(in srgb, var(--pp-brand-500) 34%, transparent);
    color: var(--pp-texte);
    font-weight: 600;
    cursor: pointer;
}
.calendrier__case.est-jour-foyer:hover { background: color-mix(in srgb, var(--pp-brand-500) 26%, transparent); }

.calendrier__case.est-choisi {
    background: var(--pp-brand-500);
    border-color: var(--pp-brand-600);
    color: #fff;
}

.calendrier__case:disabled { opacity: .45; }

.calendrier__periode { margin: .7rem 0 0; font-size: .8125rem; color: var(--pp-texte-2); }

/* Calendrier replié : un résumé compact de la date retenue, avec « Modifier la date ». */
.calendrier--reduit {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
    flex-wrap: wrap;
}
.calendrier-reduit__infos { display: grid; gap: .1rem; min-width: 0; }
.calendrier-reduit__date { font-weight: 650; text-transform: capitalize; }
.calendrier-reduit__periode { font-size: .8125rem; color: var(--pp-texte-2); }

/* Calendrier replié : la date (avec « Modifier ») et la durée commune tiennent sur
   une SEULE ligne. Plus minimaliste que deux champs empilés. */
.impression__reglages {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .6rem 1.25rem;
    flex-wrap: wrap;
}
.impression__reglages-date { display: flex; align-items: center; gap: .6rem; min-width: 0; flex-wrap: wrap; }
.impression__reglages-duree { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.impression__reglages-duree .champ__etiquette { white-space: nowrap; }

/* --- Liste des piluliers à préparer --- */
.impression__piluliers h3 { margin: 0 0 .5rem; font-size: .9375rem; }
.impression__patient { margin: .9rem 0 .35rem; font-size: .8125rem; color: var(--pp-texte-2); }

.impression__outils { display: flex; gap: .4rem; flex-wrap: wrap; margin-bottom: .6rem; }

/* Carte compacte : nom + détails à gauche, durée à droite, sur une seule rangée. */
.impression__ligne {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .35rem .45rem;
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-sm);
    line-height: 1.2;
}
.impression__ligne:hover { border-color: var(--pp-bordure-forte); }

/* Colonne de gauche : le nom (avec la case) puis les détails, empilés serré. */
.impression__principal { flex: 1 1 auto; min-width: 0; display: grid; gap: .05rem; }
.impression__coche { display: flex; align-items: center; gap: .5rem; cursor: pointer; min-width: 0; }
.impression__coche input { flex: 0 0 auto; }
.impression__nom { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.impression__meta { font-size: .75rem; color: var(--pp-texte-3); }

/* Durée propre au pilulier : un fond distinct (légèrement teinté) qui dit « ça se
   modifie à la main ». Compacte, à droite de la carte. */
.impression__jours {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    padding: .2rem .4rem;
    border-radius: var(--pp-r-sm);
    background: color-mix(in srgb, var(--pp-brand-500) 10%, var(--pp-surface));
    border: 1px solid color-mix(in srgb, var(--pp-brand-500) 24%, transparent);
    cursor: text;
}
.impression__jours input.champ__saisie {
    width: 2.9rem;
    flex: 0 0 auto;
    text-align: center;
    padding: .1rem .2rem;
    background: var(--pp-surface);
    border: 1px solid var(--pp-bordure-forte);
    border-radius: 5px;
    font-size: .85rem;
}
.impression__jours .impression__jours-unite { font-size: .75rem; color: var(--pp-texte-2); }

/* Une rangée par patient : ses piluliers en grille, jusqu'à quatre de front sur une
   fenêtre large, puis moins à mesure qu'elle rétrécit. */
.impression__rangee {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    gap: .5rem;
    align-items: stretch;
}
.impression__rangee + .impression__rangee { margin-top: .5rem; }

/* Déjà généré pour cette période : proposé décoché, et signalé comme tel. */
.impression__ligne.est-deja-genere { background: var(--pp-surface-2); }

/* --- Avancement --- */
.impression__avancement > * + * { margin-top: .8rem; }

/* --- Document terminé : toutes les pages, dans le cadre --- */
.impression__document { display: grid; gap: .8rem; }

.impression__document-barre {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
}

.impression__apercu {
    width: 100%;
    height: min(62vh, 720px);
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-sm);
    background: var(--pp-surface-2);
}

/* La largeur de la fenêtre d'impression est fixée par le navigateur selon le nombre
   de piluliers à aligner (voir impression.js) : jusqu'à quatre de front, moins large
   en deçà. La règle ci-dessous ne vaut que pour l'étape « document ». */

/* La fenêtre s'élargit quand elle porte un document. */
.modale__boite:has(.impression__document) { width: min(62rem, 100%); }

.progression {
    height: 10px;
    border-radius: 999px;
    background: var(--pp-surface-3);
    overflow: hidden;
}

.progression__remplissage {
    height: 100%;
    width: 0;
    border-radius: 999px;
    background: var(--pp-brand-500);
    transition: width .25s ease;
}

@media (prefers-reduced-motion: reduce) {
    .progression__remplissage { transition: none; }
}

/* Aperçu du document imprimé : une page A4, cadrée comme une feuille.
   Le cadre défile ; la page, elle, grandit avec le zoom. */
.apercu-gabarit {
    padding: 1rem;
    border-radius: var(--pp-r-md);
    background: var(--pp-surface-2);
    max-height: 78vh;
    overflow: auto;
}

.apercu-gabarit__page {
    position: relative;
    margin: 0 auto;
    /* Proportion A4 : les poignées se placent en pourcentage, le zoom n'a rien à recalculer. */
    aspect-ratio: 210 / 297;
    background: #fff;
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-sm);
    box-shadow: 0 6px 18px rgb(0 0 0 / .18);
}

.apercu-gabarit__page img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
}

.apercu-outils { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
.apercu-outils .champ__saisie { font-size: .8125rem; padding-block: .3rem; }

/* Poignée de repositionnement : elle recouvre le bloc qu'elle déplace. */
.apercu-poignee {
    position: absolute;
    min-height: 22px;
    padding: .15rem .35rem;
    font: inherit;
    font-size: .6875rem;
    font-weight: 700;
    text-align: left;
    color: var(--pp-brand-600);
    background: color-mix(in srgb, var(--pp-brand-500) 14%, transparent);
    border: 1px dashed var(--pp-brand-500);
    border-radius: var(--pp-r-xs);
    cursor: grab;
    touch-action: none;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

.apercu-poignee:hover { background: color-mix(in srgb, var(--pp-brand-500) 24%, transparent); }
.apercu-poignee.est-saisie { cursor: grabbing; border-style: solid; }
.apercu-poignee:focus-visible { outline: 2px solid var(--pp-focus); outline-offset: 2px; }

:root[data-theme="dark"] .apercu-poignee { color: var(--pp-brand-300); }

/* -----------------------------------------------------------------------------
   14. Suivi de production — tableau de bord, graphique, entonnoir

   Les couleurs du graphique ne sont pas choisies à l'œil. Elles sortent de la rampe
   de marque, et chaque extrémité a été vérifiée par calcul :

     · la série quotidienne emploie une seule teinte, --pp-brand-400, qui garde un
       contraste de 3,2 sur le fond clair et de 4,8 sur le fond sombre — au-dessus du
       minimum de 3 dans les deux ambiances, donc une seule valeur suffit ;
     · l'entonnoir est une échelle ORDONNÉE : une teinte unique, du clair au foncé,
       jamais six couleurs distinctes. Deux rampes ont été retenues, une par extrémité
       du curseur jour/nuit, chacune contrôlée sur SA surface : clarté strictement
       monotone, écart d'au moins 0,06 en clarté OKLCH entre deux marches voisines, et
       marche extrême détachée du fond. Le sens s'inverse en mode sombre : l'étape la
       plus avancée reste la plus lumineuse des deux côtés.
   -------------------------------------------------------------------------- */
:root {
    --graph-serie: var(--pp-brand-400);

    --jour-etape-1: #7FB2FC;
    --jour-etape-2: #4A90F0;
    --jour-etape-3: #2372DE;
    --jour-etape-4: #145CBC;
    --jour-etape-5: #124A97;
    --jour-etape-6: #0C2A54;

    /* Même SENS que la rampe de jour — du clair au foncé — mais décalée vers le haut
       pour que sa marche la plus sombre se détache encore du fond de nuit.
       Inverser le sens serait une faute : à mi-course du curseur, les deux rampes se
       croiseraient et les cinq marches se retrouveraient à la même clarté. */
    --nuit-etape-1: #D5E7FF;
    --nuit-etape-2: #B0D0FF;
    --nuit-etape-3: #7FB2FC;
    --nuit-etape-4: #4A90F0;
    --nuit-etape-5: #2372DE;
    --nuit-etape-6: #145CBC;

    --etape-1: color-mix(in oklab, var(--jour-etape-1), var(--nuit-etape-1) calc(var(--pp-nuit) * 100%));
    --etape-2: color-mix(in oklab, var(--jour-etape-2), var(--nuit-etape-2) calc(var(--pp-nuit) * 100%));
    --etape-3: color-mix(in oklab, var(--jour-etape-3), var(--nuit-etape-3) calc(var(--pp-nuit) * 100%));
    --etape-4: color-mix(in oklab, var(--jour-etape-4), var(--nuit-etape-4) calc(var(--pp-nuit) * 100%));
    --etape-5: color-mix(in oklab, var(--jour-etape-5), var(--nuit-etape-5) calc(var(--pp-nuit) * 100%));
    --etape-6: color-mix(in oklab, var(--jour-etape-6), var(--nuit-etape-6) calc(var(--pp-nuit) * 100%));
}

/* Rang de filtres : au-dessus de ce qu'il commande, jamais dans une carte. */
.filtres-periode { display: flex; align-items: center; gap: .35rem; flex-wrap: wrap; }

.filtres-periode__libelle {
    font-size: .78125rem;
    color: var(--pp-texte-3);
    margin-right: .2rem;
}

.btn--choisi {
    background: var(--pp-surface-3);
    border-color: var(--pp-bordure-forte);
    color: var(--pp-texte);
}

/* La tuile de tête : le nombre qui commande la journée de travail. */
.stat--vedette { grid-column: span 1; }
.stat--vedette .stat__valeur { font-size: 2.6rem; }
.stat--vedette::before { background: linear-gradient(180deg, var(--pp-brand-400), var(--pp-brand-600)); }

/* Chiffres proportionnels sur les grandes valeurs : à cette taille, des chiffres
   de largeur fixe font paraître « 121 » ajouré. */
.stat__valeur { font-variant-numeric: proportional-nums; }

/* -------- Compacité du tableau de bord --------

   Un tableau de bord qu'il faut faire défiler ne répond plus à sa question : où en
   est la journée. Les quatre compteurs perdent donc une ligne — le libellé au-dessus,
   la valeur et sa note sur la MÊME ligne de base — et les deux cartes se rangent
   côte à côte dès qu'il y a la largeur pour le faire sans les étrangler. */
.grille-stats--compact {
    grid-template-columns: repeat(auto-fit, minmax(11.5rem, 1fr));
    gap: .7rem;
}

.grille-stats--compact .stat {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas:
        "libelle libelle"
        "valeur  note";
    align-items: baseline;
    column-gap: .5rem;
    padding: .5rem .8rem .55rem 1rem;
}

.grille-stats--compact .stat__libelle { grid-area: libelle; font-size: .6875rem; }

.grille-stats--compact .stat .stat__valeur {
    grid-area: valeur;
    margin-top: .15rem;
    font-size: 1.5rem;
}

.grille-stats--compact .stat--vedette .stat__valeur { font-size: 1.75rem; }

.grille-stats--compact .stat__note {
    grid-area: note;
    margin-top: 0;
    font-size: .72rem;
    line-height: 1.2;
}

/* Le graphique garde la part large ; sous cette largeur, une colonne unique reste
   plus lisible que deux moitiés serrées. */
.tableau-bord__rangee {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 1.4rem;
    align-items: start;
}

@media (min-width: 1280px) {
    .tableau-bord__rangee { grid-template-columns: minmax(0, 1.75fr) minmax(0, 1fr); }
}

.tableau-bord__rangee .carte__entete { padding: .7rem 1.1rem; }
.tableau-bord__rangee .carte__corps { padding: .7rem 1.1rem .9rem; }

/* -------- Graphique quotidien --------

   Plafond de largeur : un viewBox se met à l'échelle avec son conteneur, donc sans
   plafond le graphique et ses textes enflaient sur un grand écran — 400 px de haut,
   des graduations de 20 px, et l'avancement de la semaine repoussé hors de vue. */
.graphique { margin: 0; max-width: 56rem; }

.graphique__toile { width: 100%; height: auto; display: block; overflow: visible; }

.graphique__repere {
    stroke: var(--pp-bordure);
    stroke-width: 1;
}

.graphique__graduation,
.graphique__date {
    fill: var(--pp-texte-3);
    font-size: 13px;
    font-variant-numeric: tabular-nums;
}

.graphique__graduation { text-anchor: end; }
.graphique__date { text-anchor: middle; }

.graphique__barre {
    fill: var(--graph-serie);
    transition: fill var(--pp-transition);
}

/* Le nombre posé au-dessus de chaque chandelle. Sa taille arrive en ATTRIBUT de
   présentation, calculée d'après la largeur d'un créneau : la feuille ne doit donc
   pas fixer « font-size » ici, sinon elle écraserait ce calcul.
   L'entrée — silhouette et décompte — est tenue par « tableau-bord.js ». */
.graphique__valeur {
    fill: var(--pp-texte-2);
    font-weight: 650;
    font-variant-numeric: tabular-nums;
    text-anchor: middle;
}

.graphique__jour:hover .graphique__valeur { fill: var(--pp-texte); }

.graphique__sommet {
    fill: var(--pp-texte-2);
    font-size: 12px;
    font-weight: 650;
    text-anchor: middle;
}

/* Cible de survol pleine hauteur : viser une barre de trois pixels au pixel près
   serait impraticable. Invisible, mais large. */
.graphique__cible { fill: transparent; }
.graphique__jour:hover .graphique__cible { fill: var(--pp-surface-2); }
.graphique__jour:hover .graphique__barre { fill: var(--pp-brand-600); }

:root[data-theme="dark"] .graphique__jour:hover .graphique__barre { fill: var(--pp-brand-300); }

.graphique__legende {
    margin-top: .5rem;
    font-size: .78125rem;
    color: var(--pp-texte-3);
}

/* Repli des données : une infobulle ne doit jamais être le seul accès à une valeur. */
.tableau-repli { margin-top: .9rem; }

.tableau-repli > summary {
    cursor: pointer;
    font-size: .8125rem;
    color: var(--pp-texte-2);
    padding: .35rem 0;
}

.tableau-repli > summary:focus-visible { outline: 2px solid var(--pp-focus); outline-offset: 2px; }

.tableau--compact th, .tableau--compact td { padding: .4rem .7rem; }
.col-nombre { text-align: right; font-variant-numeric: tabular-nums; }

/* -------- Entonnoir des cinq étapes -------- */
.entonnoir { list-style: none; margin: 0; padding: 0; display: grid; gap: .35rem; }

/* La marche qui MÈNE quelque part (les états d’une carte) et celle qui ne fait
   qu’afficher (les marches du comptage) partagent la mise en page ; seule la
   première prend le survol et le focus — rien ne promet un clic qui n’existe pas. */
.entonnoir__lien,
.entonnoir__ligne {
    display: grid;
    grid-template-columns: minmax(9rem, 12rem) 1fr auto;
    align-items: center;
    gap: .85rem;
    padding: .45rem .6rem;
    border-radius: var(--pp-r-sm);
    color: inherit;
    text-decoration: none;
    transition: background var(--pp-transition);
}

.entonnoir__lien:hover { background: var(--pp-surface-2); }
.entonnoir__lien:focus-visible { outline: 2px solid var(--pp-focus); outline-offset: 1px; }

.entonnoir__nom {
    display: flex;
    align-items: center;
    gap: .4rem;
    font-size: .875rem;
    color: var(--pp-texte-2);
}

.entonnoir__piste {
    height: 12px;
    border-radius: var(--pp-r-full);
    background: var(--pp-surface-3);
    overflow: hidden;
}

.entonnoir__remplissage {
    display: block;
    height: 100%;
    border-radius: var(--pp-r-full);
    background: var(--etape-1);
    transition: width var(--pp-transition);
}

.entonnoir__marche[data-etape="20"] .entonnoir__remplissage { background: var(--etape-2); }
.entonnoir__marche[data-etape="30"] .entonnoir__remplissage { background: var(--etape-3); }
.entonnoir__marche[data-etape="40"] .entonnoir__remplissage { background: var(--etape-4); }
.entonnoir__marche[data-etape="50"] .entonnoir__remplissage { background: var(--etape-5); }
.entonnoir__marche[data-etape="60"] .entonnoir__remplissage { background: var(--etape-6); }

.entonnoir__valeur {
    min-width: 3ch;
    text-align: right;
    font-weight: 650;
    font-variant-numeric: tabular-nums;
    color: var(--pp-texte);
}

.etiquette--reserve {
    padding: .05rem .25rem;
    background: var(--pp-surface-3);
    color: var(--pp-texte-3);
}

.etiquette--reserve svg { width: 13px; height: 13px; }

/* -------- Suivi : liste et fiche -------- */
.suivi-filtres {
    display: flex;
    gap: .6rem;
    align-items: flex-end;
    flex-wrap: wrap;
    margin-bottom: 1rem;
}

.suivi-filtres .champ { margin: 0; }
.suivi-filtres .champ__saisie, .suivi-filtres select { min-width: 12rem; }

.pastille-etat {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: .15rem .55rem;
    border-radius: var(--pp-r-full);
    font-size: .78125rem;
    font-weight: 600;
    background: var(--pp-surface-3);
    color: var(--pp-texte-2);
    white-space: nowrap;
}

.pastille-etat::before {
    content: "";
    width: 8px; height: 8px;
    border-radius: 50%;
    background: var(--etape-1);
    flex: none;
}

.pastille-etat[data-etape="20"]::before { background: var(--etape-2); }
.pastille-etat[data-etape="30"]::before { background: var(--etape-3); }
.pastille-etat[data-etape="40"]::before { background: var(--etape-4); }
.pastille-etat[data-etape="50"]::before { background: var(--etape-5); }
.pastille-etat[data-etape="60"]::before { background: var(--etape-6); }

.code-carte {
    font-family: ui-monospace, "Cascadia Mono", Menlo, Consolas, monospace;
    font-size: .8125rem;
    letter-spacing: .04em;
    color: var(--pp-texte-2);
}

/* Frise du parcours : les cinq étapes, franchies ou en attente. */
.frise { list-style: none; margin: 0; padding: 0; display: grid; gap: 0; }

.frise__etape {
    display: grid;
    grid-template-columns: 26px 1fr;
    gap: .85rem;
    padding-bottom: 1.1rem;
    position: relative;
}

.frise__etape:last-child { padding-bottom: 0; }

/* Le fil qui relie les étapes : il s'arrête à la dernière. */
.frise__etape:not(:last-child)::before {
    content: "";
    position: absolute;
    left: 12px;
    top: 26px;
    bottom: 2px;
    width: 2px;
    background: var(--pp-bordure);
}

.frise__etape--franchie:not(:last-child)::before { background: var(--pp-brand-400); }

.frise__puce {
    width: 26px; height: 26px;
    border-radius: 50%;
    display: grid;
    place-items: center;
    background: var(--pp-surface-3);
    border: 2px solid var(--pp-bordure);
    color: var(--pp-texte-3);
    z-index: 1;
}

.frise__puce svg { width: 14px; height: 14px; }

.frise__etape--franchie .frise__puce {
    background: var(--pp-brand-500);
    border-color: var(--pp-brand-500);
    color: #FFFFFF;
}

.frise__etape--suivante .frise__puce {
    border-color: var(--pp-brand-400);
    border-style: dashed;
    color: var(--pp-brand-500);
}

/* Pour reculer, l'étape active elle-même est le bouton. Le formulaire transparent
   couvre sa rangée; le texte reste celui de la frise et le survol rend le geste visible. */
.frise__etape--retirable {
    border-radius: var(--pp-r-xs);
    transition: background .15s ease;
}

.frise__etape--retirable:hover,
.frise__etape--retirable:has(.frise__recul-bouton:focus-visible) {
    background: color-mix(in srgb, var(--pp-brand-500) 9%, transparent);
}

.frise__retrait-indice {
    color: var(--pp-brand-600);
    font-size: .75rem;
    font-weight: 600;
}

.frise__recul-form {
    position: absolute;
    z-index: 2;
    inset: -.35rem 0 .75rem -.35rem;
}

.frise__etape--retirable:last-child .frise__recul-form { bottom: -.35rem; }

.frise__recul-bouton {
    width: 100%;
    height: 100%;
    padding: 0;
    border: 0;
    border-radius: var(--pp-r-xs);
    background: transparent;
    cursor: pointer;
}

.frise__recul-bouton:focus-visible {
    outline: 3px solid var(--pp-focus);
    outline-offset: 2px;
}

.frise__titre {
    display: flex;
    align-items: center;
    gap: .5rem;
    font-weight: 600;
    flex-wrap: wrap;
}

.frise__signature { font-size: .8125rem; color: var(--pp-texte-2); margin-top: .15rem; }
.frise__attente { font-size: .8125rem; color: var(--pp-texte-3); margin-top: .15rem; }
.frise__note {
    margin-top: .35rem;
    padding: .35rem .6rem;
    border-left: 2px solid var(--pp-bordure-forte);
    font-size: .8125rem;
    color: var(--pp-texte-2);
}

/* Le contenu du pilulier, en consultation seule. */
.lecture-seule { position: relative; }

.lecture-seule .grille-pilulier,
.lecture-seule .liste-molecules { pointer-events: none; }

.molecule {
    display: grid;
    grid-template-columns: 44px 1fr auto;
    gap: .75rem;
    align-items: center;
    padding: .55rem .7rem;
    border-bottom: 1px solid var(--pp-bordure);
}

.molecule:last-child { border-bottom: 0; }
.molecule--inactive { opacity: .55; }

.molecule__vignette {
    width: 44px; height: 44px;
    border-radius: var(--pp-r-xs);
    object-fit: contain;
    background: var(--pp-surface-2);
    border: 1px solid var(--pp-bordure);
}

.molecule__vignette--vide { display: grid; place-items: center; color: var(--pp-texte-3); }
.molecule__nom { font-weight: 600; }
.molecule__detail { font-size: .8125rem; color: var(--pp-texte-2); }
.molecule__commentaire { font-size: .8125rem; color: var(--pp-texte-2); font-style: italic; margin-top: .2rem; }

.molecule__doses {
    display: flex;
    gap: .3rem;
    flex-wrap: wrap;
    justify-content: flex-end;
}

.dose-puce {
    padding: .1rem .45rem;
    border-radius: var(--pp-r-xs);
    background: var(--pp-surface-3);
    font-size: .78125rem;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* Grille de la carte, en consultation. Reprend la disposition de l'éditeur. */
.grille-carte { width: 100%; border-collapse: collapse; }

.grille-carte th, .grille-carte td {
    border: 1px solid var(--pp-bordure);
    padding: .4rem .5rem;
    vertical-align: top;
    font-size: .78125rem;
}

.grille-carte thead th {
    background: var(--pp-surface-2);
    text-align: center;
    font-weight: 650;
}

.grille-carte tbody th {
    background: var(--pp-surface-2);
    text-align: left;
    white-space: nowrap;
    font-weight: 600;
}

.grille-carte td { min-width: 6rem; }
.grille-carte__vide { background: var(--pp-surface-2); opacity: .5; }

/* Date d'une alvéole, dans les dispositions où la rangée n'en désigne pas une seule :
   sur deux semaines côte à côte, ou sur vingt-huit journées, chaque case tombe un
   autre jour du calendrier. Discrète — c'est un repère, pas le contenu. */
.pilulier-grille__date {
    font-size: .6875rem;
    margin-bottom: .3rem;
    white-space: nowrap;
}
.grille-carte__prise { display: block; }
.grille-carte__prise + .grille-carte__prise { margin-top: .15rem; }
.grille-carte__qte { font-weight: 650; font-variant-numeric: tabular-nums; }

/* -----------------------------------------------------------------------------
   15. Pagination et traitements longs
   -------------------------------------------------------------------------- */
.pagination {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    padding: .75rem 1.35rem;
    border-top: 1px solid var(--pp-bordure);
}

.pagination__compte { font-size: .8125rem; color: var(--pp-texte-2); font-variant-numeric: tabular-nums; }
.pagination__boutons { display: flex; align-items: center; gap: .6rem; }
.pagination__rang { font-size: .8125rem; color: var(--pp-texte-3); font-variant-numeric: tabular-nums; }

/* Voile posé pendant un traitement long. Il bloque la page volontairement :
   un second envoi déposerait le fichier une deuxième fois. */
.voile-traitement {
    position: fixed;
    inset: 0;
    z-index: 60;
    display: grid;
    place-items: center;
    background: color-mix(in srgb, var(--pp-bg) 82%, transparent);
    backdrop-filter: blur(3px);
}

.voile-traitement__boite {
    display: grid;
    justify-items: center;
    gap: .55rem;
    padding: 2rem 2.6rem;
    border-radius: var(--pp-r-lg);
    background: var(--pp-surface);
    border: 1px solid var(--pp-bordure);
    box-shadow: var(--pp-ombre-3);
    text-align: center;
    max-width: min(26rem, 90vw);
}

.voile-traitement__anneau {
    width: 42px;
    height: 42px;
    border-radius: 50%;
    border: 3px solid var(--pp-surface-3);
    border-top-color: var(--pp-brand-500);
    animation: voile-tourne 900ms linear infinite;
}

@keyframes voile-tourne { to { transform: rotate(360deg); } }

.voile-traitement__titre { font-weight: 650; }
.voile-traitement__detail { font-size: .8125rem; color: var(--pp-texte-2); }
.voile-traitement__chrono { font-size: .8125rem; color: var(--pp-texte-3); font-variant-numeric: tabular-nums; }

/* Mouvement réduit : l'anneau cesse de tourner, la barre reste visible.
   Le compteur, lui, continue de dire que quelque chose se passe. */
@media (prefers-reduced-motion: reduce) {
    .voile-traitement__anneau { animation: none; border-top-color: var(--pp-brand-500); }
}

/* -----------------------------------------------------------------------------
   16. Fin de session imminente

   Le bandeau se place entre l'entête et le contenu, pleine largeur : il doit se
   voir sans recouvrir la page ni déplacer ce qu'on est en train de lire. Le ton
   est celui de l'avertissement, pas de l'erreur — rien n'est encore perdu.
   -------------------------------------------------------------------------- */
.expiration {
    display: flex;
    align-items: center;
    gap: .75rem;
    margin: 1rem 1.5rem 0;
    padding: .8rem 1rem;
    border: 1px solid color-mix(in srgb, var(--pp-warn) 34%, transparent);
    border-radius: var(--pp-r-md);
    background: var(--pp-warn-bg);
    color: var(--pp-warn);
}

.expiration[hidden] { display: none; }

.expiration svg { flex: 0 0 auto; }

.expiration__texte {
    display: flex;
    flex-direction: column;
    gap: .1rem;
    flex: 1 1 auto;
    min-width: 0;
    font-size: .875rem;
}

/* Le décompte change chaque seconde : une chasse fixe l'empêche de tressauter. */
.expiration__texte [data-veille-compte] {
    color: var(--pp-texte-2);
    font-variant-numeric: tabular-nums;
}

.expiration .btn { flex: 0 0 auto; }

@media (max-width: 640px) {
    .expiration { margin: .75rem 1rem 0; flex-wrap: wrap; }
    .expiration .btn { width: 100%; }
}

/* Saisie du nombre de jours et raccourcis, sur une seule ligne. */
.impression__duree {
    display: flex;
    align-items: center;
    gap: .6rem;
    flex-wrap: wrap;
}
.impression__duree .champ__boite { flex: 0 0 auto; width: 6.5rem; }

/* Raccourcis de durée : à côté de la saisie libre, jamais à sa place. */
.impression__raccourcis {
    display: flex;
    flex-wrap: wrap;
    gap: .35rem;
}

/* -----------------------------------------------------------------------------
   Segments : un choix unique, habillé en tuiles côte à côte.

   Les boutons radio restent de vrais radios — parcours aux flèches, annonce du
   groupe, exclusion mutuelle — mais sortis du flux visuel : c'est la tuile entière
   qui se coche, pas une pastille de six pixels au bout d'une ligne.
   -------------------------------------------------------------------------- */
.segments {
    display: flex;
    flex-wrap: wrap;
    gap: .45rem;
}

.segments__choix {
    position: relative;
    display: grid;
    flex: 1 1 8.5rem;
    padding: .55rem .75rem;
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-md);
    background: var(--pp-surface);
    cursor: pointer;
    transition: border-color .12s ease, background-color .12s ease;
}

.segments__choix:hover { border-color: var(--pp-bordure-forte); background: var(--pp-surface-2); }

/* Le radio commande sans se montrer : masqué à l'œil, jamais au clavier ni au
   lecteur d'écran — d'où une taille non nulle et une position dans la tuile. */
.segments__radio {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    opacity: 0;
    cursor: pointer;
}

.segments__radio:disabled { cursor: default; }

.segments__texte { display: grid; gap: .1rem; pointer-events: none; }

.segments__nom { font-size: .9375rem; font-weight: 650; }
.segments__duree { font-size: .8125rem; color: var(--pp-texte-2); }
.segments__reste { font-size: .75rem; color: var(--pp-texte-3); }

/* Retenu : le liseré de marque, deux fois — bordure et ombre — pour tenir aussi
   en contraste élevé, où les couleurs de fond peuvent être écrasées. */
.segments__choix.est-choisi {
    border-color: var(--pp-brand-500);
    /* Teinte mélangée à la surface courante : elle suit le thème d'elle-même,
       là où une valeur fixe éclairerait la tuile en sombre. */
    background: color-mix(in srgb, var(--pp-brand-500) 10%, var(--pp-surface));
    box-shadow: inset 0 0 0 1px var(--pp-brand-500);
}

.segments__choix.est-choisi .segments__nom { color: var(--pp-brand-600); }

/* La tuile porte le focus du radio qu'elle habille : sans cela, la navigation au
   clavier avancerait sans que rien ne bouge à l'écran. */
.segments__radio:focus-visible + .segments__texte::after {
    content: "";
    position: absolute;
    inset: -3px;
    border-radius: calc(var(--pp-r-md) + 3px);
    outline: 2px solid var(--pp-brand-500);
    outline-offset: 0;
}

:root[data-theme="dark"] .segments__choix.est-choisi .segments__nom { color: var(--pp-brand-300); }

/* Piluliers séparés par capacité dans la boîte d'impression. La durée d'impression
   suit la capacité : mélanger 7, 14 et 28 jours dans une seule colonne fait cocher
   un 28 jours en croyant prendre une semaine. */
.impression__groupe + .impression__groupe {
    margin-top: 1.1rem;
    padding-top: .9rem;
    border-top: 1px solid var(--pp-bordure);
}

.impression__groupe-tete {
    display: flex;
    align-items: center;
    gap: .55rem;
    margin-bottom: .45rem;
}

.impression__groupe-tete h4 {
    margin: 0;
    font-size: .9375rem;
    font-weight: 650;
}

/* Carte annulée dans la liste : reléguée en fin de tableau par le tri, et rouge.
   Elle reste lisible — on doit pouvoir retrouver une carte dont le papier traîne
   encore — mais rien ne doit la faire confondre avec du travail à faire. */
.ligne-annulee td { color: var(--pp-danger); }
.ligne-annulee .attenue { color: color-mix(in srgb, var(--pp-danger) 72%, transparent); }
.ligne-annulee .code-carte { color: var(--pp-danger); }
.ligne-annulee:hover { background: var(--pp-danger-bg); }

/* Renvoi vers la carte qui a pris le relais.

   Il sort du rouge de l'annulation : c'est le seul élément de la ligne qui mène
   à quelque chose de valide, et l'y noyer reviendrait à le cacher. */
.remplacement {
    display: flex;
    align-items: center;
    gap: .35rem;
    margin-top: .3rem;
    font-size: .8125rem;
}

.remplacement--liste { color: var(--pp-texte-2); }

.ligne-annulee .remplacement,
.ligne-annulee .remplacement .code-carte { color: var(--pp-brand-600); }

.code-carte--lien {
    color: var(--pp-brand-600);
    text-decoration: underline;
    text-underline-offset: 2px;
}

.code-carte--lien:hover { color: var(--pp-brand-500); text-decoration-thickness: 2px; }

:root[data-theme="dark"] .code-carte--lien,
:root[data-theme="dark"] .ligne-annulee .remplacement,
:root[data-theme="dark"] .ligne-annulee .remplacement .code-carte { color: var(--pp-brand-300); }

/* Annuler une carte : séparé du geste qui la fait avancer, et visuellement en
   retrait. Ce n'est pas le chemin normal — mais il doit exister, et se voir. */
.frise__action--annulation {
    margin-top: 1.1rem;
    padding-top: 1.1rem;
    border-top: 1px dashed var(--pp-bordure);
}

/* -----------------------------------------------------------------------------
   Cartes déjà générées pour un pilulier

   Ouvert depuis l'écran de préparation, ce panneau est le pont vers le suivi :
   chaque entrée est un lien entier, pas une ligne rendue cliquable par script —
   le clic du milieu et « ouvrir dans un onglet » y fonctionnent d'eux-mêmes.
   -------------------------------------------------------------------------- */
.cartes-generees { display: grid; gap: .5rem; }

.carte-generee {
    display: block;
    padding: .6rem .75rem;
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-md);
    background: var(--pp-surface);
    color: inherit;
    text-decoration: none;
}

/* Le survol se dit par le cadre et le fond, pas par un soulignement : la règle
   générale des liens en poserait un sous le bloc entier. */
.carte-generee:hover {
    border-color: var(--pp-brand-400);
    background: var(--pp-surface-2);
    color: inherit;
    text-decoration: none;
}

.carte-generee__haut {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .6rem;
    flex-wrap: wrap;
}

/* La date d'impression prime : c'est elle qu'on cherche, un carton à la main. */
.carte-generee__date { margin-top: .35rem; font-size: .8125rem; }

.carte-generee__periode { font-size: .78125rem; }

/* Annulée : rouge, mais toujours lisible — son papier peut encore traîner. */
.carte-generee--annulee { border-color: color-mix(in srgb, var(--pp-danger) 40%, var(--pp-bordure)); }
.carte-generee--annulee .carte-generee__date,
.carte-generee--annulee .code-carte { color: var(--pp-danger); }
.carte-generee--annulee:hover { background: var(--pp-danger-bg); }

/* Le renvoi vers la remplaçante sort du rouge : c'est le seul élément de
   l'entrée qui mène à quelque chose de valide. */
.carte-generee--annulee .remplacement { color: var(--pp-brand-600); }

:root[data-theme="dark"] .carte-generee--annulee .remplacement { color: var(--pp-brand-300); }

/* -----------------------------------------------------------------------------
   Compteurs en un seul rang

   La variante des « grille-stats » pour les pages dont la liste est le sujet :
   les compteurs y sont un repère, pas le propos. Ils tiennent sur une ligne et
   laissent le tableau commencer haut.
   -------------------------------------------------------------------------- */
.stats-rang {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .4rem 1.4rem;
    margin-bottom: .9rem;
    padding: .55rem .9rem;
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-md);
    background: var(--pp-surface-1);
}

.stats-rang__item {
    display: inline-flex;
    align-items: baseline;
    gap: .4rem;
    font-size: .8125rem;
}

.stats-rang__valeur {
    font-size: 1.0625rem;
    font-weight: 650;
    font-variant-numeric: tabular-nums;
    color: var(--pp-texte-1);
}

.stats-rang__libelle { color: var(--pp-texte-2); }

.stats-rang__item--alerte .stats-rang__valeur { color: var(--pp-warn); }

/* -----------------------------------------------------------------------------
   17. Mouvement réduit
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .001ms !important;
        /* Le RETARD aussi, sinon la règle ne tient pas sa promesse : une animation
           d'entrée en « backwards » garde son état de départ pendant tout son délai.
           Durée écrasée mais retard conservé, l'élément reste donc invisible 400 ms
           puis surgit d'un coup — exactement le clignotement qu'on voulait épargner.
           Vu sur les capsules de la page de connexion. */
        animation-delay: .001ms !important;
    }
}

/* -----------------------------------------------------------------------------
   Barre latérale réduite aux logos
   -------------------------------------------------------------------------- */
/* Le bouton qui replie la barre : au repos, une entrée discrète sous la navigation. */
.nav-bascule {
    display: flex;
    align-items: center;
    gap: .7rem;
    width: 100%;
    padding: .55rem .75rem;
    margin-top: .4rem;
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-sm);
    background: transparent;
    color: var(--pp-texte-2);
    font: inherit;
    font-size: .8125rem;
    cursor: pointer;
    transition: background var(--pp-transition), color var(--pp-transition);
}
.nav-bascule:hover { background: var(--pp-surface-3); color: var(--pp-texte); }
.nav-bascule svg { flex: 0 0 auto; opacity: .8; }

/* Un seul bouton, deux rôles — donc deux libellés. Barre dépliée, il la réduit ;
   barre réduite (dépliée le temps d'un survol), il la garde ouverte. */
.nav-bascule__mot--garder { display: none; }
:root[data-nav="reduit"] .nav-bascule__mot--reduire { display: none; }
:root[data-nav="reduit"] .nav-bascule__mot--garder { display: block; }

/* Repli : icône et petit libellé restent ensemble. Réservé au grand écran — en
   dessous, la barre devient un ruban horizontal et le repli n'a plus de sens. */
@media (min-width: 901px) {
    :root[data-nav="reduit"] .app { grid-template-columns: 84px minmax(0, 1fr); }

    /* Sortie de la grille, la barre ne consomme plus sa cellule : sans cette ligne,
       la colonne principale remonterait en première colonne, sous la barre. */
    :root[data-nav="reduit"] .app__principal { grid-column: 2; }

    /* Réduite, la barre sort de la grille et se pose au-dessus du contenu. La colonne
       de 84 px lui reste réservée : quand le survol la déplie, elle glisse par-dessus
       la page au lieu de la repousser — rien ne bouge sous le curseur. */
    :root[data-nav="reduit"] .app__lateral {
        position: fixed; top: 0; left: 0; z-index: 20;
        width: 84px;
        overflow-x: hidden;
        transition: width var(--pp-transition), padding var(--pp-transition),
                    box-shadow var(--pp-transition);
    }

    /* Dépliée : la barre reprend sa largeur pleine. L'attribut « data-nav-ouvert » est
       posé par pharma.js à l'entrée de la souris (ou du focus) plutôt que par un
       « :hover » de feuille de style — sinon, le pointeur restant sur la barre après un
       clic sur « Réduire », celle-ci se rouvrirait aussitôt et le bouton paraîtrait sans
       effet. Les règles de repli qui suivent portent toutes « :not([data-nav-ouvert]) » :
       elles cessent alors de s'appliquer et la barre redevient d'elle-même celle du mode
       déplié, libellés compris. */
    :root[data-nav="reduit"][data-nav-ouvert] .app__lateral {
        width: 264px;
        box-shadow: var(--pp-ombre-3);
    }

    :root[data-nav="reduit"]:not([data-nav-ouvert]) .app__lateral { padding-inline: .5rem; }
    :root[data-nav="reduit"]:not([data-nav-ouvert]) .app__lateral .app__marque {
        display: flex; justify-content: center; padding-inline: 0;
    }
    :root[data-nav="reduit"]:not([data-nav-ouvert]) .app__lateral .marque__texte { display: none; }

    /* Chaque catégorie devient une petite tuile verticale. Le libellé reste lisible
       sous l'icône, en minuscules, et l'espace sépare franchement deux destinations. */
    :root[data-nav="reduit"]:not([data-nav-ouvert]) .app__lateral .nav { gap: .1rem; }
    :root[data-nav="reduit"]:not([data-nav-ouvert]) .app__lateral .nav__lien {
        min-height: 44px; padding: .3rem .15rem; flex-direction: column;
        justify-content: center; align-items: center; gap: .15rem; text-align: center;
    }
    :root[data-nav="reduit"]:not([data-nav-ouvert]) .app__lateral .nav__lien svg { width: 18px; height: 18px; }
    :root[data-nav="reduit"]:not([data-nav-ouvert]) .app__lateral .nav__lien > span:not(.nav__badge):not(.nav__pastille) {
        display: block; width: 100%; font-size: .58rem; font-weight: 650;
        line-height: 1.12; text-transform: lowercase; overflow-wrap: anywhere;
    }
    :root[data-nav="reduit"]:not([data-nav-ouvert]) .app__lateral .nav__badge { display: none; }
    :root[data-nav="reduit"]:not([data-nav-ouvert]) .app__lateral .nav__groupe { display: none; }

    /* Un seul bouton à deux états; les deux métiers restent visibles. */
    :root[data-nav="reduit"]:not([data-nav-ouvert]) .app__lateral .nav__mode {
        flex: none; margin: 0 0 .25rem; padding: 0;
    }
    :root[data-nav="reduit"]:not([data-nav-ouvert]) .app__lateral .nav__mode-bascule { grid-template-columns: 1fr; }
    :root[data-nav="reduit"]:not([data-nav-ouvert]) .app__lateral .nav__mode-bascule::before {
        width: calc(100% - 6px); height: calc(50% - 3px);
    }
    :root[data-nav="reduit"]:not([data-nav-ouvert]) .app__lateral .nav__mode-bascule--prescription::before {
        transform: translateY(100%);
    }
    :root[data-nav="reduit"]:not([data-nav-ouvert]) .app__lateral .nav__mode-etat {
        min-height: 37px; padding: .2rem .1rem; flex-direction: column; gap: .1rem;
    }
    :root[data-nav="reduit"]:not([data-nav-ouvert]) .app__lateral .nav__mode-etat > span { font-size: .54rem; line-height: 1.1; }
    :root[data-nav="reduit"]:not([data-nav-ouvert]) .app__lateral .nav__mode-etat svg { width: 14px; height: 14px; }

    /* Le dépliant « Paramètres » : icône seule, sans chevron ni décalage, et toujours
       calé au bas puisque son libellé de groupe est masqué. */
    :root[data-nav="reduit"]:not([data-nav-ouvert]) .app__lateral .nav__repli { margin-top: auto; }
    :root[data-nav="reduit"]:not([data-nav-ouvert]) .app__lateral .nav__repli__tete::after { display: none; }
    :root[data-nav="reduit"]:not([data-nav-ouvert]) .app__lateral .nav__repli__corps .nav__lien { padding-left: 0; }

    /* Le bouton lui-même : centré sur son icône. */
    :root[data-nav="reduit"]:not([data-nav-ouvert]) .app__lateral .nav-bascule { justify-content: center; padding-inline: 0; }
    :root[data-nav="reduit"]:not([data-nav-ouvert]) .app__lateral .nav-bascule > span { display: none; }
}

/* Ruban horizontal (petit écran) : pas de repli, le bouton n'a pas lieu d'être. */
@media (min-width: 901px) and (max-height: 850px) {
    :root[data-nav="reduit"]:not([data-nav-ouvert]) .app__lateral { gap: .4rem; padding-block: .55rem; }
    :root[data-nav="reduit"]:not([data-nav-ouvert]) .app__lateral .nav { gap: 0; }
    :root[data-nav="reduit"]:not([data-nav-ouvert]) .app__lateral .nav__lien { min-height: 38px; padding-block: .15rem; gap: .1rem; }
    :root[data-nav="reduit"]:not([data-nav-ouvert]) .app__lateral .nav__lien svg { width: 16px; height: 16px; }
    :root[data-nav="reduit"]:not([data-nav-ouvert]) .app__lateral .nav__mode-etat { min-height: 32px; }
}

@media (max-width: 900px) {
    .nav-bascule { display: none; }
}

/* -----------------------------------------------------------------------------
   Scanner une carte — champ centré dans l'en-tête
   -------------------------------------------------------------------------- */
/* La douchette tape le numéro et envoie « Entrée » : ce champ soumet vers le suivi,
   qui reconnaît un code à douze chiffres et ouvre la fiche. Centré entre le contexte
   et les outils, tous deux à parts égales de chaque côté. */
/* Colonne centrale de l'en-tête : ses marges automatiques absorbent l'espace libre de
   part et d'autre — le scan se pose au milieu, entre le nom de la pharmacie et les
   outils, et ces derniers restent calés à droite. Vide (console, sans accès suivi),
   elle se réduit à un simple ressort qui pousse les outils au bord. */
.app__centre { margin-inline: auto; display: flex; justify-content: center; min-width: 0; }

.scan {
    display: flex;
    align-items: center;
    gap: .5rem;
    width: clamp(180px, 24vw, 300px);
    min-width: 0;
    padding: .35rem .8rem;
    border: 1px solid var(--pp-bordure-forte);
    border-radius: var(--pp-r-full);
    background: var(--pp-surface);
    transition: border-color var(--pp-transition), box-shadow var(--pp-transition);
}
.scan:focus-within {
    border-color: var(--pp-brand-500);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--pp-brand-500) 18%, transparent);
}
.scan__icone { display: flex; flex: 0 0 auto; color: var(--pp-texte-3); }
.scan__icone svg { width: 18px; height: 18px; }
.scan:focus-within .scan__icone { color: var(--pp-brand-500); }
.scan__saisie {
    flex: 1 1 auto;
    min-width: 0;
    border: 0;
    background: transparent;
    color: var(--pp-texte);
    font: inherit;
    font-size: .875rem;
}
.scan__saisie:focus { outline: none; }
.scan__saisie::placeholder { color: var(--pp-texte-3); }

/* En dessous, l'en-tête devient trop étroit pour trois zones : le scan s'efface. */
@media (max-width: 820px) {
    .scan { display: none; }
}

/* =============================================================================
   LE POSTE DE MONTAGE (V-Assyst)

   Un écran de travail, pas de consultation : la personne est debout, les mains
   prises, et regarde de loin. D'où des lignes hautes, des couleurs franches, et
   un code de couleur qui se lit sans se déchiffrer.

   L'aide-mémoire des couleurs, repris de l'appareil de bureau :
     vert       validé au lecteur          orange   validé sans le lecteur
     jaune      produit manquant           rouge    générique de remplacement
     vert pâle  hors pilulier              neutre   reste à faire

   Le plateau lumineux est un CONFORT : tout le poste fonctionne sans lui, les
   alvéoles s'allumant à l'écran. Rien ici ne dépend du port série.
   ============================================================================= */

/* --- Lecture d'une feuille, à l'accueil du poste ------------------------- */
.montage-lecture { display: flex; flex-direction: column; gap: .4rem; }
.montage-lecture__ligne { display: flex; gap: .6rem; align-items: stretch; flex-wrap: wrap; }
.montage-lecture__boite { flex: 1 1 16rem; min-width: 0; }
.montage-lecture__boite .champ__saisie {
    font-size: 1.15rem;
    font-variant-numeric: tabular-nums;
    letter-spacing: .06em;
}

/* Les feuilles scannées se posent CÔTE À CÔTE, comme les plateaux du poste : jusqu'à
   quatre sur une rangée. auto-fit + minmax : une seule feuille prend toute la largeur,
   deux se partagent la ligne, quatre tiennent de front dès que l'écran le permet ; faute
   de largeur, elles reviennent à la ligne d'elles-mêmes. */
.montage-retenues {
    list-style: none;
    margin: 1rem 0 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: .6rem;
    align-items: stretch;
}
.montage-retenue {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: .7rem .9rem;
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-md);
    background: var(--pp-surface-2);
}
.montage-retenue__corps { min-width: 0; }
.montage-retenue__meta {
    display: flex;
    align-items: center;
    gap: .5rem;
    flex-wrap: wrap;
    margin-top: .3rem;
    font-size: .82rem;
}

/* La douchette peut envoyer plusieurs Entrée avant que la première requête ne
   revienne. Le suivi reste compact sous le champ : il renseigne sans déplacer
   la main ni voler le focus à la lecture suivante. */
.montage-lecture__etat {
    margin-top: .55rem;
    min-height: 1.3rem;
    color: var(--pp-texte-3);
    font-size: .88rem;
    font-weight: 600;
}

/* Les erreurs de la rafale restent ensemble. Une mauvaise feuille ne masque
   donc pas la précédente et, surtout, n'arrête pas celles qui la suivent. */
.montage-erreurs {
    margin-top: .8rem;
    padding: .75rem .85rem;
    border: 1px solid color-mix(in srgb, var(--pp-danger) 32%, transparent);
    border-radius: var(--pp-r-md);
    background: var(--pp-danger-bg);
    color: var(--pp-danger);
}
.montage-erreurs__entete,
.montage-erreurs__entete > div {
    display: flex;
    align-items: center;
    gap: .55rem;
}
.montage-erreurs__entete { justify-content: space-between; }
.montage-erreurs__compte {
    border-color: color-mix(in srgb, var(--pp-danger) 30%, transparent);
    background: color-mix(in srgb, var(--pp-danger) 12%, transparent);
    color: var(--pp-danger);
}
.montage-erreurs__liste {
    margin: .65rem 0 0;
    padding: 0;
    list-style: none;
    display: grid;
    gap: .35rem;
}
.montage-erreurs__liste li {
    display: grid;
    grid-template-columns: minmax(5.5rem, auto) 1fr auto;
    align-items: center;
    gap: .65rem;
    padding-top: .35rem;
    border-top: 1px solid color-mix(in srgb, var(--pp-danger) 18%, transparent);
    font-size: .88rem;
}
.montage-erreurs__liste code { color: inherit; font-weight: 700; }
.montage-erreurs__action { justify-self: end; }

@media (max-width: 720px) {
    .montage-erreurs__liste li { grid-template-columns: 1fr; }
    .montage-erreurs__action { justify-self: start; }
}

@keyframes montage-retenue-entree {
    from { opacity: 0; transform: translateY(-.35rem); }
    to { opacity: 1; transform: translateY(0); }
}
.montage-retenue--nouvelle {
    animation: montage-retenue-entree .22s ease-out both;
}

@media (prefers-reduced-motion: reduce) {
    .montage-retenue--nouvelle { animation: none; }
}

.montage-jauge {
    height: 6px;
    border-radius: var(--pp-r-full);
    background: var(--pp-surface-3);
    overflow: hidden;
    min-width: 6rem;
}
.montage-jauge__barre {
    display: block;
    height: 100%;
    background: var(--pp-ok);
    border-radius: inherit;
}

/* --- Le poste ------------------------------------------------------------ */
.poste { display: flex; flex-direction: column; gap: .9rem; }

/* Le bandeau se règle sur ses boutons : il n'a aucune raison d'être plus haut
   qu'eux. Le message de lecture voyage donc À CÔTÉ du champ, sur la même
   ligne, au lieu de s'empiler dessous et d'ajouter une rangée de vide. */
.poste__barre {
    position: sticky;
    top: 0;
    z-index: 5;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .8rem;
    flex-wrap: wrap;
    padding: .45rem .7rem;
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-lg);
    background: var(--pp-verre);
    backdrop-filter: blur(10px);
    box-shadow: var(--pp-ombre-2);
}

/* Le champ de lecture ne se tape pas : la douchette y écrit et disparaît. Il
   n'a donc besoin que d'assez de place pour montrer un code — le reste de la
   barre appartient aux boutons, qu'on vise avec le doigt, parfois ganté. */
.poste__lecture {
    display: flex;
    align-items: center;
    gap: .6rem;
    flex: 1 1 auto;
    min-width: 0;
}
.poste__boite { flex: 0 0 14rem; }
.poste__boite .champ__saisie {
    font-size: 1rem;
    padding-block: .35rem;
    font-variant-numeric: tabular-nums;
    letter-spacing: .04em;
}

/* Le message dit ce qui vient d'être lu — ou ce qui a échoué. Sur la même
   ligne que le champ : il ne fait donc plus monter le bandeau, et un nom
   trop long se coupe plutôt que de pousser les boutons. */
.poste__message {
    margin: 0;
    min-width: 0;
    font-size: .88rem;
    color: var(--pp-texte-3);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.poste__message--ok { color: var(--pp-ok); font-weight: 600; }
.poste__message--erreur { color: var(--pp-danger); font-weight: 600; }

/* Les boutons du poste se visent debout, à bout de bras, parfois avec un gant :
   ils ont la taille d'une cible, pas celle d'un lien. */
.poste__actions {
    display: flex;
    gap: .5rem;
    flex-wrap: wrap;
    align-items: center;
    flex: 1 1 auto;
    justify-content: flex-end;
}
.poste__actions .btn {
    min-height: 3rem;
    padding-inline: 1.1rem;
    font-size: 1rem;
    font-weight: 600;
}
.poste__actions .btn svg { width: 20px; height: 20px; }

/* Défaire n'a que sa flèche : le geste est universel, le mot était de trop
   dans une barre où chaque millimètre revient aux boutons qu'on lit. Le titre
   et l'étiquette accessible portent le nom — rien n'est perdu pour qui
   survole, ni pour un lecteur d'écran. */
.poste__actions .btn--carre {
    width: 3rem;
    padding-inline: 0;
    justify-content: center;
}

/* Le brancher-plateau reste discret : il ne sert qu'une fois par séance. */
.poste__actions .btn--petit {
    min-height: 2.2rem;
    padding-inline: .7rem;
    font-size: .85rem;
    font-weight: 500;
}
.poste__actions .btn--petit svg { width: 16px; height: 16px; }

.btn--manquant { border-color: var(--pp-warn); color: var(--pp-warn); }
.btn--hors { border-color: var(--pp-ok); color: var(--pp-ok); }

/* --- Les plateaux : l'écran se partage comme la table de travail ---------
   Un plateau prend toute la page. Deux la coupent en deux, gauche et droite.
   Trois ou quatre la coupent en quatre — 1 et 2 en haut, 3 et 4 en dessous,
   dans l'ordre où les feuilles ont été lues. C'est la disposition de l'appareil
   de bureau, et c'est celle des plateaux posés devant la personne.
   ------------------------------------------------------------------------- */
.poste__plateaux { display: grid; gap: .9rem; }
.poste__plateaux--1 { grid-template-columns: minmax(0, 1fr); }
.poste__plateaux--2,
.poste__plateaux--3,
.poste__plateaux--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }

/* Dès qu'il y a plus d'un carton, le poste tient dans l'écran : c'est la
   LISTE qui défile à l'intérieur de son panneau, jamais la page. Faire
   descendre la page ferait sortir le bandeau de lecture du champ de vision —
   et avec lui le seul endroit où la douchette écrit. */
@media (min-width: 1101px) {
    .poste__plateaux {
        height: calc(100vh - 11rem);
        min-height: 34rem;
    }
    .poste__plateaux--3,
    .poste__plateaux--4 { grid-template-rows: repeat(2, minmax(0, 1fr)); }

    .poste__plateaux .plateau { min-height: 0; }
}

/* Sous 1100 px, deux panneaux côte à côte deviennent illisibles : ils
   s'empilent, et chacun retrouve sa pleine largeur. */
@media (max-width: 1100px) {
    .poste__plateaux--2,
    .poste__plateaux--3,
    .poste__plateaux--4 { grid-template-columns: minmax(0, 1fr); }
}

.plateau {
    display: flex;
    flex-direction: column;
    gap: .7rem;
    padding: .9rem;
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-lg);
    background: var(--pp-surface);
    box-shadow: var(--pp-ombre-1);
    min-width: 0;
    overflow: hidden;
}

/* Un carton terminé s'efface sans disparaître : il reste à l'écran le temps
   qu'on retire le plateau, mais il n'appelle plus le regard. */
.plateau--termine { opacity: .55; }

.plateau__entete {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: .8rem;
    flex-wrap: wrap;
}
.plateau__patient { margin: 0; font-size: 1.05rem; }
.plateau__meta {
    display: flex;
    gap: .5rem;
    flex-wrap: wrap;
    margin-top: .25rem;
    font-size: .8rem;
    color: var(--pp-texte-3);
}
.plateau__etat { display: flex; gap: .4rem; align-items: center; flex-wrap: wrap; }

.plateau__note {
    display: flex;
    gap: .5rem;
    align-items: flex-start;
    margin: 0;
    padding: .5rem .7rem;
    border-radius: var(--pp-r-sm);
    background: var(--pp-info-bg);
    color: var(--pp-info);
    font-size: .85rem;
}

/* --- Les alvéoles -------------------------------------------------------- */
/* Le carton en miniature. Les cases s'allument au fil des lectures : c'est le
   geste du plateau lumineux, rendu à qui n'en a pas. */
.alveoles {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: .7rem;
    border-radius: var(--pp-r-md);
    background: var(--pp-surface-2);
    border: 1px solid var(--pp-bordure);
}
/* Toujours quatre colonnes sur sept rangées : le carton compte vingt-huit
   alvéoles, quelle que soit sa disposition. C'est DispositionCarte qui dit
   quelle journée et quel temps de prise chaque case porte — et la rangée du
   bas porte la date la plus ancienne, comme à la création du pilulier. */
.alveoles__tete,
.alveoles__jour {
    display: grid;
    grid-template-columns: 1.9rem repeat(4, minmax(0, 1fr));
    gap: 4px;
    align-items: stretch;
}

/* Au-delà des journées couvertes, l'alvéole est sortie vide de l'imprimante :
   la grille le montre plutôt que de laisser croire à une case à remplir. */
.alveole--hors {
    background: transparent;
    border: 1px dashed var(--pp-bordure);
}
.alveoles__tete span {
    font-size: .72rem;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--pp-texte-3);
    text-align: center;
    padding-bottom: .25rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.alveoles__no {
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: .8rem;
    font-weight: 600;
    color: var(--pp-texte-3);
    font-variant-numeric: tabular-nums;
}
/* Des cases hautes : la quantité se lit debout, à un mètre de l'écran, et c'est
   le chiffre qui compte — pas la case. Un pilulier compressé n'a qu'une ou deux
   colonnes pour vingt-huit jours : les rangées y gagnent encore en hauteur. */
.alveole {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 2.35rem;
    border-radius: var(--pp-r-sm);
    background: var(--pp-surface-3);
    font-size: 1.05rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    line-height: 1;
    color: transparent;
    transition: background var(--pp-transition), color var(--pp-transition);
}
/* Une alvéole allumée : c'est là que le comprimé va. */
.alveole--active {
    background: var(--pp-brand-500);
    color: #fff;
    box-shadow: 0 0 0 2px var(--pp-brand-200);
}
/* La quantité N'EST PAS 1 : la case bat, comme le plateau du V-Assyst.
   C'est l'avertissement qui compte le plus de tout l'écran — une demi-dose ou
   deux comprimés se ratent bien plus facilement qu'un comprimé unique.

   Le battement n'est PAS une animation par case : une animation démarre quand
   la classe arrive, et quatre cartons éclairés l'un après l'autre battraient
   chacun pour soi — l'écran se met à grésiller. C'est une seule horloge, en
   JavaScript, qui pose « poste--battement » sur le socle. Toutes les cases
   changent alors dans la même image, sur tous les cartons à la fois. */
.alveole--multiple {
    background: var(--pp-warn);
    color: #1a1206;
    box-shadow: 0 0 0 3px var(--pp-warn);
    transition: none;
}
.poste--battement .alveole--multiple {
    background: var(--pp-surface);
    color: var(--pp-warn);
}

/* --- Les molécules ------------------------------------------------------- */
/* --- Le corps d'un plateau : la liste à gauche, le carton à droite -------
   La disposition de l'appareil de bureau. On lit la ligne à gauche, on voit
   où le comprimé tombe à droite : l'œil ne descend pas puis ne remonte pas.
   Le carton garde une largeur fixe — il représente un objet réel, il n'a pas
   à s'étirer ; toute la place gagnée revient aux noms de médicaments.
   ------------------------------------------------------------------------- */
.plateau__corps {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(15rem, 22rem);
    gap: .8rem;
    align-items: start;
    flex: 1 1 auto;
    min-height: 0;
}

/* Un seul carton : la page entière est à lui. La grille reçoit presque la
   moitié du panneau, jusqu'à 52 rem. La liste garde encore assez de largeur
   pour le nom et les quatre prises, mais son grand espace vide pèse moins dans
   l'écran quand le patient prend peu de médicaments. */
@media (min-width: 1101px) {
    .poste__plateaux--1 .plateau__corps {
        grid-template-columns: minmax(0, 1fr) clamp(32rem, 46%, 52rem);
        gap: 1.2rem;
    }
    .poste__plateaux--1 .alveole { min-height: 3.1rem; font-size: 1.2rem; }
    .poste__plateaux--1 .alveoles { padding: .9rem; gap: 6px; }
    .poste__plateaux--1 .alveoles__tete,
    .poste__plateaux--1 .alveoles__jour {
        grid-template-columns: 2.2rem repeat(4, minmax(0, 1fr));
        gap: 6px;
    }
    .poste__plateaux--1 .alveoles__tete span { font-size: .78rem; }
    .poste__plateaux--1 .alveoles__no { font-size: .95rem; }
}

/* La liste prend ce qui reste du panneau et défile SEULE : c'est elle qui
   cède, jamais l'en-tête ni le carton, et jamais la page. */
.plateau__liste { min-height: 0; max-height: 30rem; overflow-y: auto; }

@media (min-width: 1101px) {
    .poste__plateaux .plateau__corps { height: 100%; min-height: 0; }
    .poste__plateaux .plateau__liste { max-height: none; height: 100%; }
}

/* Sous 1100 px le panneau occupe déjà toute la largeur : le carton repasse
   sous la liste plutôt que de l'écraser. */
@media (max-width: 1100px) {
    .plateau__corps { grid-template-columns: minmax(0, 1fr); }
}
.tableau--serre td, .tableau--serre th { padding: .55rem .6rem; }
.plateau__col-no { width: 2rem; text-align: right; color: var(--pp-texte-3); }

/* Les quantités sont ce qu'on vient chercher sur cette ligne : elles ont droit
   à la place et au corps qu'il faut pour se lire d'un coup d'œil. */
.plateau__col-prise {
    width: 4rem;
    text-align: center;
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    font-size: 1.05rem;
}
th.plateau__col-prise { font-size: .72rem; font-weight: 600; }

/* --- Panneaux côte à côte : tout se resserre, rien ne disparaît ----------
   À deux, trois ou quatre cartons, chaque panneau n'a plus qu'une demi-page.
   Les colonnes de quantité et les alvéoles se resserrent — le nom du
   médicament, lui, garde la place qu'on lui laisse : c'est ce qu'on lit.
   ------------------------------------------------------------------------- */
@media (min-width: 1101px) {
    /* À DEUX, chaque panneau tient la demi-largeur mais toute la hauteur : la
       liste passe DESSUS et le carton DESSOUS. Côte à côte, ni l'une ni
       l'autre n'aurait eu la place — empilés, tous deux respirent. */
    /* Le CARTON garde toujours la même taille — c'est un objet réel, posé sur
       la table : un patient qui prend moins de médicaments n'a pas un plus
       grand pilulier. La main s'habitue à viser toujours au même endroit, et
       cette habitude vaut mieux que quelques pixels gagnés.

       C'est donc la LISTE qui absorbe la différence : elle prend tout le reste
       et défile si elle est longue. */
    .poste__plateaux--2 .plateau__corps {
        grid-template-columns: minmax(0, 1fr);
        grid-template-rows: minmax(0, 1fr) auto;
        gap: .6rem;
    }

    /* Le carton sort du jeu d'étirement commun : hauteur de ses rangées, point. */
    /* Le carton se centre sous la liste et ne s.étire pas au-delà du
       raisonnable : au-delà, les alvéoles deviennent des rectangles. */
    .poste__plateaux--2 .alveoles { max-width: 34rem; margin-inline: auto; width: 100%; }
    .poste__plateaux--2 .alveole { min-height: 2.75rem; font-size: 1.05rem; }

    /* À TROIS ou QUATRE, le panneau n'a plus que le quart de l'écran : on
       revient au côte-à-côte, seule façon de garder les deux visibles.

       La liste se resserre pour que le CARTON prenne toute la hauteur du
       panneau : ses vingt-huit alvéoles se répartissent la place au lieu de
       rester tassées en haut. C'est le carton qu'on regarde en déposant le
       comprimé — la liste, on l'a déjà lue. */
    .poste__plateaux--3 .plateau__corps,
    .poste__plateaux--4 .plateau__corps {
        grid-template-columns: minmax(0, 1fr) minmax(15rem, 21rem);
        gap: .6rem;
        align-items: stretch;
    }

    /* Le carton s'étire, quel que soit le nombre de plateaux : ses sept
       rangées se partagent la hauteur du panneau au lieu de rester tassées
       en haut. Une hauteur plancher reste posée par mode — en dessous, la
       case cesserait d'être lisible et mieux vaut alors laisser la liste
       défiler. */
    .poste__plateaux .plateau__corps { align-items: stretch; }
    .poste__plateaux .alveoles { height: 100%; min-height: 0; }
    .poste__plateaux .alveoles__jour { flex: 1 1 0; min-height: 0; }
    .poste__plateaux .alveole { height: 100%; }

    /* Le mode DOUBLE se retire de cet étirement : son carton garde toujours la
       même taille, quel que soit le nombre de médicaments. Un patient qui en
       prend moins n'a pas un plus grand pilulier, et la main s'habitue à viser
       toujours au même endroit. Écrit APRÈS la règle commune : à spécificité
       égale, c'est l'ordre qui tranche. */
    .poste__plateaux--2 .alveoles { height: auto; }
    .poste__plateaux--2 .alveoles__jour { flex: 0 0 auto; }
    .poste__plateaux--2 .alveole { height: auto; }

    .poste__plateaux--3 .plateau__col-prise,
    .poste__plateaux--4 .plateau__col-prise { width: 3.25rem; font-size: .9rem; }

    /* Les quatre prises doivent tenir dans la liste visible. Le nom occupe
       la largeur restante et revient à la ligne au besoin. */
    .poste__plateaux--3 .plateau__liste .tableau,
    .poste__plateaux--4 .plateau__liste .tableau { table-layout: fixed; }
    .poste__plateaux--3 .plateau__liste th:nth-child(3),
    .poste__plateaux--3 .plateau__liste td:nth-child(3),
    .poste__plateaux--4 .plateau__liste th:nth-child(3),
    .poste__plateaux--4 .plateau__liste td:nth-child(3) { white-space: normal; overflow-wrap: anywhere; }
    .poste__plateaux--3 th.plateau__col-prise,
    .poste__plateaux--4 th.plateau__col-prise { font-size: .65rem; letter-spacing: 0; }

    /* --- Des rangées basses, pour que tout tienne dans l'écran -----------
       À plusieurs cartons, chaque ligne de produit doit se contenter du
       strict nécessaire : le nom, sa vignette, ses quantités. Rien n'est
       retiré — c'est l'air autour qui part, pas l'information. */
    .poste__plateaux--2 .tableau--serre td,
    .poste__plateaux--2 .tableau--serre th,
    .poste__plateaux--3 .tableau--serre td,
    .poste__plateaux--3 .tableau--serre th,
    .poste__plateaux--4 .tableau--serre td,
    .poste__plateaux--4 .tableau--serre th { padding: .2rem .3rem; }

    .poste__plateaux--2 .molecule__vignette--mini,
    .poste__plateaux--3 .molecule__vignette--mini,
    .poste__plateaux--4 .molecule__vignette--mini { width: 24px; height: 24px; }

    .poste__plateaux--2 .plateau__col-photo,
    .poste__plateaux--3 .plateau__col-photo,
    .poste__plateaux--4 .plateau__col-photo { width: 26px; }

    .poste__plateaux--2 .ligne-mol__nom,
    .poste__plateaux--3 .ligne-mol__nom,
    .poste__plateaux--4 .ligne-mol__nom { font-size: .82rem; line-height: 1.15; }

    .poste__plateaux--2 .ligne-mol__meta,
    .poste__plateaux--3 .ligne-mol__meta,
    .poste__plateaux--4 .ligne-mol__meta { font-size: .68rem; gap: .3rem; }

    /* L'en-tête du panneau se resserre aussi : c'est de la hauteur prise à
       la liste, et le nom du patient reste parfaitement lisible. */
    .poste__plateaux--2 .plateau,
    .poste__plateaux--3 .plateau,
    .poste__plateaux--4 .plateau { padding: .6rem; gap: .45rem; }

    .poste__plateaux--2 .plateau__patient,
    .poste__plateaux--3 .plateau__patient,
    .poste__plateaux--4 .plateau__patient { font-size: .95rem; }

    .poste__plateaux--3 .alveole,
    .poste__plateaux--4 .alveole { min-height: 1.5rem; font-size: .9rem; }

    .poste__plateaux--3 .alveoles,
    .poste__plateaux--4 .alveoles { padding: .45rem; gap: 3px; }

    .poste__plateaux--3 .alveoles__tete,
    .poste__plateaux--3 .alveoles__jour,
    .poste__plateaux--4 .alveoles__tete,
    .poste__plateaux--4 .alveoles__jour { grid-template-columns: 1.4rem repeat(4, minmax(0, 1fr)); gap: 3px; }

    .poste__plateaux--3 .alveoles__tete span,
    .poste__plateaux--4 .alveoles__tete span { font-size: .62rem; }
}

/* La vignette du produit, comme dans la grille du suivi mais plus petite : au
   poste, la colonne doit rester étroite pour laisser la place aux quantités. */
.plateau__col-photo { width: 34px; padding-right: 0; }
.molecule__vignette--mini { width: 30px; height: 30px; }
.molecule__vignette--mini svg { width: 15px; height: 15px; }

.ligne-mol { cursor: pointer; }
.ligne-mol:focus-visible { outline: 2px solid var(--pp-focus); outline-offset: -2px; }
.ligne-mol__nom { font-weight: 600; line-height: 1.25; }
.ligne-mol__meta {
    display: flex;
    gap: .45rem;
    flex-wrap: wrap;
    align-items: center;
    font-size: .76rem;
    color: var(--pp-texte-3);
}
.ligne-mol__comm {
    font-size: .78rem;
    color: var(--pp-info);
    font-style: italic;
}
.ligne-mol__substitut:empty { display: none; }
.ligne-mol__substitut {
    display: flex;
    gap: .35rem;
    align-items: center;
    flex-wrap: wrap;
    margin-top: .2rem;
    font-size: .76rem;
    color: var(--pp-danger);
    font-weight: 600;
}

/* Le code de couleur. Une bande à gauche double le fond : la couleur se voit
   de loin, et le texte reste lisible dans les deux ambiances. */
.ligne-mol--attente > td:first-child { box-shadow: inset 3px 0 0 var(--pp-bordure-forte); }
.ligne-mol--scanne > td:first-child { box-shadow: inset 3px 0 0 var(--pp-ok); }
.ligne-mol--clique > td:first-child { box-shadow: inset 3px 0 0 var(--pp-warn); }
.ligne-mol--manquant > td:first-child { box-shadow: inset 3px 0 0 var(--pp-warn); }
.ligne-mol--substitue > td:first-child { box-shadow: inset 3px 0 0 var(--pp-danger); }
.ligne-mol--hors-pilulier > td:first-child { box-shadow: inset 3px 0 0 var(--pp-ok); }

.ligne-mol--scanne { background: color-mix(in srgb, var(--pp-ok-bg) 70%, transparent); }
.ligne-mol--clique { background: color-mix(in srgb, var(--pp-warn-bg) 62%, transparent); }
.ligne-mol--manquant { background: color-mix(in srgb, var(--pp-warn-bg) 92%, transparent); }
.ligne-mol--manquant .ligne-mol__nom { text-decoration: line-through; }
.ligne-mol--substitue { background: color-mix(in srgb, var(--pp-danger-bg) 62%, transparent); }
/* HORS PILULIER : le produit est sur la carte mais n'entre dans aucune alvéole
   — il se sert à côté. C'est une information qu'on ne doit pas rater : à 42 %
   d'un vert déjà pâle, la ligne se confondait avec une ligne validée. Elle a
   maintenant sa teinte pleine, son filet à gauche et sa mention en toutes
   lettres, pour qu'aucun coup d'œil ne la prenne pour autre chose. */
.ligne-mol--hors-pilulier {
    background: color-mix(in srgb, var(--pp-ok) 20%, var(--pp-surface));
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--pp-ok) 45%, transparent);
}
.ligne-mol--hors-pilulier > td:first-child { box-shadow: inset 4px 0 0 var(--pp-ok); }
.ligne-mol--hors-pilulier .ligne-mol__nom { color: var(--pp-ok); }

/* Les chiffres s'effacent : ce produit n'occupe aucune alvéole. */
.ligne-mol--hors-pilulier .plateau__col-prise { color: var(--pp-texte-3); opacity: .5; }

/* --- L'aide-mémoire ------------------------------------------------------ */
.poste__legende {
    display: flex;
    gap: .9rem;
    flex-wrap: wrap;
    padding: .6rem .9rem;
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-md);
    background: var(--pp-surface-2);
    font-size: .78rem;
    color: var(--pp-texte-3);
}
.legende { display: inline-flex; align-items: center; gap: .4rem; }
.legende::before {
    content: '';
    width: .85rem;
    height: .85rem;
    border-radius: var(--pp-r-xs);
    background: var(--pp-bordure-forte);
}
.legende--scanne::before { background: var(--pp-ok); }
.legende--clique::before { background: var(--pp-warn); }
.legende--manquant::before { background: var(--pp-warn-bg); border: 2px solid var(--pp-warn); }
.legende--substitue::before { background: var(--pp-danger); }
.legende--hors-pilulier::before { background: color-mix(in srgb, var(--pp-ok) 40%, transparent); }
.legende--chronopac::before { background: var(--pp-info); }
.legende--farmadosis::before { background: var(--pp-brand-500); }

/* --- Le générique de remplacement ---------------------------------------- */
/* Une balise <dialog> native, et non le « .modale » du module d'impression :
   celui-ci est un calque en position fixe, dont le « display: grid » écraserait
   la règle du navigateur qui garde un dialogue fermé invisible. D'où un nom à
   part — la boîte ne doit s'ouvrir qu'au clic sur une molécule. */
.dialogue {
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-lg);
    background: var(--pp-surface);
    color: var(--pp-texte);
    box-shadow: var(--pp-ombre-3);
    padding: 0;
    max-width: min(38rem, 92vw);
    width: 100%;
}
.dialogue::backdrop { background: rgb(10 26 43 / .55); backdrop-filter: blur(2px); }
.dialogue__corps { display: flex; flex-direction: column; gap: .8rem; padding: 1.2rem; }
.dialogue__corps h2 { margin: 0; font-size: 1.1rem; }
.dialogue__erreur:empty { display: none; }
.dialogue__erreur {
    margin: 0;
    padding: .5rem .7rem;
    border-radius: var(--pp-r-sm);
    background: var(--pp-danger-bg);
    color: var(--pp-danger);
    font-size: .85rem;
    font-weight: 600;
}

/* -------------------------------------------------------------------------------------
   Changer un médicament EN LOT — un dialogue plus large : deux sélecteurs, un aperçu.
   ------------------------------------------------------------------------------------- */
.dialogue--large { max-width: min(46rem, 94vw); }

/* Ancien → nouveau, côte à côte ; la flèche au milieu. À l'étroit, tout s'empile. */
.echange__grille {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
    gap: .8rem;
    align-items: end;
}
.echange__fleche {
    display: flex;
    align-items: center;
    justify-content: center;
    padding-bottom: .55rem;
    color: var(--pp-texte-3);
}
.echange__fleche svg { width: 1.4rem; height: 1.4rem; }

@media (max-width: 34rem) {
    .echange__grille { grid-template-columns: minmax(0, 1fr); }
    .echange__fleche { padding: 0; transform: rotate(90deg); }
}

/* Un sélecteur : la boîte de saisie, puis la liste des suggestions en surimpression. */
.echange__picker { position: relative; }

.echange__suggestions {
    position: absolute;
    z-index: 5;
    top: calc(100% + .25rem);
    left: 0;
    right: 0;
    margin: 0;
    padding: .3rem;
    list-style: none;
    display: grid;
    gap: .25rem;
    max-height: 15rem;
    overflow-y: auto;
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-sm);
    background: var(--pp-surface);
    box-shadow: var(--pp-ombre-3);
}
.echange__suggestion {
    padding: .45rem .6rem;
    border-radius: var(--pp-r-sm);
    font-size: .85rem;
    line-height: 1.3;
    cursor: pointer;
}
.echange__suggestion:hover,
.echange__suggestion:focus { background: var(--pp-surface-2); outline: none; }
.echange__suggestion--vide { color: var(--pp-texte-3); cursor: default; }
.echange__suggestion--vide:hover { background: transparent; }

/* Le produit retenu, une fois choisi : une pastille discrète sous la saisie. */
.echange__choisi {
    margin: .4rem 0 0;
    padding: .4rem .6rem;
    border-radius: var(--pp-r-sm);
    background: var(--pp-ok-bg);
    color: var(--pp-ok);
    font-size: .85rem;
    font-weight: 600;
}

.echange__message { margin: 0; font-size: .85rem; }
.echange__message:empty { display: none; }
.echange__message--alerte { color: var(--pp-danger); font-weight: 600; }

.echange__apercu { display: grid; gap: .7rem; }
.echange__apercu[hidden] { display: none; }

/* Le sort des cartes déjà imprimées : un choix posé, jamais deviné. */
.echange__cartes {
    margin: 0;
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-sm);
    padding: .7rem .8rem;
    display: grid;
    gap: .45rem;
}
.echange__cartes[hidden] { display: none; }
.echange__cartes legend { padding: 0 .35rem; font-weight: 600; font-size: .9rem; }
.echange__choix {
    display: flex;
    gap: .55rem;
    align-items: flex-start;
    font-size: .875rem;
    line-height: 1.35;
    cursor: pointer;
}
.echange__choix input { margin-top: .15rem; flex: 0 0 auto; }

/* Résumé d'un échange sur la page « cartes à réimprimer » : ancien → nouveau. */
.echange-resume {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .6rem;
    margin-bottom: 1rem;
    font-size: .95rem;
}
.echange-resume__produit {
    padding: .35rem .7rem;
    border-radius: var(--pp-r-sm);
    background: var(--pp-surface-2);
    border: 1px solid var(--pp-bordure);
    font-weight: 600;
}
.echange-resume__produit--nouveau {
    background: var(--pp-ok-bg);
    color: var(--pp-ok);
    border-color: color-mix(in srgb, var(--pp-ok) 32%, transparent);
}
.echange-resume__fleche { display: inline-flex; color: var(--pp-texte-3); }
.echange-resume__fleche svg { width: 1.2rem; height: 1.2rem; }

/* Cellule d'action en bout de rangée : le bouton se cale à droite. */
.cellule-action { text-align: right; white-space: nowrap; }

/* La question d'abord, le remplaçant ensuite : deux temps dans la même boîte. */
.substitut__question p { margin: 0 0 .7rem; }
.substitut__choix-produit { display: flex; flex-direction: column; gap: .8rem; }

/* Qui a déjà l'autorité voit qui signe, au lieu qu'on lui redemande son mot de passe. */
.substitut__signataire {
    display: flex;
    gap: .5rem;
    align-items: center;
    margin: 0;
    padding: .55rem .7rem;
    border-radius: var(--pp-r-sm);
    background: var(--pp-ok-bg);
    color: var(--pp-ok);
    font-size: .85rem;
    font-weight: 600;
}

.substitut__resultats { display: flex; flex-direction: column; gap: .35rem; }
/* Un choix de produit : sa photo à gauche, son identité à droite. */
.substitut__choix {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: .6rem;
    text-align: left;
    padding: .55rem .7rem;
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-sm);
    background: var(--pp-surface-2);
    color: inherit;
    font: inherit;
    cursor: pointer;
}
.substitut__choix:hover { border-color: var(--pp-brand-400); }
.substitut__choix--retenu {
    border-color: var(--pp-brand-500);
    box-shadow: 0 0 0 2px var(--pp-brand-100);
}

/* Le produit existe mais sort de la liste d'équivalence : il reste choisissable,
   et se voit du premier coup d'œil. Refuser de l'afficher reviendrait à dire
   introuvable un produit qu'on a dans la main. */
.substitut__choix--hors { border-left: 3px solid var(--pp-warn); }
.substitut__choix--hors .etiquette { margin-top: .25rem; align-self: flex-start; }
.substitut__signature {
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-md);
    padding: .8rem;
    margin: 0;
}
.substitut__signature legend { padding: 0 .4rem; font-weight: 600; font-size: .85rem; }

/* --- Remplacement de code-barres ----------------------------------------- */
.transfert-scan { padding: clamp(1rem, 2.5vw, 1.5rem); }
.transfert-scan__grille {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
    align-items: stretch;
    gap: 1rem;
}
.transfert-scan__etape {
    min-width: 0;
    padding: 1rem;
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-md);
    background: var(--pp-surface-2);
}
.transfert-scan__entete {
    display: flex;
    align-items: flex-start;
    gap: .75rem;
    margin-bottom: 1rem;
}
.transfert-scan__entete h2 { margin: 0; font-size: 1.12rem; }
.transfert-scan__entete p {
    margin: .25rem 0 0;
    color: var(--pp-texte-2);
    font-size: .9rem;
}
.transfert-scan__numero {
    display: grid;
    place-items: center;
    flex: 0 0 2rem;
    width: 2rem;
    height: 2rem;
    border-radius: var(--pp-r-full);
    color: #fff;
    background: var(--pp-brand-600);
    font-weight: 700;
}
.transfert-scan__formulaire {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: end;
    gap: .65rem;
}
.transfert-scan__formulaire .champ__etiquette { grid-column: 1 / -1; }
.transfert-scan__fleche {
    display: grid;
    place-items: center;
    color: var(--pp-texte-3);
}
.transfert-scan__fleche .icone { width: 1.6rem; height: 1.6rem; }
.transfert-scan__fiche {
    display: grid;
    gap: .75rem;
    margin-top: 1rem;
    padding: .85rem;
    border: 1px solid color-mix(in srgb, var(--pp-brand-500) 24%, var(--pp-bordure));
    border-radius: var(--pp-r-sm);
    background: var(--pp-surface);
    box-shadow: var(--pp-ombre-1);
}
.transfert-scan__fiche-entete {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: .75rem;
}
.transfert-scan__fiche-entete code { flex: 0 0 auto; }
.transfert-scan__progression { display: grid; gap: .35rem; font-size: .86rem; }
.transfert-scan__progression progress {
    width: 100%;
    height: .7rem;
    accent-color: var(--pp-ok);
}
.transfert-scan__stats {
    display: flex;
    flex-wrap: wrap;
    gap: .45rem;
    align-items: center;
}
.transfert-scan__stat--scan {
    color: var(--pp-ok);
    border-color: color-mix(in srgb, var(--pp-ok) 35%, var(--pp-bordure));
    background: color-mix(in srgb, var(--pp-ok) 10%, transparent);
}
.transfert-scan__stat--clic {
    color: var(--pp-warn);
    border-color: color-mix(in srgb, var(--pp-warn) 35%, var(--pp-bordure));
    background: color-mix(in srgb, var(--pp-warn) 10%, transparent);
}
.transfert-scan__etat { margin: 1rem 0 0; }
.transfert-scan__actions {
    display: flex;
    justify-content: flex-end;
    gap: .65rem;
    margin-top: 1rem;
}

@media (max-width: 800px) {
    .transfert-scan__grille { grid-template-columns: 1fr; }
    .transfert-scan__fleche { transform: rotate(90deg); }
}

@media (max-width: 520px) {
    .transfert-scan__formulaire { grid-template-columns: 1fr; }
    .transfert-scan__formulaire .champ__etiquette { grid-column: auto; }
    .transfert-scan__formulaire .btn { width: 100%; }
    .transfert-scan__fiche-entete { flex-direction: column; }
}

/* Deux boutons serrés dans une cellule de tableau : « reprendre le lot » et
   « seule ». Ils tiennent sur une ligne tant que la colonne le permet. */
.barre-actions--serree {
    display: flex;
    gap: .35rem;
    flex-wrap: wrap;
    align-items: center;
}

/* --- Les lots de montage -------------------------------------------------
   Des cartons montés ensemble forment un BLOC visible : un bandeau qui les
   annonce, un filet coloré qui court le long de leurs lignes, et un seul
   bouton pour tout reprendre. On voit d'un coup d'œil lesquels vont ensemble
   — ce que quatre lignes indépendantes portant le même bouton ne disaient pas.
   ------------------------------------------------------------------------- */
.montage-lot { position: relative; }

.montage-lot__tete td {
    background: var(--pp-info-bg);
    border-top: 2px solid var(--pp-brand-500);
    padding-block: .5rem;
}
/* Le bandeau court sur toute la largeur du tableau : le libellé à gauche,
   le bouton du lot à droite. */
.montage-lot__bandeau {
    display: flex;
    align-items: center;
    gap: .5rem;
    flex-wrap: wrap;
}
.montage-lot__action { margin-left: auto; }
.montage-lot__bandeau > svg { color: var(--pp-brand-500); flex: none; }
.montage-lot__tete .attenue { font-size: .82rem; }

/* Le filet : il relie visuellement les lignes d'un même lot. */
.montage-lot__membre td:first-child { box-shadow: inset 3px 0 0 var(--pp-brand-500); }
.montage-lot__membre td { background: var(--pp-info-bg); }

/* Le dernier carton du lot ferme le bloc. */
.montage-lot .montage-lot__membre:last-child td { border-bottom: 2px solid var(--pp-brand-500); }

/* La photo du produit proposé en remplacement : on reconnaît un comprimé d'un
   coup d'œil bien avant de relire son DIN, et c'est le dernier contrôle avant
   de changer de générique. */
.substitut__photo {
    flex: none;
    width: 40px;
    height: 40px;
    border-radius: var(--pp-r-xs);
    background: var(--pp-surface-3);
    border: 1px solid var(--pp-bordure);
    overflow: hidden;
    display: grid;
    place-items: center;
}
.substitut__photo img { width: 100%; height: 100%; object-fit: contain; }
.substitut__photo--vide { background: var(--pp-surface-3); }

.substitut__choix__texte {
    display: flex;
    flex-direction: column;
    gap: .15rem;
    min-width: 0;
}
.substitut__choix__texte strong { overflow-wrap: anywhere; }

/* Les deux réponses se partagent la ligne à parts EXACTEMENT égales : aucune
   des deux n'est « la bonne », et une largeur qui suivrait la longueur du texte
   laisserait croire le contraire.

   Une grille, et non des « flex » : en flex, le bouton au libellé le plus long
   refuse de descendre sous sa largeur de contenu — les deux finissaient
   inégaux quoi qu'on demande. « Annuler » passe dessous, en retrait. */
.substitut__question .barre-actions {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: .5rem;
}
.substitut__question .barre-actions .btn {
    justify-content: center;
    min-width: 0;
    white-space: normal;
}
.substitut__question .barre-actions .btn--fantome { grid-column: 1 / -1; }

/* Le bouton d'autorisation porte le geste : il prend la largeur. */
.substitut__choix-produit .barre-actions { display: flex; gap: .5rem; flex-wrap: wrap; }
.substitut__choix-produit .barre-actions .btn--primaire { flex: 1 1 14rem; justify-content: center; }

/* --- La boîte de confirmation du poste -----------------------------------
   Une question posée par le site, et non par le navigateur : elle dit de quoi
   elle parle, et ses boutons nomment le geste.
   ------------------------------------------------------------------------- */
.dialogue--confirme { max-width: min(30rem, 92vw); }

.confirme__entete { display: flex; gap: .75rem; align-items: flex-start; }

.confirme__icone {
    flex: none;
    display: grid;
    place-items: center;
    width: 40px; height: 40px;
    border-radius: 50%;
    background: var(--pp-warn-bg);
    color: var(--pp-warn);
}
.dialogue--danger .confirme__icone { background: var(--pp-danger-bg); color: var(--pp-danger); }

.confirme__propos { min-width: 0; }
.confirme__propos h2 { margin: 0; font-size: 1.05rem; }

/* Le sujet : le produit dont on parle. Il porte la question — sans lui,
   « voulez-vous continuer ? » ne veut rien dire. */
.confirme__sujet {
    margin: .15rem 0 0;
    font-weight: 600;
    overflow-wrap: anywhere;
}
.confirme__sujet:empty { display: none; }

.confirme__texte { margin: 0; color: var(--pp-texte-2); line-height: 1.45; }

/* La précision demandée avec la question (le motif d'un refus). « .champ » impose
   un display, il faut donc dire explicitement ce que « hidden » veut dire ici. */
.confirme__champ[hidden] { display: none; }
.confirme__champ .champ__etiquette:empty { display: none; }

/* Les deux réponses à parts égales, et le refus d'abord sous la main : on ne
   confirme pas un geste par habitude. */
.dialogue--confirme .barre-actions {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: .5rem;
    margin-top: .4rem;
}
.dialogue--confirme .barre-actions .btn {
    justify-content: center;
    min-width: 0;
    white-space: normal;
}

/* -------------------------------------------------------------------------
   Avertissement « hors liste d'équivalence ».

   Le produit existe : la boîte ne le nie pas. Elle nomme les DEUX produits,
   dit ce que le réseau n'atteste pas, et fait cocher l'acceptation avant
   d'ouvrir le bouton. Elle propose dans la foulée d'envoyer la paire.
   ------------------------------------------------------------------------- */
.dialogue--avertissement { max-width: min(34rem, 94vw); }
.dialogue--avertissement .dialogue__corps { gap: .9rem; }

.hors-equivalence__paire {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
    align-items: center;
    gap: .6rem;
}

.hors-equivalence__face {
    min-width: 0;
    padding: .55rem .7rem;
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-sm);
    background: var(--pp-surface-2);
}
.hors-equivalence__face strong { display: block; overflow-wrap: anywhere; }

.hors-equivalence__role {
    display: block;
    font-size: .75rem;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--pp-texte-3);
}

.hors-equivalence__fleche { display: inline-flex; color: var(--pp-warn); }
.hors-equivalence__fleche svg { width: 1.2rem; height: 1.2rem; }

/* Une case dont le texte fait plusieurs lignes : la case reste en haut, en
   face de la première ligne, et non centrée au milieu du paragraphe. */
.case--bloc { display: flex; align-items: flex-start; }
.case--bloc input { margin-top: .1rem; flex: none; }

/* L'engagement se distingue de la simple offre : c'est lui qui débloque. */
.case--engagement {
    padding: .55rem .7rem;
    border-radius: var(--pp-r-sm);
    background: var(--pp-warn-bg);
    color: var(--pp-texte);
    font-weight: 600;
}

@media (max-width: 560px) {
    .hors-equivalence__paire { grid-template-columns: 1fr; }
    .hors-equivalence__fleche { justify-self: center; transform: rotate(90deg); }
}

/* ==========================================================================
   PRESCRIPTION UNIQUE
   ========================================================================== */

/* Une molécule terminée : barrée et estompée. La procédure l'a déjà glissée en fin de liste. */
.rx--terminee { color: var(--pp-texte-3); }
.rx--terminee strong { text-decoration: line-through; }

/* Fiche de prescription : mêmes lignes compactes et même ajout en pied de carte
   que dans le module Piluliers, avec les données propres à une ordonnance. */
.prescription-carte__corps { padding-top: 0; }
.prescription-liste {
    display: grid;
    grid-template-columns:
        54px
        minmax(15rem, 1fr)
        10.5rem
        17.5rem
        minmax(22rem, auto);
    column-gap: .8rem;
}

.prescription-ligne {
    display: grid;
    grid-column: 1 / -1;
    grid-template-columns: subgrid;
    gap: .8rem;
    align-items: center;
    padding: .9rem 0;
    border-bottom: 1px solid var(--pp-bordure);
}

.prescription-ligne__identite,
.prescription-ligne__reference,
.prescription-ligne__periode {
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: .12rem;
}

.prescription-ligne__identite > strong { line-height: 1.25; }
.prescription-ligne__identite > .attenue,
.prescription-ligne__reference > .attenue,
.prescription-ligne__periode > .attenue { font-size: .78rem; }

.prescription-ligne__libelle {
    color: var(--pp-texte-3);
    font-size: .6875rem;
    font-weight: 600;
    letter-spacing: .055em;
    text-transform: uppercase;
}

.prescription-ligne__posologie { font-size: .83rem; color: var(--pp-texte-2); }
.prescription-ligne__note {
    display: flex;
    gap: .25rem;
    align-items: center;
    font-size: .78rem;
    color: var(--pp-brand-600);
}
.prescription-ligne__note svg { width: 14px; height: 14px; }

.prescription-ligne__actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: .35rem;
    flex-wrap: wrap;
}
.prescription-ligne__actions form { display: inline-flex; }

.prescription-vide { padding-block: 1.2rem; }

.prescription-ajout {
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px dashed var(--pp-bordure);
}

.prescription-ajout__editeur {
    margin-top: .9rem;
    padding: 1rem;
    border: 1px solid var(--pp-bordure);
    border-left: 3px solid var(--pp-brand-500);
    border-radius: var(--pp-r-md);
    background: var(--pp-surface-2);
}
.prescription-ajout__editeur[hidden] { display: none; }
.prescription-ajout__erreur { margin-top: 1rem; }

/* Filtres de l'historique RX : une ligne compacte dans l'en-tête, qui se replie
   proprement sans décaler les colonnes du tableau en dessous. */
.prescription-rx-entete { align-items: flex-end; flex-wrap: wrap; }
.prescription-rx-entete > div { display: flex; flex-direction: column; gap: .15rem; }
.prescription-rx-filtres {
    display: flex;
    align-items: flex-end;
    justify-content: flex-end;
    gap: .55rem;
    flex-wrap: wrap;
}
.prescription-rx-filtre { display: grid; gap: .2rem; }
.prescription-rx-filtre > span {
    color: var(--pp-texte-3);
    font-size: .6875rem;
    font-weight: 600;
    letter-spacing: .055em;
    text-transform: uppercase;
}
.prescription-rx-filtre select { min-width: 5.5rem; }
.prescription-rx-filtre input[type="date"] { min-width: 9.8rem; }
.prescription-rx-filtres .champ__saisie { padding-block: .42rem; }

@media (max-width: 1100px) {
    .prescription-liste {
        grid-template-columns: 54px minmax(12rem, 1fr) minmax(8rem, .55fr) minmax(12rem, .8fr);
    }
    .prescription-ligne__actions { grid-column: 2 / -1; justify-content: flex-start; }
}

@media (max-width: 720px) {
    .prescription-liste { grid-template-columns: 44px 1fr; }
    .prescription-ligne { align-items: start; }
    .prescription-ligne > .pilulier-vignette { width: 44px; height: 44px; }
    .prescription-ligne__reference,
    .prescription-ligne__periode,
    .prescription-ligne__actions { grid-column: 2; }
    .prescription-rx-filtres { justify-content: flex-start; width: 100%; }
}

/* Le formulaire reprend le vocabulaire du pilulier : une carte pour choisir le
   produit, avec les mêmes vignettes et suggestions, puis une carte pour sa posologie. */
.prescription-formulaire {
    display: grid;
    gap: 1rem;
    width: 100%;
    max-width: 64rem;
}

.prescription-suggestions {
    max-height: 22rem;
    overflow-y: auto;
    padding-right: .15rem;
}

.prescription-suggestions__vide {
    padding: .8rem;
    text-align: center;
    border: 1px dashed var(--pp-bordure);
    border-radius: var(--pp-r-sm);
}

.prescription-selection {
    display: flex;
    align-items: center;
    gap: .8rem;
    margin-top: .65rem;
    padding: .65rem .75rem;
    border: 1px solid var(--pp-brand-500);
    border-radius: var(--pp-r-md);
    background: color-mix(in srgb, var(--pp-brand-500) 7%, var(--pp-surface));
}

.prescription-selection[hidden] { display: none; }
.prescription-selection .pilulier-suggestion__texte { min-width: 0; }
.prescription-selection__etat { margin-left: auto; }

@media (max-width: 560px) {
    .prescription-selection { align-items: flex-start; flex-wrap: wrap; }
    .prescription-selection__etat { margin-left: 0; }
}

/* Sélecteur du métier d'une pharmacie (cartes radio). */
.choix-mode {
    display: grid;
    gap: .5rem;
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    margin-top: .35rem;
}
.choix-mode__option {
    display: flex;
    gap: .6rem;
    align-items: flex-start;
    padding: .7rem .8rem;
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-md);
    cursor: pointer;
    transition: var(--pp-transition);
}
.choix-mode__option:hover { border-color: var(--pp-bordure-forte); }
.choix-mode__option:has(input:checked) {
    border-color: var(--pp-brand-500);
    background: var(--pp-brand-100);
}
.choix-mode__option input { margin-top: .2rem; }
.choix-mode__corps { display: flex; flex-direction: column; gap: .15rem; }
.choix-mode__corps strong { color: var(--pp-texte-1); }
.choix-mode__corps .attenue { font-size: .85em; }

/* Bascule unique : les deux modes sont toujours lisibles, le fond désigne l'actif. */
.nav__mode {
    display: flex;
    flex-shrink: 0;
    margin: 0 0 .4rem;
}
.nav__mode-bascule {
    position: relative;
    isolation: isolate;
    display: grid;
    grid-template-columns: 1fr 1fr;
    width: 100%;
    padding: 3px;
    border: 1px solid var(--pp-bordure);
    border-radius: 13px;
    background: var(--pp-surface-2);
    color: var(--pp-texte-2);
    font: inherit;
    cursor: pointer;
}
.nav__mode-bascule::before {
    content: ""; position: absolute; z-index: -1; top: 3px; left: 3px;
    width: calc(50% - 3px); height: calc(100% - 6px); border-radius: 10px;
    background: linear-gradient(135deg, var(--pp-brand-500), var(--pp-brand-700));
    box-shadow: 0 2px 6px rgb(25 92 180 / .2);
    transition: transform .2s ease;
}
.nav__mode-bascule--prescription::before { transform: translateX(100%); }
.nav__mode-bascule:hover { border-color: var(--pp-brand-500); }
.nav__mode-bascule:focus-visible { outline: 3px solid var(--pp-brand-300); outline-offset: 3px; }
.nav__mode-etat {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .3rem; min-height: 40px; padding: .35rem .2rem;
    font-size: .72rem; font-weight: 650; text-transform: lowercase;
}
.nav__mode-etat svg { width: 16px; height: 16px; flex-shrink: 0; }
.nav__mode-etat--actif { color: #fff; font-weight: 750; }
.nav__mode-etat { transition: color .2s ease; }
.nav__mode-bascule[aria-busy="true"] { cursor: progress; }
@media (prefers-reduced-motion: reduce) {
    .nav__mode-bascule::before, .nav__mode-etat { transition: none; }
}

/* Import de médicaments — choix de la portée et sélecteur de pharmacies */
.import-portee { border: 0; margin: 0 0 1rem; padding: 0; }
.import-portee__choix {
    display: inline-flex; align-items: center; gap: .5rem;
    margin-right: 1.25rem; font-weight: 600;
}
/* Rappel de la portée d'un import personnalisé : les pharmacies visées, sans phrase. */
.import-portee-visee {
    display: flex; align-items: center; flex-wrap: wrap; gap: .5rem;
    margin: 0 0 1rem; color: var(--pp-texte-2);
}
/* La liste des pharmacies n'apparaît qu'en base personnalisée (CSS seul, aucun JS). */
.import-pharmacies { display: none; margin-bottom: 1rem; }
form:has(.import-portee__perso:checked) .import-pharmacies { display: block; }
.import-pharmacies__barre { margin: .35rem 0 .5rem; }
.import-pharmacies__liste {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: .35rem .9rem; max-height: 280px; overflow-y: auto;
    padding: .6rem .75rem;
    border: 1px solid var(--pp-bordure, #d7deea); border-radius: 10px;
}
.import-pharmacies__item {
    display: flex; align-items: center; gap: .5rem;
    font-size: .9rem; font-weight: 500;
}
.import-pharmacies__item input { flex-shrink: 0; }

/* ----------------------------------------------------------------------------
   Poste Farmadosis : trois onglets, la file de piluliers, les productions.
   ---------------------------------------------------------------------------- */
/* Barre du haut : les onglets à gauche, le choix de la machine à droite, même ligne. */
.farma-barre {
    display: flex; align-items: center; justify-content: space-between;
    gap: .75rem 1.5rem; flex-wrap: wrap; margin: 0 0 1.25rem;
}

.farma-onglets { display: flex; gap: .4rem; margin: 0; flex-wrap: wrap; }
.farma-onglet {
    appearance: none; cursor: pointer; font-size: 1.05rem; font-weight: 600;
    padding: .7rem 1.5rem; border-radius: 999px;
    border: 1px solid var(--pp-bordure, #d7deea); background: var(--pp-surface, #fff);
    color: var(--pp-texte-2, #4a5568);
    transition: border-color var(--pp-transition), background var(--pp-transition), color var(--pp-transition);
}
.farma-onglet:hover { border-color: var(--pp-brand-500); }
.farma-onglet.est-actif {
    background: var(--pp-brand-500); border-color: var(--pp-brand-500); color: #fff;
}

/* Le choix de la machine : étiquette et combobox alignés, sur la barre des onglets. */
.farmadosis__machine { display: flex; align-items: center; gap: .55rem; margin: 0; }
.farmadosis__machine .champ__etiquette { margin: 0; white-space: nowrap; }

/* Combobox bien défini : le select vit hors d'une .champ__boite, on lui donne donc sa
   propre bordure, son fond et son anneau de focus (la flèche vient de select.champ__saisie). */
.farmadosis__combo {
    min-width: 14rem; max-width: 100%;
    padding: .6rem 2.4rem .6rem .9rem;
    border: 1px solid var(--pp-bordure-forte);
    border-radius: var(--pp-r-sm);
    background-color: var(--pp-surface);
    font-size: .95rem; font-weight: 600;
    transition: border-color var(--pp-transition), box-shadow var(--pp-transition);
}
.farmadosis__combo:hover { border-color: var(--pp-brand-500); }
.farmadosis__combo:focus-visible {
    outline: none; border-color: var(--pp-brand-500);
    box-shadow: 0 0 0 3.5px rgb(35 114 222 / .18);
}

.farma-scan {
    display: flex; gap: .75rem; align-items: flex-end; flex-wrap: wrap; margin-bottom: .75rem;
}
.farma-scan .champ { flex: 1 1 16rem; margin: 0; }

.farma-liste { display: grid; gap: .5rem; margin: .5rem 0; }
.farma-carte {
    display: flex; align-items: center; justify-content: space-between; gap: 1rem;
    padding: .6rem .75rem; border-radius: 10px;
    border: 1px solid var(--pp-bordure, #d7deea); background: var(--pp-surface, #fff);
}

.farma-statut {
    margin-top: .75rem; padding: .6rem .75rem; border-radius: 10px; font-weight: 500;
    border: 1px solid var(--pp-bordure, #d7deea); background: var(--pp-surface-2, #f5f7fb);
}
.farma-statut--attente { color: var(--pp-texte-2, #4a5568); }
.farma-statut--succes {
    background: var(--pp-ok-bg, #ECFDF5); border-color: color-mix(in srgb, var(--pp-ok) 40%, transparent);
    color: var(--pp-ok, #059669);
}
.farma-statut--erreur {
    background: var(--pp-danger-bg, #FFF1F3); border-color: color-mix(in srgb, var(--pp-danger) 40%, transparent);
    color: var(--pp-danger, #E11D48);
}

.farma-productions { list-style: none; margin: .5rem 0 0; padding: 0; display: grid; gap: .5rem; }
.farma-production {
    display: grid; gap: .35rem; padding: .6rem .75rem; border-radius: 10px;
    border: 1px solid var(--pp-bordure, #d7deea); background: var(--pp-surface, #fff);
}
.farma-production__tete {
    display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap; align-items: baseline;
}
.farma-production__patients { font-weight: 650; color: var(--pp-texte); }
.farma-production__actions { display: flex; gap: .45rem; flex-wrap: wrap; margin-top: .15rem; }

.farma-reception-grille {
    display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 1.25rem; align-items: start;
}
.farma-reception-colonne {
    min-width: 0; padding: 1rem; border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-md); background: var(--pp-surface-2);
}
.farma-reception-colonne__entete {
    display: flex; align-items: center; justify-content: space-between; gap: .75rem;
    margin-bottom: .65rem;
}
.farma-reception-colonne__entete .section-titre { margin: 0; }
/* Filtres du journal condensés : les périodes rapides et l'intervalle Du/Au sur une ligne. */
.farma-filtres {
    display: flex; align-items: center; gap: .5rem .9rem; flex-wrap: wrap;
    margin-bottom: .75rem;
}
.farma-periodes { display: flex; gap: .3rem; flex-wrap: wrap; }
.farma-periodes .btn.est-actif {
    --btn-bg: var(--pp-brand-500); --btn-bd: var(--pp-brand-500); --btn-fg: #fff;
}
.farma-dates { display: flex; gap: .5rem; flex-wrap: wrap; }
.farma-dates .champ { display: flex; align-items: center; gap: .35rem; margin: 0; }
.farma-dates .champ__etiquette { margin: 0; }
/* Les inputs date vivent hors d'une .champ__boite : on leur donne leur propre bordure. */
.farma-dates__saisie {
    width: auto; padding: .4rem .55rem; font-size: .85rem; font-weight: 600;
    border: 1px solid var(--pp-bordure-forte); border-radius: var(--pp-r-sm);
    background: var(--pp-surface);
    transition: border-color var(--pp-transition), box-shadow var(--pp-transition);
}
.farma-dates__saisie:hover { border-color: var(--pp-brand-500); }
.farma-dates__saisie:focus-visible {
    outline: none; border-color: var(--pp-brand-500);
    box-shadow: 0 0 0 3.5px rgb(35 114 222 / .18);
}

/* Le défilement se fait DANS les listes (attentes et journal), pas sur la page entière. */
[data-farma-productions], [data-farma-historique] {
    max-height: min(56vh, 34rem); overflow-y: auto; padding-right: .3rem;
}
.farma-historique { list-style: none; margin: .5rem 0 0; padding: 0; display: grid; gap: .5rem; }
.farma-historique__item {
    display: grid; gap: .35rem; padding: .7rem .75rem; border-radius: 10px;
    border: 1px solid var(--pp-bordure); background: var(--pp-surface);
}
.farma-historique__evenement { color: var(--pp-texte-2); font-size: .875rem; font-weight: 600; }
.etiquette--recu {
    color: var(--pp-ok); background: var(--pp-ok-bg);
    border-color: color-mix(in srgb, var(--pp-ok) 35%, transparent);
}

@media (max-width: 920px) {
    .farma-reception-grille { grid-template-columns: 1fr; }
}

.dialogue--farma-attente { max-width: min(34rem, 92vw); }
.farma-attente__contenu {
    display: flex; align-items: flex-start; gap: 1rem;
}
.farma-attente__contenu p { margin: .45rem 0 0; }
.farma-attente__roue {
    display: block; flex: 0 0 auto; width: 2.25rem; height: 2.25rem;
    border-width: 3px; border-color: var(--pp-bordure); border-top-color: var(--pp-brand-500);
}

/* =============================================================================
   RAPPORTS
   -----------------------------------------------------------------------------
   Trois rapports, une même page : des onglets en tête, une période dessous, et
   le téléchargement au même rang que la période — parce qu'on exporte presque
   toujours le rapport qu'on vient de cadrer.

   Les onglets sont des LIENS, pas des boutons : la période voyage dans l'adresse,
   la page se partage et se remet en favori telle quelle, et rien n'a besoin de
   JavaScript pour changer de rapport.
   ============================================================================= */
.rapport-onglets {
    display: flex;
    gap: .25rem;
    flex-wrap: wrap;
    margin-bottom: .9rem;
    border-bottom: 1px solid var(--pp-bordure);
}

.rapport-onglets__lien {
    display: inline-flex;
    align-items: center;
    gap: .45rem;
    padding: .55rem .85rem;
    border-radius: var(--pp-r-md) var(--pp-r-md) 0 0;
    color: var(--pp-texte-2);
    font-weight: 600;
    font-size: .875rem;
    text-decoration: none;
    /* Le trait d'onglet actif est posé dès maintenant, transparent : l'onglet ne
       sautille pas de deux pixels au survol ni à la sélection. */
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
}

.rapport-onglets__lien:hover { background: var(--pp-surface-2); color: var(--pp-texte); }
.rapport-onglets__lien:focus-visible { outline: 2px solid var(--pp-focus); outline-offset: -2px; }

.rapport-onglets__lien--actif {
    color: var(--pp-brand-600);
    border-bottom-color: var(--pp-brand-500);
    background: var(--pp-surface);
}
:root[data-theme="dark"] .rapport-onglets__lien--actif { color: var(--pp-brand-300); }

.rapport-onglets__lien svg { width: 16px; height: 16px; }

/* La période à gauche, le téléchargement à droite ; l'un sous l'autre dès que la
   largeur manque, sans jamais faire déborder la page. */
.rapport-barre {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
}

.rapport-barre__periode { margin-bottom: .6rem; }
.rapport-barre__periode .champ__saisie { min-width: 9.5rem; }

.rapport-barre__raccourcis { display: inline-flex; gap: .3rem; flex-wrap: wrap; }

.rapport-exports {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    flex-wrap: wrap;
    margin-bottom: .6rem;
}

.rapport-exports__mot {
    font-size: .78125rem;
    color: var(--pp-texte-3);
    margin-right: .15rem;
}

.rapport-exports svg { width: 15px; height: 15px; }

/* Deuxième rang de tuiles : le même objet visuel, un cran plus discret, pour dire
   que la question principale a déjà été répondue au-dessus. */
.rapport-secondaire { margin-top: 1rem; }
.rapport-secondaire .stat__valeur { font-size: 1.6rem; }

/* La note de lecture. Elle accompagne aussi les fichiers exportés : un chiffre
   sorti de son écran sans sa définition devient une devinette. */
.rapport-note {
    margin-top: 1rem;
    font-size: .8125rem;
    line-height: 1.55;
    color: var(--pp-texte-3);
    max-width: 62rem;
}

@media (max-width: 720px) {
    .rapport-barre { align-items: stretch; }
    .rapport-exports { justify-content: flex-start; }
}

/* -----------------------------------------------------------------------------
   Fenêtre de génération d'un document de rapport.

   La barre INDÉTERMINÉE : une bande qui traverse, sans prétendre dire où en est le
   serveur. Elle dit « ça travaille », et rien de plus. Dès que les octets arrivent
   avec leur nombre total annoncé, « rapports-export.js » retire cette classe et la
   barre redevient un vrai pourcentage.
   ----------------------------------------------------------------------------- */
.dialogue--export .dialogue__corps { display: grid; gap: .9rem; min-width: min(26rem, 82vw); }

.export__entete { display: flex; align-items: flex-start; gap: .8rem; }

.export__icone {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 2.25rem;
    height: 2.25rem;
    border-radius: var(--pp-r-md);
    background: color-mix(in srgb, var(--pp-brand-500) 12%, transparent);
    color: var(--pp-brand-600);
}
:root[data-theme="dark"] .export__icone { color: var(--pp-brand-300); }

.export__propos h2 { margin: 0; font-size: 1.05rem; }
.export__sujet { margin: .15rem 0 0; font-size: .8125rem; }
.export__sujet:empty { display: none; }

.export__etat {
    margin: 0;
    min-height: 1.2em;
    font-size: .8125rem;
    color: var(--pp-texte-3);
    font-variant-numeric: tabular-nums;
}

.progression--indetermine .progression__remplissage {
    width: 40%;
    transition: none;
    animation: progression-va-et-vient 1.1s ease-in-out infinite;
}

@keyframes progression-va-et-vient {
    0%   { transform: translateX(-100%); }
    100% { transform: translateX(250%); }
}

/* Mouvement réduit : la bande cesse d'aller et venir et occupe la barre entière.
   L'information « ça travaille » reste, l'animation part. */
@media (prefers-reduced-motion: reduce) {
    .progression--indetermine .progression__remplissage {
        width: 100%;
        animation: none;
        opacity: .5;
    }
}

/* Un échec teinte l'icône et la barre : l'état se lit avant le texte. */
.dialogue--danger.dialogue--export .export__icone {
    background: color-mix(in srgb, var(--pp-danger) 14%, transparent);
    color: var(--pp-danger);
}
.dialogue--danger.dialogue--export .progression__remplissage { background: var(--pp-danger); }

/* =====================================================================================
   Mentions « Boîte » et « Narcotique »
   Deux pastilles qui se reconnaissent d'un coup d'œil dans une liste de milliers de
   fiches : la boîte en bleu d'information (elle change la façon de compter), le
   narcotique en rouge (produit sous contrôle).
   ===================================================================================== */
.mentions { display: inline-flex; flex-wrap: wrap; gap: .3rem; align-self: flex-start; }
.cellule-produit .mentions { margin-top: .2rem; }

.etiquette--boite {
    background: color-mix(in srgb, var(--pp-info) 14%, transparent);
    border-color: color-mix(in srgb, var(--pp-info) 34%, transparent);
    color: var(--pp-info);
}

.etiquette--narcotique {
    background: color-mix(in srgb, var(--pp-danger) 13%, transparent);
    border-color: color-mix(in srgb, var(--pp-danger) 32%, transparent);
    color: var(--pp-danger);
}

/* Formulaire produit : une case par mention, son explication à côté. */
.mentions-choix { display: flex; flex-direction: column; gap: .6rem; margin-bottom: .4rem; }
.mentions-choix .case { align-items: flex-start; }
.mentions-choix .case input { margin-top: .1rem; flex: none; }

/* Marquage en lot : un DIN à plusieurs fiches est un petit groupe à trancher. */
.mentions-barre { display: flex; gap: .5rem; flex-wrap: wrap; margin-bottom: .9rem; }

.mentions-groupe {
    border: 1px solid var(--pp-bordure);
    border-radius: var(--pp-r-md);
    padding: .4rem .9rem .8rem;
    margin: 0 0 .9rem;
    min-width: 0;
}

.mentions-groupe__titre {
    display: flex;
    align-items: baseline;
    gap: .5rem;
    flex-wrap: wrap;
    padding: 0 .35rem;
    font-size: .875rem;
}

.col-case { width: 2.5rem; }
.col-case .case { gap: 0; }

.mentions-fiches { margin: 0; padding-left: 1.1rem; }
.mentions-fiches li + li { margin-top: .2rem; }

/* Poste de comptage : une boîte se photographie sans compter. */
.comptage-poste__boite { align-self: flex-start; }
