/* ------------------------------------------------------------------ */
/* Couleurs de thème pour les colonnes et blocs rendus en Python        */
/*                                                                      */
/* Les `format_html` des admins portaient des couleurs figées, pensées   */
/* pour le thème clair : en sombre, un texte #0f172a sur fond sombre     */
/* devenait illisible. Ces variables basculent avec le thème, que        */
/* sivoni marque par la classe `dark` sur <html>.                        */
/*                                                                      */
/* Les pastilles à fond coloré (bleu clair, vert clair) gardent leurs    */
/* couleurs en dur : leur contraste est interne au badge.                */
/* ------------------------------------------------------------------ */
:root {
    --pcr-text: #0f172a;
    --pcr-muted: #475569;
    --pcr-subtle: #94a3b8;
    --pcr-faint: #cbd5e1;
    --pcr-accent: #2563eb;
    /* Variante douce de l'accent · pour une action secondaire qui
       appartient à la même famille que l'action principale sans lui
       disputer l'œil. Reprend primary-50 / 200 / 700 de la palette. */
    /* Hauteur d'une page dans la bande d'aperçu · une A4 y mesure alors
       environ 15,5 rem de large, soit quatre à cinq pages visibles d'un
       coup sur un écran de bureau. */
    --pcr-page-apercu: 22rem;
    --pcr-accent-soft: #eff6ff;
    --pcr-accent-soft-border: #bfdbfe;
    --pcr-accent-soft-ink: #1d4ed8;
    --pcr-surface: #ffffff;
    --pcr-surface-2: #f1f5f9;
    --pcr-border: #e2e8f0;
    --pcr-success: #0d9488;
    --pcr-danger: #dc2626;
}

html.dark {
    --pcr-text: #e8edf4;
    --pcr-muted: #b6c2d2;
    --pcr-subtle: #8b9bb0;
    --pcr-faint: #5b6b80;
    --pcr-accent: #7cb0fb;
    /* Sur fond sombre, un aplat primary-50 ferait une tache claire :
       c'est le bleu lui-même qui se pose en voile, et l'encre monte en
       primary-300 pour rester lisible. */
    --pcr-accent-soft: rgba(37, 99, 235, .18);
    --pcr-accent-soft-border: #2f4a76;
    --pcr-accent-soft-ink: #93c5fd;
    --pcr-surface: #111827;
    --pcr-surface-2: #1b2534;
    --pcr-border: #2a3648;
    --pcr-success: #4ade80;
    --pcr-danger: #fb7185;
}

/* PC Rénovation — surcharges sidebar.
   sivoni rend chaque entrée avec `.nav-item { @apply flex items-start ... }`,
   ce qui colle le label en haut de la ligne (pas centré avec l'icône-box de
   40 px de haut). On bascule sur `items-center` pour aligner verticalement
   le texte avec le centre de l'icône. */

#nav-sidebar .nav-item {
    align-items: center !important;
}

/* Le wrapper du label : on garde un flex column (titre + éventuelle
   description), mais on s'assure qu'il soit centré verticalement à
   l'intérieur de la ligne. */
#nav-sidebar .nav-item-content {
    display: flex;
    flex-direction: column;
    justify-content: center;
}

/* Sivoni dessine sa propre flèche via background-image sur tous les
   `select:not([class*="bg-none"]):not([multiple])` (cf. sivoni/styles.css).
   Mais sivoni n'applique PAS `appearance: none`, donc la flèche native du
   navigateur subsiste à côté → double flèche.
   On la supprime ici globalement pour tous les selects de l'admin. */
select:not([multiple]) {
    appearance: none !important;
    -webkit-appearance: none !important;
    -moz-appearance: none !important;
}

/* `list_displayed_filters` est rendu par le tag `admin_list_filter` (widget
   sidebar avec `flex-col` + `mb-6`). On veut le label AU-DESSUS du champ
   (donc on garde `flex-col`), mais aligné sur la même ligne que la searchbar.
   - On annule la marge basse pour ne pas pousser sous la barre
   - On aligne les selects par le BAS (flex-end) pour que la rangée
     "select / input search" se trouve sur la même baseline
   - Hauteur du select cohérente avec la searchbar (2.25rem ≈ h-9)
   Sélecteur structurel : la form des filtres est le frère adjacent
   de #searchbar-form dans le header sivoni. */
#searchbar-form ~ form {
    align-items: flex-end;
    flex-wrap: wrap;
}
/* La searchbar (h-9, sans label) doit s'aligner sur la baseline des selects
   de filtres (qui ont 1 ligne de label au-dessus). Sans cette règle, la
   searchbar reste collée en haut du flex container et apparaît "flottante".
   `align-self: flex-end` la pousse au bas de la ligne flex parente. */
#searchbar-form {
    align-self: flex-end;
}
#searchbar-form ~ form .flex.flex-col {
    margin-bottom: 0 !important;
    gap: 0.25rem;
}
#searchbar-form ~ form label {
    margin-bottom: 0 !important;
    white-space: nowrap;
    font-weight: 600;
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: #64748b;
}
#searchbar-form ~ form select {
    height: 2.25rem;
    padding-top: 0;
    padding-bottom: 0;
}
#searchbar-form ~ form button[type="submit"] {
    height: 2.25rem;
    align-self: flex-end;
}


/* ============================================================
   RESPONSIVE GLOBAL — admin sivoni
   ============================================================
   Sivoni rend les changelists en "block stacked rows" sous lg
   (1024 px), mais sans label par cellule → l'utilisateur voit
   une suite de valeurs sans repère. On force le retour au mode
   tableau classique avec scroll horizontal, ce qui reste
   exploitable en doigt sur mobile. */

@media (max-width: 1023px) {
    /* Changelists : forcer table + scroll horizontal.
       sivoni applique les classes Tailwind `block` (sans préfixe) sous lg
       sur table/thead/tbody/tr. On override avec sélecteurs spécifiques
       ID + classe pour battre n'importe quelle règle Tailwind. */
    table#result_list,
    table#result_list.block {
        display: table !important;
        table-layout: auto !important;
        min-width: 100%;
        width: max-content;
    }
    table#result_list thead,
    table#result_list thead.block {
        display: table-header-group !important;
    }
    table#result_list tbody,
    table#result_list tbody.block {
        display: table-row-group !important;
    }
    table#result_list tr,
    table#result_list tr.block {
        display: table-row !important;
        margin: 0 !important;
        border: 0 !important;
        box-shadow: none !important;
        border-radius: 0 !important;
        background: transparent !important;
    }
    table#result_list th,
    table#result_list td,
    table#result_list th.block,
    table#result_list td.block,
    /* Sivoni met aussi `hidden` sur les <th> hors 1er sous lg */
    table#result_list thead th.hidden {
        display: table-cell !important;
        white-space: nowrap;
        padding: .6rem .75rem !important;
        vertical-align: middle !important;
    }
    /* Sivoni rend chaque <td> avec un pseudo-element `::before` qui affiche
       `content: attr(data-label)` en mobile (pour son mode "carte sans
       header"). En forçant le mode table, on cache ces labels redondants. */
    table#result_list td::before,
    table#result_list th::before {
        content: none !important;
        display: none !important;
    }
    /* Le wrapper data-simplebar autour de la table doit autoriser le
       scroll horizontal — sivoni le pose déjà, mais on s'assure. */
    .changelist-form-container [data-simplebar],
    #changelist [data-simplebar] {
        overflow-x: auto !important;
        max-width: 100vw;
    }

    /* Bandeau filtres en ligne : sur mobile, on cache les
       `list_displayed_filters` (selects status/type/seller au-dessus
       du tableau) — ils prennent trop de place. L'utilisateur passe
       par le bouton "Filtres" (sheet sivoni) qui rend les
       `list_filter` classiques. La searchbar reste accessible. */
    #searchbar-form ~ form {
        display: none !important;
    }
    .flex.flex-col.sm\:flex-row.h-auto.mb-4 {
        flex-direction: row !important;
        align-items: center !important;
        gap: .5rem !important;
    }

    /* Change_form : sivoni utilise des `.aligned` à 2 colonnes,
       on les force en empilement vertical. */
    .field-row {
        flex-direction: column !important;
        align-items: stretch !important;
    }
    .field-line {
        max-width: 100% !important;
        flex: 1 1 100% !important;
    }
    /* Fieldsets affichés en grid 2-col deviennent 1-col */
    fieldset.module > .grid,
    .module > .grid {
        grid-template-columns: 1fr !important;
    }

    /* Inputs : confort tactile (44 px target Apple/Material) */
    input[type="text"],
    input[type="email"],
    input[type="number"],
    input[type="search"],
    input[type="tel"],
    input[type="url"],
    input[type="password"],
    input[type="date"],
    input[type="datetime-local"],
    select:not([multiple]),
    .button, button, .submit-row input[type="submit"] {
        min-height: 44px;
        font-size: 16px;
        /* font-size: 16px évite le zoom auto-iOS sur focus */
    }

    /* Sidebar : sivoni ouvre/ferme en overlay sur mobile, on
       s'assure que la zone de contenu prend toute la largeur. */
    #main-content,
    main {
        padding-left: .75rem !important;
        padding-right: .75rem !important;
    }

    /* Object-tools (Ajouter) reste tactile */
    .object-tools a, .object-tools button {
        min-height: 44px;
        display: inline-flex;
        align-items: center;
    }

    /* Pagination tactile */
    .paginator a, .paginator span {
        min-width: 40px;
        min-height: 40px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
}

/* La vue "Mes RDV (mobile)" n'a de sens que sur petit écran : on cache
   son entrée sidebar dès qu'on est sur tablette/desktop (≥ 1024 px).
   L'URL reste accessible directement (tests admin) mais ne pollue plus
   la sidebar des utilisateurs back-office. */
@media (min-width: 1024px) {
    a.nav-item[href*="/appointment/appointment/mobile/"] {
        display: none !important;
    }
}

