/* =============================================================================
   LOGIKLEAN - feuille de style de base

   Volontairement minimale et sans framework externe : l'objectif de cette
   phase est l'infrastructure, pas le design. La structure (variables CSS,
   composants nommes) permet de brancher plus tard une charte graphique ou un
   framework sans tout reecrire.
   ========================================================================== */

:root {
    --bleu:        #1f5f8b;
    --bleu-fonce:  #133b56;
    --vert:        #2e8b57;
    --rouge:       #b3261e;
    --orange:      #b26a00;

    --texte:       #1c2530;
    --texte-doux:  #5b6b7c;
    --bordure:     #dde3ea;
    --fond:        #f5f7fa;
    --blanc:       #ffffff;

    --radius: 8px;
    --ombre: 0 1px 3px rgba(16, 24, 40, .08), 0 1px 2px rgba(16, 24, 40, .04);
}

* { box-sizing: border-box; }

body {
    margin: 0;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    font-size: 15px;
    line-height: 1.55;
    color: var(--texte);
    background: var(--fond);
}

a { color: var(--bleu); text-decoration: none; }
a:hover { text-decoration: underline; }

h1 { font-size: 22px; margin: 0; }
h2 { font-size: 18px; margin: 0 0 12px; }
h3 { font-size: 15px; margin: 0 0 8px; text-transform: uppercase; letter-spacing: .04em; color: var(--texte-doux); }

.muted { color: var(--texte-doux); }
.small { font-size: 13px; }

/* Visible pour un lecteur d'ecran, invisible a l'oeil.
 *
 * Sert la ou le champ se comprend sans etiquette — la zone d'ecriture d'un
 * fil de discussion, par exemple, dont le texte d'invite dit deja tout. Une
 * etiquette « Message » au-dessus d'une zone de saisie de messagerie est du
 * bruit a l'ecran, mais son absence rendrait le champ muet pour quelqu'un qui
 * navigue a la voix. On la retire de la vue, pas du document. */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* -----------------------------------------------------------------------------
   MISE EN PAGE APPLICATIVE
   -------------------------------------------------------------------------- */
.app { display: flex; min-height: 100vh; }