/* ------------------------------------------------------------
   Flaticon UICONS — icônes utilisées comme avatars dans les
   changelists (Client/Customer/Lead/User/Signataire).
   On NE TOUCHE PAS au `display` du <i> (qui doit rester
   `inline-block` pour que le glyph `:before` se rende via la
   webfont) — on anime juste avec `transform` au survol.
   ------------------------------------------------------------ */
.fi-animated {
    display: inline-block;
    transform-origin: center;
    transition: transform .25s ease-out, color .25s ease-out;
    will-change: transform;
}
/* Animation déclenchée au hover sur la ligne <tr> entière → toute
   la cellule devient une zone de trigger, plus naturel qu'un hover
   strict sur l'icône. */
#result_list tr:hover .fi-animated,
.fi-animated:hover {
    animation: fi-pulse .6s ease-in-out;
    color: #1d4ed8;
}
@keyframes fi-pulse {
    0%   { transform: scale(1)     rotate(0deg); }
    25%  { transform: scale(1.18)  rotate(-6deg); }
    50%  { transform: scale(1.08)  rotate(4deg); }
    75%  { transform: scale(1.14)  rotate(-2deg); }
    100% { transform: scale(1)     rotate(0deg); }
}

/* Très petit mobile (< 480 px) — table déjà horizontal, on
   réduit les paddings pour gagner de la place. */
@media (max-width: 479px) {
    #result_list th,
    #result_list td {
        padding: .45rem .55rem !important;
        font-size: .85rem;
    }
}

/* ------------------------------------------------------------------ */
/* Inlines · lien « Ajouter un objet … supplémentaire »                 */
/* ------------------------------------------------------------------ */
/* L'admin Django pose son icône « + » en image de fond, en haut à
   gauche (`background-position: 0 1px`), tandis que sivoni fait du lien
   un bloc au texte centré. L'icône se retrouvait donc décrochée du
   libellé, et plus haute que lui. On aligne les deux sur une seule
   ligne. Vaut pour tous les inlines du CRM. */
/* `#content` pour passer devant : sidebar.css est chargé avant la
   feuille de sivoni et celle de l'admin Django, qui posent les mêmes
   sélecteurs — à spécificité égale, c'est la dernière chargée qui gagne. */
#content .add-row a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background-position: left center;
  padding-left: 1.5rem;
  text-align: left;
  line-height: 1.25rem;
}

/* ------------------------------------------------------------------ */
/* Barre d'actions du bas · groupe calé à droite                        */
/* ------------------------------------------------------------------ */
/* Le gabarit `admin/submit_line.html` (surchargé côté projet) met les
   boutons dans l'ordre du plus important au moins important. Le calage à
   droite se fait ici : la feuille Tailwind de sivoni est pré-compilée et
   ne contient pas `lg:justify-end`. */
@media (min-width: 1024px) {
  #submit-row .container {
    justify-content: flex-end;
  }
}

/* ------------------------------------------------------------------ */
/* Blocs d'aide enrichis (help_text)                                    */
/* ------------------------------------------------------------------ */
/* Pour les champs qui méritent plus qu'une phrase : un exemple de
   format, un rappel de règle, des liens utiles. Le `help_text` du champ
   porte ce balisage (cf. CustomerAdminForm). */
.hint-box {
  margin-top: .5rem;
  padding: .75rem .9rem;
  border: 1px solid #e2e8f0;
  border-radius: .5rem;
  background: #f8fafc;
  font-size: .78rem;
  line-height: 1.5;
  color: #475569;
}
.dark .hint-box { background: #1f2937; border-color: #374151; color: #cbd5e1; }
.hint-box__title {
  display: flex; align-items: center; gap: .4rem;
  font-weight: 700; color: #0f172a; margin-bottom: .35rem;
}
.dark .hint-box__title { color: #f1f5f9; }
.hint-box__sample {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  background: #eef2f7; color: #0f172a;
  padding: .15rem .45rem; border-radius: .3rem; margin-left: .35rem;
}
.dark .hint-box__sample { background: #33415555; color: #e2e8f0; }
.hint-links { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .5rem; }
.hint-link {
  display: inline-flex; align-items: center; gap: .3rem;
  padding: .3rem .6rem; border-radius: 9999px;
  font-size: .74rem; font-weight: 600; text-decoration: none;
  background: #eff6ff; color: #1d4ed8; border: 1px solid #bfdbfe;
}
.hint-link:hover { background: #dbeafe; }
.dark .hint-link { background: #1e3a8a33; color: #93c5fd; border-color: #1e40af; }

/* ------------------------------------------------------------------ */
/* Bandeau « prochaine action » des pages de dossier                    */
/* Écrit ici plutôt qu'en classes Tailwind : le build de sivoni est     */
/* pré-compilé, les utilitaires absents de son CSS sont ignorés.        */
/* ------------------------------------------------------------------ */
.next-action {
    display: flex;
    gap: .75rem;
    align-items: flex-start;
    margin-bottom: 1.25rem;
    padding: .875rem 1rem;
    border: 1px solid transparent;
    border-left-width: 3px;
    border-radius: .375rem;
}

.next-action__icon {
    flex: none;
    font-size: 1.25rem;
    line-height: 1.4;
}

.next-action__body { min-width: 0; }

.next-action__title {
    margin: 0;
    font-weight: 600;
    font-size: .875rem;
}

.next-action__text {
    margin: .125rem 0 0;
    font-size: .8125rem;
    opacity: .85;
}

.next-action__list {
    margin: .5rem 0 0;
    padding-left: 1.1rem;
    list-style: disc;
    font-size: .8125rem;
}

.next-action__list li { margin-top: .125rem; }

/* Renvoi vers l'écran qui permet réellement l'action annoncée. */
.next-action__link {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    margin-top: .5rem;
    font-size: .8125rem;
    font-weight: 600;
    text-decoration: none;
    color: inherit;
    border-bottom: 1px solid currentColor;
}

.next-action__link .material-symbols-outlined { font-size: 1rem; }

.next-action--warning {
    background: #fffbeb;
    border-color: #fde68a;
    border-left-color: #d97706;
    color: #92400e;
}

.next-action--info {
    background: #eff6ff;
    border-color: #bfdbfe;
    border-left-color: #2563eb;
    color: #1e40af;
}

.next-action--success {
    background: #ecfdf5;
    border-color: #a7f3d0;
    border-left-color: #059669;
    color: #065f46;
}

/* Mode sombre : fonds désaturés, texte porté par la teinte claire. */
.dark .next-action--warning {
    background: rgba(217, 119, 6, .12);
    border-color: rgba(217, 119, 6, .3);
    border-left-color: #f59e0b;
    color: #fcd34d;
}

.dark .next-action--info {
    background: rgba(37, 99, 235, .12);
    border-color: rgba(37, 99, 235, .3);
    border-left-color: #3b82f6;
    color: #93c5fd;
}

.dark .next-action--success {
    background: rgba(5, 150, 105, .12);
    border-color: rgba(5, 150, 105, .3);
    border-left-color: #10b981;
    color: #6ee7b7;
}

/* ------------------------------------------------------------------ */
/* Cartes du pipeline (vue Kanban des dossiers)                         */
/* ------------------------------------------------------------------ */
.pipeline-card {
    padding: .6rem .7rem;
    background: var(--pcr-surface);
    border: 1px solid var(--pcr-border);
    border-radius: .5rem;
    line-height: 1.35;
    transition: box-shadow .12s ease, transform .12s ease;
}

.pipeline-card:hover {
    box-shadow: 0 2px 8px rgba(15, 23, 42, .1);
    transform: translateY(-1px);
}

.pipeline-card__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: .4rem;
}

.pipeline-card__number {
    font-size: .72rem;
    font-weight: 700;
    letter-spacing: .02em;
    color: #475569;
}

.pipeline-card__amount {
    flex: none;
    font-size: .72rem;
    font-weight: 700;
    color: #15803d;
    white-space: nowrap;
}

.pipeline-card__client {
    margin-top: .2rem;
    font-size: .82rem;
    font-weight: 600;
    color: #0f172a;
}

.pipeline-card__muted { color: #94a3b8; font-weight: 400; }

.pipeline-card__city {
    font-size: .74rem;
    color: #64748b;
}

.pipeline-card__foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .4rem;
    margin-top: .45rem;
    font-size: .68rem;
    color: #64748b;
}

.pipeline-card__type {
    padding: .1rem .35rem;
    background: #f1f5f9;
    border-radius: .25rem;
    font-weight: 600;
    white-space: nowrap;
}

.pipeline-card__seller {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.dark .pipeline-card__client { color: #e2e8f0; }
.dark .pipeline-card__number,
.dark .pipeline-card__city,
.dark .pipeline-card__foot { color: #94a3b8; }
.dark .pipeline-card__type { background: #1e293b; color: #cbd5e1; }

/* Colonne du pipeline tronquée · le reste est annoncé, pas rendu. */
.kanban-truncated {
    margin: .35rem .15rem 0;
    font-size: .7rem;
    font-style: italic;
    color: #64748b;
}

.dark .kanban-truncated { color: #94a3b8; }

/* Colonnes du pipeline.

   La colonne portait une bordure, une ombre et un aplat : trois traits
   pour une seule séparation, et la carte qu'elle contient en ajoutait
   deux de plus. Il n'en reste qu'un filet, sur la carte — l'objet que
   l'on saisit. La colonne, elle, se lit à son aplat et à la gouttière
   qui la sépare de la suivante. */
.kanbanScrollContainer {
    padding-bottom: .75rem;
    scrollbar-width: thin;
}

.kanban-col {
    display: flex;
    flex: none;
    flex-direction: column;
    width: 16rem;
    background: var(--pcr-surface-2);
    border-radius: .65rem;
}

.kanban-col__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    padding: .65rem .75rem .5rem;
}

.kanban-col__title {
    min-width: 0;
    overflow: hidden;
    font-size: .78rem;
    font-weight: 600;
    letter-spacing: .01em;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--pcr-text);
}

/* Le décompte en pastille plutôt qu'en phrase : douze colonnes de
   « 3 cartes correspondent aux filtres appliqués » noyaient les titres. */
.kanban-col__count {
    flex: none;
    min-width: 1.4rem;
    padding: .05rem .4rem;
    background: var(--pcr-surface);
    border-radius: 999px;
    font-size: .7rem;
    font-weight: 600;
    text-align: center;
    color: var(--pcr-muted);
}

.kanban-col__body {
    display: flex;
    flex-direction: column;
    gap: .4rem;
    padding: 0 .5rem .5rem;
    min-height: 2.5rem;
    max-height: calc(100vh - 355px);
    overflow-y: auto;
    scrollbar-width: thin;
    border-radius: 0 0 .65rem .65rem;
    transition: background-color .15s ease;
}

/* Carte par défaut, quand l'écran n'en fournit pas de sur-mesure. */
.kanban-card {
    padding: .55rem .7rem;
    background: var(--pcr-surface);
    border: 1px solid var(--pcr-border);
    border-radius: .5rem;
    font-size: .82rem;
    font-weight: 500;
    line-height: 1.35;
    overflow-wrap: anywhere;
    color: var(--pcr-text);
    transition: box-shadow .12s ease, transform .12s ease;
}

.kanban-card:hover {
    box-shadow: 0 2px 8px rgba(15, 23, 42, .1);
    transform: translateY(-1px);
}

/* ------------------------------------------------------------------ */
/* Bascule des vues d'une liste (Tableau · Kanban · …)                  */
/* Écrit ici : le build Tailwind de sivoni est figé.                    */
/* ------------------------------------------------------------------ */
.view-switch {
    display: inline-flex;
    align-items: stretch;
    height: 2.5rem;
    overflow: hidden;
    background: #fff;
    border: 1px solid #e2e8f0;
    border-radius: .375rem;
    box-shadow: 0 1px 2px rgba(15, 23, 42, .05);
}

.view-switch__item {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: 0 .85rem;
    border: 0;
    background: transparent;
    color: #475569;
    font-size: .8125rem;
    font-weight: 500;
    white-space: nowrap;
    cursor: pointer;
}

.view-switch__item + .view-switch__item { border-left: 1px solid #e2e8f0; }

.view-switch__item:hover { background: #f1f5f9; }

.view-switch__item .material-symbols-outlined { font-size: 1.05rem; }

.view-switch__item--on,
.view-switch__item--on:hover {
    /* Sivoni publie ses couleurs en triplets RGB pour Tailwind
       (`147 51 234`) : les passer telles quelles à `background` donne
       une valeur invalide, et le bouton actif restait sans fond. */
    background: rgb(var(--color-primary-600, 37 99 235));
    color: #fff;
}

.dark .view-switch {
    background: #0f172a;
    border-color: #1e293b;
}

.dark .view-switch__item { color: #cbd5e1; }
.dark .view-switch__item + .view-switch__item { border-left-color: #1e293b; }
.dark .view-switch__item:hover { background: #1e293b; }

/* Barre d'outils d'une liste · les filtres affichés en ligne portent
   leur libellé au-dessus du select, la recherche et les boutons n'en ont
   pas : centrés, ils se décalaient d'une demi-hauteur de libellé. Tout
   s'aligne donc sur la ligne du bas. */
@media (min-width: 640px) {
    .list-toolbar { align-items: flex-end; }
    .list-toolbar > form.flex { align-items: flex-end; }
    .list-toolbar__views { align-items: flex-end; }
    .list-toolbar__tools { align-items: flex-end; }
}

/* Actions propres à un écran, dans la barre de liste.

   L'action se range à droite, juste avant le filtre. C'est elle qui prend
   l'espace libre laissé par la recherche ; le filtre porte lui aussi un
   `lg:ml-auto`, et deux marges automatiques se partageant cet espace
   laissaient l'action flotter au milieu de la ligne — d'où la remise à
   zéro de celle du filtre, juste en dessous. La hauteur est celle du
   filtre — 2,5 rem — pour que la rangée reste une rangée. */
.list-toolbar__tools {
    display: flex;
    flex: none;
    align-items: center;
    gap: .4rem;
    margin-left: auto;
    margin-right: .5rem;
}

/* La barre d'outils a pris l'espace : ce qui la suit se colle à elle. */
.list-toolbar__tools ~ [class*="ml-auto"] { margin-left: 0; }

.list-tool {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    height: 2.5rem;
    padding: 0 .9rem;
    background: var(--pcr-surface);
    border: 1px solid var(--pcr-border);
    border-radius: .375rem;
    box-shadow: 0 1px 2px rgba(15, 23, 42, .05);
    font-size: .875rem;
    font-weight: 500;
    line-height: 1;
    white-space: nowrap;
    text-decoration: none;
    color: var(--pcr-text);
}

.list-tool .material-symbols-outlined { font-size: 1.15rem; }

.list-tool--primary {
    background: rgb(var(--color-primary-600, 37 99 235));
    border-color: rgb(var(--color-primary-600, 37 99 235));
    color: #fff;
}

.list-tool--primary:hover { filter: brightness(.92); }

/* Action secondaire · même famille que l'action principale, un ton en
   dessous. Le gris neutre les faisait lire comme étrangères à la barre,
   alors qu'elles ouvrent trois vues du même écran. */
.list-tool--soft {
    background: var(--pcr-accent-soft);
    border-color: var(--pcr-accent-soft-border);
    color: var(--pcr-accent-soft-ink);
}

.list-tool--soft:hover { filter: brightness(.96); }

html.dark .list-tool--soft:hover { filter: brightness(1.25); }

/* Sous 1280 px la rangée est pleine : l'infobulle prend le relais du
   libellé, comme sur la barre des rendez-vous. */
@media (max-width: 1280px) {
    .list-tool { padding: 0; width: 2.5rem; justify-content: center; }
    .list-tool__label { display: none; }
}

/* En-tête d'une liste · il ne reste que le bouton d'ajout, les actions
   propres à l'écran ayant rejoint la barre d'outils au-dessus du tableau.
   Les rendez-vous le peignaient en vert (#16a34a) dans une feuille en
   ligne qui leur était propre : c'est le bleu du thème, comme partout
   ailleurs, et une seule règle pour les deux écrans. */
.lead-tools-bar,
.appt-tools-bar {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: .4rem;
}

.lead-tools-bar .btn-add,
.appt-tools-bar .btn-add {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    margin: 0 !important;
    padding: 0;
    background: rgb(var(--color-primary-600, 37 99 235));
    border: 1px solid rgb(var(--color-primary-600, 37 99 235));
    border-radius: 9999px;
    color: #fff;
    text-decoration: none;
}

.lead-tools-bar .btn-add:hover,
.appt-tools-bar .btn-add:hover { filter: brightness(.92); }

/* Rangée des filtres affichés en ligne · elle a sa propre ligne, sous la
   recherche : partagée avec elle, elle débordait dès trois filtres et le
   bouton « Appliquer » se retrouvait seul à la ligne, séparé des selects
   qu'il valide. Le bouton reste collé au dernier filtre. */
.list-filters {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: .75rem 1rem;
    margin-bottom: 1rem;
}

.list-filters__item { min-width: 0; }

/* Sivoni enveloppe chaque champ dans un bloc à marge basse (`mb-6`,
   `mb-4` sur le dernier) prévu pour un formulaire en colonne. Sur une
   rangée, cette marge décale les selects vers le haut et « Appliquer »
   paraît descendre : ici l'espacement vient du `gap`. Sélecteur à deux
   classes pour passer devant `last:mb-4`. */
.list-filters > .list-filters__item > div { margin-bottom: 0; }

.list-filters__apply {
    height: 2.5rem;
    flex: none;
    font-size: .8125rem;
    font-weight: 600;
}

/* ------------------------------------------------------------------ */
/* Estimation du cumac et de la prime (bloc « Estimation »)            */
/* ------------------------------------------------------------------ */
.cee-sim {
    padding: .65rem .8rem;
    background: #f8fafc;
    border: 1px solid #e2e8f0;
    border-radius: .4rem;
}

.cee-sim__figures {
    display: flex;
    align-items: baseline;
    gap: .6rem;
    margin: 0;
}

.cee-sim__kwhc {
    font-size: .95rem;
    font-weight: 700;
    color: #1d4ed8;
}

.cee-sim__prime {
    font-size: .95rem;
    font-weight: 700;
    color: #15803d;
}

.cee-sim__apply {
    margin-left: auto;
    padding: .25rem .6rem;
    background: #fff;
    border: 1px solid #cbd5e1;
    border-radius: .3rem;
    color: #334155;
    font-size: .72rem;
    font-weight: 600;
    cursor: pointer;
}

.cee-sim__apply:hover { background: #f1f5f9; }

.cee-sim__steps {
    margin: .4rem 0 0;
    padding: 0;
    list-style: none;
    font-size: .72rem;
    color: #64748b;
}

.cee-sim__steps li { display: flex; gap: .35rem; }
.cee-sim__steps span { min-width: 8.5rem; color: #94a3b8; }

.cee-sim__none {
    margin: 0;
    font-size: .78rem;
    color: #64748b;
}

.cee-sim__warn {
    margin: .45rem 0 0;
    font-size: .68rem;
    font-style: italic;
    color: #b45309;
}

.dark .cee-sim {
    background: #0b1220;
    border-color: #1e293b;
}

.dark .cee-sim__kwhc { color: #93c5fd; }
.dark .cee-sim__prime { color: #6ee7b7; }
.dark .cee-sim__steps { color: #94a3b8; }
.dark .cee-sim__steps span { color: #64748b; }
.dark .cee-sim__none { color: #94a3b8; }
.dark .cee-sim__warn { color: #fbbf24; }

.dark .cee-sim__apply {
    background: #111827;
    border-color: #334155;
    color: #cbd5e1;
}

.dark .cee-sim__apply:hover { background: #1e293b; }

/* En tête de dossier, l'estimation est une bande compacte : c'est le
   chiffre qu'on montre au client, il doit rester visible quel que soit
   l'onglet où l'on saisit. */
.cee-sim { margin-bottom: .75rem; }

.cee-sim .cee-sim__body {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .25rem 1.5rem;
}

.cee-sim .cee-sim__figures { flex: none; }

.cee-sim .cee-sim__steps {
    display: flex;
    flex-wrap: wrap;
    gap: .15rem 1rem;
    margin-top: 0;
}

.cee-sim .cee-sim__steps span { min-width: 0; }

/* ------------------------------------------------------------------ */
/* Parcours documentaire du dossier                                     */
/* ------------------------------------------------------------------ */
.doc-flow {
    margin: 0;
    padding: 0;
    list-style: none;
}

.doc-flow__block {
    position: relative;
    padding: 0 0 1rem 2rem;
    border-left: 2px solid #e2e8f0;
}

.doc-flow__block:last-child { border-left-color: transparent; padding-bottom: 0; }

.doc-flow__block--done { border-left-color: #86efac; }
.doc-flow__block--current { border-left-color: #93c5fd; }

.doc-flow__head { display: flex; gap: .6rem; align-items: flex-start; }

.doc-flow__marker {
    position: absolute;
    left: -.72rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.4rem;
    height: 1.4rem;
    background: #fff;
    border: 2px solid #e2e8f0;
    border-radius: 9999px;
    font-size: .7rem;
    font-weight: 700;
    color: #64748b;
}

.doc-flow__block--done .doc-flow__marker {
    background: #16a34a;
    border-color: #16a34a;
    color: #fff;
}

.doc-flow__block--current .doc-flow__marker {
    border-color: #2563eb;
    color: #2563eb;
}

.doc-flow__title {
    margin: 0;
    font-size: .85rem;
    font-weight: 600;
    color: #0f172a;
}

.doc-flow__desc {
    margin: .1rem 0 0;
    font-size: .74rem;
    color: #64748b;
}

.doc-flow__block--locked .doc-flow__title,
.doc-flow__block--locked .doc-flow__desc { opacity: .55; }

.doc-flow__docs {
    margin: .5rem 0 0;
    padding: 0;
    list-style: none;
}

.doc-flow__doc {
    display: flex;
    align-items: center;
    gap: .45rem;
    padding: .18rem 0;
    font-size: .78rem;
    color: #475569;
}

.doc-flow__dot {
    flex: none;
    width: .45rem;
    height: .45rem;
    background: #cbd5e1;
    border-radius: 9999px;
}

.doc-flow__doc--ok .doc-flow__dot { background: #16a34a; }

.doc-flow__label { font-weight: 500; }

.doc-flow__status {
    padding: .05rem .35rem;
    background: #f1f5f9;
    border-radius: .25rem;
    font-size: .68rem;
    color: #475569;
}

.doc-flow__await { font-size: .7rem; font-style: italic; color: #94a3b8; }

.doc-flow__tag {
    padding: .05rem .35rem;
    background: #eff6ff;
    border-radius: .25rem;
    font-size: .64rem;
    font-weight: 600;
    color: #1d4ed8;
}

.doc-flow__link { font-size: .7rem; color: #2563eb; }

.dark .doc-flow__block { border-left-color: #1e293b; }
.dark .doc-flow__block--done { border-left-color: #166534; }
.dark .doc-flow__block--current { border-left-color: #1d4ed8; }
.dark .doc-flow__marker { background: #0f172a; border-color: #1e293b; color: #94a3b8; }
.dark .doc-flow__title { color: #e2e8f0; }
.dark .doc-flow__desc { color: #94a3b8; }
.dark .doc-flow__doc { color: #cbd5e1; }
.dark .doc-flow__status { background: #1e293b; color: #cbd5e1; }
.dark .doc-flow__tag { background: rgba(37,99,235,.18); color: #93c5fd; }

/* Le bouton d'une étape du parcours · aligné à droite de sa pièce. */
.doc-flow__action {
    margin-left: auto;
    padding: .15rem .55rem;
    background: #2563eb;
    border-radius: .28rem;
    color: #fff;
    font-size: .7rem;
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
}

.doc-flow__action:hover { background: #1d4ed8; color: #fff; }

/* ------------------------------------------------------------------ */
/* Carte du RDV · rendue dans son onglet                               */
/* `overflow:hidden` sur le cadre : Leaflet pose ses tuiles en absolu   */
/* et elles débordaient du bloc pendant le recalcul de taille.          */
/* ------------------------------------------------------------------ */
.appt-map-card {
    max-width: 100%;
    padding: 1.1rem 1.25rem;
    background: var(--pcr-surface);
    border: 1px solid var(--pcr-border);
    border-radius: .6rem;
    overflow: hidden;
}

.appt-map-card__title {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin: 0 0 .25rem;
    font-size: .95rem;
    font-weight: 700;
    color: var(--pcr-text);
}

.appt-map-card__title .material-symbols-outlined {
    font-size: 1.2rem;
    color: var(--pcr-accent);
}

.appt-map-card__hint {
    margin: 0 0 .7rem;
    font-size: .76rem;
    color: var(--pcr-muted);
}

#appt-map {
    width: 100%;
    max-width: 100%;
    height: 340px;
    border: 1px solid var(--pcr-border);
    border-radius: .4rem;
    overflow: hidden;
    background: var(--pcr-surface-2);
}

/* Les tuiles OpenStreetMap sont claires : on adoucit leur contraste en
   thème sombre plutôt que de les laisser éblouir la page. */
html.dark #appt-map .leaflet-tile-pane { filter: brightness(.85) saturate(.9); }

/* Département du RDV, à côté du titre de la carte. */
.appt-map-card__dept {
    margin-left: auto;
    padding: .1rem .45rem;
    background: var(--pcr-surface-2);
    border-radius: .25rem;
    font-size: .7rem;
    font-weight: 600;
    color: var(--pcr-muted);
}

.appt-map-card__dept:empty { display: none; }

/* Aide sous le sélecteur d'opérateur · tient sur une ligne : repliée,
   elle décalait le champ suivant à chaque changement de message. */
.appt-avail-hint {
    font-style: italic;
    color: var(--pcr-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ------------------------------------------------------------------ */
/* Barres d'onglets des pages de détail                                */
/*                                                                      */
/* Les onglets n'étaient que du texte souligné : rien ne disait où la    */
/* barre commençait, ni que les deux rangées étaient deux niveaux. On    */
/* encadre la barre principale et on donne à l'onglet actif l'aspect     */
/* d'une carte posée dessus ; le second niveau reste discret pour qu'on  */
/* ne confonde pas les deux.                                            */
/* L'état actif suit la classe `is-active`, posée par le gabarit puis    */
/* par le gestionnaire d'onglets de sivoni.                              */
/* ------------------------------------------------------------------ */
.tabbar {
    display: flex;
    flex-wrap: wrap;
    gap: .2rem;
    margin: 0 0 1.1rem;
    padding: .25rem;
    list-style: none;
    background: var(--pcr-surface-2);
    border: 1px solid var(--pcr-border);
    border-radius: .5rem;
}

.tabbar__cell { display: flex; }

.tabbar__item {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .45rem .85rem;
    border: 1px solid transparent;
    border-radius: .375rem;
    color: var(--pcr-muted);
    font-size: .82rem;
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
    transition: background .12s ease, color .12s ease;
}

.tabbar__item:hover { color: var(--pcr-text); background: var(--pcr-surface); }

.tabbar__item.is-active,
.tabbar__item.is-active:hover {
    background: var(--pcr-surface);
    border-color: var(--pcr-border);
    color: rgb(var(--color-primary-600, 37 99 235));
    box-shadow: 0 1px 2px rgba(15, 23, 42, .06);
}

/* Second niveau · les sections d'un onglet. Plus petit, sans cadre :
   deux barres encadrées côte à côte se disputeraient l'attention. */
.tabbar--sub {
    gap: .15rem;
    margin-bottom: .9rem;
    padding: 0 0 .35rem;
    background: transparent;
    border: 0;
    border-bottom: 1px solid var(--pcr-border);
    border-radius: 0;
}

.tabbar--sub .tabbar__item {
    padding: .3rem .6rem;
    font-size: .76rem;
    font-weight: 500;
}

.tabbar--sub .tabbar__item.is-active,
.tabbar--sub .tabbar__item.is-active:hover {
    background: var(--pcr-surface-2);
    border-color: transparent;
    box-shadow: none;
}

/* Sivoni retire ses utilitaires de soulignement au clic ; on neutralise
   ce qu'il pourrait laisser, pour que le seul repère reste `is-active`. */
.tabbar__item.border-b { border-bottom-width: 1px; }

/* ------------------------------------------------------------------ */
/* Aperçu du fichier à l'étape 2 de l'import de leads                  */
/* ------------------------------------------------------------------ */
.import-preview {
    margin-bottom: 1.25rem;
    border: 1px solid var(--pcr-border);
    border-radius: .5rem;
    overflow: hidden;
}

.import-preview__title {
    margin: 0;
    padding: .5rem .75rem;
    background: var(--pcr-surface-2);
    border-bottom: 1px solid var(--pcr-border);
    font-size: .76rem;
    font-weight: 600;
    color: var(--pcr-muted);
}

/* Un fichier large défile dans son cadre, pas la page entière. */
.import-preview__scroll {
    max-height: 18rem;
    overflow: auto;
}

.import-preview__table {
    width: 100%;
    border-collapse: collapse;
    font-size: .74rem;
}

.import-preview__table th,
.import-preview__table td {
    max-width: 14rem;
    padding: .3rem .55rem;
    border-bottom: 1px solid var(--pcr-border);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    text-align: left;
}

.import-preview__table th {
    position: sticky;
    top: 0;
    background: var(--pcr-surface);
    font-weight: 600;
    color: var(--pcr-text);
}

.import-preview__table td { color: var(--pcr-muted); }

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

/* ------------------------------------------------------------------ */
/* Import de leads · étapes 1 et 2                                     */
/*                                                                      */
/* La page emprunte les composants de l'admin — barre d'étapes calquée  */
/* sur la barre d'onglets, panneaux au même rayon que les fieldsets,     */
/* barre d'actions identique à `submit_line`. Ne restent ici que les     */
/* éléments propres à l'import.                                         */
/* ------------------------------------------------------------------ */
/* La colonne est centrée dans la zone de contenu · calée à gauche, elle
   laissait un vide à droite d'autant plus visible que le contenu de
   l'étape 1 est étroit. */
.import-page {
    max-width: 1200px;
    margin-left: auto;
    margin-right: auto;
}

/* L'étape 1 ne porte qu'un champ : une colonne large l'aurait noyé. */
.import-page--narrow { max-width: 44rem; }

.import-steps {
    max-width: 18rem;
    margin-left: auto;
    margin-right: auto;
    /* Les deux étapes se partagent la barre à parts égales. */
    justify-content: center;
}

.import-steps .tabbar__cell { flex: 1; }

.import-steps .tabbar__item {
    flex: 1;
    justify-content: center;
}

.import-steps .tabbar__item { cursor: default; }

.import-steps__num {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.15rem;
    height: 1.15rem;
    margin-right: .1rem;
    background: var(--pcr-border);
    border-radius: 9999px;
    font-size: .66rem;
    font-weight: 700;
}

.import-steps .is-active .import-steps__num {
    background: rgb(var(--color-primary-600, 37 99 235));
    color: #fff;
}

.import-page__file {
    margin: 0 0 1rem;
    font-size: .8rem;
    color: var(--pcr-muted);
}

.import-card {
    margin-bottom: 1.25rem;
    padding: .9rem 1rem;
    background: var(--pcr-surface);
    border: 1px solid var(--pcr-border);
    /* Même rayon que les fieldsets de l'admin. */
    border-radius: .375rem;
}

.import-card__title {
    display: flex;
    align-items: center;
    gap: .4rem;
    margin: 0 0 .25rem;
    font-size: .875rem;
    font-weight: 600;
    color: var(--pcr-text);
}

.import-card__title .material-symbols-outlined {
    font-size: 1.05rem;
    color: var(--pcr-subtle);
}

.import-card__hint {
    margin: 0 0 .75rem;
    font-size: .76rem;
    color: var(--pcr-muted);
}

/* La fiche du lot ouvre la page : c'est la seule décision obligatoire. */
.import-card--lot { border-left: 3px solid rgb(var(--color-primary-600, 37 99 235)); }

.import-card__field { max-width: 26rem; }

/* Sur l'étape 1, le champ et son cadre sont centrés comme le reste. */
.import-page--narrow .import-card__field,
.import-page--narrow .import-card__title,
.import-page--narrow .import-card__hint { text-align: center; }

.import-page--narrow .import-card__title { justify-content: center; }

.import-card__field label {
    display: block;
    margin-bottom: .3rem;
    font-size: .78rem;
    font-weight: 600;
    color: var(--pcr-text);
}

.import-card__field input[type="file"] {
    width: 100%;
    font-size: .8rem;
    color: var(--pcr-muted);
}

.import-card__field .import-card__hint { margin: .3rem 0 0; }

/* Tant qu'un champ obligatoire manque, l'état passe en alerte : le
   message fixe d'avant ne disait pas où l'on en était. */
#import-required-state.is-incomplete {
    padding: .4rem .6rem;
    background: rgba(217, 119, 6, .12);
    border-left: 3px solid #d97706;
    border-radius: .25rem;
    color: #b45309;
    font-weight: 600;
}

html.dark #import-required-state.is-incomplete { color: #fcd34d; }

.import-mapping {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: .6rem;
}

.import-mapping__cell {
    min-width: 0;
    padding: .5rem .6rem;
    background: var(--pcr-surface-2);
    border: 1px solid var(--pcr-border);
    border-radius: .3rem;
    overflow: hidden;
}

.import-mapping__cell--error { border-color: var(--pcr-danger, #dc2626); }

.import-mapping__header {
    display: block;
    margin-bottom: .3rem;
    font-size: .7rem;
    font-weight: 600;
    letter-spacing: .02em;
    color: var(--pcr-muted);
    text-transform: uppercase;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.import-mapping select,
.import-card__field select {
    width: 100%;
    max-width: 100%;
    box-sizing: border-box;
    padding: .38rem 1.7rem .38rem .55rem;
    background: var(--pcr-surface);
    border: 1px solid var(--pcr-border);
    border-radius: .3rem;
    color: var(--pcr-text);
    font-size: .78rem;
    /* Sivoni peint sa propre flèche : la native ferait doublon. */
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    text-overflow: ellipsis;
    overflow: hidden;
    white-space: nowrap;
}

.import-mapping select:focus,
.import-card__field select:focus {
    outline: 2px solid rgb(var(--color-primary-600, 37 99 235));
    outline-offset: -1px;
}

.import-mapping__error {
    margin: .25rem 0 0;
    font-size: .68rem;
    color: var(--pcr-danger, #dc2626);
}

.import-mapping__empty {
    margin: 0;
    font-size: .8rem;
    color: var(--pcr-danger, #dc2626);
}

/* En-tête de colonne de l'aperçu : le champ retenu au-dessus, le nom du
   fichier en dessous · la correspondance se lit sans quitter le tableau. */
.import-preview__target {
    display: block;
    font-size: .7rem;
    font-weight: 600;
    color: rgb(var(--color-primary-600, 37 99 235));
}

.import-preview__source {
    display: block;
    font-size: .64rem;
    font-weight: 400;
    color: var(--pcr-subtle);
}

/* Colonne écartée · le libellé « ignorée » vient du serveur, la classe
   est posée au rendu puis suivie par le script. */
.import-preview__table td.is-ignored,
.import-preview__table th.is-ignored { opacity: .45; }

.import-preview__table th.is-ignored .import-preview__target {
    font-weight: 400;
    font-style: italic;
    color: var(--pcr-subtle);
}

/* ------------------------------------------------------------------ */
/* Zone de dépôt du fichier d'import                                   */
/* Le champ natif reste dans la page, invisible : c'est lui qui porte   */
/* la valeur, ce cadre ne fait que l'habiller.                          */
/* ------------------------------------------------------------------ */
.import-drop {
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .2rem;
    max-width: 30rem;
    padding: 1.6rem 1rem;
    background: var(--pcr-surface-2);
    border: 1px dashed var(--pcr-border);
    border-radius: .45rem;
    cursor: pointer;
    text-align: center;
    transition: border-color .12s ease, background .12s ease;
}

.import-drop input[type="file"] {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

.import-drop:hover,
.import-drop.is-hovered {
    background: var(--pcr-surface);
    border-color: rgb(var(--color-primary-600, 37 99 235));
}

.import-drop__icon {
    font-size: 1.7rem;
    color: var(--pcr-subtle);
}

.import-drop__label {
    font-size: .82rem;
    font-weight: 600;
    color: var(--pcr-text);
}

.import-drop__hint {
    font-size: .7rem;
    color: var(--pcr-subtle);
}

/* Fichier retenu · le cadre cesse d'appeler au dépôt. */
.import-drop.has-file {
    background: var(--pcr-surface);
    border-style: solid;
    border-color: rgb(var(--color-primary-600, 37 99 235));
}

.import-drop.has-file .import-drop__icon { color: rgb(var(--color-primary-600, 37 99 235)); }

/* Les deux choix du lot · fiche CEE et type de lead, côte à côte. */
.import-lot {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 1rem;
    text-align: left;
}

.import-lot__field label {
    display: block;
    margin-bottom: .3rem;
    font-size: .78rem;
    font-weight: 600;
    color: var(--pcr-text);
}

.import-lot select {
    width: 100%;
    max-width: 100%;
    box-sizing: border-box;
    padding: .38rem 1.7rem .38rem .55rem;
    background: var(--pcr-surface);
    border: 1px solid var(--pcr-border);
    border-radius: .3rem;
    color: var(--pcr-text);
    font-size: .78rem;
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
}

.import-lot select:focus {
    outline: 2px solid rgb(var(--color-primary-600, 37 99 235));
    outline-offset: -1px;
}

.import-lot__field .import-card__hint { margin: .3rem 0 0; }

/* Lien vers le classeur modèle, sous la zone de dépôt. */
.import-model {
    margin: .85rem 0 0;
    font-size: .76rem;
    color: var(--pcr-muted);
}

.import-model a {
    display: inline-flex;
    align-items: center;
    gap: .2rem;
    margin-left: .25rem;
    color: rgb(var(--color-primary-600, 37 99 235));
    font-weight: 600;
    text-decoration: none;
}

.import-model a:hover { text-decoration: underline; }

.import-model .material-symbols-outlined { font-size: 1rem; }

.import-model__note {
    margin: .3rem auto 0;
    max-width: 30rem;
    font-size: .72rem;
    color: var(--pcr-subtle);
}

/* ------------------------------------------------------------------ */
/* Planning dans la fiche du lead                                      */
/* ------------------------------------------------------------------ */
.lead-planning { position: relative; }

.lead-planning__bar {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin-bottom: .75rem;
}

.lead-planning__nav {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.9rem;
    height: 1.9rem;
    padding: 0;
    background: var(--pcr-surface);
    border: 1px solid var(--pcr-border);
    border-radius: .3rem;
    color: var(--pcr-muted);
    cursor: pointer;
}

.lead-planning__nav:hover { color: var(--pcr-text); }

.lead-planning__range {
    font-size: .82rem;
    font-weight: 600;
    color: var(--pcr-text);
}

.lead-planning__hint {
    margin-left: auto;
    font-size: .74rem;
    color: var(--pcr-muted);
}

.lead-planning__hint.is-error {
    color: var(--pcr-danger, #dc2626);
    font-weight: 600;
}

/* La grille défile dans son cadre : une semaine large ne doit pas
   pousser la page à défiler. */
.lead-planning__scroll {
    max-height: 26rem;
    overflow: auto;
    border: 1px solid var(--pcr-border);
    border-radius: .375rem;
}

.lead-planning__grid {
    width: 100%;
    border-collapse: collapse;
    font-size: .74rem;
}

.lead-planning__grid th {
    position: sticky;
    top: 0;
    padding: .3rem .4rem;
    background: var(--pcr-surface-2);
    color: var(--pcr-muted);
    font-weight: 600;
    text-align: center;
}

.lead-planning__hour {
    position: static;
    width: 3.5rem;
    background: var(--pcr-surface);
    text-align: right;
}

.lead-planning__slot {
    height: 2.2rem;
    padding: .1rem;
    border: 1px solid var(--pcr-border);
    background: var(--pcr-surface);
    vertical-align: top;
}

/* Un créneau où il reste quelqu'un s'ouvre au clic · complet ou passé,
   il ne propose rien, et le serveur le refuserait de toute façon. */
.lead-planning__slot.is-open { cursor: pointer; }

.lead-planning__slot.is-open:hover {
    background: rgba(var(--color-primary-600, 37 99 235), .1);
}

.lead-planning__slot.is-full { background: var(--pcr-surface-2); }

.lead-planning__slot.is-past {
    background: var(--pcr-surface-2);
    opacity: .5;
}

/* Rendez-vous posé · celui de ce lead se distingue des autres. */
.lead-planning__event {
    display: block;
    margin-bottom: .1rem;
    padding: .1rem .3rem;
    background: var(--pcr-surface-2);
    border-left: 2px solid var(--pcr-subtle);
    border-radius: .2rem;
    color: var(--pcr-muted);
    font-size: .66rem;
    line-height: 1.3;
    text-decoration: none;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.lead-planning__event.is-mine {
    background: rgba(var(--color-primary-600, 37 99 235), .14);
    border-left-color: rgb(var(--color-primary-600, 37 99 235));
    color: rgb(var(--color-primary-600, 37 99 235));
    font-weight: 600;
}

/* Sélecteur du commercial, sous le créneau cliqué. */
.lead-planning__picker {
    position: absolute;
    z-index: 20;
    min-width: 13rem;
    max-width: 16rem;
    padding: .4rem;
    background: var(--pcr-surface);
    border: 1px solid var(--pcr-border);
    border-radius: .375rem;
    box-shadow: 0 6px 18px rgba(15, 23, 42, .16);
}

.lead-planning__picker-title {
    margin: 0 0 .3rem;
    padding: 0 .3rem;
    font-size: .7rem;
    font-weight: 700;
    color: var(--pcr-muted);
}

.lead-planning__picker-item {
    display: block;
    width: 100%;
    padding: .32rem .45rem;
    background: transparent;
    border: 0;
    border-radius: .25rem;
    color: var(--pcr-text);
    font-size: .78rem;
    text-align: left;
    cursor: pointer;
}

.lead-planning__picker-item:hover {
    background: rgba(var(--color-primary-600, 37 99 235), .12);
}

.lead-planning__picker-empty {
    margin: 0;
    padding: .3rem;
    font-size: .74rem;
    font-style: italic;
    color: var(--pcr-subtle);
}

.lead-planning__empty {
    margin: .75rem 0 0;
    font-size: .8rem;
    font-style: italic;
    color: var(--pcr-muted);
}

/* ------------------------------------------------------------------ */
/* Tableau de bord                                                      */
/*                                                                      */
/* Une seule couleur d'accent et des neutres : les neuf pastilles       */
/* pastel d'avant ne portaient aucune information — un lecteur les      */
/* parcourait sans savoir où regarder. La hiérarchie se fait par la     */
/* taille et le rang, pas par la teinte.                                */
/* ------------------------------------------------------------------ */
.kpi-grid {
    display: grid;
    gap: .75rem;
    margin-bottom: 1rem;
}

.kpi-grid--primary {
    grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
}

/* `auto-fit` et non `auto-fill` : le second garde les colonnes vides à
   droite, si bien que cinq cartes s'arrêtaient aux deux tiers de la
   page. Le premier les efface et répartit la place entre les cartes. */
.kpi-grid--secondary {
    grid-template-columns: repeat(auto-fit, minmax(195px, 1fr));
}

.kpi-card {
    display: flex;
    align-items: flex-start;
    gap: .7rem;
    padding: .85rem 1rem;
    background: var(--pcr-surface);
    border: 1px solid var(--pcr-border);
    border-radius: .5rem;
    color: inherit;
    text-decoration: none;
}

.kpi-card--link { transition: border-color .12s ease, box-shadow .12s ease; }

.kpi-card--link:hover {
    border-color: rgb(var(--color-primary-600, 37 99 235));
    box-shadow: 0 1px 6px rgba(15, 23, 42, .08);
}

.kpi-card__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 2.1rem;
    height: 2.1rem;
    background: var(--pcr-surface-2);
    border-radius: .4rem;
    color: var(--pcr-subtle);
}

.kpi-card--primary .kpi-card__icon {
    width: 2.5rem;
    height: 2.5rem;
    background: rgba(var(--color-primary-600, 37 99 235), .12);
    color: rgb(var(--color-primary-600, 37 99 235));
}

.kpi-card__icon i { font-size: 1.05rem; }
.kpi-card--primary .kpi-card__icon i { font-size: 1.2rem; }

.kpi-card__body {
    display: flex;
    flex-direction: column;
    min-width: 0;
    overflow-wrap: anywhere;
}

.kpi-card__label {
    font-size: .7rem;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--pcr-subtle);
}

/* Deux lignes, pas quatre : le libellé, puis le chiffre et ce qui le
   précise. `align-items: baseline` pose « 0 € », « TTC · déposés » et
   « +100 % » sur la même assise malgré leurs trois tailles de police ;
   `flex-wrap` les replie quand la colonne est trop étroite plutôt que
   de faire déborder la carte. */
.kpi-card__figure {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .1rem .55rem;
    margin-top: .2rem;
    min-width: 0;
}

.kpi-card__value {
    font-size: 1.15rem;
    font-weight: 700;
    line-height: 1.15;
    color: var(--pcr-text);
    word-break: break-word;
}

.kpi-card--primary .kpi-card__value { font-size: 1.55rem; }

.kpi-card__value.is-success { color: #15803d; }
html.dark .kpi-card__value.is-success { color: #4ade80; }

.kpi-card__sub {
    font-size: .72rem;
    color: var(--pcr-muted);
}

/* Variation · la couleur dit le sens, la flèche le confirme. */
.kpi-card__trend {
    display: inline-flex;
    align-items: center;
    gap: .2rem;
    font-size: .7rem;
    font-weight: 600;
}

/* La variation se cale à droite de la ligne : l'œil descend la colonne
   des chiffres sans être coupé par les longueurs de libellé. */
.kpi-card__figure .kpi-card__trend { margin-left: auto; }

.kpi-card__trend em { font-style: normal; font-weight: 400; opacity: .75; }
.kpi-card__trend .material-symbols-outlined { font-size: .95rem; }
.kpi-card__trend.is-up { color: #15803d; }
.kpi-card__trend.is-down { color: #b45309; }
html.dark .kpi-card__trend.is-up { color: #4ade80; }
html.dark .kpi-card__trend.is-down { color: #fbbf24; }

.dash-panel {
    padding: 1rem 1.15rem;
    background: var(--pcr-surface);
    border: 1px solid var(--pcr-border);
    border-radius: .5rem;
}

.dash-panel--empty {
    text-align: center;
    font-style: italic;
    color: var(--pcr-muted);
}

.role-badge {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    padding: .18rem .5rem;
    margin-left: .5rem;
    background: var(--pcr-surface-2);
    border-radius: .3rem;
    font-size: .68rem;
    font-weight: 600;
    letter-spacing: .03em;
    color: var(--pcr-muted);
}

.role-badge i { font-size: .8rem; }

/* Panneaux du tableau de bord · la grille est écrite ici : les classes
   de colonnes de Tailwind ne sont pas dans le build figé de sivoni, le
   pipeline prenait donc toute la largeur et le palmarès passait dessous. */
.dash-grid {
    display: grid;
    gap: 1rem;
    grid-template-columns: 1fr;
    margin-bottom: 1rem;
}

@media (min-width: 1024px) {
    .dash-grid { grid-template-columns: 2fr 1fr; }
}

.dash-grid { align-items: start; }

.dash-panel { margin-bottom: 1rem; }
.dash-grid .dash-panel { margin-bottom: 0; }

/* Hauteur des graphiques · l'attribut `height` d'un canvas responsive
   ne décide de rien : sur une page large, Chart.js tenait son rapport
   de forme et un graphe de trois barres occupait tout l'écran. */
.dash-chart {
    position: relative;
    height: 16rem;
}

.dash-chart--short { height: 13rem; }

.dash-chart canvas { max-height: 100%; }

/* Variation · seul le couple flèche + pourcentage est insécable. Tout
   mettre en `nowrap` faisait déborder la carte ; ne rien fixer laissait
   « 100,0 » et « % » se séparer sur trois lignes. */
.kpi-card__trend {
    flex-wrap: wrap;
    white-space: normal;
}

.kpi-card__trend-value {
    display: inline-flex;
    align-items: center;
    gap: .15rem;
    white-space: nowrap;
}

.kpi-card__trend em { white-space: normal; }

/* ------------------------------------------------------------------ */
/* En-tête de page · l'espace entre les icônes de compte et l'action    */
/* ------------------------------------------------------------------ */
/* Le bloc d'actions est posé juste après le groupe drapeau / thème /
   profil, sans rien entre les deux : le bouton d'ajout venait toucher
   l'icône de compte et se lisait comme en faisant partie. */
.object-tools { margin-left: .75rem; }

/* ------------------------------------------------------------------ */
/* Tableaux de liste · une ligne par enregistrement                     */
/* ------------------------------------------------------------------ */
/* Les colonnes étroites repliaient leur contenu : « Hugo test Dubois »
   tenait sur trois lignes, une adresse e-mail sur deux, et la hauteur
   des lignes variait d'un enregistrement à l'autre — l'œil ne pouvait
   plus balayer une colonne. Chaque cellule tient donc sur une ligne, et
   c'est le tableau qui défile horizontalement.

   Les en-têtes portaient déjà `whitespace-nowrap` ; ce sont les cellules
   qui manquaient à l'appel. */
#result_list td { white-space: nowrap; }

/* Sauf ce qui demande explicitement à se replier : la règle ci-dessus
   porte un identifiant, elle l'emporterait sinon sur la classe. */
#result_list td .whitespace-normal { white-space: normal; }

/* ------------------------------------------------------------------ */
/* Date + heure · un seul champ, une seule ligne                        */
/* ------------------------------------------------------------------ */
/* « Dernier contact » et « Prochain rappel » occupaient six lignes à
   elles deux : le gabarit de sivoni empile « Date : » au-dessus de son
   champ puis « Heure : » au-dessous, et ne passe en ligne qu'au point
   de rupture `lg` — jamais dans une colonne de formulaire, qui reste
   étroite quelle que soit la largeur de la fenêtre.

   `min-width` est remis à zéro : les deux moitiés portent `min-w-52`
   (13 rem chacune), de quoi déborder une colonne dès qu'elles sont
   côte à côte. */
/* `div.datetime` et non `.datetime` : `flex-col` du gabarit a la même
   spécificité, et l'emporterait selon l'ordre de chargement. Le type
   suffit à départager, sans `!important`. */
div.datetime {
    flex-direction: row;
    align-items: center;
    flex-wrap: nowrap;
    gap: .5rem;
}

.datetime > div {
    flex-direction: row;
    align-items: center;
    gap: .375rem;
    /* `lg:ml-auto` sur la moitié « Heure » poussait les deux groupes
       aux extrémités, avec un trou au milieu. */
    margin-left: 0;
}

/* Les sous-libellés « Date : » et « Heure : » · au ras de leur champ,
   et sans retour à la ligne qui rouvrirait une deuxième rangée. */
.datetime > div > div:first-child {
    margin-bottom: 0;
    white-space: nowrap;
    font-size: .8125rem;
}

/* `min-w-52` est posé deux fois par niveau — sur la colonne du champ et
   sur le cadre de l'input. Deux classes suffisent à l'emporter. */
.datetime .min-w-52 { min-width: 0; }

/* ------------------------------------------------------------------ */
/* Champs de relation · le select et ses boutons dans la colonne        */
/* ------------------------------------------------------------------ */
/* Statut, lead, client, adresse de chantier, dossier CEE : tous sont
   enrobés par `RelatedFieldWidgetWrapper`, qui ajoute jusqu'à quatre
   boutons (modifier, ajouter, voir, supprimer) à droite du select —
   46 px chacun, `shrink-0`, et `flex-wrap: nowrap` forcé par sivoni.

   La cellule qui les contient est un `flex-grow` sans `min-width: 0` :
   son minimum automatique vaut donc `min-content`, boutons compris. Dès
   que deux de ces champs partagent une ligne — « Lead » et « Client »
   sur l'onglet Sujet — la ligne ne peut plus se réduire et déborde de
   la carte, les boutons passant par-dessus le bord droit.

   Deux corrections, et l'une ne suffit pas sans l'autre : `min-width: 0`
   tout au long de la chaîne pour lever ce plancher, et `flex: 1 1 0` sur
   le contrôle pour qu'il se dimensionne sur la place restante au lieu de
   partir de 100 %. `flex-basis: 0` l'emporte sur `width`, ce qui neutralise
   au passage le `width: 100% !important` que sivoni pose sur les
   conteneurs Select2 — sans avoir à surenchérir en `!important`. */
/* La ligne elle-même est `flex-wrap` : quand deux champs n'y tiennent
   pas, le second passe dessous. C'est la cellule seule sur sa ligne —
   « Adresse de chantier », « Dossier CEE » — qui débordait, faute de
   pouvoir descendre sous son `min-content`. */
/* Sélecteur de descendance et non d'enfant : l'enrobage du label n'est
   pas au même niveau selon les champs — `.flex-grow` est enfant direct
   de la cellule pour un lead, petit-enfant pour un statut. */
div.field-row,
div.field-line,
div.field-line > div,
div.field-line .flex-grow,
.related-widget-wrapper,
.flex-col.items-center.w-full {
    min-width: 0;
}

.related-widget-wrapper > .select2.select2-container,
.related-widget-wrapper > select,
.related-widget-wrapper > .sivoni-date-input-wrapper,
.related-widget-wrapper > .sivoni-time-input-wrapper {
    flex: 1 1 0;
    min-width: 0;
}

/* Les quatre boutons à la hauteur exacte du champ · `mt-2` les décalait
   de 8 px vers le bas, et `h-9.5` leur imposait une hauteur fixe qui ne
   suivait pas celle du select. `align-self: stretch` les met à la
   hauteur de la ligne, donc à celle du contrôle, quelle qu'elle soit. */
.related-widget-wrapper > .related-widget-wrapper-link {
    margin-top: 0;
    height: auto;
    align-self: stretch;
}

/* Le cadre d'un champ de date ou d'heure ouvre le panneau du navigateur
   au clic (cf. les gabarits date.html / time.html) · le curseur le dit,
   sinon rien ne distingue ce cadre d'une simple zone de saisie. */
div:has(> input[type="date"]),
div:has(> input[type="time"]) {
    cursor: pointer;
}

div:has(> input[type="date"]) > input,
div:has(> input[type="time"]) > input {
    cursor: pointer;
}

/* L'icône du navigateur, au bord droit du champ, faisait doublon avec
   celle du raccourci juste à côté — deux calendriers voisins, dont un
   seul ouvrait le panneau. Le clic sur le cadre ouvrant désormais ce
   panneau, elle n'apporte plus rien.

   La classe est posée par le script, et seulement quand ce clic prend
   effectivement le relais : sur un navigateur sans `showPicker`, l'icône
   reste en place plutôt que de laisser un champ qu'on ne peut plus
   ouvrir du tout. */
.sivoni-picker-au-clic > input::-webkit-calendar-picker-indicator {
    display: none;
    -webkit-appearance: none;
}

/* ------------------------------------------------------------------ */
/* Zone de dépôt de fichier                                            */
/* ------------------------------------------------------------------ */
/* Le champ natif n'affiche qu'un bouton « Choisir un fichier » et le nom
   en petit à côté : on ne voit pas ce qu'on a retenu, on ne peut pas y
   glisser un fichier, et rien ne dit qu'on peut se reprendre.

   L'input reste dans le DOM — c'est lui qui porte le fichier posté — mais
   sorti du flux. Pas `display:none` : un champ ainsi masqué échappe à la
   validation natale du navigateur, et `input.click()` n'ouvre plus le
   sélecteur sur certains d'entre eux. */
.pcr-drop__input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

.pcr-drop {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .5rem;
    min-height: 5.5rem;
    padding: 1rem;
    border: 1.5px dashed var(--pcr-border);
    border-radius: .5rem;
    background: var(--pcr-surface-2);
    color: var(--pcr-muted);
    font-size: .85rem;
    text-align: center;
    cursor: pointer;
    transition: border-color .15s, background .15s;
}

.pcr-drop:hover,
.pcr-drop:focus-visible {
    border-color: rgb(var(--color-primary-600, 37 99 235));
    background: var(--pcr-accent-soft);
    outline: none;
}

/* Pendant le survol d'un fichier glissé · la cible doit être évidente,
   sinon on lâche à côté. */
.pcr-drop--survol {
    border-color: rgb(var(--color-primary-600, 37 99 235));
    border-style: solid;
    background: var(--pcr-accent-soft);
    color: var(--pcr-accent-soft-ink);
}

.pcr-drop--rempli {
    border-style: solid;
    justify-content: flex-start;
    text-align: left;
    min-height: 0;
    padding: .6rem .75rem;
}

.pcr-drop__icone { font-size: 1.6rem; }

.pcr-drop__choix {
    display: flex;
    align-items: center;
    gap: .6rem;
    width: 100%;
    min-width: 0;
}

/* La vignette · l'image elle-même quand c'en est une, son extension
   sinon. Reconnaître le fichier d'un coup d'œil évite de le rouvrir. */
.pcr-drop__vignette {
    display: flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 2.5rem;
    height: 2.5rem;
    border: 1px solid var(--pcr-border);
    border-radius: .3rem;
    background: var(--pcr-surface);
    background-size: cover;
    background-position: center;
    color: var(--pcr-muted);
    font-size: .65rem;
    font-weight: 700;
    letter-spacing: .02em;
}

.pcr-drop__vignette--image { border-style: solid; }

.pcr-drop__nom {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    color: var(--pcr-text);
    font-weight: 500;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.pcr-drop__poids { flex: none; color: var(--pcr-subtle); font-size: .78rem; }

.pcr-drop__retirer {
    flex: none;
    padding: .2rem .55rem;
    border: 1px solid var(--pcr-border);
    border-radius: .3rem;
    background: var(--pcr-surface);
    color: var(--pcr-danger);
    font-size: .78rem;
    cursor: pointer;
}

.pcr-drop__retirer:hover { border-color: var(--pcr-danger); }

/* ------------------------------------------------------------------ */
/* Aperçu d'un fichier de document                                     */
/* ------------------------------------------------------------------ */
/* Le cadre se règle sur le fichier, et non l'inverse : à 100 % de large
   avec une hauteur fixe, la visionneuse réduisait une page portrait à la
   hauteur disponible et laissait deux bandes grises sur les côtés. */
.pcr-preview {
    width: 100%;
    border: 1px solid var(--pcr-border);
    border-radius: .45rem;
    overflow: hidden;
    background: var(--pcr-surface-2);
}

/* Les pages en rang · une page A4 seule ne peut pas remplir la largeur
   d'une carte d'administration sans devenir démesurément haute, et la
   laisser seule abandonnait toute la bande de droite. Les pages du
   document s'y succèdent donc, et la bande défile au-delà. */
.pcr-preview__bande {
    display: flex;
    gap: .5rem;
    align-items: flex-start;
    overflow-x: auto;
    padding: .5rem;
    background: var(--pcr-surface-2);
}

/* Hauteur imposée, largeur libre : chaque page garde son rapport, donc
   aucun liseré autour d'elle. */
.pcr-preview__page {
    flex: none;
    width: auto;
    height: var(--pcr-page-apercu);
    border: 1px solid var(--pcr-border);
    border-radius: .25rem;
    background: #fff;
    cursor: zoom-in;
}

.pcr-preview__page:hover { border-color: rgb(var(--color-primary-600, 37 99 235)); }

.pcr-preview__compte {
    margin-right: auto;
    color: var(--pcr-subtle);
}

/* Repli quand la rasterisation n'est pas possible · la visionneuse du
   navigateur, avec son liseré, vaut mieux que rien. A4 portrait est le
   seul format que le projet produise (`page-size: A4` dans
   documents/quote.py, sans réglage d'orientation). */
.pcr-preview__pdf {
    display: block;
    width: auto;
    max-width: 100%;
    height: min(26rem, 60vh);
    aspect-ratio: 210 / 297;
    border: none;
    background: #fff;
}

.pcr-preview__image {
    display: block;
    width: auto;
    max-width: 100%;
    max-height: min(26rem, 60vh);
    background: #fff;
}

/* Ni PDF ni image · une archive d'audit trail, par exemple. */
.pcr-preview__vide {
    padding: 1.6rem;
    color: var(--pcr-muted);
    font-size: .85rem;
    text-align: center;
}

.pcr-preview__vide .material-symbols-outlined {
    display: block;
    margin-bottom: .3rem;
    font-size: 2rem;
}

/* Le pied ne doit pas élargir le cadre · l'URL y était répétée alors
   que le champ juste en dessous la porte déjà, et sa longueur imposait
   sa largeur à tout l'aperçu. */
.pcr-preview__pied {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .45rem .6rem;
    border-top: 1px solid var(--pcr-border);
    font-size: .8rem;
}

.pcr-preview__plein {
    background: var(--pcr-accent-soft);
    border: 1px solid var(--pcr-accent-soft-border);
    border-radius: .3rem;
    color: var(--pcr-accent-soft-ink);
    cursor: pointer;
    font-size: .78rem;
    padding: .15rem .5rem;
}

.pcr-preview__ouvrir {
    color: var(--pcr-accent);
    text-decoration: underline;
}

/* ------------------------------------------------------------------ */
/* Famille du devis · onglet Signature                                 */
/* ------------------------------------------------------------------ */
/* Un nom de type et un statut ne disent pas si c'est le bon document.
   Chaque ligne porte donc sa première page en vignette — c'est ici qu'on
   vient comparer le devis à sa version signée. */
.pcr-famille__ligne {
    display: flex;
    align-items: center;
    gap: .7rem;
    margin-bottom: .4rem;
    padding: .5rem .6rem;
    border: 1px solid var(--pcr-border);
    border-radius: .35rem;
    background: var(--pcr-surface-2);
    font-size: .85rem;
}

.pcr-famille__vignette {
    flex: none;
    width: auto;
    height: 4.5rem;
    border: 1px solid var(--pcr-border);
    border-radius: .2rem;
    background: #fff;
    cursor: zoom-in;
}

.pcr-famille__vignette:hover {
    border-color: rgb(var(--color-primary-600, 37 99 235));
}

/* Un fichier qu'on ne sait pas rasteriser · l'icône tient la place pour
   que les lignes restent alignées. */
.pcr-famille__vignette--vide {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 3.2rem;
    color: var(--pcr-subtle);
    cursor: default;
}

.pcr-famille__texte { flex: 1; min-width: 0; }

.pcr-famille__meta { color: var(--pcr-muted); margin-top: .15rem; }

.pcr-famille__titre {
    color: var(--pcr-text);
    font-weight: 600;
    text-decoration: none;
}

.pcr-famille__ici { color: var(--pcr-accent); font-weight: 600; }

.pcr-famille__lien { color: var(--pcr-accent); text-decoration: underline; }

.pcr-famille__lien--voir { cursor: zoom-in; }

/* Une explication à la place de ce qui n'existe pas encore. */
.pcr-note { color: var(--pcr-subtle); font-size: .85rem; }

/* L'état de la procédure de signature · référence de la demande, date
   de signature, motif de refus. Rien de tout cela n'apparaissait ailleurs
   que dans le JSON de l'onglet technique. */
.pcr-yousign {
    margin-top: .6rem;
    padding: .55rem .7rem;
    border: 1px solid var(--pcr-accent-soft-border);
    border-radius: .35rem;
    background: var(--pcr-accent-soft);
    font-size: .85rem;
}

.pcr-yousign__titre {
    margin-bottom: .3rem;
    color: var(--pcr-accent-soft-ink);
    font-weight: 600;
}

.pcr-yousign__cle { color: var(--pcr-muted); }
.pcr-yousign__valeur { color: var(--pcr-text); }
.pcr-yousign__lien { margin-top: .4rem; }

/* ------------------------------------------------------------------ */
/* Actions d'une ligne, en boutons                                     */
/* ------------------------------------------------------------------ */
/* Quand il ne reste qu'une ou deux actions applicables, elles s'affichent
   directement : un clic au lieu de deux (ouvrir le menu ⋯, puis choisir).
   Cf. sivoni/helpers/actions_row.html et `actions_row_inline_max`. */
.pcr-row-actions {
    display: flex;
    flex-wrap: nowrap;
    gap: .3rem;
}

.pcr-row-action {
    display: inline-flex;
    align-items: center;
    padding: .2rem .55rem;
    border: 1px solid var(--pcr-accent-soft-border);
    border-radius: .3rem;
    background: var(--pcr-accent-soft);
    color: var(--pcr-accent-soft-ink);
    font-size: .75rem;
    font-weight: 600;
    line-height: 1.4;
    text-decoration: none;
    white-space: nowrap;
}

.pcr-row-action:hover {
    border-color: rgb(var(--color-primary-600, 37 99 235));
    background: rgb(var(--color-primary-600, 37 99 235));
    color: #fff;
}

/* Le titre d'un document tient sur une ligne.
 *
 * « Note de dimensionnement PCR-2026-00099 » fait trente-huit caractères
 * et repliait sa colonne sur deux lignes, ce qui décalait la hauteur de
 * toute la rangée. La cellule porte déjà `overflow-hidden` : on lui
 * ajoute de quoi couper proprement plutôt que rogner au milieu d'un mot.
 *
 * Visé sur cet écran seul — `field-title` est la colonne-lien de tout
 * modèle qui a un titre, et les autres n'ont pas demandé ça. La table
 * défile horizontalement (`list_horizontal_scrollbar_top`), donc rien
 * n'est perdu si la colonne s'élargit.
 */
body.model-document th.field-title {
    white-space: nowrap;
    text-overflow: ellipsis;
}

/* Les feuilles de réponses tiennent sur une ligne.
 *
 * Toutes les cellules se repliaient : la date de soumission passait sur
 * deux lignes, le rattachement sur trois, et chaque rangée prenait une
 * hauteur différente de sa voisine. Une liste qu'on parcourt des yeux ne
 * doit pas avoir de relief.
 *
 * Contrairement à l'écran des documents, la règle porte sur l'ensemble
 * des cellules et pas sur une colonne : c'est bien tout le tableau qui
 * était concerné. Les cellules portent déjà `overflow-hidden`, on leur
 * ajoute de quoi couper proprement plutôt que rogner au milieu d'un mot.
 *
 * Le projet tenait déjà ses cellules sur une ligne — mais seulement dans
 * `@media (max-width: 1023px)`, là où sivoni bascule sa liste en mode
 * tableau. Au-delà, c'est sa mise en page `lg:table-cell` qui prend la
 * main, et plus rien n'empêchait le repli : le défaut ne se voyait donc
 * que sur un écran large.
 *
 * La table défile horizontalement (`list_horizontal_scrollbar_top`),
 * donc rien n'est perdu si une colonne s'élargit.
 */
body.model-responsesheet #result_list th[class*="field-"],
body.model-responsesheet #result_list td[class*="field-"] {
    white-space: nowrap;
    text-overflow: ellipsis;
}

/* La désignation d'un produit tient sur une ligne.
 *
 * « Déstratificateur d'air caréné 6500 m³/h » se repliait sur deux
 * lignes, et chaque rangée du catalogue prenait une hauteur différente
 * de sa voisine selon la longueur du nom. Une liste qu'on parcourt des
 * yeux ne doit pas avoir de relief.
 *
 * Visé sur l'application entière plutôt que modèle par modèle : les
 * sept écrans du catalogue portent la même colonne et posent le même
 * problème, et un produit de plus demain serait un sélecteur de plus à
 * ne pas oublier. La cellule porte déjà `overflow-hidden` : on lui
 * ajoute de quoi couper proprement plutôt que rogner au milieu d'un
 * mot.
 *
 * Le projet tenait déjà ses cellules sur une ligne — mais seulement
 * dans `@media (max-width: 1023px)`, là où sivoni bascule sa liste en
 * mode tableau. Au-delà, c'est sa mise en page `lg:table-cell` qui
 * prend la main, et plus rien n'empêchait le repli.
 *
 * La table défile horizontalement, donc rien n'est perdu si la colonne
 * s'élargit.
 */
body.app-catalog #result_list th.field-name,
body.app-catalog #result_list td.field-name {
    white-space: nowrap;
    text-overflow: ellipsis;
}

/* `.nowrap` : la classe que la liste pose elle-même, et que personne ne
 * dessinait.
 *
 * `sivoni_list.items_for_result` ajoute `nowrap` aux cellules qui
 * portent une date, une heure ou une clé étrangère — elles n'ont rien à
 * gagner à se replier, et c'est le comportement que Django a toujours
 * eu. Mais la feuille Tailwind de sivoni ne connaît que
 * `whitespace-nowrap` : la classe nue n'est définie nulle part, ni chez
 * elle, ni ici. Elle était donc inerte, et toutes ces colonnes se
 * repliaient — « Fiche parente » sur les sous-types comme les dates
 * ailleurs.
 *
 * On la dessine une fois, limitée au tableau de liste : elle n'est
 * posée que là, et la restreindre évite d'atteindre un `nowrap` qui
 * viendrait d'ailleurs. Les cellules portent déjà `overflow-hidden`, on
 * leur ajoute de quoi couper proprement plutôt que rogner au milieu
 * d'un mot.
 */
#result_list th.nowrap,
#result_list td.nowrap {
    white-space: nowrap;
    text-overflow: ellipsis;
}