.sidebar {
    width: 250px;
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    color: #d6e0ea;
    background: var(--bleu-fonce);
}
.sidebar-tenant { background: #17313f; }

/* L'espace CLIENT porte une couleur differente, et c'est volontaire : celui
   qui gere plusieurs onglets doit voir d'un coup d'oeil qu'il n'est pas dans
   le logiciel de l'entreprise. Les acces sont differents, l'ecran aussi. */
.sidebar-portal { background: #123a35; }
.sidebar-portal .brand-mark { background: #1f7a68; }

.brand {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 18px 16px;
    border-bottom: 1px solid rgba(255, 255, 255, .08);
}
.brand-mark {
    display: grid;
    place-items: center;
    width: 34px; height: 34px;
    border-radius: var(--radius);
    background: var(--bleu);
    color: #fff;
    font-weight: 700;
    font-size: 13px;
}
.brand-text { display: flex; flex-direction: column; font-weight: 600; color: #fff; line-height: 1.2; }
.brand-text small { font-weight: 400; font-size: 11px; color: #8fa6b8; }

.nav { display: flex; flex-direction: column; padding: 12px 8px; gap: 2px; flex: 1; }
.nav a {
    padding: 9px 12px;
    border-radius: 6px;
    color: #cfdae4;
    font-size: 14px;
}
.nav a:hover { background: rgba(255, 255, 255, .08); color: #fff; text-decoration: none; }
.nav a.active {
    background: rgba(255, 255, 255, .14);
    color: #fff;
    font-weight: 600;
}

.nav-section {
    margin: 16px 12px 6px;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: #7e94a6;
}
.nav-disabled {
    padding: 8px 12px;
    font-size: 14px;
    color: #6f8496;
    display: flex;
    justify-content: space-between;
}
.nav-disabled em { font-style: normal; font-size: 11px; opacity: .8; }

.sidebar-footer { padding: 12px; border-top: 1px solid rgba(255, 255, 255, .08); }
.user-chip { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; color: #fff; }
.user-chip:hover { text-decoration: none; }
.user-chip strong { display: block; font-size: 13px; }
.user-chip small { color: #8fa6b8; font-size: 11px; }
.avatar {
    display: grid; place-items: center;
    width: 32px; height: 32px;
    border-radius: 50%;
    background: rgba(255, 255, 255, .14);
    font-size: 12px; font-weight: 600;
}

.main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

.topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 16px 28px;
    background: var(--blanc);
    border-bottom: 1px solid var(--bordure);
}
.topbar-actions { display: flex; gap: 8px; }

/* -----------------------------------------------------------------------------
   LE MENU LATERAL SE REPLIE
   --------------------------------------------------------------------------
   Il DISPARAIT au lieu de retrecir. Le repli habituel garde un rail etroit ou
   des icones remplacent les libelles ; ce menu-ci n'a pas d'icones, ses
   entrees sont des mots. Un rail n'aurait affiche que des mots tronques —
   une colonne qui prend de la place sans rien dire.

   Le bouton, lui, reste dans la barre du haut : visible dans les deux etats,
   toujours au meme endroit. Un bouton qui partirait avec le menu ne
   permettrait plus de le rappeler.
   -------------------------------------------------------------------------- */
.topbar-gauche { display: flex; align-items: center; gap: 12px; min-width: 0; }
.topbar-gauche h1 { min-width: 0; }

.menu-bascule {
    flex-shrink: 0;
    display: grid;
    place-items: center;
    width: 34px; height: 34px;
    border: 1px solid var(--bordure);
    border-radius: 6px;
    background: var(--blanc);
    color: var(--texte-doux);
    font-size: 15px;
    line-height: 1;
    cursor: pointer;
}
.menu-bascule:hover { background: var(--fond); color: var(--texte); }
.menu-bascule:focus-visible { outline: 2px solid var(--bleu); outline-offset: 2px; }

body.menu-replie .sidebar { display: none; }

.content { padding: 24px 28px; }

/* -----------------------------------------------------------------------------
   CARTES ET GRILLES
   -------------------------------------------------------------------------- */
.card {
    background: var(--blanc);
    border: 1px solid var(--bordure);
    border-radius: var(--radius);
    box-shadow: var(--ombre);
    padding: 18px 20px;
    margin-bottom: 18px;
}
.card-title { display: flex; justify-content: space-between; align-items: center; margin-bottom: 14px; }

.grid { display: grid; gap: 16px; }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); }

.stat { background: var(--blanc); border: 1px solid var(--bordure); border-radius: var(--radius); padding: 16px; }
.stat-value { font-size: 26px; font-weight: 700; line-height: 1.1; }
.stat-label { font-size: 12px; text-transform: uppercase; letter-spacing: .04em; color: var(--texte-doux); }

/* -----------------------------------------------------------------------------
   TABLEAUX
   -------------------------------------------------------------------------- */
.table-wrapper { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; font-size: 14px; }
th, td { padding: 10px 12px; text-align: left; border-bottom: 1px solid var(--bordure); }
th { font-size: 12px; text-transform: uppercase; letter-spacing: .04em; color: var(--texte-doux); font-weight: 600; }
tbody tr:hover { background: #fafbfc; }
.table-empty { padding: 28px; text-align: center; color: var(--texte-doux); }

/* -----------------------------------------------------------------------------
   BADGES
   -------------------------------------------------------------------------- */
.badge {
    display: inline-block;
    padding: 2px 9px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .03em;
    background: #eef1f5;
    color: var(--texte-doux);
}
.badge-ACTIVE, .badge-ok      { background: #e3f4ea; color: #1f6b41; }
.badge-TRIAL                  { background: #e6effa; color: #1f5f8b; }
.badge-SUSPENDED, .badge-warn { background: #fdf0dd; color: #8a5200; }
.badge-DISABLED, .badge-off   { background: #fae8e7; color: #8f201a; }

/* -----------------------------------------------------------------------------
   FORMULAIRES
   -------------------------------------------------------------------------- */
.field { margin-bottom: 14px; }
.field label { display: block; font-size: 13px; font-weight: 600; margin-bottom: 5px; }
.required { color: var(--rouge); }

.form-control, .form-select {
    width: 100%;
    padding: 9px 11px;
    font: inherit;
    color: var(--texte);
    background: var(--blanc);
    border: 1px solid var(--bordure);
    border-radius: 6px;
}
.form-control:focus, .form-select:focus {
    outline: none;
    border-color: var(--bleu);
    box-shadow: 0 0 0 3px rgba(31, 95, 139, .12);
}
.field-error .form-control, .field-error .form-select { border-color: var(--rouge); }

.help { margin: 5px 0 0; font-size: 12px; color: var(--texte-doux); }
.error { margin: 5px 0 0; font-size: 12px; color: var(--rouge); }

.form-row { display: grid; gap: 0 16px; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
.form-actions { display: flex; gap: 10px; margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--bordure); }

/* -----------------------------------------------------------------------------
   BOUTONS
   -------------------------------------------------------------------------- */
.btn {
    display: inline-block;
    padding: 9px 16px;
    font: inherit;
    font-size: 14px;
    font-weight: 500;
    border: 1px solid transparent;
    border-radius: 6px;
    cursor: pointer;
    background: var(--bleu);
    color: #fff;
}
.btn:hover { background: var(--bleu-fonce); text-decoration: none; color: #fff; }
.btn-secondary { background: var(--blanc); color: var(--texte); border-color: var(--bordure); }
.btn-secondary:hover { background: #f2f5f8; color: var(--texte); }
.btn-danger { background: var(--rouge); }
.btn-danger:hover { background: #8f201a; }
.btn-ghost { background: transparent; border-color: rgba(255, 255, 255, .2); color: #d6e0ea; width: 100%; }
.btn-ghost:hover { background: rgba(255, 255, 255, .1); color: #fff; }
.btn-sm { padding: 5px 10px; font-size: 12.5px; }
.btn-block { width: 100%; }

/* -----------------------------------------------------------------------------
   MESSAGES
   -------------------------------------------------------------------------- */
.messages { margin-bottom: 18px; }
.alert { padding: 11px 14px; border-radius: 6px; border: 1px solid; margin-bottom: 10px; font-size: 14px; }
.alert-success { background: #e9f6ee; border-color: #b9e0c9; color: #1f6b41; }
.alert-info    { background: #eaf1f8; border-color: #c2d7ea; color: #1f5f8b; }
.alert-warning { background: #fdf3e3; border-color: #eed4a7; color: #8a5200; }
.alert-error   { background: #fbeceb; border-color: #f0c4c1; color: #8f201a; }

/* -----------------------------------------------------------------------------
   PAGES D'AUTHENTIFICATION
   -------------------------------------------------------------------------- */
.layout-auth { display: grid; place-items: center; min-height: 100vh; padding: 24px; }
.auth-wrapper { width: 100%; max-width: 420px; }
.auth-card {
    background: var(--blanc);
    border: 1px solid var(--bordure);
    border-radius: 12px;
    box-shadow: 0 4px 16px rgba(16, 24, 40, .07);
    padding: 32px;
}
.auth-brand { text-align: center; margin-bottom: 26px; }
.auth-brand .brand-mark { margin: 0 auto 12px; width: 44px; height: 44px; font-size: 15px; }
.auth-brand h1 { font-size: 21px; }
.auth-brand p { margin: 4px 0 0; font-size: 13px; }
.auth-footer { text-align: center; margin-top: 20px; font-size: 12px; color: var(--texte-doux); }
.auth-links { margin-top: 16px; text-align: center; font-size: 13px; }

/* -----------------------------------------------------------------------------
   FILTRES ET PAGINATION
   -------------------------------------------------------------------------- */
.filters { display: flex; gap: 10px; flex-wrap: wrap; align-items: flex-end; margin-bottom: 16px; }
.filters .field { margin-bottom: 0; }
.filters .form-control, .filters .form-select { min-width: 180px; width: auto; }

.pagination { display: flex; gap: 8px; align-items: center; justify-content: center; margin-top: 18px; font-size: 14px; }

/* -----------------------------------------------------------------------------
   PAGES D'ERREUR
   -------------------------------------------------------------------------- */
.error-page { display: grid; place-items: center; min-height: 100vh; text-align: center; padding: 24px; }
.error-code { font-size: 64px; font-weight: 700; color: var(--bleu); line-height: 1; }

/* =============================================================================
   RESPONSIVE — TELEPHONE, TABLETTE, ORDINATEUR
   =============================================================================
   TROIS PALIERS, ET ILS CORRESPONDENT A TROIS FACONS DE TENIR L'APPAREIL :

     > 1024px   ORDINATEUR. Menu lateral deploye, tableaux en colonnes.
     641-1024   TABLETTE. Menu en TIROIR, tableaux encore en colonnes mais
                les grilles se resserrent. C'est le format du responsable qui
                fait le tour du depot avec une tablette a la main.
     <= 640px   TELEPHONE. Menu en tiroir, et LES TABLEAUX DEVIENNENT DES
                CARTES (voir `.table-stack`).

   POURQUOI UN TIROIR ET PAS UN MENU EMPILE
   -----------------------------------------
   La version precedente passait la barre laterale en pleine largeur au-dessus
   du contenu. Sur un telephone, cela voulait dire faire defiler QUINZE
   ENTREES DE MENU avant d'atteindre le titre de la page — a chaque page, y
   compris quand on ne cherchait pas le menu. Un menu qu'on traverse pour
   arriver ailleurs est un menu mal place.

   Le tiroir sort par-dessus le contenu, sur commande, et se referme au
   changement de page. Il utilise le meme bouton que le repli du bureau
   (`menu.js` choisit la classe selon la largeur).
   ========================================================================== */

/* -----------------------------------------------------------------------------
   LE TABLEAU QUI DEVIENT DES CARTES
   --------------------------------------------------------------------------
   Un tableau de six colonnes sur un ecran de 360 px ne se lit pas : soit il
   deborde, soit les colonnes se reduisent a deux caracteres. Les deux
   solutions habituelles sont mauvaises — le defilement horizontal fait
   perdre la premiere colonne (celle qui dit DE QUOI on parle), et masquer des
   colonnes cache justement ce qu'on est venu lire.

   `.table-stack` transforme donc chaque LIGNE en carte : l'en-tete disparait,
   et chaque cellule porte son intitule a gauche, pris dans son `data-label`.

   USAGE : `<table class="table-stack">` et `<td data-label="Client">`.
   Une cellule sans `data-label` s'affiche simplement sans intitule — c'est ce
   qu'on veut pour les colonnes d'actions.
   -------------------------------------------------------------------------- */
@media (max-width: 640px) {
    .table-stack thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
    .table-stack, .table-stack tbody, .table-stack tr, .table-stack td { display: block; width: 100%; }

    .table-stack tr {
        margin-bottom: 10px;
        padding: 10px 12px;
        border: 1px solid var(--bordure);
        border-radius: var(--radius);
        background: var(--blanc);
    }
    .table-stack td {
        display: flex;
        justify-content: space-between;
        align-items: baseline;
        gap: 12px;
        padding: 5px 0;
        border: 0;
        text-align: left;
    }
    .table-stack td::before {
        content: attr(data-label);
        flex-shrink: 0;
        font-size: 12px;
        font-weight: 600;
        color: var(--texte-doux);
    }
    /* Une cellule sans intitule prend toute la largeur : c'est le cas des
       colonnes d'actions, ou un intitule vide laisserait un trou. */
    .table-stack td:not([data-label]) { display: block; }
    .table-stack td.row-actions { padding-top: 10px; }
    .table-stack td.row-actions .btn { width: 100%; }
    .table-stack .table-empty { text-align: center; }
    .table-stack tfoot tr { background: #f7f9fc; }
}

/* -----------------------------------------------------------------------------
   TABLETTE ET TELEPHONE — le tiroir
   -------------------------------------------------------------------------- */
@media (max-width: 1024px) {
    .sidebar {
        position: fixed;
        top: 0; bottom: 0; left: 0;
        z-index: 60;
        width: 270px;
        max-width: 85vw;
        overflow-y: auto;
        transform: translateX(-100%);
        transition: transform .18s ease;
        box-shadow: 0 0 24px rgba(16, 24, 40, .28);
    }
    /* Le repli du bureau ne s'applique plus : sur ces largeurs, le tiroir est
       ferme PAR DEFAUT, et `menu-ouvert` est la seule chose qui l'ouvre. Sans
       cette regle, un `menu-replie` restaure depuis localStorage laisserait le
       tiroir invisible mais toujours declare `display:none`. */
    body.menu-replie .sidebar { display: flex; }
    body.menu-ouvert .sidebar { transform: none; }

    /* Le voile : il ferme le tiroir au toucher, et il dit surtout que le
       contenu derriere n'est pas actif. Sans lui, on tape sur un bouton du
       fond en croyant fermer le menu. */
    body.menu-ouvert::after {
        content: "";
        position: fixed;
        inset: 0;
        z-index: 55;
        background: rgba(16, 24, 40, .45);
    }

    .content { padding: 16px; }
    .topbar { padding: 12px 16px; }
    .grid-4 { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }
}

/* -----------------------------------------------------------------------------
   TELEPHONE
   -------------------------------------------------------------------------- */
@media (max-width: 640px) {
    /* LA BARRE DU HAUT PASSE SUR DEUX LIGNES. Le titre et les boutons se
       partageaient une ligne : a trois boutons, le titre tombait a deux
       caracteres. */
    .topbar {
        flex-direction: column;
        align-items: stretch;
        gap: 10px;
    }
    .topbar h1 { font-size: 18px; }
    .topbar-actions { flex-wrap: wrap; }

    /* LES BOUTONS D'ACTION S'ETALENT. Un bouton de 90 px au milieu d'une
       ligne vide se vise mal ; sur un telephone on tape large. */
    .topbar-actions .btn,
    .form-actions .btn,
    .form-actions button { flex: 1 1 auto; min-height: 42px; }
    .form-actions { flex-wrap: wrap; }

    .content { padding: 12px; }
    .card { padding: 14px; margin-bottom: 12px; }

    /* LES FILTRES EN COLONNE, ET EN PLEINE LARGEUR. Poses cote a cote sur
       360 px, ils se coupaient au milieu d'un menu deroulant. */
    .filters { flex-direction: column; align-items: stretch; }
    .filters .field,
    .filters .form-control,
    .filters .form-select { width: 100%; min-width: 0; }

    /* 16 px EXACTEMENT SUR LES CHAMPS, ET C'EST UNE REGLE DE SAFARI : en
       dessous, iOS ZOOME sur le champ au moment ou on le touche, et la page
       reste zoomee ensuite. C'est le defaut mobile le plus courant, et le
       plus penible. */
    .form-control, .form-select, input, select, textarea { font-size: 16px; }

    /* Les cibles tactiles : 44 px est le minimum recommande, et c'est aussi
       la largeur d'un pouce. */
    .btn { min-height: 42px; }
    .btn-sm { min-height: 34px; }
    .nav a { padding: 12px; }

    h1 { font-size: 19px; }
    h2 { font-size: 16px; }

    .grid-2, .grid-4 { grid-template-columns: 1fr; }

    /* Les statistiques se lisent sur deux colonnes plutot qu'une : quatre
       chiffres empiles font quatre ecrans de defilement. */
    .grid-4 { grid-template-columns: repeat(2, 1fr); }
    .stat { padding: 12px; }
    .stat-value { font-size: 21px; }

    .card-title { flex-wrap: wrap; gap: 8px; }
    .subnav { margin-bottom: 14px; }
    .pagination { flex-wrap: wrap; }
}

/* -----------------------------------------------------------------------------
   CE QUI NE DOIT JAMAIS DEBORDER
   --------------------------------------------------------------------------
   Une page qui defile horizontalement sur telephone est cassee : on perd la
   colonne de gauche des qu'on regarde la droite. Tout ce qui ne peut pas se
   redimensionner defile donc DANS SON PROPRE CADRE.
   -------------------------------------------------------------------------- */
.table-wrapper { overflow-x: auto; -webkit-overflow-scrolling: touch; }
img, video, canvas { max-width: 100%; height: auto; }
pre, code { overflow-wrap: anywhere; }
.main { overflow-x: hidden; }

/* =============================================================================
   MODULE INVENTAIRE
   ========================================================================== */

/* -----------------------------------------------------------------------------
   SOUS-NAVIGATION
   -------------------------------------------------------------------------- */
.subnav {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 2px;
    margin-bottom: 20px;
    padding-bottom: 2px;
    border-bottom: 1px solid var(--bordure);
    overflow-x: auto;
}
.subnav a {
    padding: 8px 13px;
    font-size: 13.5px;
    color: var(--texte-doux);
    border-bottom: 2px solid transparent;
    white-space: nowrap;
}
.subnav a:hover { color: var(--texte); text-decoration: none; }
.subnav a.active { color: var(--bleu); border-bottom-color: var(--bleu); font-weight: 600; }
.subnav-sep { flex: 1; min-width: 12px; }

/* -----------------------------------------------------------------------------
   BADGES SPECIFIQUES
   -------------------------------------------------------------------------- */
/* Types de catalogue */
.badge-kind-PRODUCT { background: #e6effa; color: #1f5f8b; }
.badge-kind-MACHINE { background: #ede7f6; color: #4a2f83; }
.badge-kind-TOOL    { background: #e7f2ec; color: #256b45; }

/* Statuts de document */
.badge-doc-DRAFT     { background: #eef1f5; color: #5b6b7c; }
.badge-doc-VALIDATED { background: #e3f4ea; color: #1f6b41; }
.badge-doc-CLOSED    { background: #e6effa; color: #1f5f8b; }

/* Localisation des unites */
.badge-eq-IN_STOCK     { background: #e3f4ea; color: #1f6b41; }
.badge-eq-AT_CLIENT    { background: #e6effa; color: #1f5f8b; }
.badge-eq-IN_TRANSPORT { background: #fdf0dd; color: #8a5200; }
.badge-eq-INTERNAL_USE { background: #ede7f6; color: #4a2f83; }
.badge-eq-RETIRED      { background: #eef1f5; color: #5b6b7c; }

/* Cartes de statistiques en alerte */
.stat-warn   { border-color: #eed4a7; background: #fffcf6; }
.stat-danger { border-color: #f0c4c1; background: #fffafa; }

/* -----------------------------------------------------------------------------
   BARRE DE SCAN
   -------------------------------------------------------------------------- */
/* LE SCAN PREND TOUTE LA LARGEUR, la recherche manuelle passe dessous, dans
   un repli. C'etaient deux colonnes de meme poids : un partage faux, puisque
   le scan sert dans la quasi-totalite des cas et la recherche seulement quand
   l'etiquette est abimee ou qu'il n'y a pas de camera. */
.scanbar {
    padding: 16px;
    background: #f7f9fc;
    border: 1px solid var(--bordure);
    border-radius: var(--radius);
}
.scanbar label {
    display: block;
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .03em;
    color: var(--texte-doux);
    margin-bottom: 6px;
}
.scanbar-row { display: flex; gap: 8px; }
.scanbar-row .form-control { flex: 1; min-width: 0; }
.scanbar-scan .form-control {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    letter-spacing: .04em;
}
.scanbar-scan .scanbar-row { margin-top: 10px; }
.scanbar-manuel { margin-top: 12px; }

/* -----------------------------------------------------------------------------
   LE BLOC REPLIE
   --------------------------------------------------------------------------
   `<details>` habille. On s'en sert partout ou une aide occasionnelle
   poussait vers le bas ce qu'on relit sans arret : la recherche manuelle
   d'un bon, la liste du materiel d'un service.

   Il fonctionne SANS JAVASCRIPT, et le navigateur sait deja le rendre
   accessible au clavier. C'est la raison de ce choix plutot qu'un onglet.
   -------------------------------------------------------------------------- */
.bloc-replie {
    margin-top: 14px;
    border: 1px solid var(--bordure);
    border-radius: var(--radius);
    background: var(--blanc);
}
.bloc-replie > summary {
    padding: 10px 14px;
    font-size: 13.5px;
    font-weight: 600;
    color: var(--texte-doux);
    cursor: pointer;
    list-style: none;
}
.bloc-replie > summary::-webkit-details-marker { display: none; }
.bloc-replie > summary::before {
    content: "▸ ";
    color: var(--bleu);
}
.bloc-replie[open] > summary::before { content: "▾ "; }
.bloc-replie > summary:hover { color: var(--texte); }
.bloc-replie > summary:focus-visible { outline: 2px solid var(--bleu); outline-offset: -2px; }
.bloc-replie > *:not(summary) { padding: 0 14px 14px; }

/* -----------------------------------------------------------------------------
   L'EN-TETE D'UN BON — quatre valeurs sur une ligne
   --------------------------------------------------------------------------
   C'etaient quatre cartes occupant un quart d'ecran chacune, en haut de la
   page qu'on ouvre pour SAISIR. Quatre valeurs courtes tiennent sur une
   ligne, et une ligne se lit d'un coup d'oeil mieux que quatre cartes.
   -------------------------------------------------------------------------- */
.doc-entete {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 28px;
    margin: 0;
}
.doc-entete div { min-width: 0; }
.doc-entete dt {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--texte-doux);
}
.doc-entete dd { margin: 2px 0 0; font-weight: 600; }

/* -----------------------------------------------------------------------------
   QR CODES
   -------------------------------------------------------------------------- */
.qr-cell { white-space: nowrap; }
.qr-cell img { vertical-align: middle; margin-right: 8px; border: 1px solid var(--bordure); border-radius: 4px; background: #fff; }

.qr-inline { display: flex; gap: 16px; align-items: flex-start; margin-bottom: 16px; }
.qr-inline img { border: 1px solid var(--bordure); border-radius: 6px; background: #fff; padding: 4px; }

.qr-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 14px; }
.qr-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    padding: 12px;
    text-align: center;
    border: 1px solid var(--bordure);
    border-radius: var(--radius);
    background: #fff;
}
.qr-card code { font-size: 11px; }

/* -----------------------------------------------------------------------------
   FORMATS ET CHECKLISTS
   -------------------------------------------------------------------------- */
.formats-block { margin: 20px 0; padding: 16px; background: #f7f9fc; border-radius: var(--radius); }
.format-table td { padding: 4px 6px; }
.format-table .form-control { padding: 6px 8px; font-size: 13.5px; }

.checklist td { vertical-align: top; }
.radio-inline { display: inline-block; margin-right: 12px; font-size: 13.5px; }
.radio-inline input { margin-right: 4px; }

/* -----------------------------------------------------------------------------
   ACTIONS RAPIDES
   -------------------------------------------------------------------------- */
.quick-actions { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 12px; }
.quick-action {
    display: flex;
    flex-direction: column;
    gap: 3px;
    padding: 14px 16px;
    border: 1px solid var(--bordure);
    border-radius: var(--radius);
    background: #fff;
    color: var(--texte);
}
.quick-action:hover { border-color: var(--bleu); background: #f7f9fc; text-decoration: none; }

/* -----------------------------------------------------------------------------
   DETAILS / SUMMARY UTILISES COMME BOUTONS
   -------------------------------------------------------------------------- */
details > summary { list-style: none; display: inline-block; }
details > summary::-webkit-details-marker { display: none; }
details > summary { cursor: pointer; }

/* -----------------------------------------------------------------------------
   PAGE PUBLIQUE (telephone)
   -------------------------------------------------------------------------- */
.layout-public { background: var(--fond); }
.public-wrapper { max-width: 620px; margin: 0 auto; padding: 18px 14px 40px; }
.public-header {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px 16px;
    margin-bottom: 16px;
    background: var(--bleu-fonce);
    border-radius: var(--radius);
    color: #fff;
}
.public-header .muted { color: #a8bccd; }
.public-actions { display: grid; gap: 10px; margin-bottom: 16px; }
.public-footer { margin-top: 26px; text-align: center; font-size: 12px; color: var(--texte-doux); }

.check-card { padding: 14px 16px; }
.check-choices { display: flex; gap: 16px; margin: 10px 0; }
.check-choices label { display: flex; align-items: center; gap: 6px; font-size: 15px; }
.check-choices input { width: 20px; height: 20px; }

/* -----------------------------------------------------------------------------
   IMPRESSION DES ETIQUETTES
   -------------------------------------------------------------------------- */
.print-page { padding: 20px; }
.print-toolbar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 18px;
    padding-bottom: 12px;
    border-bottom: 1px solid var(--bordure);
}
.labels-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 10px; }
.label-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 3px;
    padding: 10px;
    text-align: center;
    border: 1px dashed #999;
    border-radius: 4px;
    background: #fff;
    page-break-inside: avoid;
}
/* LE NOM SOUS LE QR, EN GROS. C'est ce qu'on lit de loin en cherchant une
   etagere ; le QR, lui, ne se lit qu'a dix centimetres avec un telephone. Il
   passe donc devant tout le reste, et il ne se coupe pas — un « Detergent
   surodorant PIN… » tronque ne designe rien. */
.label-nom {
    font-size: 13px;
    line-height: 1.25;
    overflow-wrap: anywhere;
}
.label-card code { font-size: 10px; }
.label-sub { font-size: 12px; color: var(--texte-doux); }
.label-hint { font-size: 9px; color: #999; }

.print-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; }

@media print {
    .no-print { display: none !important; }
    body { background: #fff; }
    .print-page { padding: 0; }
    .labels-grid { grid-template-columns: repeat(4, 1fr); }
    /* Le gris clair de l'ecran devient illisible a l'impression : sur du
       papier, tout ce qu'on garde doit etre noir. */
    .label-sub, .label-card code { color: #000; }
    .label-hint { display: none; }
}

/* -----------------------------------------------------------------------------
   SCANNER CAMERA
   -------------------------------------------------------------------------- */
.btn-scan {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 15px;
    padding: 11px 18px;
}
.scan-icon { font-size: 18px; line-height: 1; }

.scanner-unsupported {
    margin: 6px 0 0;
    padding: 8px 10px;
    background: #fdf3e3;
    border: 1px solid #eed4a7;
    border-radius: 6px;
    color: #8a5200;
}

/* Plein ecran : sur un telephone, viser une etiquette demande de la place. */
.scanner-modal {
    position: fixed;
    inset: 0;
    z-index: 1000;
    display: grid;
    place-items: center;
    padding: 16px;
    background: rgba(12, 20, 28, .82);
}
/* INDISPENSABLE : `display: grid` ci-dessus est plus specifique que le
   `display: none` que le navigateur applique a [hidden]. Sans cette regle, la
   modale reste affichee en permanence et, etant en position fixed sur tout
   l'ecran, intercepte TOUS les clics de la page. */
.scanner-modal[hidden] { display: none; }
.scanner-unsupported[hidden] { display: none; }
.scanner-box {
    width: 100%;
    max-width: 480px;
    background: var(--blanc);
    border-radius: 12px;
    overflow: hidden;
    box-shadow: 0 10px 40px rgba(0, 0, 0, .3);
}
.scanner-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px 14px;
    border-bottom: 1px solid var(--bordure);
}

.scanner-video-wrap { position: relative; background: #000; aspect-ratio: 4 / 3; }
.scanner-video-wrap video { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Viseur : aide a cadrer l'etiquette. */
.scanner-reticle {
    position: absolute;
    top: 50%; left: 50%;
    width: 62%; aspect-ratio: 1;
    transform: translate(-50%, -50%);
    border: 3px solid rgba(255, 255, 255, .9);
    border-radius: 14px;
    box-shadow: 0 0 0 100vmax rgba(0, 0, 0, .35);
}

.scanner-status { margin: 0; padding: 12px 14px; text-align: center; }

/* -----------------------------------------------------------------------------
   FICHE TECHNIQUE (fichier joint)
   -------------------------------------------------------------------------- */
.file-link {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 9px 13px;
    border: 1px solid var(--bordure);
    border-radius: 6px;
    background: #f7f9fc;
    color: var(--texte);
    font-size: 14px;
}
.file-link:hover { border-color: var(--bleu); text-decoration: none; }
.file-icon { font-size: 17px; }

.file-preview {
    max-width: 100%;
    margin-top: 10px;
    border: 1px solid var(--bordure);
    border-radius: 6px;
}

input[type="file"].form-control { padding: 7px 9px; background: #fff; }

/* --------------------------------------------------------------------------
   PHOTO D'UN ELEMENT DE CATALOGUE
   Deux tailles : la vignette des listes et des resultats de recherche, et la
   photo de la fiche. Le format est toujours carre et l'image recadree
   (object-fit: cover) pour que les listes restent alignees quelle que soit la
   photo envoyee par l'utilisateur.
   -------------------------------------------------------------------------- */
.item-thumb {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    object-fit: cover;
    border: 1px solid var(--bordure);
    border-radius: 6px;
    background: #f7f9fc;
}
.item-thumb-empty { font-size: 18px; opacity: .45; }

.item-photo {
    display: block;
    max-width: 240px;
    max-height: 240px;
    margin-bottom: 12px;
    object-fit: cover;
    border: 1px solid var(--bordure);
    border-radius: 8px;
    background: #f7f9fc;
}

/* ==========================================================================
   GLISSER-DEPOSER — commun au calendrier et au tableau des feedbacks
   (voir static/js/dragdrop.js)
   ========================================================================== */
.is-dragging { opacity: .35; }
.is-saving   { opacity: .6; }

.drag-ghost {
    position: fixed;
    z-index: 1000;
    pointer-events: none;
    box-shadow: 0 8px 20px rgba(16, 24, 40, .22);
    transform: rotate(-1deg);
    opacity: .95;
}
body.is-dragging-item { cursor: grabbing; user-select: none; }

/* -----------------------------------------------------------------------------
   CE QUI SE DEPLACE NE SE SELECTIONNE PAS
   --------------------------------------------------------------------------

   LE DEFAUT QUE CECI CORRIGE, sur tablette : on pose le doigt sur une
   intervention pour la deplacer, et le navigateur ouvre SON menu d'appui long
   — selection de texte sur Android, bulle « Copier / Rechercher » sur iOS. Il
   reprend alors le pointeur, notre glisser meurt, et il faut s'y reprendre a
   trois fois.

   POURQUOI LA REGLE DU DESSUS NE SUFFISAIT PAS. `body.is-dragging-item` ne
   s'applique QU'UNE FOIS LE GLISSER COMMENCE, c'est-a-dire apres 250 ms. Or
   le navigateur arme sa selection des le `pointerdown` — a 0 ms. La regle
   arrivait toujours trop tard, et ne pouvait pas defaire ce qui etait deja
   arme.

   Elle vit donc SUR LES ELEMENTS EUX-MEMES, en permanence.

   LES SELECTEURS SONT CEUX QUE LE JAVASCRIPT UTILISE COMME POIGNEES
   (`options.handle` dans dragdrop.js) : `[data-place]` pour les cartes a
   placer, `[data-move]` pour les blocs du calendrier, `[data-status-url]`
   pour les tableaux taches et signalements. Les lier ainsi evite qu'un
   nouvel ecran deplacable naisse sans sa regle — c'est le meme nom des deux
   cotes.

   `-webkit-touch-callout` N'EST PAS UN DOUBLON de `user-select` : le premier
   supprime la bulle d'iOS, le second la poignee de selection d'Android. Les
   deux navigateurs n'ecoutent pas la meme propriete.

   ON NE PERD RIEN : il n'y a rien a selectionner dans un bloc de planning ou
   une carte de tableau. Le texte reste copiable sur la FICHE, ou il est
   affiche pour etre lu. */
[data-place],
[data-move],
[data-status-url] {
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    user-select: none;
}

.is-drop-target {
    outline: 2px dashed var(--bleu);
    outline-offset: -2px;
    background: #f2f7fe !important;
}

/* ==========================================================================
   CALENDRIER DU PLANNING

   Trois mises en page pour un seul composant de creneau (.cal-slot) :
     .cal-month  une grille de sept colonnes, comme un calendrier mural
     .cal-grid   une grille horaire, comme un agenda : la hauteur d'un bloc
                 EST sa duree
     .cal-list   une liste par jour, pour les periodes libres

   Les prestations se deplacent au glisser (voir static/js/calendar.js).
   ========================================================================== */

/* -- Barre d'outils ------------------------------------------------------- */
.cal-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 14px;
}
.cal-views {
    display: inline-flex;
    border: 1px solid var(--bordure);
    border-radius: 8px;
    overflow: hidden;
    background: var(--blanc);
}
.cal-view {
    padding: 8px 14px;
    font-size: 13px;
    color: var(--texte-doux);
    border-right: 1px solid var(--bordure);
}
.cal-view:last-child { border-right: 0; }
.cal-view:hover { background: #f5f8fd; text-decoration: none; color: var(--texte); }
.cal-view.is-active { background: var(--bleu); color: #fff; font-weight: 600; }

.cal-filters { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.cal-filters .form-control, .cal-filters .form-select { width: auto; min-width: 130px; }
.cal-check { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; }
.cal-summary { margin: 0 0 12px; }

/* -- Le creneau, commun aux trois mises en page --------------------------- */
.cal-slot {
    display: block;
    padding: 5px 8px;
    border-radius: 5px;
    border-left: 3px solid var(--bleu);
    background: #eef4fc;
    color: var(--texte);
    font-size: 12px;
    line-height: 1.35;
    overflow: hidden;
    touch-action: manipulation;
}
.cal-slot:hover { text-decoration: none; filter: brightness(.97); }
.cal-slot span { display: block; }
.cal-slot-time  { font-weight: 700; }
.cal-slot-title { font-weight: 600; }
.cal-slot-sub, .cal-slot-team { color: var(--texte-doux); }
.cal-slot-team  { font-style: italic; }
.cal-slot-flag {
    margin-top: 3px;
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--orange);
}

/* LES CINQ ETATS.
   Le lisere donne l'etat d'un coup d'oeil ; le libelle ecrit sur le creneau
   le donne a ceux pour qui la couleur ne suffit pas. Les deux, jamais l'un
   sans l'autre. L'ordre suit le cycle de vie : orange -> bleu -> violet ->
   vert, du « rien n'est fait » au « c'est prouve ». */
.cal-slot.status-PLANNED    { border-left-color: #d99b1c; background: #fff8ec; }
.cal-slot.status-ASSIGNED   { border-left-color: var(--bleu); background: #eef4fc; }
.cal-slot.status-INPROGRESS { border-left-color: #7a4fbf; background: #f4effc; }
.cal-slot.status-DONE       { border-left-color: #2f9e5f; background: #f1faf5; }
.cal-slot.status-CANCELLED  { border-left-color: #b3b8bf; background: #f5f6f7; opacity: .65; }
.cal-slot.is-short          { box-shadow: inset 0 0 0 1px #eec98a; }

/* L'etat sur SA propre ligne : en flottant a droite de l'heure il la
   percutait des que la colonne se resserrait (« 08:00 -> 10:00PLANIFIEE »). */
.cal-slot-state {
    display: block;
    font-style: normal;
    font-weight: 600;
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: .03em;
    opacity: .7;
}

/* -- La legende ------------------------------------------------------------
   Obligatoire : jusqu'ici les couleurs existaient mais rien ne les
   expliquait, ce qui revenait a ne pas les avoir. */
.cal-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 18px;
    align-items: center;
    margin: 0 0 12px;
    font-size: 12px;
    color: var(--texte-doux);
}
.cal-legend-item { display: inline-flex; align-items: center; gap: 6px; }
.cal-legend-chip {
    width: 12px; height: 12px;
    border-radius: 3px;
    border-left: 3px solid;
    flex-shrink: 0;
}
.cal-legend-chip.status-PLANNED    { border-left-color: #d99b1c; background: #fff8ec; }
.cal-legend-chip.status-ASSIGNED   { border-left-color: var(--bleu); background: #eef4fc; }
.cal-legend-chip.status-INPROGRESS { border-left-color: #7a4fbf; background: #f4effc; }
.cal-legend-chip.status-DONE       { border-left-color: #2f9e5f; background: #f1faf5; }
.cal-legend-chip.status-CANCELLED  { border-left-color: #b3b8bf; background: #f5f6f7; }
.cal-legend-item strong { color: var(--texte); font-weight: 600; }

.cal-slot[data-move] { cursor: grab; }

/* -- Vue MOIS ------------------------------------------------------------- */
.cal-month {
    background: var(--blanc);
    border: 1px solid var(--bordure);
    border-radius: var(--radius);
    overflow: hidden;
}
.cal-month-head, .cal-month-week { display: grid; grid-template-columns: repeat(7, 1fr); }
.cal-month-head > div {
    padding: 8px;
    text-align: center;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--texte-doux);
    background: #f7f9fc;
    border-bottom: 1px solid var(--bordure);
}
.cal-month-day {
    min-height: 118px;
    padding: 6px;
    border-right: 1px solid var(--bordure);
    border-bottom: 1px solid var(--bordure);
}
.cal-month-day:nth-child(7n) { border-right: 0; }
.cal-month-week:last-child .cal-month-day { border-bottom: 0; }
.cal-month-day.is-weekend { background: #fbfcfd; }
.cal-month-day.is-outside { background: #f6f7f9; }
.cal-month-day.is-outside .cal-month-daynum a { color: #a9b2bc; }
.cal-month-day.is-today { background: #f2f7fe; box-shadow: inset 0 0 0 2px var(--bleu); }

.cal-month-daynum {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 5px;
    font-size: 13px;
    font-weight: 600;
}
.cal-month-daynum a { color: var(--texte); }
.cal-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--orange); }

.cal-month-slots { display: flex; flex-direction: column; gap: 3px; min-height: 24px; }
.cal-month-slots .cal-slot-sub,
.cal-month-slots .cal-slot-state,
.cal-month-slots .cal-slot-team { display: none; }

/* -- Vues SEMAINE et JOURNEE : la grille horaire -------------------------- */
.cal-grid {
    background: var(--blanc);
    border: 1px solid var(--bordure);
    border-radius: var(--radius);
    overflow: auto;
    /* La grille defile DANS son cadre plutot que d'etirer la page : l'en-tete
       des jours reste visible, et la barre d'outils ne part pas en haut de
       l'ecran des qu'une journee est chargee. */
    max-height: calc(100vh - 250px);
    min-height: 340px;
}
.cal-grid-head, .cal-grid-body {
    display: grid;
    grid-template-columns: 58px repeat(auto-fit, minmax(0, 1fr));
    min-width: 640px;
}
.cal-grid-head {
    position: sticky;
    top: 0;
    z-index: 3;
    background: #f7f9fc;
    border-bottom: 1px solid var(--bordure);
}
.cal-grid-daylabel { padding: 8px 6px; text-align: center; font-size: 12px; }
.cal-grid-daylabel a { color: var(--texte); }
.cal-grid-daylabel strong { display: block; text-transform: capitalize; }
.cal-grid-daylabel span { color: var(--texte-doux); }
.cal-grid-daylabel.is-today { background: #e9f1fd; box-shadow: inset 0 -2px 0 var(--bleu); }

.cal-grid-body { position: relative; }
.cal-grid-gutter { position: relative; border-right: 1px solid var(--bordure); }
.cal-grid-hour {
    position: absolute;
    right: 6px;
    transform: translateY(-50%);
    font-size: 11px;
    color: var(--texte-doux);
}
/* La premiere heure est a top:0 : centree, elle serait coupee en deux. */
.cal-grid-hour:first-child { transform: none; }
.cal-grid-col {
    position: relative;
    border-right: 1px solid var(--bordure);
}
.cal-grid-col:last-child { border-right: 0; }
.cal-grid-col.is-weekend { background: #fbfcfd; }
.cal-grid-col.is-today { background: #f7fbff; }
.cal-grid-line {
    position: absolute;
    left: 0; right: 0;
    border-top: 1px solid #eef1f5;
}

/* Dans la grille horaire, le bloc est positionne : sa hauteur EST sa duree. */
.cal-grid-col .cal-slot {
    position: absolute;
    z-index: 2;
    /* `left` et `width` viennent du style en ligne : ce sont les couloirs
       calcules par `_assign_lanes`, pour que deux prestations simultanees se
       partagent la colonne au lieu de se recouvrir. La bordure interieure
       tient lieu de gouttiere.

       TROIS COTES, JAMAIS LE QUATRIEME — et c'est tout le sujet.

       Cette regle disait `border: 2px solid transparent`. Le raccourci
       `border` ecrit les QUATRE cotes, donc `border-left-color` aussi : il
       effacait la couleur d'etat posee vingt lignes plus haut par
       `.cal-slot.status-*`. A specificite egale (deux classes de chaque
       cote), c'est la regle la plus BASSE dans le fichier qui gagne — celle-ci.

       Consequence : dans la vue jour et la vue semaine — c'est-a-dire dans le
       planning qu'on regarde tous les jours — tous les creneaux avaient le
       meme lisere invisible. La legende annoncait cinq couleurs, l'ecran n'en
       montrait aucune. Les vues mois et periode, elles, etaient correctes :
       elles n'ont pas de `.cal-grid-col`, ce qui rendait le defaut
       incomprehensible.

       On declare donc les trois cotes separement. Le bord gauche garde ses
       3 px de couleur, qui font aussi tres bien office de gouttiere. */
    border-top: 2px solid transparent;
    border-right: 2px solid transparent;
    border-bottom: 2px solid transparent;
    background-clip: padding-box;
}
.cal-grid-col .cal-slot-sub { display: none; }

/* -- Periode libre : une liste par jour ----------------------------------- */
.cal-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
    gap: 12px;
}
.cal-list-day {
    background: var(--blanc);
    border: 1px solid var(--bordure);
    border-radius: var(--radius);
    padding: 12px;
    min-height: 110px;
}
.cal-list-day.is-today { border-color: var(--bleu); box-shadow: inset 0 0 0 1px var(--bleu); }
.cal-list-day.is-weekend { background: #fafbfc; }
.cal-list-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    padding-bottom: 8px;
    margin-bottom: 8px;
    border-bottom: 1px solid var(--bordure);
    font-size: 13px;
}
.cal-list-slots { display: flex; flex-direction: column; gap: 6px; min-height: 24px; }
.cal-empty { color: var(--texte-doux); font-size: 13px; margin: 0; }

.conflict { color: #8a5200; }

/* Apercu des dates produites par une frequence. */
.date-chips { list-style: none; margin: 10px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.date-chips li {
    padding: 4px 9px;
    border: 1px solid var(--bordure);
    border-radius: 999px;
    background: #f7f9fc;
    font-size: 12.5px;
}

@media (max-width: 720px) {
    /* Sur telephone, le mois devient une pile de journees : sept colonnes de
       120 px de large ne sont lisibles nulle part. */
    .cal-month-head { display: none; }
    .cal-month-week { grid-template-columns: 1fr; }
    .cal-month-day { min-height: 0; border-right: 0; }
    .cal-month-day.is-outside { display: none; }
    .cal-month-slots .cal-slot-sub { display: block; }

    /* Une journee vide se reduit a sa date : sur un mois complet, cela evite
       un millier de pixels de defilement pour rien. Elle reste une cible de
       depot — on doit pouvoir y glisser une prestation. */
    .cal-month-day:not(:has(.cal-slot)) { padding: 2px 6px; opacity: .55; }
    .cal-month-day:not(:has(.cal-slot)) .cal-month-slots { min-height: 0; }

    .cal-toolbar { flex-direction: column; align-items: stretch; }
    .cal-views { overflow-x: auto; }
    .cal-filters .form-control, .cal-filters .form-select { flex: 1 1 140px; }
}

/* ==========================================================================
   BON D'INTERVENTION
   Mis en page pour etre lu par le client, et imprimable tel quel.
   ========================================================================== */
.voucher {
    background: var(--blanc);
    border: 1px solid var(--bordure);
    border-radius: var(--radius);
    padding: 24px;
}
.voucher-head {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    padding-bottom: 14px;
    margin-bottom: 18px;
    border-bottom: 2px solid var(--texte);
}
.voucher-head h2 { margin: 0; letter-spacing: .08em; }
.voucher h3 {
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--texte-doux);
    margin: 18px 0 6px;
}
.voucher-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 4px 18px;
}
.voucher-grid h3 { margin-top: 10px; }
.voucher-grid p { margin: 0; }

.voucher-tasks { list-style: none; margin: 0; padding: 0; }
.voucher-tasks li { padding: 4px 0; border-bottom: 1px solid var(--bordure); }
.voucher-tasks li.done { color: var(--texte); }
.voucher-tasks li.undone { color: var(--texte-doux); }

.voucher-signatures {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 24px;
    margin-top: 26px;
    padding-top: 18px;
    border-top: 1px solid var(--bordure);
}

.signature-pad {
    display: block;
    width: 100%;
    height: 160px;
    border: 1px dashed var(--bordure);
    border-radius: 6px;
    background: #fff;
    touch-action: none; /* sinon le doigt fait defiler la page au lieu de signer */
}
.signature-actions { margin: 8px 0; }
.signature-image {
    display: block;
    max-width: 100%;
    height: 120px;
    object-fit: contain;
    border-bottom: 1px solid var(--texte);
}

/* Photos d'intervention et documents illustres. */
.photo-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    gap: 12px;
}
.photo-item { margin: 0; }
.photo-item img {
    width: 100%;
    height: 110px;
    object-fit: cover;
    border: 1px solid var(--bordure);
    border-radius: 6px;
}
.photo-item figcaption { font-size: 12px; color: var(--texte-doux); margin-top: 4px; }

/* Statuts d'un feedback : la meme couleur que la colonne du tableau. */
.badge-status-RECEIVED    { background: #fdf0dd; color: #8a5200; }
.badge-status-IN_PROGRESS { background: #e6effa; color: #1f5f8b; }
.badge-status-DONE        { background: #e3f4ea; color: #1f6b41; }

/* Frise de l'historique : on ajoute, on n'efface jamais. */
.timeline { list-style: none; margin: 0; padding: 0; }
.timeline li {
    display: grid;
    grid-template-columns: 118px 1fr;
    gap: 10px;
    padding: 9px 0;
    border-bottom: 1px solid var(--bordure);
    font-size: 13px;
}
.timeline li:last-child { border-bottom: 0; }
.timeline-when { color: var(--texte-doux); font-size: 12px; }

/* Statuts commerciaux d'un client. */
.badge-status-PROSPECT  { background: #eef2f7; color: #445; }
.badge-status-ACTIVE    { background: #e3f4ea; color: #1f6b41; }
.badge-status-SUSPENDED { background: #fdf0dd; color: #8a5200; }
.badge-status-ENDED     { background: #fae8e7; color: #8f201a; }

/* A l'impression, on ne garde que le bon. */
@media print {
    .sidebar, .menu-bascule, .topbar-actions, .subnav, .messages, .form-actions { display: none !important; }
    .voucher { border: 0; padding: 0; }
    .main, .content { padding: 0; }
}

/* ==========================================================================
   TABLEAU DES FEEDBACKS

   Trois colonnes, des cartes qu'on deplace au glisser. Le deplacement change
   le STATUT : ce n'est pas un rangement visuel.
   ========================================================================== */
.kanban {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 14px;
    align-items: start;
}
.kanban-col {
    background: #f0f3f7;
    border: 1px solid var(--bordure);
    border-radius: var(--radius);
    padding: 12px;
    min-height: 220px;
}
.kanban-col-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 10px;
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--texte-doux);
}
/* Le liseré de tete dit d'un coup d'oeil ou l'on en est. */
.kanban-col[data-column="RECEIVED"]    { box-shadow: inset 0 3px 0 #d99b1c; }
.kanban-col[data-column="IN_PROGRESS"] { box-shadow: inset 0 3px 0 var(--bleu); }
.kanban-col[data-column="DONE"]        { box-shadow: inset 0 3px 0 #2f9e5f; }

.kanban-cards { display: flex; flex-direction: column; gap: 10px; min-height: 60px; }

.kanban-card {
    display: block;
    background: var(--blanc);
    border: 1px solid var(--bordure);
    border-radius: 6px;
    box-shadow: var(--ombre);
    padding: 11px 12px;
    color: var(--texte);
    font-size: 13px;
    cursor: grab;
    /* `pan-y` ET NON `none`, ET C'EST TOUT LE SUJET.
     *
     * Sans regle, le navigateur reclamait le geste des que le doigt bougeait :
     * il annulait le pointeur, et le glisser mourait avant d'avoir commence.
     * C'est pour ca qu'il ne marchait qu'a la souris.
     *
     * `none` l'aurait repare — en rendant la page impossible a faire defiler
     * des qu'un doigt se pose sur une carte, c'est-a-dire presque partout sur
     * ce tableau.
     *
     * `pan-y` partage : le defilement VERTICAL reste au navigateur, le
     * mouvement HORIZONTAL nous revient. Or d'une colonne a l'autre, on va
     * justement de cote. Sur telephone, ou les colonnes s'empilent, c'est le
     * bouton de deplacement qui prend le relais — voir `.kanban-deplacer`. */
    touch-action: pan-y;
}
/* Le lien porte le contenu ; la carte porte la boite et le glisser. Ils
 * etaient un seul element, mais un bouton ne peut pas vivre dans une ancre. */
.kanban-card-lien {
    display: block;
    color: inherit;
    font: inherit;
}
.kanban-card-lien:hover { text-decoration: none; }
.kanban-card:hover { border-color: var(--bleu); }

/* -- Deplacer sans glisser -------------------------------------------------
   Le seul chemin qui marche sur tous les appareils, et le seul qui marche au
   clavier. Discret — une ligne de mots sous la carte — parce qu'on ne s'en
   sert pas a chaque coup d'oeil, mais toujours la quand on en a besoin. */
.kanban-deplacer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    margin-top: 9px;
    padding-top: 8px;
    border-top: 1px dashed var(--bordure);
    font-size: 11px;
}
.kanban-vers {
    padding: 4px 9px;
    border: 1px solid var(--bordure);
    border-radius: 999px;
    background: var(--blanc);
    color: var(--bleu);
    font-size: 11px;
    font-weight: 600;
    cursor: pointer;
    /* Le doigt doit atteindre le bouton, pas la carte qui est dessous. */
    touch-action: manipulation;
}
.kanban-vers:hover { background: #eaf2fd; border-color: var(--bleu); }
.kanban-vers:active { background: #dce9fb; }

/* Sur un ecran tactile, la cible doit etre attrapable sans viser. */
@media (hover: none) {
    .kanban-vers { padding: 7px 12px; font-size: 12px; }
}
.kanban-card-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 5px;
}
.kanban-card-ref { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11px; color: var(--texte-doux); }
.kanban-card-title { display: block; font-weight: 600; margin-bottom: 3px; }
.kanban-card-meta { display: block; color: var(--texte-doux); font-size: 12px; }
.kanban-card-foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    margin-top: 7px;
    font-size: 11px;
    color: var(--texte-doux);
}
.kanban-card.is-stale { border-left: 3px solid var(--orange); }
.kanban-card-photos { color: var(--texte-doux); }

.kanban-empty {
    padding: 18px 8px;
    text-align: center;
    color: var(--texte-doux);
    font-size: 12.5px;
    border: 1px dashed var(--bordure);
    border-radius: 6px;
}

@media (max-width: 860px) {
    /* Trois colonnes de 110 px ne se lisent pas : on empile. */
    .kanban { grid-template-columns: 1fr; }
}

/* ==========================================================================
   FORMULAIRE PUBLIC DE FEEDBACK

   Vu sur un telephone, debout, souvent d'une main. Grands champs, gros
   bouton, aucune colonne.
   ========================================================================== */
.public-form { max-width: 560px; margin: 0 auto; }
.public-form .field label { font-size: 14px; }
.public-form .form-control, .public-form .form-select { padding: 12px 13px; font-size: 16px; }
.public-form textarea.form-control { min-height: 130px; }

/* Le champ photo, habille.
   Le champ fichier natif affiche « Choose Files / No file chosen » : trois
   mots en anglais sur une page que lit le personnel du client, avec un bouton
   gris qui ne ressemble a rien du reste. On masque l'entree et on habille son
   label — le clic sur le label ouvre le selecteur, sans JavaScript. */
.public-file { display: block; cursor: pointer; }
/* Double classe : le widget porte aussi `.form-control`, qui lui donnerait
   sinon un padding et une taille de police — donc une zone cliquable
   fantome decalee sous le label. */
.public-form .public-file input {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    opacity: 0;
}
.public-file span {
    display: block;
    padding: 13px;
    text-align: center;
    border: 1px dashed var(--bordure);
    border-radius: 8px;
    font-size: 15px;
    color: var(--texte-doux);
    background: var(--blanc);
}
.public-file:hover span,
.public-file input:focus-visible + span { border-color: var(--bleu); color: var(--bleu); }

/* Les vignettes de ce qu'on vient de choisir.
   Le champ etant masque, rien ne disait qu'une photo etait jointe : on
   photographiait la fuite, la page ne bougeait pas, et on recommencait. */
.public-thumbs {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 10px;
}
.public-thumb {
    position: relative;
    width: 72px;
    height: 72px;
    border-radius: 8px;
    overflow: hidden;
    border: 1px solid var(--bordure);
    background: var(--blanc);
}
.public-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* La croix de retrait.
   Les photos se cumulent : sans elle, une photo ratee ne se corrige qu'en
   rechargeant la page — et en perdant le texte deja ecrit. Dimensionnee pour
   le pouce (26 px), pas pour la souris. */
.public-thumb-del {
    position: absolute;
    top: 3px;
    right: 3px;
    width: 26px;
    height: 26px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: rgba(20, 26, 33, .72);
    color: #fff;
    font-size: 17px;
    line-height: 26px;
    text-align: center;
    cursor: pointer;
}
.public-thumb-del:hover { background: var(--rouge); }

/* Le piege a robots : invisible a l'oeil, present pour un script. */
.honeypot { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* L'affiche a coller sur le site. */
.poster {
    max-width: 620px;
    margin: 0 auto;
    padding: 32px;
    text-align: center;
    background: var(--blanc);
    border: 2px solid var(--texte);
    border-radius: var(--radius);
}
.poster h2 { font-size: 26px; margin-bottom: 6px; }
.poster-site { font-size: 17px; font-weight: 600; margin-bottom: 18px; }
.poster img { width: 260px; height: 260px; }
.poster-url {
    margin-top: 14px;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 12px;
    word-break: break-all;
    color: var(--texte-doux);
}

@media print {
    .sidebar, .menu-bascule, .topbar-actions, .subnav, .messages, .form-actions, .no-print { display: none !important; }
    .poster { border: 0; }
    .main, .content { padding: 0; }
}

/* -----------------------------------------------------------------------------
   CONTACTS D'UN CLIENT
   Chaque contact est un bloc : ses actions — ouvrir un acces, rattacher un
   compte existant, suspendre — ne tiennent pas dans une cellule de tableau.
   -------------------------------------------------------------------------- */
.contact-row {
    padding: 12px 0;
    border-bottom: 1px solid var(--bordure);
}
.contact-row:last-of-type { border-bottom: 0; }

.contact-head {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 12px;
}

.contact-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 10px;
}

.contact-grant { margin-top: 12px; }
.contact-grant .btn { margin-right: 8px; }

/* -----------------------------------------------------------------------------
   SE DEPLACER DANS LE TEMPS

   Deux fleches et « Aujourd'hui », AU CONTACT du calendrier. Elles etaient en
   haut a droite de la page, loin de la grille, et le seul moyen evident de
   changer de semaine restait le petit champ date — il fallait ouvrir un
   calendrier pour en parcourir un autre.

   Les listes de pastilles employe / client qui vivaient ici ont ete retirees :
   elles repetaient les menus deroulants et, avec vingt salaries, repoussaient
   le calendrier sous la ligne de flottaison.
   -------------------------------------------------------------------------- */
.cal-period {
    display: flex;
    align-items: center;
    gap: 4px;
}
.cal-arrow,
.cal-today {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 34px;
    border: 1px solid var(--bordure);
    border-radius: 6px;
    background: var(--blanc);
    color: var(--texte);
    font-size: 15px;
    line-height: 1;
    text-decoration: none;
}
/* Larges : on vise ces fleches vingt fois par jour, parfois au doigt. */
.cal-arrow { width: 40px; font-size: 18px; }
.cal-today { padding: 0 12px; font-size: 13px; }
.cal-arrow:hover,
.cal-today:hover {
    background: var(--bleu);
    border-color: var(--bleu);
    color: #fff;
    text-decoration: none;
}

.cal-active-filters { margin: 0 0 8px; }
.cal-active-filters a { margin-right: 10px; }

/* -----------------------------------------------------------------------------
   VUE PAR EMPLOYE
   Meme grille horaire, mais les colonnes sont des gens. Elle defile
   horizontalement : dix agents ne tiennent pas dans une largeur d'ecran, et
   les tasser les rendrait illisibles.
   -------------------------------------------------------------------------- */
.cal-team {
    background: var(--blanc);
    border: 1px solid var(--bordure);
    border-radius: var(--radius);
    box-shadow: var(--ombre);
    overflow: auto;
    max-height: 78vh;
}
.cal-team-head {
    display: flex;
    position: sticky;
    top: 0;
    z-index: 3;
    background: var(--blanc);
    border-bottom: 1px solid var(--bordure);
}
.cal-team-name {
    flex: 1 0 150px;
    min-width: 150px;
    padding: 8px 10px;
    text-align: center;
    border-left: 1px solid var(--bordure);
    font-size: 12px;
}
.cal-team-name strong { display: block; font-size: 13px; }
.cal-team-name span { display: block; color: var(--texte-doux); font-size: 11px; }
.cal-team-name.is-unassigned { background: #fff8ec; }

.cal-team-body { display: flex; position: relative; }
.cal-team-head .cal-grid-gutter,
.cal-team-body .cal-grid-gutter {
    flex: 0 0 58px;
    width: 58px;
    position: sticky;
    left: 0;
    z-index: 2;
    background: var(--blanc);
}
.cal-team-body .cal-grid-col {
    flex: 1 0 150px;
    min-width: 150px;
}
.cal-team-body .cal-grid-col.is-unassigned { background: #fffdf7; }
.cal-team-empty {
    position: absolute;
    top: 8px;
    left: 0; right: 0;
    text-align: center;
    font-size: 11px;
    color: #b6c0ca;
    margin: 0;
}

/* -----------------------------------------------------------------------------
   LE TABLEAU « A PLACER »
   A droite du calendrier sur grand ecran, en dessous sur telephone : un
   panneau lateral de 260px sur un mobile ecraserait le calendrier, qui est
   l'essentiel.
   -------------------------------------------------------------------------- */
.cal-with-backlog {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 268px;
    gap: 16px;
    align-items: start;
}
.cal-with-backlog-main { min-width: 0; }

@media (max-width: 1100px) {
    .cal-with-backlog { grid-template-columns: minmax(0, 1fr); }
}

.cal-backlog {
    background: var(--blanc);
    border: 1px solid var(--bordure);
    border-radius: var(--radius);
    box-shadow: var(--ombre);
    padding: 14px 16px;
    max-height: 78vh;
    overflow-y: auto;
}
.cal-backlog-head { display: flex; align-items: center; gap: 8px; }
.cal-backlog-head h2 { margin: 0; }
.cal-backlog-empty { font-size: 13px; color: var(--texte-doux); }
.cal-backlog-empty span { display: block; margin-top: 6px; }

.backlog-card {
    display: block;
    padding: 9px 11px;
    margin-top: 10px;
    border-radius: 6px;
    border-left: 3px solid #6b4fbf;
    background: #f5f2fc;
    font-size: 12px;
    line-height: 1.35;
}

/* AU DOIGT, `manipulation` CONFISQUAIT LE GESTE.
 *
 * La carte portait `touch-action: manipulation`, qui laisse au navigateur le
 * defilement dans LES DEUX sens : des que le doigt bougeait, il reclamait le
 * geste pour faire defiler la page et annulait le pointeur avant que le
 * deplacement n'existe. On appuyait, la fiche s'ouvrait comme sur un appui
 * long — exactement le defaut des tableaux taches et signalements.
 *
 * `pan-y` partage : le defilement VERTICAL reste au navigateur, le mouvement
 * HORIZONTAL nous revient. Or le tableau est A DROITE du calendrier : on tire
 * vers la gauche, donc de cote.
 *
 * SUR ECRAN ETROIT, LE TABLEAU PASSE SOUS LE CALENDRIER et le glisser devrait
 * alors remonter — verticalement, c'est-a-dire dans le sens qu'on vient de
 * rendre au navigateur. C'est pourquoi chaque carte porte aussi un
 * FORMULAIRE de secours (`.backlog-poser`) : une date, un bouton, aucun
 * glisser. Meme reponse que les boutons « Deplacer » des tableaux. */
.backlog-card[data-place] { cursor: grab; touch-action: pan-y; }
.backlog-card span { display: block; }
.backlog-card-title { font-weight: 700; }
.backlog-card-sub, .backlog-card-meta { color: var(--texte-doux); }
.backlog-card-quota { margin-top: 4px; }
.backlog-card-quota em { font-style: normal; color: var(--texte-doux); font-size: 11px; }

/* Engagement tenu : la carte reste visible — savoir que c'est fait vaut
   mieux que de la voir disparaitre sans explication — mais elle s'efface. */
.backlog-card.is-done {
    border-left-color: #2f9e5f;
    background: #f4f8f5;
    opacity: .6;
}
.backlog-card.is-saving { opacity: .5; }

/* -----------------------------------------------------------------------------
   LE CAHIER DES CHARGES
   L'atelier : le formulaire d'ajout reste en place pendant qu'on fait defiler
   les lignes — on en saisit dix d'affilee, remonter a chaque fois serait une
   corvee.
   -------------------------------------------------------------------------- */
.specs-layout {
    display: grid;
    grid-template-columns: 340px minmax(0, 1fr);
    gap: 18px;
    align-items: start;
}
@media (max-width: 1000px) {
    .specs-layout { grid-template-columns: minmax(0, 1fr); }
}
.specs-form { position: sticky; top: 16px; }

/* La colonne de commandes de l'atelier. Les cartes empilees qu'elle
   accompagnait ont ete remplacees par le meme tableau que le document : on
   construit desormais ce qu'on obtient, au lieu de le decouvrir en cliquant
   sur « Voir le document ». */
.spec-line-actions { display: flex; gap: 4px; flex-shrink: 0; }
.spec-line-actions form { display: inline; }

/* -----------------------------------------------------------------------------
   LE DOCUMENT — mise en page d'imprimeur
   Il s'imprime tel quel : le navigateur sait l'enregistrer en PDF, ce qui
   evite une bibliotheque de rendu cote serveur.
   -------------------------------------------------------------------------- */
.cdc {
    background: var(--blanc);
    border: 1px solid var(--bordure);
    border-radius: var(--radius);
    box-shadow: var(--ombre);
    padding: 40px 44px;
    max-width: 900px;
    /* Un document se lit, il ne se parcourt pas : un peu plus d'air et une
       ligne un peu plus haute qu'ailleurs dans le logiciel. */
    line-height: 1.5;
}

/* -- En-tete ---------------------------------------------------------------- */
.cdc-head {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 24px;
    padding-bottom: 18px;
    border-bottom: 2px solid var(--bleu-fonce);
}
.cdc-kicker {
    display: block;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .12em;
    color: var(--texte-doux);
}
.cdc-head h1 {
    font-size: 26px;
    margin: 2px 0 0;
    line-height: 1.2;
}
.cdc-where { margin: 6px 0 0; font-size: 14px; color: var(--texte-doux); }
.cdc-issuer { text-align: right; font-size: 12px; white-space: nowrap; }
.cdc-issuer strong { display: block; font-size: 14px; }
.cdc-issuer span { color: var(--texte-doux); }

/* -- Le bandeau d'identite --------------------------------------------------
   Une <dl> en grille plutot qu'un tableau : quatre couples libelle/valeur ne
   sont pas un tableau de donnees, et la <dl> se replie proprement en une
   colonne sur un ecran etroit. */
.cdc-identity {
    display: grid;
    /* Deux colonnes au maximum : a trois, « Lundi, Mardi, Mercredi, Jeudi,
       Vendredi » se casse sur trois lignes et le bandeau devient un pave. */
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
    gap: 2px 32px;
    margin: 22px 0 30px;
    padding: 16px 18px;
    background: #f7f9fb;
    border-radius: 6px;
    font-size: 13px;
}
.cdc-identity div { display: flex; gap: 10px; padding: 3px 0; }
.cdc-identity dt {
    min-width: 92px;
    color: var(--texte-doux);
    font-size: 12px;
}
.cdc-identity dd { margin: 0; font-weight: 600; }

/* -- Les prestations -------------------------------------------------------- */
.cdc-section { margin-top: 8px; }
.cdc-section-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    border-bottom: 1px solid var(--bordure);
    padding-bottom: 8px;
    margin-bottom: 14px;
}
.cdc-section-head h2 {
    margin: 0;
    font-size: 15px;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--bleu-fonce);
}
.cdc-count { color: var(--texte-doux); font-size: 12px; }
.cdc-table { font-size: 13px; border-collapse: collapse; width: 100%; }
.cdc-table thead th {
    background: transparent;
    border-bottom: 1px solid var(--bordure);
    padding: 0 10px 6px;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--texte-doux);
    text-align: left;
}
.cdc-table td { padding: 9px 10px; border-bottom: 1px solid #eef1f5; vertical-align: top; }

/* Le bandeau de zone : une ligne de la table, pas un titre au-dessus. Les
   colonnes restent donc alignees d'un bout a l'autre du document. */
.cdc-zone th {
    text-align: left;
    padding: 16px 10px 6px;
    border-bottom: 2px solid var(--bleu);
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: .07em;
    color: var(--bleu-fonce);
}
.cdc-zone th span {
    float: right;
    font-weight: 400;
    text-transform: none;
    letter-spacing: 0;
    color: var(--texte-doux);
}
.cdc-table tbody tr:first-child .cdc-zone th,
.cdc-zone:first-child th { padding-top: 4px; }

.cdc-num { width: 38px; color: var(--texte-doux); text-align: right; }

/* Les deux colonnes qui remplacent « Frequence particuliere ».

   Le RYTHME et les JOURS sont separes parce qu'un tableau se lit
   verticalement : on compare une colonne, on ne relit pas trente phrases.
   Largeurs fixes pour que les bandes de jours s'alignent exactement d'une
   ligne a l'autre — c'est cet alignement qui permet de les comparer d'un
   coup d'oeil, et il disparait des que la colonne respire. */
.cdc-rythme { width: 150px; font-size: 12px; color: var(--texte-doux); white-space: nowrap; }
.cdc-jours { width: 172px; }
.cdc-commandes { width: 1%; white-space: nowrap; }

/* La ligne qui a SA PROPRE frequence. La seule chose que le tableau ne montre
   pas tout seul : deux lignes peuvent afficher le meme rythme, l'une parce
   qu'elle suit le service, l'autre parce qu'on l'a decide pour elle. */
.cdc-propre {
    display: inline-block;
    margin-left: 6px;
    padding: 0 5px;
    border-radius: 3px;
    background: #eaf1f7;
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--bleu);
}

.cdc-label { display: block; font-weight: 600; }
.cdc-details {
    display: block;
    margin-top: 2px;
    font-size: 12px;
    color: var(--texte-doux);
}
.cdc-optional {
    display: inline-block;
    margin-left: 6px;
    padding: 0 6px;
    border-radius: 3px;
    background: #f1f3f7;
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--texte-doux);
}
/* -- La bande des sept jours ------------------------------------------------
   Sept cases, dont les actives sont pleines. Une phrase — « Lundi, Mercredi,
   Vendredi » — se lit ligne par ligne ; une bande se COMPARE d'une ligne a
   l'autre sans rien lire. C'est le principe d'un calendrier mural, et c'est
   ce qui rend un cahier de trente lignes parcourable.

   Les deux « M » et les deux « S » se distinguent par leur POSITION, jamais
   par leur lettre. La phrase complete reste dans l'infobulle et dans
   l'`aria-label`. */
.jours { display: inline-flex; gap: 3px; }
.jour {
    display: grid;
    place-items: center;
    width: 21px; height: 21px;
    border: 1px solid var(--bordure);
    border-radius: 4px;
    background: var(--blanc);
    font-size: 11px;
    font-weight: 600;
    color: #c4ccd8;
}
.jour.is-on {
    border-color: var(--bleu);
    background: var(--bleu);
    color: #fff;
}
/* Une frequence mensuelle n'a sincerement aucun jour de semaine : sept cases
   vides laisseraient croire a une information manquante. */
.jours-aucun { color: #c4ccd8; }

/* A l'impression, les aplats de couleur ne sortent pas sur toutes les
   imprimantes : la case active reste reconnaissable par sa bordure epaisse et
   son texte noir, meme en noir et blanc. */
@media print {
    .jour { width: 19px; height: 19px; color: #b9c2ce; }
    .jour.is-on {
        background: #fff;
        border: 2px solid #000;
        color: #000;
    }
}

/* -- Signatures ------------------------------------------------------------- */
.cdc-foot { margin-top: 36px; padding-top: 18px; border-top: 1px solid var(--bordure); }
.cdc-foot-note { margin: 0 0 20px; font-size: 12px; color: var(--texte-doux); }
.cdc-signs { display: flex; gap: 40px; }
.cdc-sign { flex: 1; font-size: 12px; }
.cdc-sign-who { display: block; font-weight: 600; }
.cdc-sign-date { display: block; color: var(--texte-doux); font-size: 11px; }
.cdc-sign-box {
    height: 96px;
    margin-top: 8px;
    border: 1px solid var(--bordure);
    border-radius: 4px;
    background: #fcfdfe;
}

@media print {
    .sidebar, .topbar, .no-print, .cal-toolbar { display: none !important; }
    .content { padding: 0; }
    .cdc { border: 0; box-shadow: none; padding: 0; max-width: none; }
    .cdc-identity { background: #f7f9fb !important; -webkit-print-color-adjust: exact; }
    .cdc-table { page-break-inside: auto; }
    .cdc-table tr { page-break-inside: avoid; }
    /* L'en-tete de colonnes se repete en haut de chaque page imprimee : sans
       ca, la deuxieme page presente trois colonnes sans nom. */
    .cdc-table thead { display: table-header-group; }
    .cdc-foot { page-break-inside: avoid; }
}

/* -----------------------------------------------------------------------------
   INSPECTIONS
   -------------------------------------------------------------------------- */
.score-pill {
    display: inline-block;
    padding: 3px 12px;
    border-radius: 999px;
    font-size: 13px;
    font-weight: 700;
}
.score-good { background: #e3f4ea; color: #1f6b41; }
.score-fair { background: #fdf0dd; color: #8a5200; }
.score-bad  { background: #fae8e7; color: #8f201a; }
.score-none { background: #eef1f5; color: var(--texte-doux); }

.inspection-head {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 20px;
}
.inspection-head table { flex: 1; }
.inspection-head th { width: 120px; }

.inspection-score {
    flex-shrink: 0;
    text-align: center;
    padding: 16px 22px;
    border-radius: var(--radius);
    min-width: 130px;
}
.inspection-score.score-good { background: #e3f4ea; }
.inspection-score.score-fair { background: #fdf0dd; }
.inspection-score.score-bad  { background: #fae8e7; }
.inspection-score.score-none { background: #eef1f5; }
.inspection-score-value { display: block; font-size: 30px; font-weight: 700; line-height: 1.1; }
.inspection-score-label { display: block; font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--texte-doux); }

.inspection-tally { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 14px; font-size: 12px; }
.tally { color: var(--texte-doux); }
.tally strong { font-size: 15px; color: var(--texte); }
.tally-COMPLIANT strong     { color: #1f6b41; }
.tally-PARTIAL strong       { color: #8a5200; }
.tally-NON_COMPLIANT strong { color: #8f201a; }

.result-badge-COMPLIANT     { background: #e3f4ea; color: #1f6b41; }
.result-badge-PARTIAL       { background: #fdf0dd; color: #8a5200; }
.result-badge-NON_COMPLIANT { background: #fae8e7; color: #8f201a; }
.result-badge-NOT_CHECKED   { background: #eef1f5; color: var(--texte-doux); }

/* -- L'identite, en <dl> plutot qu'en tableau ------------------------------- */
.inspection-identity { flex: 1; display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 2px 24px; margin: 0; }
.inspection-identity div { display: flex; gap: 10px; padding: 3px 0; font-size: 13px; }
.inspection-identity dt { min-width: 86px; color: var(--texte-doux); font-size: 12px; }
.inspection-identity dd { margin: 0; font-weight: 600; }

/* ==========================================================================
   LA GRILLE D'INSPECTION, ZONE PAR ZONE

   ON INSPECTE UNE PIECE, PAS UNE LISTE. Sur le terrain on entre dans les
   sanitaires, on regarde tout ce qui les concerne, on passe aux bureaux. Un
   tableau par zone se remplit en marchant.

   La version precedente posait une CARTE par ligne : sur un cahier de trente
   lignes, ca faisait une page sans fin ou l'on ne savait plus ou l'on en
   etait — et trente formulaires a enregistrer un par un.
   ========================================================================== */
.insp-zone { padding: 0; overflow: hidden; }
.insp-zone-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    padding: 14px 18px;
    background: #f7f9fb;
    border-bottom: 1px solid var(--bordure);
}
.insp-zone-head h2 {
    margin: 0;
    font-size: 14px;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--bleu-fonce);
}
.insp-zone-count { font-size: 12px; color: var(--texte-doux); }
.insp-zone-count.is-complete { color: #1f6b41; font-weight: 600; }
/* Une zone entierement controlee s'efface visuellement : ce qui reste a
   faire doit ressortir dans une page qui en compte cinq. */
.insp-zone.is-complete .insp-zone-head { background: #f2f8f4; }

.insp-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.insp-table thead th {
    padding: 8px 12px;
    background: transparent;
    border-bottom: 1px solid var(--bordure);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--texte-doux);
    text-align: left;
}
.insp-table td { padding: 8px 12px; border-bottom: 1px solid #eef1f5; vertical-align: top; }
.insp-table tbody tr:last-child td { border-bottom: 0; }

.insp-num { width: 34px; text-align: right; color: var(--texte-doux); }
.insp-etat { width: 250px; }
.insp-note { width: 26%; }
.insp-photo { width: 130px; }

.insp-label { display: block; font-weight: 600; }
.insp-freq { display: block; margin-top: 2px; font-size: 11px; color: var(--texte-doux); }
.insp-comment { display: block; padding-left: 8px; border-left: 2px solid var(--bordure); }

/* Le lisere de la ligne donne son etat sans avoir a lire la pastille. */
.insp-row { box-shadow: inset 3px 0 0 transparent; }
.insp-row.result-COMPLIANT     { box-shadow: inset 3px 0 0 #2f9e5f; }
.insp-row.result-PARTIAL       { box-shadow: inset 3px 0 0 #d99b1c; }
.insp-row.result-NON_COMPLIANT { box-shadow: inset 3px 0 0 #c0392b; background: #fffafa; }
.insp-row.result-NOT_CHECKED   { box-shadow: inset 3px 0 0 #dde3ea; }

/* -- Les quatre etats, en pastilles ----------------------------------------
   Un <select> demanderait deux clics et cacherait la reponse une fois
   choisie. En pastilles, on lit la colonne entiere d'un coup d'oeil. */
.insp-choices { display: flex; flex-wrap: wrap; gap: 4px; }
.insp-choice {
    display: inline-flex;
    align-items: center;
    padding: 4px 10px;
    border: 1px solid var(--bordure);
    border-radius: 999px;
    font-size: 12px;
    line-height: 1.2;
    cursor: pointer;
    background: var(--blanc);
    white-space: nowrap;
}
/* Le bouton radio lui-meme est masque : c'est la pastille qu'on clique.
   `appearance: none` plutot que `display: none` — un champ retire du flux
   n'est plus atteignable au clavier, et la grille deviendrait inutilisable
   sans souris. */
.insp-choice input {
    appearance: none;
    width: 0;
    margin: 0;
    outline-offset: 6px;
}
.insp-choice:hover { border-color: var(--bleu); }
.insp-choice.is-active { font-weight: 600; }
.insp-choice.choice-COMPLIANT.is-active     { background: #e3f4ea; border-color: #2f9e5f; color: #1f6b41; }
.insp-choice.choice-PARTIAL.is-active       { background: #fdf0dd; border-color: #d99b1c; color: #8a5200; }
.insp-choice.choice-NON_COMPLIANT.is-active { background: #fae8e7; border-color: #c0392b; color: #8f201a; }
.insp-choice.choice-NOT_CHECKED.is-active   { background: #eef1f5; color: var(--texte-doux); }
.insp-choice:has(input:focus-visible) { outline: 2px solid var(--bleu); outline-offset: 2px; }

/* -- Photos ----------------------------------------------------------------- */
.insp-thumbs { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 6px; }
.insp-thumbs:empty { margin-bottom: 0; }
.insp-thumb { position: relative; display: inline-block; }

/* LA PHOTO QU'ON VIENT DE CHOISIR, pas encore enregistree.

   Elle se distingue de celles qui sont deja en base : un liseré et un fond
   pointillés. Sans cette difference, on ne saurait pas laquelle disparaitrait
   si l'on quittait la page sans enregistrer — et on croirait avoir enregistre
   ce qui n'est qu'affiche. */
.insp-thumb-neuve img {
    border: 2px dashed var(--bleu);
    opacity: .9;
}
.insp-thumb img {
    width: 42px;
    height: 42px;
    object-fit: cover;
    border-radius: 4px;
    border: 1px solid var(--bordure);
    display: block;
}
.insp-thumb-del {
    position: absolute;
    top: -5px;
    right: -5px;
    width: 17px;
    height: 17px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: #c0392b;
    color: #fff;
    font-size: 12px;
    line-height: 17px;
    cursor: pointer;
}
/* Le champ fichier natif affiche « Choose File / no file selected » : trois
   fois trop large pour une colonne de tableau, et en anglais. On masque le
   champ et on habille son label — le clic sur le label ouvre le selecteur,
   sans une ligne de JavaScript. */
.insp-file { display: inline-block; cursor: pointer; }
.insp-file input { position: absolute; width: 1px; height: 1px; opacity: 0; }
.insp-file span {
    display: inline-block;
    padding: 3px 9px;
    border: 1px dashed var(--bordure);
    border-radius: 999px;
    font-size: 11px;
    color: var(--texte-doux);
    white-space: nowrap;
}
.insp-file:hover span { border-color: var(--bleu); color: var(--bleu); }
/* Un fichier choisi ne se voit pas autrement : le champ est masque, et rien
   ne dirait qu'une photo partira au prochain enregistrement. */
.insp-file:has(input:valid:not(:placeholder-shown)) span,
.insp-file input:focus-visible + span { border-color: var(--bleu); color: var(--bleu); }

/* -- La barre d'enregistrement, collee en bas -------------------------------
   Sur un cahier de trente lignes, un bouton place apres la derniere zone
   obligerait a faire defiler toute la page pour valider ce qu'on vient de
   cocher trois zones plus haut. */
.insp-savebar {
    position: sticky;
    bottom: 0;
    z-index: 5;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    margin-top: 14px;
    padding: 12px 18px;
    background: var(--blanc);
    border: 1px solid var(--bordure);
    border-radius: var(--radius);
    box-shadow: 0 -2px 12px rgba(0, 0, 0, .08);
    font-size: 13px;
    color: var(--texte-doux);
}
/* La barre est collee en bas du cadre : sans cette marge, la derniere zone
   passe dessous et ses dernieres lignes deviennent illisibles. */
#grille { padding-bottom: 8px; }

@media print {
    .insp-savebar, .insp-file, .insp-thumb-del,
    .insp-thumb-neuve { display: none !important; }
    .insp-zone { break-inside: auto; }
    .insp-table tr { break-inside: avoid; }
}

/* ==========================================================================
   TACHES

   Le tableau reutilise integralement le kanban des feedbacks (memes valeurs
   de colonnes, donc memes liseres de tete sans une ligne de plus). Ce qui
   suit ne concerne que ce qui est PROPRE aux taches : l'urgence, l'echeance,
   et l'etat de chaque destinataire.
   ========================================================================== */

/* -- Le compteur du menu -------------------------------------------------- */
/* Visible depuis n'importe quelle page : c'est ce qui fait qu'on y pense. */
.nav-badge {
    display: inline-block;
    min-width: 20px;
    padding: 0 6px;
    margin-left: 6px;
    border-radius: 999px;
    background: rgba(255, 255, 255, .18);
    color: #fff;
    font-size: 11px;
    font-weight: 700;
    text-align: center;
    line-height: 18px;
}
/* Rouge des qu'il y a du retard : le nombre seul ne dit pas s'il presse. */
.nav-badge-late { background: #c0392b; }

/* -- L'urgence ------------------------------------------------------------ */
/*
 * Quatre niveaux, quatre couleurs, et un liseré a gauche de la carte. La
 * couleur seule ne suffirait pas — le libelle reste ecrit en toutes lettres,
 * pour que la carte reste lisible en noir et blanc comme pour un daltonien.
 */
.badge-urgency-LOW      { background: #eef1f5; color: #5b6b7c; }
.badge-urgency-NORMAL   { background: #e6effa; color: #1f5f8b; }
.badge-urgency-HIGH     { background: #fdf0dd; color: #8a5200; }
.badge-urgency-CRITICAL { background: #fae8e7; color: #8f201a; }

.task-card { border-left: 3px solid transparent; }
.task-urgency-HIGH     { border-left-color: #d99b1c; }
.task-urgency-CRITICAL { border-left-color: #c0392b; }
.task-card.is-late     { border-left-color: #c0392b; background: #fffafa; }

/* -- L'echeance ----------------------------------------------------------- */
.task-due { font-size: 11.5px; color: var(--texte-doux); }
.is-late { color: #b3261e; font-weight: 600; }
.is-soon { color: #8a5200; font-weight: 600; }
.row-late td { background: #fffafa; }

/* -- L'etat d'un destinataire --------------------------------------------- */
/* Memes couleurs que les colonnes du tableau : un etat se reconnait au meme
 * endroit du spectre, qu'on le lise sur une carte ou dans un tableau. */
.badge-state-RECEIVED    { background: #fdf0dd; color: #8a5200; }
.badge-state-IN_PROGRESS { background: #e6effa; color: #1f5f8b; }
.badge-state-DONE        { background: #e3f4ea; color: #1f6b41; }

/*
 * Le suivi de l'expediteur : une pastille par personne.
 *
 * C'est la colonne qui justifie tout le modele — avec un statut unique par
 * tache, elle ne pourrait afficher qu'un seul mot pour trois personnes.
 */
.who-list { display: flex; flex-wrap: wrap; gap: 6px; }
.who {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 9px;
    border-radius: 999px;
    font-size: 12px;
    white-space: nowrap;
}
.who em { font-style: normal; opacity: .75; }
.who-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: currentColor;
    flex: 0 0 7px;
}

/* -- Le choix des destinataires, en cases a cocher ------------------------ */
/*
 * « Maintenir Ctrl enfonce pour choisir plusieurs personnes » est la
 * fonctionnalite la moins decouverte de l'histoire des formulaires — et sur
 * telephone elle n'existe pas. Des cases a cocher rendent le choix multiple
 * evident ; il restait a les empecher de deborder de l'ecran.
 */

/* -- La zone de destinataires, qui defile -----------------------------------
   Django rend le widget en <div class="checkbox-scroll"> contenant un <div>
   par option : la classe est posee sur le widget (forms.py) plutot que visee
   par un selecteur large, qui attraperait les autres formulaires.

   Des cases a cocher posees a la suite deroulaient tout l'effectif entre
   l'echeance et le client : avec quarante salaries, le formulaire faisait
   quatre ecrans de haut et le bouton d'envoi disparaissait de l'horizon.

   Un cadre de hauteur fixe le ramene a la taille d'un champ ordinaire. Le
   `max-height` en `rem` (et non en `vh`) garantit qu'on voit toujours environ
   six lignes, quelle que soit la hauteur de l'ecran — sur un telephone,
   `vh` aurait donne une zone de deux lignes. */
.checkbox-scroll {
    max-height: 13.5rem;
    overflow-y: auto;
    padding: 5px;
    border: 1px solid var(--bordure);
    border-radius: 8px;
    background: var(--blanc);
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
    gap: 2px 10px;
    /* Le defilement s'arrete a cette zone : sans cela, arriver en bas de la
       liste continue de faire defiler la page entiere, et on perd le
       formulaire de vue au moment ou l'on cochait quelqu'un. */
    overscroll-behavior: contain;
}
.checkbox-scroll label {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 9px;
    border-radius: 6px;
    font-weight: 400;
    cursor: pointer;
}
.checkbox-scroll label:hover { background: #f2f5f8; }
.checkbox-scroll input { width: 16px; height: 16px; margin: 0; flex: none; }
/* Ce qui est coche doit se retrouver sans relire toute la liste. */
.checkbox-scroll label:has(input:checked) {
    background: #eaf2fd;
    color: var(--bleu);
    font-weight: 500;
}

/* Case a cocher isolee dans une barre de filtres. */
.check {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 8px 10px;
    font-weight: 400;
    white-space: nowrap;
}
.check input { width: 16px; height: 16px; margin: 0; }

/* -- Etat d'une demande de produits --------------------------------------- */
/* Memes teintes que partout : en attente = orange, accepte = vert, refuse =
 * rouge. Un etat se reconnait a la meme couleur d'un bout a l'autre du
 * logiciel. */
.badge-req-PENDING  { background: #fdf0dd; color: #8a5200; }
.badge-req-DONE     { background: #e3f4ea; color: #1f6b41; }
.badge-req-REFUSED  { background: #fae8e7; color: #8f201a; }

/* =============================================================================
   HORAIRES SOUPLES : la bande « a definir » et l'etirement des blocs
   ========================================================================== */

/* --- La bande « a definir » -----------------------------------------------
   Une prestation sans horaire n'a aucune position juste dans une grille ou la
   hauteur SIGNIFIE une heure. Elle vit donc au-dessus, comme un evenement
   « toute la journee » d'un agenda — visible, mais hors de l'echelle. */
.cal-undated {
    display: grid;
    grid-template-columns: 58px repeat(auto-fit, minmax(0, 1fr));
    min-width: 640px;
    background: #fffbf2;
    border: 1px solid var(--bordure);
    border-bottom: 0;
    border-radius: 6px 6px 0 0;
    /* HORS du cadre qui defile, et non dedans.
       A l'interieur, il aurait fallu la coller sous un en-tete lui-meme
       colle — deux `sticky` empiles dont la hauteur depend du contenu, donc
       un chevauchement des que l'en-tete grandit d'une ligne. Au-dessus, elle
       reste simplement toujours visible, sans calcul. */
    overflow-x: auto;
}
.cal-undated-label {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    padding: 4px 6px 4px 0;
    line-height: 1.1;
    text-align: right;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--texte-doux);
}
.cal-undated-col {
    padding: 4px;
    border-left: 1px solid var(--bordure);
    min-height: 30px;
}
.cal-undated-col.is-today { background: #fff7e6; }
.cal-undated-slot {
    display: block;
    padding: 3px 6px;
    margin-bottom: 3px;
    border-radius: 4px;
    /* Le tirete dit « pas encore pose » sans avoir besoin d'une legende : un
       bloc plein aurait l'air d'une decision prise. */
    border: 1px dashed var(--orange, #d98324);
    background: #fff;
    font-size: 11px;
    line-height: 1.3;
    color: var(--texte);
}
.cal-undated-slot:hover { text-decoration: none; filter: brightness(.97); }
.cal-undated-slot strong { display: block; }
.cal-undated-slot span { display: block; color: var(--texte-doux); }

/* --- Les poignees d'etirement ---------------------------------------------
   Huit pixels de haut, invisibles au repos. Elles n'apparaissent qu'au survol
   du bloc : affichees en permanence, elles surchargeraient une semaine
   chargee de quarante liseres colores pour une action qu'on fait rarement. */
.cal-slot-grip {
    position: absolute;
    left: 0;
    right: 0;
    height: 8px;
    z-index: 3;
    opacity: 0;
    transition: opacity .12s;
}
.cal-slot-grip-top    { top: 0; cursor: ns-resize; }
.cal-slot-grip-bottom { bottom: 0; cursor: ns-resize; }
.cal-slot-grip::after {
    content: "";
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    width: 26px;
    height: 3px;
    border-radius: 2px;
    background: var(--bleu);
}
.cal-slot-grip-top::after    { top: 1px; }
.cal-slot-grip-bottom::after { bottom: 1px; }

.cal-grid-col .cal-slot:hover .cal-slot-grip,
.cal-team-col .cal-slot:hover .cal-slot-grip { opacity: .85; }
.cal-slot.is-resizing .cal-slot-grip { opacity: 1; }

/* Pendant l'etirement : pas de selection de texte parasite, et un bloc qui
   passe au-dessus de ses voisins pour qu'on voie ce qu'on fait. */
.cal-slot.is-resizing {
    z-index: 10;
    user-select: none;
    box-shadow: 0 2px 10px rgba(0, 0, 0, .18);
}

/* Les poignees n'ont de sens que dans une grille horaire : ailleurs, la
   hauteur ne represente aucune duree. */
.cal-list .cal-slot-grip,
.cal-month .cal-slot-grip { display: none; }

@media print {
    .cal-slot-grip { display: none !important; }
}

/* =============================================================================
   L'ECRAN DE REGLAGE DES DROITS
   =============================================================================
   Une soixantaine de cases a cocher tiennent sur une page a condition qu'on
   les organise : un interrupteur par module, ses capacites en colonnes en
   dessous. Une liste verticale de soixante lignes aurait fait defiler trois
   ecrans et personne n'aurait vu la fin.
   ========================================================================== */
.module-acces { padding-bottom: 14px; }

.module-switch {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 1.02rem;
    cursor: pointer;
    margin-bottom: 6px;
}
.module-switch input { width: 18px; height: 18px; cursor: pointer; }

.perm-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: 8px 18px;
    margin-top: 12px;
}

.perm {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    cursor: pointer;
    line-height: 1.35;
}
.perm input { margin-top: 3px; flex-shrink: 0; cursor: pointer; }

/* Grise sans desactiver : un champ `disabled` ne serait pas envoye, et
   l'enregistrement effacerait les cases d'un module qu'on vient de fermer.
   On veut precisement l'inverse — les retrouver en le rouvrant. */
.perm-grid.desactive,
#liste-clients.desactive {
    opacity: .4;
    pointer-events: none;
}

/* Le texte qui explique un module ferme : masque par le script quand il est
   ouvert. Visible par defaut pour que la page reste juste sans JavaScript. */
.module-ferme { margin-top: 10px; font-style: italic; }

/* =============================================================================
   LE SUIVI D'UNE INTERVENTION — UN FIL DE DISCUSSION
   =============================================================================
   C'ETAIT UNE PILE DE CARTES IDENTIQUES, du plus recent au plus ancien,
   chacune repetant le nom de l'auteur et la date complete. On ne voyait ni
   qui parlait, ni dans quel ordre les choses s'etaient passees.

   C'est maintenant une conversation :

       le plus ancien en haut       un suivi est un recit, il se lit dans le
                                    sens ou il s'est ecrit ;
       une bulle par message        les siennes a droite, celles des autres a
                                    gauche — c'est ce qui rend un fil lisible
                                    sans lire les noms ;
       une pastille d'initiales     on reconnait un interlocuteur a sa
                                    pastille, pas a son nom relu quinze fois ;
       un separateur par journee    « Aujourd'hui », « Hier », « mardi 10
                                    mars » : une date par jour au lieu d'une
                                    date par message ;
       la saisie en bas             la ou elle est dans toutes les
                                    messageries, apres le dernier message.

   TOUT EST EN FLUX NORMAL, sans hauteur fixe ni zone qui defile a l'interieur
   de la page. Un fil de discussion enferme dans un cadre de 400 pixels est
   une prison sur un telephone : ici on fait defiler LA PAGE, comme partout
   ailleurs dans le logiciel.
   ========================================================================== */

/* -- L'en-tete de la conversation ----------------------------------------- */
/* Un bandeau et non une carte : il dit ou l'on est, il n'est pas du contenu. */
.fil-entete {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    padding: 12px 16px;
    margin-bottom: 18px;
    background: var(--blanc);
    border: 1px solid var(--bordure);
    border-radius: var(--radius);
    box-shadow: var(--ombre);
}

/* -- Le fil ---------------------------------------------------------------- */
.fil { display: flex; flex-direction: column; gap: 14px; }

/* -- Le separateur de journee ---------------------------------------------
   Un filet qui traverse, avec le libelle pose dessus. C'est le repere
   universel des messageries, et il remplace quinze dates repetees par une. */
.fil-jour {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 10px 0 2px;
    color: var(--texte-doux);
    font-size: 12px;
}
.fil-jour::before,
.fil-jour::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--bordure);
}
.fil-jour span {
    padding: 3px 12px;
    border-radius: 999px;
    background: var(--blanc);
    border: 1px solid var(--bordure);
    white-space: nowrap;
    text-transform: capitalize;
}

/* -- Un message ------------------------------------------------------------ */
.fil-message {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    /* La bulle ne prend jamais toute la largeur : c'est cette marge libre a
       droite (ou a gauche) qui fait LIRE le fil comme une conversation. Sur
       un ecran large, une bulle pleine largeur redevient un paragraphe. */
    max-width: min(760px, 100%);
}

/* SES PROPRES MESSAGES PARTENT A DROITE. C'est la signature visuelle d'une
   messagerie, et elle vaut mieux que n'importe quelle etiquette : on sait qui
   parle sans lire. `margin-left:auto` pousse le bloc entier, `row-reverse`
   met la pastille du bon cote. */
.fil-message.est-moi {
    margin-left: auto;
    flex-direction: row-reverse;
}

/* -- La pastille d'initiales ----------------------------------------------- */
.fil-pastille {
    display: grid;
    place-items: center;
    flex: 0 0 34px;
    width: 34px;
    height: 34px;
    margin-top: 2px;
    border-radius: 50%;
    background: #e6effa;
    color: var(--bleu);
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .02em;
}
.fil-message.est-moi .fil-pastille { background: var(--bleu); color: #fff; }

/* -- La bulle -------------------------------------------------------------- */
.fil-bulle {
    min-width: 0;              /* sans quoi une longue URL deborde du fil */
    padding: 9px 13px 11px;
    background: var(--blanc);
    border: 1px solid var(--bordure);
    /* Le coin coupe du cote de la pastille : le detail qui fait « bulle »
       plutot que « carte ». */
    border-radius: 14px 14px 14px 4px;
    box-shadow: var(--ombre);
}
.fil-message.est-moi .fil-bulle {
    background: #eaf2fd;
    border-color: #cfe0f5;
    border-radius: 14px 14px 4px 14px;
}

.fil-auteur {
    display: flex;
    align-items: baseline;
    gap: 8px;
    margin-bottom: 4px;
    font-size: 12.5px;
}
.fil-auteur strong { font-size: 13px; }
/* La croix de suppression se pousse au bout de la ligne, quel que soit le
   cote ou la bulle est posee. */
.fil-auteur form { margin-left: auto; line-height: 1; }
.fil-retirer {
    border: 0;
    background: none;
    color: var(--texte-doux);
    font-size: 17px;
    line-height: 1;
    padding: 0 2px;
    cursor: pointer;
    opacity: .5;
}
.fil-retirer:hover { opacity: 1; color: var(--rouge); }
/* Sur un ecran tactile il n'y a pas de survol : invisible en permanence, la
   croix serait introuvable. */
@media (hover: none) { .fil-retirer { opacity: .8; } }

/* `linebreaksbr` cote gabarit conserve les retours a la ligne ; la mesure de
   lecture est bornee ici, une note de terrain pouvant faire dix lignes. */
.fil-texte { margin: 0; max-width: 62ch; overflow-wrap: anywhere; }

/* -- Les photos d'un message ----------------------------------------------
   Une mosaique serree, comme dans une messagerie. Des vignettes plus petites
   que dans le reste du logiciel : ici elles sont DANS le message, et la
   visionneuse est a un clic pour les regarder vraiment. */
.fil-photos {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
    gap: 6px;
    margin-top: 8px;
}
.fil-photos .photo-item { margin: 0; }
.fil-photos img {
    display: block;
    width: 100%;
    aspect-ratio: 1 / 1;
    object-fit: cover;
    border-radius: 8px;
}

/* -- Le fil vide ------------------------------------------------------------ */
.fil-vide {
    padding: 18px;
    text-align: center;
    background: var(--blanc);
    border: 1px dashed var(--bordure);
    border-radius: var(--radius);
}

/* -- La zone d'ecriture, en bas -------------------------------------------- */
.fil-composer {
    margin-top: 20px;
    padding: 14px 16px;
    background: var(--blanc);
    border: 1px solid var(--bordure);
    border-radius: var(--radius);
    box-shadow: var(--ombre);
    /* Le fil s'arrete visuellement au-dessus : un filet plus marque separe ce
       qu'on lit de ce qu'on ecrit. */
    border-top: 2px solid var(--bleu);
}
.fil-composer .field:last-of-type { margin-bottom: 6px; }

/* -- La croix sur une vignette --------------------------------------------
   Posee SUR la photo : une colonne de boutons a cote de trente vignettes
   aurait double la hauteur du message. */
.suivi-photo { position: relative; }
.suivi-photo-del {
    position: absolute;
    top: 4px;
    right: 4px;
    width: 24px;
    height: 24px;
    line-height: 1;
    border: 0;
    border-radius: 50%;
    background: rgba(0, 0, 0, .55);
    color: #fff;
    font-size: 16px;
    cursor: pointer;
    opacity: 0;
    transition: opacity .12s;
}
.suivi-photo:hover .suivi-photo-del,
.suivi-photo:focus-within .suivi-photo-del { opacity: 1; }

@media (hover: none) {
    .suivi-photo-del { opacity: .8; }
}

/* DANS L'APERCU AVANT ENVOI, la croix est TOUJOURS visible.
   Les photos s'y cumulent choix apres choix : retirer celle qu'on vient
   d'ajouter par erreur est un geste courant, pas une correction rare. La
   cacher derriere un survol la rendrait introuvable au moment ou l'on en a
   le plus besoin. */
#suivi-apercu .suivi-photo-del { opacity: .85; }
#suivi-apercu .suivi-photo:hover .suivi-photo-del { opacity: 1; }

/* A L'IMPRESSION : le fil se lit, il ne s'ecrit pas. */
@media print {
    .suivi-photo-del, .fil-retirer, .fil-composer { display: none !important; }
    .fil-message.est-moi { margin-left: 0; flex-direction: row; }
    .fil-bulle { box-shadow: none; }
}

/* =============================================================================
   LA VISIONNEUSE
   =============================================================================
   Une photo en grand, par-dessus la page, avec des fleches pour parcourir la
   serie. Elle remplace l'ouverture dans un nouvel onglet : pour regarder les
   vingt-cinq photos d'une note, il fallait vingt-cinq onglets — ou vingt-cinq
   allers-retours, en perdant sa place dans le fil a chaque fois.
   ========================================================================== */
.visionneuse {
    position: fixed;
    inset: 0;
    z-index: 2000;          /* au-dessus de tout, barre laterale comprise */
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 16px;
    /* Presque opaque : a .92, le formulaire et le fil restaient lisibles
       derriere la photo et tiraient l'oeil. On vient regarder UNE image. */
    background: rgba(12, 18, 26, .96);
}
.visionneuse[hidden] { display: none; }

/* Le fond ne defile pas derriere le calque : sur un telephone, le balayage
   vertical emporterait la page sous la photo. */
body.visionneuse-ouverte { overflow: hidden; }

.visionneuse-cadre {
    margin: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    max-width: 100%;
    max-height: 100%;
    min-width: 0;           /* sinon l'image deborde du flex sur mobile */
}
.visionneuse-cadre img {
    max-width: 100%;
    /* On reserve la place de la legende et des marges : sans ce calcul, une
       photo en hauteur pousse le compteur hors de l'ecran. */
    max-height: calc(100vh - 110px);
    object-fit: contain;
    border-radius: 6px;
    background: #0c121a;
}
.visionneuse-cadre figcaption {
    display: flex;
    gap: 14px;
    align-items: baseline;
    color: rgba(255, 255, 255, .82);
    font-size: 13px;
    text-align: center;
}
.visionneuse-compteur { color: rgba(255, 255, 255, .6); }

/* Les commandes. Grandes : on les vise avec un pouce, parfois ganté. */
.visionneuse-fleche,
.visionneuse-fermer {
    flex-shrink: 0;
    border: 0;
    border-radius: 50%;
    background: rgba(255, 255, 255, .12);
    color: #fff;
    cursor: pointer;
    line-height: 1;
    transition: background .12s;
}
.visionneuse-fleche:hover,
.visionneuse-fermer:hover { background: rgba(255, 255, 255, .24); }
.visionneuse-fleche:focus-visible,
.visionneuse-fermer:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

.visionneuse-fleche {
    width: 52px;
    height: 52px;
    font-size: 22px;
}
.visionneuse-fleche[hidden] { display: none; }

.visionneuse-fermer {
    position: absolute;
    top: 14px;
    right: 14px;
    width: 44px;
    height: 44px;
    font-size: 28px;
}

/* Sur telephone, les fleches passent SOUS l'image plutot que de la serrer sur
   les cotes : a 390 px de large, deux boutons de 52 px lui prenaient un quart
   de la place. */
@media (max-width: 640px) {
    .visionneuse {
        flex-wrap: wrap;
        align-content: center;
    }
    .visionneuse-cadre {
        order: 1;
        flex-basis: 100%;
    }
    .visionneuse-gauche { order: 2; }
    .visionneuse-droite { order: 3; }
    .visionneuse-cadre img { max-height: calc(100vh - 200px); }
}

/* La vignette qui ouvre la visionneuse : on doit voir que c'est cliquable. */
a[data-photo] { display: block; cursor: zoom-in; }

@media print {
    .visionneuse { display: none !important; }
}

/* =============================================================================
   LE HALL DE L'ADMINISTRATION
   =============================================================================
   Trois portes, et rien d'autre. Chaque carte porte UN chiffre — celui qui
   repond a « y a-t-il quelque chose a regarder de ce cote ? ». Le detail vit
   derriere la porte ; le recopier ici en ferait un quatrieme ecran, et plus
   personne n'irait dans les trois autres.
   ========================================================================== */
.admin-hall {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 16px;
    margin-top: 18px;
}

.admin-porte {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 20px;
    border: 1px solid var(--bordure);
    border-radius: var(--radius);
    background: var(--blanc);
    box-shadow: var(--ombre);
    color: var(--texte);
    /* La carte ENTIERE est le lien : viser un titre de 14 px au doigt est le
       genre de detail qui fait rater une porte deux fois sur trois. */
    text-decoration: none;
    transition: border-color .12s, box-shadow .12s;
}
.admin-porte:hover {
    border-color: var(--bleu);
    box-shadow: 0 2px 10px rgba(16, 24, 40, .1);
    text-decoration: none;
}
.admin-porte:focus-visible { outline: 2px solid var(--bleu); outline-offset: 2px; }

.admin-porte-titre {
    font-size: 15px;
    font-weight: 600;
    color: var(--bleu-fonce);
}
.admin-porte-chiffre {
    font-size: 30px;
    font-weight: 700;
    line-height: 1.1;
    color: var(--bleu);
}
.admin-porte-sous { font-size: 12px; color: var(--texte-doux); }
.admin-porte-texte {
    margin-top: 8px;
    font-size: 13px;
    color: var(--texte-doux);
    line-height: 1.45;
}
/* Le seul chiffre de la page qui appelle une action : on le voit sans lire. */
.admin-porte-alerte { display: block; margin-top: 6px; color: var(--orange); }

/* =============================================================================
   CHOISIR LE CAHIER DES CHARGES A INSPECTER
   =============================================================================
   Une carte par prestation du site. L'action est A DROITE, sur toute la
   hauteur : on parcourt les noms d'un cote, on clique de l'autre. Les
   remettre a la suite du texte obligeait a relire chaque bloc pour retrouver
   le bouton.
   ========================================================================== */
.insp-choix {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 20px;
    flex-wrap: wrap;
}
.insp-choix-corps { min-width: 0; flex: 1; }
.insp-choix-titre { margin: 0; font-size: 16px; }
.insp-choix-action {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-shrink: 0;
}
/* Une prestation sans cahier des charges reste VISIBLE, en retrait. La
   masquer laisserait devant un choix incomplet : « pourquoi la vitrerie
   n'est-elle pas proposee ? ». */
.insp-choix.is-ferme { background: var(--fond); }
.insp-choix.is-ferme .insp-choix-titre { color: var(--texte-doux); }

/* =============================================================================
   L'EQUIPE HABITUELLE
   =============================================================================
   Une ligne par salarie, une colonne par jour de passage. La case est LARGE
   et centree : on coche vingt cases d'affilee, souvent au doigt sur une
   tablette, et une case de 13 px se rate une fois sur trois.
   ========================================================================== */
.equipe-table th.equipe-jour,
.equipe-table td.equipe-jour { text-align: center; width: 96px; }

.equipe-case {
    display: block;
    padding: 6px 0;
    cursor: pointer;
}
.equipe-case input { width: 18px; height: 18px; cursor: pointer; }
/* Toute la cellule est cliquable : viser la case elle-meme, sur une colonne
   de vingt lignes, demande une precision que personne n'a. */
.equipe-table td.equipe-jour:hover { background: var(--fond); }


/* =============================================================================
   L'INVENTAIRE PHYSIQUE — le tableau de comptage
   =============================================================================
   C'est la SEULE page du logiciel dont le telephone est le support principal
   et non un repli : on ne fait pas un inventaire assis devant un ecran. Tout
   ce qui suit part de la — une main tient l'appareil, l'autre pose un bidon.
   ========================================================================== */

/* -----------------------------------------------------------------------------
   L'AVANCEMENT
   --------------------------------------------------------------------------
   Le seul chiffre qui compte pendant le comptage : combien il en reste. Il
   est en haut, il ne defile pas avec le tableau, et il tient sur une ligne.
   -------------------------------------------------------------------------- */
.count-progress {
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
    margin-bottom: 14px;
    padding: 12px 14px;
    background: var(--blanc);
    border: 1px solid var(--bordure);
    border-radius: var(--radius);
}
.count-progress strong { font-size: 20px; }
.count-progress-bar {
    flex: 1;
    min-width: 120px;
    height: 8px;
    border-radius: 4px;
    background: #e8edf3;
    overflow: hidden;
}
.count-progress-bar span {
    display: block;
    height: 100%;
    background: var(--vert);
    transition: width .2s;
}

/* -----------------------------------------------------------------------------
   SCANNER, CHERCHER, FILTRER
   -------------------------------------------------------------------------- */
.count-toolbar {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
    margin-bottom: 14px;
    padding: 14px;
    background: #f7f9fc;
    border: 1px solid var(--bordure);
    border-radius: var(--radius);
}
.count-toolbar-todo {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 8px;
    font-size: 13px;
    color: var(--texte-doux);
}

/* La ligne qu'on vient de scanner, sortie du tableau et posee en haut. C'est
   ce qui evite de la chercher du regard dans trois cents lignes — et donc ce
   qui rend le scan reellement utile. */
.count-scanned { border-color: var(--bleu); }
.count-scanned-head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 12px;
    margin-bottom: 10px;
}

/* -----------------------------------------------------------------------------
   LE TABLEAU
   -------------------------------------------------------------------------- */
.count-table td, .count-table th { vertical-align: middle; }
.count-input {
    width: 88px;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* L'ETAT SE LIT A LA COULEUR DU BORD, PAS A UNE COLONNE DE PLUS. En comptant,
   on balaie le tableau du regard pour trouver ce qui n'est pas encore fait :
   une teinte se voit sans etre lue. */
.count-row-ok  td:first-child { box-shadow: inset 3px 0 0 var(--vert); }
.count-row-gap td:first-child { box-shadow: inset 3px 0 0 var(--orange); }

.count-gap-plus  { color: var(--vert); font-weight: 600; }
.count-gap-minus { color: var(--rouge); font-weight: 600; }

/* -----------------------------------------------------------------------------
   LA BARRE D'ENREGISTREMENT, COLLEE EN BAS
   --------------------------------------------------------------------------
   Un bouton pose apres trois cents lignes est un bouton qu'on n'atteint
   jamais : il faudrait faire defiler tout le tableau apres chaque etagere.
   Il reste donc au bas de l'ecran, quel que soit le defilement.
   -------------------------------------------------------------------------- */
.count-actions {
    position: sticky;
    bottom: 0;
    z-index: 10;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    margin: 0 -12px -12px;
    padding: 12px;
    background: var(--blanc);
    border-top: 1px solid var(--bordure);
    box-shadow: 0 -2px 8px rgba(16, 24, 40, .06);
}

/* Une carte qui appelle l'attention sans crier : un filet bleu a gauche. */
.card-accent { border-left: 3px solid var(--bleu); }

/* Les colonnes de nombres s'alignent a droite et gardent des chiffres de
   meme largeur : sans cela, une colonne de quantites ne se compare pas. */
.num { text-align: right; font-variant-numeric: tabular-nums; }

@media (max-width: 640px) {
    .count-toolbar { grid-template-columns: 1fr; }

    /* LE CHAMP DE SAISIE PREND TOUTE LA LARGEUR DE SA CARTE. Un champ de
       88 px au milieu d'une carte se vise mal quand on tient l'appareil
       d'une main. */
    .table-stack .count-input { width: 100%; max-width: 140px; }
    .count-progress strong { font-size: 18px; }
    .count-actions { margin: 0 -14px -14px; }
    .count-actions .btn { flex: 1 1 100%; }

    /* Le liseré d'etat passe en haut de la carte : sur le cote gauche d'une
       carte empilee, il ne marquerait qu'une cellule. */
    .count-row-ok  { border-left: 3px solid var(--vert); }
    .count-row-gap { border-left: 3px solid var(--orange); }
    .count-row-ok td:first-child,
    .count-row-gap td:first-child { box-shadow: none; }
}


/* -----------------------------------------------------------------------------
   LE RESUME DE LA QUALITE
   --------------------------------------------------------------------------
   Une pastille de score et une phrase, sur une ligne. C'etait un bandeau de
   titre avec deux badges empiles dans un `<h2>` : il fallait le lire pour
   comprendre qu'il resumait, alors qu'un resume doit se voir.
   -------------------------------------------------------------------------- */
.qualite-resume {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    margin: 0 0 18px;
    padding-bottom: 14px;
    border-bottom: 1px solid var(--bordure);
}
.qualite-resume .score-pill { font-size: 17px; padding: 6px 12px; }


/* =============================================================================
   LA CARTE DES CLIENTS
   =============================================================================
   Une hauteur FIXE, et c'est volontaire. Une carte qui s'adapte a son contenu
   n'existe pas : Leaflet a besoin d'une hauteur pour calculer son cadrage, et
   un conteneur sans hauteur donne une carte de zero pixel — le defaut le plus
   courant de cette bibliotheque, et celui qui ne dit rien dans la console.
   ========================================================================== */
.carte-clients {
    height: 380px;
    border-radius: var(--radius);
    border: 1px solid var(--bordure);
    /* Le fond gris SE VOIT quand les tuiles ne chargent pas — reseau coupe,
       entreprise qui les bloque. Les epingles, elles, restent posees dessus :
       la forme du semis reste lisible sans le fond. */
    background: #e8edf3;
    /* Sous la barre laterale en tiroir et sous le voile : une carte qui
       passerait au-dessus du menu ouvert serait cliquable a travers lui. */
    z-index: 0;
}
.carte-mention { margin: 8px 0 0; }

/* Leaflet pose ses propres liens dans les bulles ; on aligne leur taille sur
   celle du reste du logiciel plutot que de garder ses valeurs par defaut. */
.leaflet-popup-content { font-size: 13.5px; line-height: 1.5; }
.leaflet-container { font: inherit; }

@media (max-width: 640px) {
    .carte-clients { height: 300px; }
}

@media print {
    /* Une carte ne s'imprime pas : les tuiles ne sont pas dans le flux et la
       plupart des navigateurs rendent un cadre vide. */
    .carte-clients { display: none !important; }
}


/* =============================================================================
   LA REPRISE DU STOCK INITIAL
   =============================================================================
   Un tableau qu'on remplit au clavier, du haut vers le bas. Les champs y sont
   plus compacts qu'ailleurs : on en voit huit d'un coup, et l'on passe de
   l'un a l'autre a la tabulation.
   ========================================================================== */
.init-table td { padding: 4px 6px; }
.init-table .form-control,
.init-table .form-select { padding: 6px 8px; font-size: 13.5px; }
.init-table input.num { text-align: right; }

/* LES CHAMPS QUI NE S'APPLIQUENT PAS SONT GRISES, PAS CACHES. Un tableau dont
   les colonnes disparaissent d'une ligne a l'autre ne se lit plus, et
   l'alignement des en-tetes n'aurait plus de sens. */
.init-table .form-control:disabled,
.init-table .form-select:disabled {
    background: #f2f5f8;
    color: #9aa8b6;
    cursor: not-allowed;
}
.init-ligne-materiel { background: #fbfcfd; }

@media (max-width: 640px) {
    /* En mode carte, un champ desactive n'apporte rien : il occupe une ligne
       pour dire qu'il ne sert pas. On le retire, l'intitule avec. */
    .init-table td:has(.form-control:disabled) { display: none; }
    .init-table .form-control, .init-table .form-select { font-size: 16px; }
}

/* =============================================================================
   « PRENDRE UNE PHOTO »
   =============================================================================

   LE BOUTON N'EXISTE QUE LA OU IL SERT. `capture` est ignore sur un
   ordinateur : le bouton y ouvrirait exactement le meme selecteur de fichiers
   que le champ d'a cote, et un bouton qui ment est pire que pas de bouton.

   `pointer: coarse` veut dire « on designe au doigt » — telephone, tablette.
   C'est precisement la ou l'on prend des photos sur place, et c'est de la ou
   le manque a ete signale. Aucune detection de navigateur, aucun JavaScript :
   la regle est ici, elle se lit, et elle ne se trompe pas de modele.

   Il est cache PAR DEFAUT et montre par exception : un navigateur qui ne
   comprendrait pas la requete media ne montre donc rien, plutot qu'un bouton
   sans effet. */
.bouton-appareil { display: none; }

@media (pointer: coarse) {
    .bouton-appareil {
        display: inline-flex;
        align-items: center;
        gap: 8px;
        margin-top: 8px;
        padding: 11px 18px;
        font: inherit;
        font-size: 15px;
        font-weight: 500;
        border: 1px solid var(--bordure);
        border-radius: 6px;
        background: var(--blanc);
        color: var(--texte);
        cursor: pointer;
    }
    .bouton-appareil::before { content: "\1F4F7"; font-size: 18px; line-height: 1; }
    .bouton-appareil:active { background: #f2f5f8; }
}

/* =============================================================================
   LES HEURES
   ============================================================================= */
.heures-filtres { display: grid; gap: 12px; }
.heures-periode { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.heures-periode label { font-size: 13px; color: var(--texte-doux); }
.heures-periode .form-control { width: auto; }
.heures-raccourcis { display: flex; flex-wrap: wrap; gap: 6px; }

/* LES CASES A COCHER, EN COLONNES QUI SE REORGANISENT.
   Trente salaries en une colonne feraient une page a faire defiler avant
   d'atteindre le bouton ; en grille, ils tiennent en quelques lignes, et sur
   telephone la grille retombe naturellement a une colonne. */
.heures-salaries > summary { cursor: pointer; font-weight: 600; font-size: 14px; }
.heures-cases {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
    gap: 4px 12px;
    margin: 10px 0 4px;
}
.heures-cases .case { display: flex; align-items: center; gap: 7px; font-size: 13px; }
/* Une cible tactile decente : au doigt, une case de 13 px ne se coche pas. */
.heures-cases input[type="checkbox"] { width: 17px; height: 17px; flex: none; }

/* -- La ligne d'un salarie : un LIEN vers son releve ------------------------
   Elle etait depliable, elle ne l'est plus. Deplier gardait le detail sur la
   page du total : deux fiches ouvertes et la troisieme passait sous la ligne
   de flottaison, sur l'ecran meme ou l'on venait comparer. Le releve a
   maintenant sa page, donc cette ligne n'a plus qu'un role : y conduire.

   TOUTE LA LIGNE EST CLIQUABLE, pas un « voir le detail » de 60 px a droite.
   Au doigt, la cible d'un lien est ce qui decide s'il est utilisable. */
.fiche-salarie {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    /* PLUS SERRE QU'UNE CARTE ORDINAIRE : ce n'est plus une fiche, c'est une
       LIGNE DE LISTE. L'espacement des cartes (18 px) etalait trente salaries
       sur trois ecrans. */
    margin-top: 8px;
    margin-bottom: 0;
    padding: 13px 16px;
    color: inherit;
    text-decoration: none;
}
.fiche-salarie:hover,
.fiche-salarie:focus-visible { border-color: var(--accent); background: #fbfdff; }
.fiche-nom { font-weight: 700; }
.fiche-chiffres { display: flex; align-items: baseline; gap: 10px; }
/* Le chevron dit « ca ouvre quelque chose » sans ajouter un mot a lire. */
.fiche-fleche { color: var(--texte-doux); font-size: 18px; line-height: 1; }
.fiche-corps { padding: 14px 16px; }

.fiche-repartition { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.repartition-part {
    display: inline-flex;
    align-items: baseline;
    gap: 6px;
    padding: 4px 9px;
    border: 1px solid var(--bordure);
    border-radius: 999px;
    font-size: 12px;
}
.repartition-part em { font-style: normal; font-size: 11px; }

/* L'ECART SE VOIT SANS LE CHERCHER. Trente lignes conformes n'apprennent rien ;
   les deux qui debordent expliquent le mois. */
.texte-ecart { color: #8a5200; font-weight: 600; }
.ligne-ecart { background: #fffcf6; }

/* -- Le filtre par salaries du calendrier ---------------------------------- */
/* Replie par defaut : trente noms deployes repousseraient la grille sous la
   ligne de flottaison, sur l'ecran dont on vient justement voir le calendrier. */
.cal-salaries > summary { cursor: pointer; font-size: 13px; white-space: nowrap; }
.cal-salaries[open] {
    position: relative;
    background: var(--blanc);
    border: 1px solid var(--bordure);
    border-radius: 6px;
    padding: 8px 10px;
}
.cal-salaries-cases {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
    gap: 4px 12px;
    margin-top: 8px;
    max-height: 210px;
    overflow-y: auto;
}
.cal-salaries-cases .case { display: flex; align-items: center; gap: 7px; font-size: 13px; }
.cal-salaries-cases input[type="checkbox"] { width: 17px; height: 17px; flex: none; }

/* -- Poser une prestation sans la glisser ---------------------------------- */
/* VISIBLE SEULEMENT AU DOIGT, comme les boutons « Deplacer » des tableaux.
   Sur ordinateur le glisser marche et il est plus rapide ; y ajouter un champ
   de date encombrerait chaque carte pour rien. */
.backlog-poser { display: none; }

@media (pointer: coarse) {
    .backlog-poser {
        display: flex;
        gap: 6px;
        align-items: center;
        margin-top: 8px;
    }
    .backlog-poser .form-control { font-size: 15px; padding: 7px 8px; flex: 1; min-width: 0; }
    .backlog-poser .btn { flex: none; }
}

/* -----------------------------------------------------------------------------
   LE GROUPE, ET LE FIL D'ACTUALITES DU CLIENT
   -------------------------------------------------------------------------- */

/* Le badge du groupe est un LIEN : il doit se voir cliquable sans crier plus
   fort que le nom du client a cote duquel il se trouve. */
.badge-groupe {
    background: #e6effa;
    color: var(--bleu);
    text-decoration: none;
    text-transform: none;
    letter-spacing: 0;
}
.badge-groupe:hover { background: #d6e4f4; }

/* -- Le fil ---------------------------------------------------------------- */
.note-form { margin-bottom: 16px; }
.note-form textarea {
    width: 100%;
    font: inherit;
    padding: 8px 10px;
    border: 1px solid var(--bordure);
    border-radius: var(--radius);
    resize: vertical;
}
.note-form-actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-top: 8px;
}

.fil { list-style: none; margin: 0; padding: 0; }

.fil-item {
    padding: 12px 0;
    border-top: 1px solid var(--bordure);
}
.fil-item:first-child { border-top: none; }

/* L'EPINGLE SE VOIT AU BORD, PAS AU FOND. Un aplat de couleur sur toute la
   carte ferait crier chaque consigne d'acces plus fort que le texte voisin ;
   une barre a gauche suffit a la retrouver en balayant la colonne. */
.fil-epingle {
    border-left: 3px solid var(--orange);
    padding-left: 10px;
    margin-left: -13px;
}

.fil-texte { margin-bottom: 4px; }
.fil-texte .badge { margin-right: 6px; }

.fil-pied { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

/* Un formulaire qui ne doit pas casser la ligne : le bouton « supprimer » d'un
   POST ne peut pas etre un simple lien (un GET qui efface est le debut d'un
   autre probleme), mais il doit s'y ressembler. */
.inline { display: inline; margin: 0; }

.lien-bouton {
    background: none;
    border: none;
    padding: 0;
    font: inherit;
    font-size: 12px;
    color: var(--texte-doux);
    text-decoration: underline;
    cursor: pointer;
}
.lien-bouton:hover { color: var(--bleu); }
.lien-danger:hover { color: var(--rouge); }

/* -----------------------------------------------------------------------------
   LE LOGO
   -------------------------------------------------------------------------- */
.auth-logo { display: block; margin: 0 auto 10px; }

.auth-footer-mark {
    vertical-align: -3px;
    margin-right: 5px;
    opacity: .75;
}

/* Dans la barre laterale, la marque du LOGICIEL accompagne le nom de
   l'entreprise sans lui disputer la place : 14 px, alignee sur le texte. */
.brand-logiciel { vertical-align: -2px; margin-right: 4px; }

/* -----------------------------------------------------------------------------
   LE POINTAGE
   -------------------------------------------------------------------------- */
/* Une alerte SOS concerne une PERSONNE, pas une machine : c'est la seule
   carte du logiciel qui a le droit d'etre bruyante. */
.card-alerte { border-left: 4px solid var(--rouge); }
.card-alerte h2 { color: var(--rouge); }

.ligne-anomalie { background: #fdf8ef; }

.alerte-item {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    align-items: flex-start;
    justify-content: space-between;
    padding: 12px 0;
    border-top: 1px solid var(--bordure);
}
.alerte-item:first-of-type { border-top: none; }

.alerte-form { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.alerte-form .form-control { min-width: 220px; }

.inline-form { display: flex; gap: 6px; align-items: center; }
.form-select-sm { padding: 3px 6px; font-size: 13px; }

@media (max-width: 720px) {
    .alerte-item { flex-direction: column; }
    .alerte-form { width: 100%; }
    .alerte-form .form-control { flex: 1; min-width: 0; }
}
