/* SmartClinic Plus — Custom overrides */

/* ─── Inputs numériques : global, tout le projet ───
   Retire les flèches spinner (moche + clic accidentel). L'autofill navigateur
   « Informations enregistrées » est neutralisé côté JS (autocomplete=off). */
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}
input[type="number"] {
    -moz-appearance: textfield;
    appearance: textfield;
}

/* Footer du sidebar : indicateur réseau + icône customizer, fixé en bas */
.sidebar-footer {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.5rem 0.75rem;
    background: inherit;
    z-index: 2;
    border-top: 1px solid rgba(255, 255, 255, 0.05);
}
.sidebar-footer-customizer {
    display: flex;
    align-items: center;
    color: rgba(200, 200, 200, 0.5);
    cursor: pointer;
    transition: color 0.15s;
}
.sidebar-footer-customizer:hover { color: var(--bs-primary); }

/* Indicateur réseau */
.sc-net-indicator {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 6px;
    border-radius: 4px;
    font-size: 0.7rem;
    font-weight: 600;
    color: rgba(200, 200, 200, 0.8);
    background: rgba(255, 255, 255, 0.05);
    line-height: 1;
}
.sc-net-indicator.net-good { color: #10B981; background: rgba(16, 185, 129, 0.1); }
.sc-net-indicator.net-medium { color: #F59E0B; background: rgba(245, 158, 11, 0.1); }
.sc-net-indicator.net-bad { color: #EF4444; background: rgba(239, 68, 68, 0.12); }
.sc-net-indicator.net-offline {
    color: #fff;
    background: #DC2626;
    animation: sc-net-blink 1.6s infinite;
}
@keyframes sc-net-blink {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.5; }
}
.sc-net-icon { flex-shrink: 0; }
.sc-net-label { font-variant-numeric: tabular-nums; }

/* HTMX progress bar — barre fine au top, animée pendant chaque requête */
#htmx-progress {
    position: fixed; top: 0; left: 0; right: 0; height: 3px;
    background: linear-gradient(90deg, transparent, #3a57e8 20%, #3a57e8 80%, transparent);
    background-size: 200% 100%;
    transform: scaleX(0); transform-origin: left;
    transition: transform 0.2s ease-out, opacity 0.2s;
    z-index: 9999; pointer-events: none; opacity: 0;
}
#htmx-progress.active {
    opacity: 1;
    animation: htmx-progress-anim 1.5s ease-in-out infinite;
}
@keyframes htmx-progress-anim {
    0%   { transform: scaleX(0);   background-position: 0%   0; }
    50%  { transform: scaleX(0.7); background-position: 100% 0; }
    100% { transform: scaleX(1);   background-position: 200% 0; }
}
/* Désactive uniquement les boutons en cours de requête (évite double-click) */
button.htmx-request, a.htmx-request {
    opacity: 0.7;
    pointer-events: none;
}

/* Réserver la place en bas du sidebar pour ne pas cacher les derniers items
   (user block ~52px + footer customizer ~40px = ~92px) */
.sidebar .data-scrollbar { max-height: calc(100vh - 152px) !important; }

/* Sidebar user block — avatar + nom + bouton logout, juste au-dessus du footer customizer */
.sidebar-user-block {
    position: absolute;
    bottom: 48px;
    left: 0;
    right: 0;
    border-top: 1px solid rgba(255, 255, 255, 0.08);
    background: inherit;
    z-index: 2;
}
.sidebar-user-avatar {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: var(--bs-primary);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    font-size: 0.85rem;
}
.sidebar-user-info { font-size: 0.78rem; }
.sidebar-user-name {
    font-weight: 600;
    color: rgba(200, 200, 200, 0.95);
    line-height: 1.2;
}
.sidebar-user-email {
    color: rgba(200, 200, 200, 0.55);
    font-size: 0.7rem;
    line-height: 1.2;
}
.sidebar-user-logout {
    color: rgba(200, 200, 200, 0.55);
    padding: 4px;
    border-radius: 4px;
    transition: all 0.15s ease;
    display: inline-flex;
}
.sidebar-user-logout:hover {
    color: #ef4444;
    background: rgba(239, 68, 68, 0.1);
}

/* En mode sidebar mini (collapsed icons), masquer le texte user mais garder avatar + logout */
.sidebar.sidebar-mini .sidebar-user-info { display: none; }
.sidebar.sidebar-mini .sidebar-user-block .d-flex { justify-content: center; }

/* ══════════════════════════════════════════════════════════════
 * App-shell layout (opt-in, desktop only)
 *
 * Les frameworks CSS (Hope-UI, Bootstrap) sont faits pour le pattern
 * "page-scroll" par défaut : contenu défile globalement. SmartClinic
 * veut le pattern "app-shell" en desktop (chaque panneau scroll interne,
 * pas de scroll global) — standard pour interfaces denses (Figma, Linear).
 *
 * Règle : opt-in via .sc-page ET seulement à ≥992px (lg). En dessous,
 * on laisse le comportement natif Hope-UI (page-scroll empilé).
 *
 * Classes utilitaires :
 *   .sc-page        — racine page app-shell
 *   .sc-fill        — enfant qui prend l'espace restant
 *   .sc-scroll-y    — scroll vertical interne
 * ══════════════════════════════════════════════════════════════ */
@media (min-width: 992px) {
    body:has(.sc-page) main.main-content {
        height: 100dvh;
        display: flex;
        flex-direction: column;
        overflow: hidden;
    }
    body:has(.sc-page) main.main-content > .iq-banner {
        flex: 0 0 auto;
    }
    /* Hope-UI force min-height via !important. On garde son margin-top:-3rem
       (effet float visuel au-dessus du gradient) qui fonctionne avec flex. */
    body:has(.sc-page) main.main-content > .iq-banner + .content-inner {
        flex: 1 1 0;
        min-height: 0 !important;
        display: flex;
        flex-direction: column;
        overflow: hidden;
    }
    .sc-page {
        flex: 1 1 0;
        min-height: 0;
        display: flex;
        flex-direction: column;
        overflow: hidden;
    }
    .sc-fill { flex: 1 1 0; min-height: 0; }
    .sc-scroll-y { overflow-y: auto; min-height: 0; }
    /* Corps de modal découpé en colonnes qui défilent indépendamment : le corps
       ne défile plus lui-même, chaque colonne porte .sc-scroll-y. Sous 992px le
       comportement natif reprend (corps qui défile, colonnes empilées). */
    .sc-modal-split {
        overflow: hidden !important;
        padding: 0;
        display: flex;
        flex-direction: column;
    }
    /* La rangée prend la hauteur restante (base auto : une base 0 la ferait
       s'effondrer, le corps du modal n'ayant pas de hauteur propre). */
    .sc-modal-split > .row {
        flex: 1 1 auto;
        min-height: 0;
        margin: 0;
        width: 100%;
        align-items: stretch;
    }
    /* Toute colonne d'une grille du corps, pas seulement celles du premier
       niveau : sans `min-height: 0`, un enfant flex refuse de descendre sous la
       hauteur de son contenu et le défilement interne ne s'amorce jamais — la
       liste d'actes poussait alors le total hors de vue. */
    .sc-modal-split .row > [class*="col-"] { min-height: 0; }
}
/* <992px : comportement natif Hope-UI (page-scroll), classes sc-* = no-op */

/* ══════════════════════════════════════════════════════════════
 * Scrollbar app-shell — même look que HopeUI (.iq-scroller-effect) :
 * fin, arrondi, thumb discret. S'applique globalement à nos conteneurs
 * de scroll interne (.sc-scroll-y, cellules du kanban, modals
 * scrollables) → plus de scrollbar navigateur brute. Dark-mode aware.
 * ══════════════════════════════════════════════════════════════ */
.sc-scroll-y,
.sc-card-scroll,
.kanban-cell,
.kanban-sub-cell,
.kanban-board.sc-fill,
.modal-dialog-scrollable .modal-body {
    scrollbar-width: thin;                                  /* Firefox */
    scrollbar-color: rgba(0, 0, 0, 0.22) transparent;
}
.sc-scroll-y::-webkit-scrollbar,
.sc-card-scroll::-webkit-scrollbar,
.kanban-cell::-webkit-scrollbar,
.kanban-sub-cell::-webkit-scrollbar,
.kanban-board.sc-fill::-webkit-scrollbar,
.modal-dialog-scrollable .modal-body::-webkit-scrollbar {
    width: 6px;
    height: 6px;
}
.sc-scroll-y::-webkit-scrollbar-track,
.kanban-cell::-webkit-scrollbar-track,
.kanban-sub-cell::-webkit-scrollbar-track,
.modal-dialog-scrollable .modal-body::-webkit-scrollbar-track {
    background: transparent;
}
.sc-scroll-y::-webkit-scrollbar-thumb,
.kanban-cell::-webkit-scrollbar-thumb,
.kanban-sub-cell::-webkit-scrollbar-thumb,
.modal-dialog-scrollable .modal-body::-webkit-scrollbar-thumb {
    background: rgba(0, 0, 0, 0.22);
    border-radius: 20px;
    transition: background 200ms ease;
}
.sc-scroll-y::-webkit-scrollbar-thumb:hover,
.modal-dialog-scrollable .modal-body::-webkit-scrollbar-thumb:hover {
    background: rgba(0, 0, 0, 0.35);
}
[data-bs-theme="dark"] .sc-scroll-y,
[data-bs-theme="dark"] .kanban-cell,
[data-bs-theme="dark"] .kanban-sub-cell,
[data-bs-theme="dark"] .modal-dialog-scrollable .modal-body {
    scrollbar-color: rgba(255, 255, 255, 0.24) transparent;
}
[data-bs-theme="dark"] .sc-scroll-y::-webkit-scrollbar-thumb,
[data-bs-theme="dark"] .kanban-cell::-webkit-scrollbar-thumb,
[data-bs-theme="dark"] .kanban-sub-cell::-webkit-scrollbar-thumb,
[data-bs-theme="dark"] .modal-dialog-scrollable .modal-body::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.24);
}
[data-bs-theme="dark"] .sc-scroll-y::-webkit-scrollbar-thumb:hover,
[data-bs-theme="dark"] .modal-dialog-scrollable .modal-body::-webkit-scrollbar-thumb:hover {
    background: rgba(255, 255, 255, 0.4);
}

/* Scroll HORIZONTAL — même look fin. Aligne .table-responsive (HopeUI le met
 * à 10px, on l'harmonise à 6px) et .sc-scroll-x (utilitaire horizontal, ex. les
 * onglets qui débordent en mobile). */
.table-responsive,
.sc-scroll-x {
    scrollbar-width: thin;
    scrollbar-color: rgba(0, 0, 0, 0.22) transparent;
}
.table-responsive::-webkit-scrollbar,
.sc-scroll-x::-webkit-scrollbar {
    width: 6px;
    height: 6px;
}
.table-responsive::-webkit-scrollbar-track,
.sc-scroll-x::-webkit-scrollbar-track {
    background: transparent;
}
.table-responsive::-webkit-scrollbar-thumb,
.sc-scroll-x::-webkit-scrollbar-thumb {
    background: rgba(0, 0, 0, 0.22);
    border-radius: 20px;
    transition: background 200ms ease;
}
.table-responsive::-webkit-scrollbar-thumb:hover,
.sc-scroll-x::-webkit-scrollbar-thumb:hover {
    background: rgba(0, 0, 0, 0.35);
}
[data-bs-theme="dark"] .table-responsive,
[data-bs-theme="dark"] .sc-scroll-x {
    scrollbar-color: rgba(255, 255, 255, 0.24) transparent;
}
[data-bs-theme="dark"] .table-responsive::-webkit-scrollbar-thumb,
[data-bs-theme="dark"] .sc-scroll-x::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.24);
}
/* Onglets de la card-glass (sur fond dégradé) : thumb clair discret. */
.card-glass .nav.overflow-x-auto { scrollbar-color: rgba(255, 255, 255, 0.4) transparent; }
.card-glass .nav.overflow-x-auto::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.4); }

/* ── Flexbox : rétrécir sous la taille du contenu ─────────────────────────
 * Un enfant flex refuse de descendre sous la taille de son contenu tant que
 * `min-width` / `min-height` valent `auto`. Sans ça, `text-truncate` ne
 * tronque pas et une zone scrollable déborde au lieu de défiler. Bootstrap
 * n'a pas ces deux utilitaires ; ils sont trop généraux pour vivre dans une
 * règle d'app. */
/* Curseurs de préhension : Bootstrap n'a aucun utilitaire de `cursor`, et
   sans lui rien ne dit qu'un élément se déplace. `touch-action: none` est
   obligatoire pour que le glisser tactile ne défile pas la page. */
/* Prise de glisser-déposer. Pas de `touch-action: none` ici : la prise est
   une ligne entière, et le doigt doit pouvoir faire défiler la liste — c'est
   l'appui long de Sortable (`delayOnTouchOnly`) qui distingue les deux. */
.sc-grab { cursor: grab; }
.sc-grab:active { cursor: grabbing; }

.min-w-0 { min-width: 0 !important; }
.min-h-0 { min-height: 0 !important; }

/* ── Panneau latéral d'une page pleine hauteur ────────────────────────────
 * Bootstrap n'a pas d'utilitaire de largeur fixe : ses `w-*` sont des
 * pourcentages, et un panneau qui borde une grille a besoin d'une largeur en
 * pixels — sinon il grandit avec la grille au lieu de la laisser respirer.
 * À combiner avec `flex-shrink-0` et `sc-scroll-y`. */
.sc-side-panel { width: var(--sc-side-panel-w, 280px); max-height: 80vh; }

/* ── Bandeau défilant (ticker) ─────────────────────────────────────────────
 * Une BANDE de mise en page : elle prend sa ligne dans la coquille, donc elle
 * ne recouvre jamais le contenu — contrairement à un élément flottant.
 *
 * Le rail contient DEUX copies du contenu qui se suivent : c'est ce qui rend
 * la boucle continue, sans blanc au rebouclage. L'animation ne part que si le
 * contenu dépasse (classe posée par le JS) — une seule tâche qui glisse en
 * rond serait agaçante pour rien.
 *
 * Bootstrap n'a ni masque de débordement animé ni défilement infini : c'est
 * un composant qu'il ne connaît pas, donc un utilitaire global à nous. */
.sc-ticker { display: flex; align-items: center; gap: 0.25rem; padding: 0.35rem 0.75rem; }
.sc-ticker-mask { overflow: hidden; }
.sc-ticker-rail { display: flex; gap: 2rem; width: max-content; }
/* Tant que tout tient dans la largeur, rien ne défile : on centre, comme on
   centrerait n'importe quelle bande d'information courte. */
.sc-ticker-rail:not(.is-scrolling) { width: 100%; justify-content: center; }
/* La copie de rebouclage ne se masque PAS avec `hidden` ni `d-none` : elle
   porte `d-flex`, dont le `!important` de Bootstrap les écrase tous les deux.
   Il faut une règle plus spécifique — et `!important` à son tour. */
.sc-ticker-rail:not(.is-scrolling) [data-ticker-clone] { display: none !important; }
.sc-ticker-rail.is-scrolling {
    animation: sc-ticker-scroll var(--sc-ticker-duration, 30s) linear infinite;
}
/* On lit le bandeau en le survolant, et on clique dedans : il s'arrête. */
.sc-ticker:hover .sc-ticker-rail.is-scrolling { animation-play-state: paused; }
@keyframes sc-ticker-scroll {
    from { transform: translateX(0); }
    to   { transform: translateX(calc(-50% - 1rem)); }   /* la moitié + le gap */
}
@media (prefers-reduced-motion: reduce) {
    .sc-ticker-rail.is-scrolling { animation: none; }
}

/* Empty state — zone vide stylisée (remplace "Aucun X" gris nu) */
.sc-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 2rem 1rem;
    color: var(--bs-secondary-color);
    text-align: center;
    min-height: 140px;
}
.sc-empty .sc-empty-icon {
    opacity: 0.3;
    width: 32px;
    height: 32px;
}
.sc-empty .sc-empty-text { font-size: 0.85rem; margin: 0; }
.sc-empty .sc-empty-cta { margin-top: 0.25rem; }

/* Accent latéral d'état — absent de Bootstrap, et il faut aussi la COULEUR :
   `border-3` s'applique aux quatre côtés et `border-success` colore les quatre
   bords, ce qui teintait aussi le séparateur bas des lignes de liste. D'où un
   jeu minimal de modificateurs, en tokens --bs-* uniquement. */
.sc-accent-start        { border-left: 3px solid var(--bs-border-color) !important; }
.sc-accent-success      { border-left-color: var(--bs-success) !important; }
.sc-accent-warning      { border-left-color: var(--bs-warning) !important; }
.sc-accent-muted        { border-left-color: var(--bs-secondary-border-subtle) !important; }
.sc-accent-primary      { border-left-color: var(--bs-primary) !important; }
.sc-accent-info         { border-left-color: var(--bs-info) !important; }
.sc-accent-danger       { border-left-color: var(--bs-danger) !important; }

/* Colonne dont les cards se PARTAGENT la hauteur de l'écran, chacune avec son
   en-tête et son pied figés — Bootstrap n'a rien pour ça (ses utilitaires flex
   ne descendent pas dans les enfants de card).

   Réservé à la colonne « actes + paiements » : ce sont les deux cards qu'on
   regarde en permanence, et le total d'un plan ou le reste à payer ne doivent
   pas s'en aller avec la liste qu'on fait défiler.

   Ailleurs, une colonne reste un bloc ordinaire : y mettre des éléments flex
   comprime les cards au lieu de les faire déborder. */
@media (min-width: 992px) {
    .sc-hub-split { min-height: 0; }
    .sc-hub-split > .card {
        flex: 1 1 0;
        min-height: 0;
        display: flex;
        flex-direction: column;
        overflow: hidden;
    }
    /* En-tête et pied gardent leur taille ; seul le corps cède. */
    .sc-hub-split > .card > .card-header,
    .sc-hub-split > .card > .card-footer { flex: 0 0 auto; }
    .sc-hub-split > .card > .card-body {
        flex: 1 1 0;
        min-height: 0;
        overflow-y: auto;
        /* Le plafond commun des cards ne s'applique plus ici : c'est la
           colonne qui décide de la hauteur. */
        max-height: none;
    }
    .sc-hub-split > .card > .card-body .sc-card-scroll { max-height: none; }
}

/* Colonne où UNE card absorbe l'espace restant — celle dont le contenu n'a pas
   de borne. Au centre du hub c'est « Visites » : l'odontogramme garde sa
   taille, l'imagerie reste compacte, et les visites prennent ce qui reste.

   `flex: 1 1 auto` et non `1 1 0` : avec une base à zéro, une card part de
   rien et s'effondre dès que les autres remplissent la colonne. Avec `auto`,
   elle part de sa hauteur naturelle et ne fait que GRANDIR s'il reste de la
   place. Étirer une card vide ne ferait qu'agrandir le vide — c'est pour ça
   qu'une seule absorbe, pas toutes. */
@media (min-width: 992px) {
    .sc-hub-grow { display: flex; flex-direction: column; }
    /* Les autres cards de la colonne gardent leur taille. Cette règle vient
       AVANT celle de `.sc-grow` : à spécificité égale, la dernière l'emporte,
       et `.sc-hub-grow > .card` figerait la card qui doit grandir — elle porte
       les deux classes. */
    .sc-hub-grow > .card { flex: 0 0 auto; }
    .sc-hub-grow > .card.sc-grow {
        flex: 1 1 auto;
        min-height: 0;
        display: flex;
        flex-direction: column;
    }
    .sc-hub-grow > .card.sc-grow > .card-header,
    .sc-hub-grow > .card.sc-grow > .card-footer { flex: 0 0 auto; }
    .sc-hub-grow > .card.sc-grow > .card-body {
        flex: 1 1 auto;
        min-height: 0;
        overflow-y: auto;
        max-height: none;
    }
}

/* ══════════════════════════════════════════════════════════════
 * Fiche patient sur téléphone — l'ordre suit le MOMENT D'USAGE, pas la
 * disposition du PC.
 *
 * Empilées dans l'ordre du DOM, les deux cards les plus utilisées — actes et
 * paiements — arrivaient en dixième et onzième position, derrière les
 * documents et l'impression. On découple.
 *
 * `display: contents` sur les colonnes : leurs cards deviennent enfants
 * directs de la rangée, donc chacune peut porter son propre rang. Sans ça on
 * ne pourrait réordonner que les colonnes, et le dossier médical resterait
 * collé au devis et à l'impression.
 *
 * `--bs-gutter-x: 0` : les gouttières de Bootstrap vivent dans le padding des
 * colonnes, qui n'existent plus. L'espacement revient par `gap`.
 *
 * L'ordre est écrit ICI, en un seul endroit, plutôt qu'en onze classes
 * dispersées dans onze gabarits : c'est une LISTE, elle doit se relire comme
 * une liste. Bootstrap s'arrête à `order-5`, il n'aurait de toute façon pas
 * suffi pour onze cards.
 * ══════════════════════════════════════════════════════════════ */
@media (max-width: 991.98px) {
    #patient-hub {
        --bs-gutter-x: 0;
        display: flex;
        flex-direction: column;
        gap: .75rem;
    }
    #patient-hub > [class*="col-lg-"] { display: contents; }
    /* `display: contents` a supprimé les colonnes — et avec elles le
       `width: 100%; max-width: 100%` que Bootstrap posait sur `.row > *`. Les
       cards sont devenues des éléments flex, dont la largeur minimale est par
       défaut celle de leur CONTENU : une seule ligne longue et toute la page
       débordait sur la droite. `min-width: 0` leur rend le droit de rétrécir.

       Le `gap` remplace le rythme vertical global des cards — en enfant direct
       seulement, pour ne pas déshabiller les cards imbriquées. */
    /* Le sélecteur passe par la colonne : `display: contents` change la MISE
       EN PAGE, pas l'arbre DOM. Les cards restent des petites-filles de la
       rangée, et un `#patient-hub > .card` ne désignait donc rien — seuls les
       `order`, écrits sur les identifiants, s'appliquaient. D'où un ordre
       correct et des largeurs qui débordaient. */
    #patient-hub > [class*="col-lg-"] > .card {
        min-width: 0;
        max-width: 100%;
        margin-bottom: 0;
    }
    /* `text-truncate` pose `white-space: nowrap`. Un élément flex refuse par
       défaut de rétrécir sous la largeur de son contenu (`min-width: auto`) :
       le libellé ne se coupait donc pas, la ligne débordait, et toute la page
       partait en défilement horizontal. Sur PC les colonnes imposaient déjà
       une largeur ; sur téléphone plus rien ne le faisait. */
    #patient-hub .text-truncate,
    #patient-hub .min-width-0 { min-width: 0; }

    /* Les quatre premières sont des coups d'œil : puis-je le soigner, que lui
       fait-on, combien doit-il, quand revient-il. Actes et paiements restent
       collés — c'est le seul couple du produit qu'on ne sépare pas. */
    #card-patient       { order: 1; }
    #card-actes         { order: 2; }
    #card-paiements     { order: 3; }
    #card-visits        { order: 4; }
    /* La cinquième est de la lecture — et le micro, geste naturel au doigt. */
    #card-consultation  { order: 5; }
    /* On les ouvre par un geste délibéré : lire, et montrer au patient. */
    #card-odontogramme  { order: 6; }
    #card-imagerie      { order: 7; }
    /* Consultation ponctuelle, puis le travail de bureau. */
    #card-quote         { order: 8; }
    #card-documents     { order: 9; }
    #card-impression    { order: 10; }
    #card-bulletins     { order: 11; }
}

/* Un texte saisi par le praticien garde ses retours à la ligne — une alerte,
   une note. Bootstrap n'a aucun utilitaire pour `white-space`, et sans lui
   chaque champ multi-ligne s'écrasait en un pavé. */
.sc-prewrap { white-space: pre-line; }

/* Corps de card qui défile — Bootstrap n'a pas d'utilitaire max-height.
   Plafond COMMUN à toutes les cards du hub : deux cards côte à côte doivent
   s'arrêter à la même hauteur, sinon la colonne part en escalier. Le style
   fin de la barre de défilement vient de la règle globale plus haut. */
.sc-card-scroll {
    max-height: 42vh;
    overflow-y: auto;
    overflow-x: hidden;
}

/* ─── Ligne mise en avant : flash doux pour attirer l'œil ───────────────────
   Bootstrap n'a aucune animation d'attention (spinner-grow est un indicateur
   de chargement). Conservée telle quelle, avec son échappatoire d'accessibilité.
   .sc-flash-row  : la ligne pulse doucement
   .sc-flash-blink: un élément clignote (badge « Aujourd'hui ») */
.sc-flash-row   { animation: scFlashRow 2s ease-in-out infinite; }
.sc-flash-blink { animation: scFlashBlink 2s ease-in-out infinite; }
@keyframes scFlashRow {
    0%, 100% { box-shadow: inset 0 0 0 1px rgba(var(--bs-primary-rgb), 0.10); }
    50%      { box-shadow: inset 0 0 0 1px rgba(var(--bs-primary-rgb), 0.45),
                           0 2px 12px rgba(var(--bs-primary-rgb), 0.20); }
}
@keyframes scFlashBlink { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
@media (prefers-reduced-motion: reduce) {
    .sc-flash-row   { animation: none; box-shadow: inset 0 0 0 1px rgba(var(--bs-primary-rgb), 0.4); }
    .sc-flash-blink { animation: none; }
}

/* Chiffres alignés verticalement SANS changer de police — Bootstrap n'expose
   pas font-variant-numeric. À poser sur tout montant listé, pour que les
   colonnes de prix s'alignent d'une card à l'autre. */
.sc-tnum { font-variant-numeric: tabular-nums; }

/* HopeUI met `text-transform: capitalize` sur les en-têtes de tableau — une
   convention anglaise. En français seule la première lettre prend la majuscule :
   « Dernière visite », pas « Dernière Visite ». Surcharge globale, notre
   feuille étant chargée après celle du thème. */
.table thead tr th { text-transform: none; }

/* Utility partagée : élément cliquable (BS n'a pas d'utilitaire cursor). */
.sc-clickable { cursor: pointer; }

/* Étiquette de section dans une card clinique — « MOTIF », « DIAGNOSTIC ».
   Le motif était recopié en style en ligne à chaque section : trois fois dans
   la seule card Consultation. Bootstrap sait tout dire ici SAUF la taille sous
   `small` et l'interlettrage. */
.sc-section-label {
    font-size: .72em;
    letter-spacing: .05em;
}

/* Un lecteur audio dans une liste : la hauteur native du contrôle natif est
   celle d'un lecteur plein écran, et fait sauter la ligne qui le porte.
   Bootstrap n'a rien pour ça — un `<audio>` n'est ni un bouton ni une image. */
.sc-audio-inline { height: 28px; }

/* Deux crans SOUS `.small` (0.875em). Les gabarits en avaient neuf, écrits en
   ligne — .6, .65, .7, .75, .82, .85, .88, .9, .92em — soit neuf tailles pour
   trois intentions, et deux libellés voisins qui ne tombaient jamais pareil.
   Bootstrap ne descend pas en dessous de `.small` en unités relatives.
     .small     0.875em  — texte secondaire
     .sc-fs-sm  0.78em   — métadonnée d'une ligne
     .sc-fs-xs  0.68em   — étiquette d'un badge */
.sc-fs-sm { font-size: .78em; }
.sc-fs-xs { font-size: .68em; }

/* Une étiquette en majuscules a besoin d'air entre ses lettres, sans quoi elle
   se lit comme un bloc. Bootstrap n'a pas d'utilitaire de `letter-spacing`. */
.sc-tracked { letter-spacing: .05em; }

/* Colonnes d'un tableau de saisie : une largeur fixe empêche les champs de
   danser d'une ligne à l'autre quand un libellé s'allonge. Bootstrap n'a pas
   d'utilitaire de largeur en pixels — et il ne peut pas en avoir, ces valeurs
   dépendent du contenu réel de la colonne. */
.sc-col-check  { width: 28px; }
.sc-col-qty    { width: 55px; }
.sc-col-money  { width: 90px; }
.sc-col-teeth  { width: 110px; }
.sc-col-act    { min-width: 240px; }
/* ══════════════════════════════════════════════════════════════
 * Une modale ouverte SUR une autre
 * ══════════════════════════════════════════════════════════════
 * Deux cartes blanches l'une sur l'autre, et plus rien ne dit laquelle répond
 * aux clics : la seconde a l'air d'un morceau de la première. Elle porte donc
 * un liseré primaire, une ombre teintée de la même couleur, et un en-tête
 * légèrement coloré.
 *
 * Le repère est la POSITION dans le document : une modale précédée d'une autre
 * déjà ouverte est celle du dessus. Rien à poser dans les gabarits, rien à
 * piloter en JavaScript — et ça vaut pour toutes les coquilles du hub, pas
 * seulement celles du devis.
 *
 * DEUX positions, et la seconde manquait. Une modale s'ouvre par-dessus une
 * autre de deux façons : en VOISINE dans le document (les coquilles du hub,
 * `~`), ou EMBOÎTÉE dans son contenu — c'est le cas des actes dictés, déclarés
 * dans le gabarit de la visite qui les porte. Le sélecteur voisin ne pouvait
 * pas l'atteindre : elle s'affichait sans liseré, indiscernable de la modale
 * qu'elle recouvrait.
 *
 * `!important` parce que le gabarit porte `.shadow`, un utilitaire Bootstrap :
 * une déclaration `!important` ne se nuance qu'avec la même arme.
 *
 * Les deux sélecteurs de POSITION ne disent pas ce que la règle veut dire.
 * Une coquille ajoutée au `body` — la création express d'un patient — n'est
 * ni la voisine qui suit, ni celle qui est dedans : elle s'affichait nue.
 * `data-sc-stacked`, posé par `sc-http.js` au moment de l'ouverture, dit la
 * seule chose qui compte : il y en a une DESSOUS. Les deux autres restent,
 * elles couvrent l'instant où le CSS s'applique avant le JavaScript.
 * ══════════════════════════════════════════════════════════════ */
.modal.show ~ .modal.show > .modal-dialog > .modal-content,
.modal.show .modal.show > .modal-dialog > .modal-content,
.modal[data-sc-stacked] > .modal-dialog > .modal-content {
    box-shadow: 0 0 0 2px var(--bs-primary),
                0 1rem 3rem rgba(var(--bs-primary-rgb), .28) !important;
}
.modal.show ~ .modal.show > .modal-dialog > .modal-content > .modal-header,
.modal.show .modal.show > .modal-dialog > .modal-content > .modal-header,
.modal[data-sc-stacked] > .modal-dialog > .modal-content > .modal-header {
    background-color: var(--bs-primary-bg-subtle);
}

/* Une modale de travail garde la MÊME taille d'un bout à l'autre : sans ça
   elle grandit à chaque acte ajouté, et son bord danse — pire avec
   `modal-dialog-centered`, qui déplace le haut autant que le bas.

   La hauteur se pose sur le CONTENU, pas sur le dialogue. Trois essais l'ont
   ratée avant de comprendre pourquoi : `.modal-dialog-centered` porte
   `min-height: calc(100% - marges)`, et un `min-height` l'emporte sur un
   `height` quel que soit le poids du sélecteur. Le dialogue restait donc plein
   écran, et le contenu en `height: 100%` avec lui. Il reste plein écran — c'est
   lui qui centre — mais le contenu, lui, fait 82 vh.

   Et le repère est ce que la modale CONTIENT, pas une classe de taille :
   `modal-xxl` est posée par le JavaScript après le swap, donc une règle qui en
   dépend s'applique trop tard ou pas du tout. `:has()` est déjà l'idiome du
   fichier (voir `.sc-page`). */
@media (min-width: 992px) {
    /* 82 vh et non 100 : plein écran, la modale colle aux bords et ne se
       distingue plus de la page. */
    .modal-dialog:has(#quote-body) > .modal-content { height: 82vh; }
}

/* Zone de dépôt d'un glisser-déposer : une hauteur minimale garde la cible
   visible quand elle se vide — sinon on ne peut plus y déposer. */
.sc-drop-zone { min-height: 44px; }
/* Bootstrap n'a pas d'utilitaire pour le curseur : ce qui se saisit doit le
   dire, et `touch-action` est obligatoire pour que le glisser marche au doigt. */
.sc-grab { cursor: grab; touch-action: none; }
.sc-grab:active { cursor: grabbing; }
/* Un montant en fin de ligne : sa colonne ne doit pas se réduire au chiffre,
   sinon la ligne se recompose à chaque changement de prix. */
.sc-money-cell { min-width: 5.5rem; }

/* Le pavé de signature. Le canevas doit avoir une taille EXPLICITE : mesuré à
   zéro, il accepte le trait et n'affiche rien. `touch-action: none` est
   indispensable — sans lui, le doigt fait défiler la page au lieu de signer. */
.sc-signature-pad {
    display: block;
    width: 100%;
    height: 120px;
    cursor: crosshair;
    touch-action: none;
}

/* Le point qui bat pendant l'enregistrement. Une seule animation, déclarée
   une fois — elle vivait dans un `<style>` au milieu d'un gabarit. */
@keyframes sc-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .3; } }
.sc-pulse-dot {
    width: 8px;
    height: 8px;
    animation: sc-pulse 1.2s infinite;
}
@media (prefers-reduced-motion: reduce) { .sc-pulse-dot { animation: none; } }

/* Le logo du cabinet dans un bandeau d'acceptation : contraint sans être
   déformé, sur fond clair pour rester lisible quel que soit son propre fond. */
.sc-stamp-logo {
    max-height: 32px;
    max-width: 80px;
    background: var(--bs-body-bg);
    border: 1px solid var(--bs-success-border-subtle);
    border-radius: 3px;
    padding: 1px;
}

/* Largeur d'un sélecteur de filtre. Bootstrap n'a pas d'utilitaire de
   `min-width`, et un sélecteur multiple sans largeur minimale se réduit à son
   contenu — le libellé de son placeholder se retrouve tronqué à trois mots. */
.sc-w-picker { min-width: 14rem; }

/* Champ numérique court accompagné de son unité — « 12 cig./jour », « 22 SA ».
   Bootstrap n'a pas d'utilitaire de largeur pour un `input-group`. */
.sc-w-narrow { width: auto; max-width: 11rem; }

/* Vignette d'identité d'une modale. Bootstrap n'a pas d'utilitaire de taille
   carrée, et `object-fit` lui manque aussi. */
.sc-avatar-lg { width: 44px; height: 44px; object-fit: cover; }

/* QR code : une image matricielle qu'il ne faut surtout pas lisser — un
   interpolage rend les modules illisibles pour l'appareil qui la scanne. */
.sc-qr {
    width: 100%;
    max-width: 260px;
    aspect-ratio: 1;
    image-rendering: pixelated;
}

/* Titre de section dans une modale : petites capitales espacées. Bootstrap a
   `text-uppercase` mais ni la taille ni l'interlettrage qui vont avec. */
.sc-section-title {
    font-size: 0.75rem;
    letter-spacing: 0.06em;
}

/* La liste d'un TomSelect monté avec `dropdownParent: body` doit passer
   au-dessus d'une modale Bootstrap (z-index 1055). */
body > .ts-dropdown { z-index: 1060; }

/* TomSelect dans un `input-group`. Bootstrap n'étire que ce qu'il connaît —
   `.form-control`, `.form-select` — et TomSelect remplace le `<select>` par un
   `div.ts-wrapper` qu'il ne reconnaît pas : le champ se réduisait à la largeur
   de son placeholder, collé contre le bouton d'à côté. `width: 1%` est le
   procédé de Bootstrap lui-même pour qu'un champ flex ne soit pas dimensionné
   par son contenu. */
.input-group > .ts-wrapper {
    position: relative;
    flex: 1 1 auto;
    width: 1%;
    min-width: 0;
}
.input-group > .ts-wrapper:not(:last-child) .ts-control {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
}

/* Barre à facettes : le champ contient les filtres posés ET la saisie. Il doit
   donc grandir avec eux au lieu de garder la hauteur d'une ligne, et rester
   cliquable sur toute sa surface. Bootstrap n'a pas de `form-control` qui
   accepte des enfants en flux. */
.sc-facets {
    height: auto;
    min-height: calc(1.5em + 0.75rem + 2px);
    padding-block: 0.25rem;
    cursor: text;
}
.sc-facets:focus-within {
    border-color: var(--bs-primary);
    box-shadow: 0 0 0 0.25rem rgba(var(--bs-primary-rgb), 0.15);
}

/* Signature manuscrite du questionnaire : une image dont on ne connaît ni le
   ratio ni la taille, à borner sans la déformer. Bootstrap n'a pas de
   `max-height` utilitaire. */
.sc-signature {
    max-width: 200px;
    max-height: 80px;
    border: 1px solid var(--bs-border-color);
    border-radius: var(--bs-border-radius);
    background: var(--bs-body-bg);
}

/* Déclaration du patient, rendue telle qu'elle a été signée : les retours à
   la ligne comptent, la police doit rester celle du produit, et le bloc ne
   doit pas pousser la modale. */
.sc-declaration {
    white-space: pre-wrap;
    font-family: inherit;
    max-height: 280px;
    overflow: auto;
}

/* Patient à risque — hachures rouges sur le BODY entier (couvre tout le hub).
   Seules les lignes sont rouges ; entre les lignes le bg du body reste intact.
   La classe .sc-hub-at-risk sur #patient-hub sert juste de marqueur :
   le sélecteur :has() applique le pattern au body sans toucher au layout. */
body:has(.sc-hub-at-risk) {
    background-image: repeating-linear-gradient(
        45deg,
        transparent 0,
        transparent 9px,
        rgba(220, 53, 69, 0.22) 9px,
        rgba(220, 53, 69, 0.22) 11px
    );
    background-attachment: fixed;
}

/* Sidebar: compact width */
body {
    --sidebar-width: 11rem;
}

/* Card patient — label texte d'une sous-catégorie (gauche d'une rangée de badges).
   Largeur fixe pour aligner verticalement les rangées (allergies/médic/patho/habitudes). */
.sc-cat-label-text {
    flex-shrink: 0;
    min-width: 78px;
    padding-top: 4px;
    font-size: 0.7em;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: #6c757d;
}

/* Hub patient : le rythme vertical vient du margin-bottom global des cards
   (.card { margin-bottom:.75rem }). On annule la gouttière verticale du row
   Actes/Paiements, sinon sa marge négative (-.75rem) colle l'odontogramme
   au-dessus. La gouttière horizontale (gutter-x) reste à .75rem. */
#patient-hub .row { --bs-gutter-y: 0; }

/* Hide navbar on desktop (≥1200px) si pas de tabs ET pas d'actions.
   Sur mobile/tablette (<1200px) la navbar reste visible — le `logo-title`
   sert de titre de page (utile sur petit écran).
   Deux mécanismes complémentaires :
   - :has() pour le rendu initial (full page reload) — évalué au render
   - .iq-navbar-empty (classe JS) pour les swaps HTMX — :has() ne se ré-évalue
     pas systématiquement après un hx-swap-oob sur les enfants */
@media (min-width: 1200px) {
    .iq-navbar:not(:has(#page-nav-tabs > li)):not(:has(#page-nav-actions > li)),
    .iq-navbar.iq-navbar-empty {
        display: none;
    }
}

/* ══════════════════════════════════════════════════════════════
   FORMULAIRES — distinguer ce qui est SAISI de ce qui est SUGGÉRÉ
   ══════════════════════════════════════════════════════════════
   Hope-UI pose --bs-body-color: #8a92a6 (gris clair) et le placeholder au
   MÊME gris à 75 % d'opacité : un champ rempli et un champ vide se
   ressemblaient. Sur un dossier clinique c'est inacceptable — le praticien
   doit voir d'un coup d'œil ce qu'il a écrit et ce qu'il reste à remplir.

   Saisie   → encre pleine (--bs-emphasis-color, dark-aware).
   Invite   → gris tertiaire + italique : lisible mais jamais confondue
              avec du contenu réel. */
.form-control,
.form-select,
textarea.form-control {
    color: var(--bs-emphasis-color);
}
.form-control::placeholder,
textarea.form-control::placeholder {
    color: var(--bs-tertiary-color);
    font-style: italic;
    opacity: 1;
}
.form-control:disabled,
.form-select:disabled {
    color: var(--bs-secondary-color);
}

/* ── Le champ doit se VOIR avant même qu'on y touche ─────────────────────────
   Hope-UI pose --bs-border-color: #eee — quasi invisible sur fond blanc : le
   praticien ne distingue pas où saisir. TomSelect, lui, dessinait son propre
   #d0d0d0, nettement plus lisible : c'est cette bordure-là qui sert de
   référence, et TOUS les champs s'alignent dessus.

   Repos : --bs-border-color-translucent — rgba(0,0,0,.175) sur blanc, soit le
           gris de TomSelect au pixel près, avec sa variante sombre déjà prévue.
   Focus : primaire franche + anneau. Hope-UI avait neutralisé l'anneau de
           Bootstrap (box-shadow:none / largeur 0rem) alors que les tokens
           --bs-focus-ring-* existent. Au fauteuil, un liseré d'1px ne suffit
           pas à savoir où part la frappe.
   Fond  : toujours --bs-body-bg, pour que le champ ressorte aussi sur les
           panneaux teintés (bg-body-tertiary du modal visite).

   La couleur est ainsi réservée à UNE seule information : où je saisis. */
/* Raccourci UNIQUE : largeur, style et couleur posés d'un bloc pour tous les
   contrôles. Des déclarations séparées laissaient chaque vendor conserver la
   part qu'il définissait de son côté. */
.form-control,
.form-select,
.ts-wrapper .ts-control,
.input-group-text,
.form-check-input {
    border: var(--bs-border-width) solid var(--bs-border-color-translucent);
}
/* Pas de background ici : Hope-UI donne déjà aux champs --bs-gray-900, qui vaut
   #fff en clair et #222738 en sombre — donc PLUS CLAIR que la page dans les deux
   thèmes. C'est exactement ce qu'on veut : le champ ressort du fond. Y poser
   --bs-body-bg (#f9f9f9 / #151824) le ferait au contraire se fondre dans la page.
   Seul TomSelect a besoin d'être aligné, il code un #fff en dur. */
.ts-wrapper .ts-control {
    background-color: var(--bs-gray-900);
}

.form-control:focus,
.form-select:focus,
.form-check-input:focus,
.ts-wrapper.focus .ts-control {
    border-color: var(--bs-primary);
    box-shadow: 0 0 0 var(--bs-focus-ring-width, 0.25rem)
                rgba(var(--bs-primary-rgb), 0.15);
}
.form-check-input:checked {
    border-color: var(--bs-primary);
}

/* TomSelect aligné sur .form-control — le vendor impose ses propres métriques
   (radius 3px, padding 8px, font-size 13px) ET des couleurs EN DUR (#fff,
   #d0d0d0, #303030) qui ignorent le thème. Résultat : à côté d'un input
   Bootstrap le picker ne ressemblait pas au même composant, et en mode sombre
   il serait resté blanc. Tout repasse par les tokens --bs-*. */
.ts-wrapper .ts-control,
.ts-wrapper .ts-control input,
.ts-wrapper .ts-dropdown {
    font-size: 1rem;
    line-height: 1.5;
    color: var(--bs-body-color);
}
.ts-wrapper .ts-control {
    min-height: calc(1.5em + 1rem + calc(var(--bs-border-width) * 2));
    padding: 0.5rem 1rem;
    border-radius: var(--bs-border-radius);
}
.ts-wrapper .ts-control input::placeholder {
    color: var(--bs-tertiary-color);
    font-style: italic;
    opacity: 1;
}
.ts-wrapper .ts-dropdown {
    background-color: var(--bs-body-bg);
    border-color: var(--bs-border-color-translucent);
    border-radius: var(--bs-border-radius);
    box-shadow: var(--bs-box-shadow, 0 0.5rem 1rem rgba(0, 0, 0, 0.15));
}
.ts-wrapper .ts-dropdown .active {
    background-color: var(--bs-primary-bg-subtle);
    color: var(--bs-primary-text-emphasis);
}
.ts-wrapper.multi .ts-control > div {
    background-color: var(--bs-secondary-bg);
    color: var(--bs-body-color);
    border-radius: var(--bs-border-radius-sm);
}

/* ══════════════════════════════════════════════════════════════
 * Imaging — masonry gallery (Pinterest-style)
 *
 * CSS Grid avec aspect-ratio par image — chaque card span le bon nombre
 * de rows pour préserver le ratio sans crop ni stretch.
 * Lecture left-to-right, top-to-bottom (chronologique).
 * ══════════════════════════════════════════════════════════════ */
.masonry-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    grid-auto-rows: 8px;
    gap: 8px;
    align-items: start;
    /* Safety: contient les enfants, prévient l'explosion en taille naturelle */
    width: 100%;
    max-width: 100%;
}
/* Mobile : minmax(180px) tomberait en 1 colonne (images pleine largeur).
   On force le masonry Pinterest à 2 colonnes comme sur PC. */
@media (max-width: 575.98px) {
    .masonry-grid { grid-template-columns: repeat(2, 1fr); gap: 6px; }
}
.masonry-item {
    overflow: hidden;
    border-radius: 8px;
    background: rgba(0,0,0,0.04);
    position: relative;
    grid-row-end: span var(--row-span, 25);
    /* Safety: empêche l'item de dépasser la largeur de sa cellule grid */
    width: 100%;
    max-width: 100%;
    min-width: 0;
    /* HARD CAP — peu importe row-span, jamais d'item plus haut que 400px */
    max-height: 400px;
    /* aspect-ratio fallback (sera override par inline style) */
    aspect-ratio: var(--ratio, 1);
}
/* Le link DEFINI la hauteur via aspect-ratio — image jamais déformée même
   avant que JS recalcule le row-span exact (anti-flash de layout cassé). */
.masonry-thumb-link, .masonry-pending {
    display: block;
    width: 100%;
    max-width: 100%;
    /* aspect-ratio appliquée inline via style="aspect-ratio: W/H" sur le parent .masonry-item;
       fallback ici si pas défini */
    aspect-ratio: var(--ratio, 1);
    text-decoration: none;
    color: inherit;
    position: relative;
    overflow: hidden;
}
.masonry-thumb {
    position: absolute;
    top: 0; left: 0;
    width: 100% !important;
    height: 100% !important;
    object-fit: cover;
    transition: transform 0.3s ease;
    display: block;
}
.masonry-thumb-link:hover .masonry-thumb {
    transform: scale(1.03);
}
.masonry-overlay {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    padding: 6px;
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    opacity: 0;
    transition: opacity 0.2s ease;
    background: linear-gradient(to top, rgba(0,0,0,0.7), transparent);
}
.masonry-thumb-link:hover .masonry-overlay,
.masonry-item:focus-within .masonry-overlay { opacity: 1; }

.masonry-pending {
    background: rgba(58, 87, 232, 0.05);
    border: 2px dashed rgba(58, 87, 232, 0.2);
    border-radius: 8px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    color: var(--bs-secondary-color);
}

/* Drop zone hover state pendant drag-and-drop */
/* La zone de dépôt est la card entière, en-tête compris : une card vide
   repliée sur son en-tête doit rester une cible de glisser-déposer. */
#card-imagerie.dz-hover,
#card-documents.dz-hover {
    background: rgba(58, 87, 232, 0.05);
    outline: 3px dashed var(--bs-primary);
    outline-offset: -8px;
    border-radius: 12px;
}

/* ══════════════════════════════════════════════════════════════
 * Documents grid (cards classiques, pas masonry — formats homogènes)
 * ══════════════════════════════════════════════════════════════ */
.doc-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
    gap: 12px;
}
.doc-card {
    border: 1px solid rgba(0,0,0,0.08);
    border-radius: 10px;
    overflow: hidden;
    background: white;
    transition: transform 0.15s, box-shadow 0.15s;
    display: flex;
    flex-direction: column;
}
.doc-card:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 16px rgba(0,0,0,0.1);
}
.doc-card-thumb-wrap {
    position: relative;
    aspect-ratio: 0.77;  /* ratio A4 portrait approx */
    background: #f5f5f5;
    overflow: hidden;
}
.doc-card-thumb {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}
.doc-card-thumb-fallback {
    width: 100%; height: 100%;
    display: flex; flex-direction: column;
    align-items: center; justify-content: center;
    color: var(--bs-secondary-color);
    font-weight: 600;
}
.doc-card-format-badge {
    position: absolute;
    top: 6px; right: 6px;
    background: rgba(0,0,0,0.7);
    color: white;
    font-size: 0.65rem;
    font-weight: 700;
    padding: 2px 6px;
    border-radius: 4px;
    letter-spacing: 0.5px;
}
.doc-card-body {
    padding: 8px 10px;
    background: white;
}
.doc-card-title {
    font-size: 0.85rem;
    font-weight: 600;
    margin-bottom: 4px;
}
.doc-card-meta {
    font-size: 0.7rem;
}
.doc-card-pending {
    opacity: 0.85;
    pointer-events: none;
}
.doc-card-pending .doc-card-thumb-wrap {
    background: rgba(58, 87, 232, 0.05);
}
#card-imagerie.dz-hover::before {
    content: 'Lâcher pour uploader';
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background: var(--bs-primary);
    color: white;
    padding: 16px 32px;
    border-radius: 8px;
    font-weight: 600;
    z-index: 10;
    pointer-events: none;
    box-shadow: 0 4px 20px rgba(0,0,0,0.2);
}

/* Upload progress (bottom right toast-like) */
#imaging-upload-progress {
    position: fixed;
    bottom: 20px;
    right: 20px;
    width: 320px;
    z-index: 1100;
    display: none;
}
#imaging-upload-progress.active { display: block; }
.imaging-upload-item {
    background: white;
    border-radius: 8px;
    padding: 10px 12px;
    margin-bottom: 8px;
    box-shadow: 0 2px 12px rgba(0,0,0,0.1);
    border-left: 3px solid var(--bs-primary);
    font-size: 0.85rem;
}
.imaging-upload-item.error { border-left-color: var(--bs-danger); }
.imaging-upload-item.done { border-left-color: var(--bs-success); }
.imaging-upload-item .progress { height: 4px; margin-top: 4px; }
.sidebar .sidebar-header .logo-title {
    font-size: 0.95rem;
}

/* Theme-2: gris plus foncé */
[data-bs-theme-color="color-2"] {
    --bs-primary: #5a5f68;
    --bs-primary-rgb: 90, 95, 104;
    --bs-primary-bg-subtle: #dfe0e2;
    --bs-primary-border-subtle: #b8bbbf;
    --bs-primary-hover-bg: #484c53;
    --bs-primary-hover-border: #3d4047;
    --bs-primary-active-bg: #484c53;
    --bs-primary-active-border: #3d4047;
}

/* ══════════════════════════════════════════════════════════════
 * Primary tint palette
 *
 * Background "light" qui suit la couleur primaire du customizer.
 * --bs-primary-rgb est mis à jour au runtime par setting.js → ces
 * variables se recalculent automatiquement quand l'utilisateur change
 * la couleur dans la fenêtre Personnalisation.
 *
 * Usage : background-color: var(--sc-primary-bg-50);
 * ══════════════════════════════════════════════════════════════ */
:root {
    --sc-primary-bg-50:  rgba(var(--bs-primary-rgb), 0.03);
    --sc-primary-bg-100: rgba(var(--bs-primary-rgb), 0.06);
    --sc-primary-bg-200: rgba(var(--bs-primary-rgb), 0.10);
    --sc-primary-bg-300: rgba(var(--bs-primary-rgb), 0.16);
}
[data-bs-theme="dark"] {
    --sc-primary-bg-50:  rgba(var(--bs-primary-rgb), 0.06);
    --sc-primary-bg-100: rgba(var(--bs-primary-rgb), 0.10);
    --sc-primary-bg-200: rgba(var(--bs-primary-rgb), 0.16);
    --sc-primary-bg-300: rgba(var(--bs-primary-rgb), 0.22);
}

/* ─── Gris secondaire plus foncé pour le contraste de lecture (global) ───
   Alimente text-secondary, .text-muted et toutes les classes sc-* grises.
   Bootstrap défaut = 0.75 d'alpha du body-color → on remonte le contraste.
   Theme-aware : body-color-rgb bascule seul en dark. */
:root,
[data-bs-theme="light"] {
    --bs-secondary-color: rgba(var(--bs-body-color-rgb), 0.9);
    --bs-tertiary-color: rgba(var(--bs-body-color-rgb), 0.72);
}
[data-bs-theme="dark"] {
    --bs-secondary-color: rgba(var(--bs-body-color-rgb), 0.85);
    --bs-tertiary-color: rgba(var(--bs-body-color-rgb), 0.68);
}

/* Body bg light primary — cards restent blanches (contraste) */
body {
    background-color: var(--sc-primary-bg-200);
}

/* Cards : tint primary léger, bord subtil, radius prononcé, shadow douce */
.card {
    background-color: color-mix(in srgb, #fff 96%, var(--bs-primary) 4%);
    border: 1px solid rgba(var(--bs-primary-rgb), 0.08);
    border-radius: 0.75rem;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04), 0 4px 12px rgba(var(--bs-primary-rgb), 0.05);
    overflow: hidden; /* coupe les enfants au radius (header, body, etc.) */
}
/* Quand un dropdown de card est ouvert, on laisse déborder pour ne pas couper le menu. */
.card:has(.dropdown-menu.show) { overflow: visible; }
/* Header transparent par défaut — sauf si une utilitaire bg-* est posée
   explicitement (ex. bg-primary-subtle des cards du hub), qui doit gagner. */
.card-header:not([class*="bg-"]) {
    background-color: transparent !important;
}
.card-header {
    border-bottom: 1px solid rgba(var(--bs-primary-rgb), 0.06);
}
.card-body,
.card-footer {
    background-color: transparent;
}
[data-bs-theme="dark"] .card {
    background-color: color-mix(in srgb, var(--bs-body-bg) 92%, var(--bs-primary) 8%);
    border-color: rgba(var(--bs-primary-rgb), 0.18);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 4px 12px rgba(var(--bs-primary-rgb), 0.08);
}

/* Section header — Encaissements / Tiers / Bulletins */
.sc-pay-section {
    margin-top: 0.75rem;
    padding-top: 0.5rem;
}
.sc-pay-section + .sc-pay-section {
    border-top: 1px solid rgba(var(--bs-primary-rgb), 0.08);
}/* ══════════════════════════════════════════════════════════════
 * Page sub-navigation — barre de tabs au-dessus du contenu
 * Pattern : titre + tabs locales + actions, en remplacement
 * de la "card-glass banner" navy précédente.
 * ══════════════════════════════════════════════════════════════ */
.sc-page-subnav {
    position: sticky;
    top: 0;
    z-index: 10;
    background: linear-gradient(135deg,
        var(--bs-primary) 0%,
        color-mix(in srgb, var(--bs-primary) 80%, #4338ca) 100%);
    color: #fff;
    padding: 0.6rem 0.85rem;
    border-radius: 8px;
    box-shadow: 0 2px 8px rgba(var(--bs-primary-rgb), 0.18);
}
.sc-page-subnav-title-wrap h5 {
    color: #fff;
}

/* Tabs dans la subnav — pills légers sur fond translucide */
.sc-page-subnav-tabs { padding: 0; margin: 0; list-style: none; }
.sc-page-subnav-tab {
    display: inline-block;
    padding: 0.3rem 0.85rem;
    border-radius: 5px;
    color: rgba(255, 255, 255, 0.6);
    font-size: 0.85rem;
    font-weight: 500;
    text-decoration: none;
    transition: all 0.15s ease;
    cursor: pointer;
    background: transparent;
    border: none;
}
.sc-page-subnav-tab:hover {
    color: rgba(255, 255, 255, 0.95);
    background: rgba(255, 255, 255, 0.1);
}
.sc-page-subnav-tab.active {
    color: #fff;
    background: rgba(255, 255, 255, 0.22);
    font-weight: 600;
}

/* Boutons d'action dans la subnav — fond clair (contraste avec navy) */
.sc-page-subnav .btn {
    background: rgba(255, 255, 255, 0.95);
    color: var(--bs-primary);
    border: none;
}
.sc-page-subnav .btn:hover {
    background: #fff;
    color: var(--bs-primary);
}
.sc-page-subnav .btn.btn-success {
    background: #10b981;
    color: #fff;
}
.sc-page-subnav .btn.btn-success:hover {
    background: #059669;
}
@keyframes sc-vn-recording-pulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(220, 38, 38, 0.7); }
    50% { box-shadow: 0 0 0 8px rgba(220, 38, 38, 0); }
}
/* ── Compact layout: reduce spacing everywhere ── */

/* Tighter content padding */
.content-inner {
    padding-top: 1rem !important;
    padding-left: 0.75rem !important;
    padding-right: 0.75rem !important;
}

/* Reduce container-fluid padding */
.container-fluid {
    padding-left: 0.75rem !important;
    padding-right: 0.75rem !important;
}

/* Compact cards */
.card {
    margin-bottom: 0.75rem !important;
}
.card-body {
    padding: 0.75rem !important;
}

/* Tighter grid gaps */
.row {
    --bs-gutter-x: 0.75rem;
    --bs-gutter-y: 0.75rem;
}

/* Compact sidebar */
.sidebar-body .nav-link {
    padding: 0.4rem 1rem !important;
}

/* Content starts at same position regardless of banner */
.iq-banner:not(.hide) + .content-inner {
    margin-top: 0 !important;
}

/* Smaller page title */
.iq-navbar-header h1 {
    font-size: 1.5rem;
}

/* DataTables search input — normal size */
.dataTables_filter input.form-control-sm {
    font-size: 1rem !important;
    padding: 0.375rem 0.75rem !important;
    min-height: calc(1.5em + 0.75rem + 2px) !important;
    border-radius: 0.375rem !important;
}

/* Tom Select: hide dropdown arrow in sidebar */
#sidebar-medical .ts-control {
    padding: 0.25rem 0.5rem !important;
}
#sidebar-medical .ts-wrapper .ts-dropdown-toggle {
    display: none;
}

/* ── Agenda et salle d'attente : les couleurs, en un seul endroit ──────────
 * Ce qui suit habille FullCalendar et le kanban — le DOM de la bibliothèque et
 * celui du tableau. Bootstrap ne sait dire ni une hachure de statut, ni un halo
 * qui respire, ni une teinte calculée par type d'acte : ces règles restent.
 *
 * Ce qui ne devait PAS rester, c'est vingt-trois couleurs écrites à la main pour
 * un seul token. Le thème sombre ne les suivait pas : les cartes du mois
 * gardaient un texte noir sur fond sombre, et le popover « +N » restait blanc au
 * milieu d'une page noire.
 *
 * Les valeurs claires ci-dessous sont EXACTEMENT celles d'avant — le thème clair
 * ne bouge pas d'un pixel. Seul le bloc sombre est nouveau.
 *
 * Les couleurs de MARQUE (WhatsApp) n'ont pas de variante : un logo ne change
 * pas de couleur avec le thème de qui le regarde.
 */
:root,
[data-bs-theme="light"] {
    /* Surfaces */
    --sc-cal-surface: #fff;              /* carte, popover, corps de bouton */
    --sc-cal-surface-2: #f1f3f5;         /* entête du popover « +N » */
    --sc-cal-panel: #f8fafc;             /* barre d'outils, entête de colonne */
    --sc-cal-panel-2: #fafbfc;           /* bande alternée */
    --sc-cal-hover: #f1f5f9;             /* survol d'un bouton */

    /* Encres — de la plus forte à la plus discrète */
    --sc-cal-ink: #1a202c;               /* texte du popover */
    --sc-cal-ink-2: #1e293b;             /* titre de la barre, bouton actif */
    --sc-cal-ink-3: #334155;
    --sc-cal-control-ink: #475569;       /* libellé d'un bouton */
    --sc-cal-muted: #64748b;             /* libellé secondaire */
    --sc-cal-faint: #94a3b8;             /* bordure au survol, texte éteint */

    /* Traits */
    --sc-cal-border: #e2e8f0;
    --sc-cal-border-2: #cbd5e0;          /* contour d'un bouton */
    --sc-cal-border-strong: #d0d5dd;

    /* Liens et statuts. Le vert « confirmé » est aussi donné en composantes :
       le flash d'enregistrement en fait un dégradé, ce qu'un hex ne sait pas. */
    --sc-cal-link: #2563eb;
    --sc-cal-link-2: #1d4ed8;
    --sc-cal-confirmed: #219653;
    --sc-cal-confirmed-rgb: 33, 150, 83;
    --sc-cal-danger: #dc2626;
    --sc-cal-danger-2: #ef4444;
    --sc-cal-walkin: #e8a23a;            /* pastille « P » du passager */

    /* Le bouton de vue sélectionnée s'inverse : fond encre, texte fond. Le
       couple se retourne en sombre, sinon le texte disparaît dans son bouton. */
    --sc-cal-active-bg: #1e293b;
    --sc-cal-active-ink: #fff;
    --sc-cal-today: rgba(59, 130, 246, .05);   /* le bandeau du jour */
    --sc-cal-event-ink: #000;            /* le texte porté par une carte teintée */

    /* Les hachures des statuts TERMINAUX. Le fond garde la couleur du type
       d'acte ; la hachure se pose par-dessus sans l'écraser, et c'est elle qui
       dit « ce rendez-vous est derrière nous ». */
    --sc-cal-hatch-cancelled: rgba(108, 117, 125, .55);   /* annulé patient */
    --sc-cal-hatch-noshow: rgba(220, 38, 38, .55);        /* absent */
    --sc-cal-hatch-clinic: rgba(14, 165, 233, .55);       /* annulé cabinet */

    /* L'état d'un message sur la carte : le logo dit le canal, le glyphe l'état. */
    --sc-msg-pending: #9aa5b1;           /* pas encore reçu */
    --sc-msg-pending-glyph: #d9a406;
    --sc-msg-delivered: #667085;

    /* Ombres et halos */
    --sc-cal-shadow: rgba(0, 0, 0, .16);
    --sc-cal-shadow-strong: rgba(0, 0, 0, .20);
    --sc-cal-shadow-drawer: rgba(0, 0, 0, .22);
    --sc-cal-shadow-soft: rgba(0, 0, 0, .07);
    --sc-cal-pulse: rgba(0, 0, 0, .18);  /* halo du rendez-vous en cours */
}

/* ── Le sombre de FullCalendar passe par SES variables ────────────────────
 * Nos tokens habillent ce que NOUS dessinons : la barre d'outils, les cartes,
 * le popover. La GRILLE, elle — le fond de la page, les traits entre les
 * heures, la colonne du jour, le fond hors horaires — est peinte par
 * FullCalendar avec ses propres variables. Elles valent `#fff` et `#ddd` par
 * défaut, quel que soit le thème : c'est pour ça que l'agenda restait blanc
 * alors que tout le reste de la page était passé au sombre.
 *
 * Le thème clair n'est PAS touché : on laisse FullCalendar à ses valeurs, qui
 * sont celles qu'on voit aujourd'hui.
 */
[data-bs-theme="dark"] .fc {
    --fc-page-bg-color: var(--sc-cal-surface);
    --fc-border-color: var(--sc-cal-border);
    --fc-neutral-bg-color: rgba(148, 163, 184, .12);   /* entêtes, cellules neutres */
    --fc-neutral-text-color: var(--sc-cal-muted);
    --fc-non-business-color: rgba(15, 23, 42, .45);    /* hors horaires d'ouverture */
    --fc-today-bg-color: var(--sc-cal-today);
    --fc-highlight-color: rgba(96, 165, 250, .18);     /* la sélection en cours */
    --fc-list-event-hover-bg-color: var(--sc-cal-hover);
    --fc-more-link-bg-color: var(--sc-cal-hover);
    --fc-more-link-text-color: var(--sc-cal-ink);
    --fc-event-selected-overlay-color: rgba(255, 255, 255, .12);
    color: var(--sc-cal-ink);
}

[data-bs-theme="dark"] {
    --sc-cal-surface: #1e293b;
    --sc-cal-surface-2: #0f172a;
    --sc-cal-panel: #172033;
    --sc-cal-panel-2: #131b2b;
    --sc-cal-hover: #243349;

    --sc-cal-ink: #e2e8f0;
    --sc-cal-ink-2: #e2e8f0;
    --sc-cal-ink-3: #cbd5e1;
    --sc-cal-control-ink: #cbd5e1;
    --sc-cal-muted: #94a3b8;
    --sc-cal-faint: #64748b;

    --sc-cal-border: #334155;
    --sc-cal-border-2: #475569;
    --sc-cal-border-strong: #475569;

    /* Les statuts s'éclaircissent : le même vert sur fond sombre passe sous le
       seuil de lisibilité, et un statut qu'on ne lit pas ne sert à rien. */
    --sc-cal-link: #60a5fa;
    --sc-cal-link-2: #93c5fd;
    --sc-cal-confirmed: #34d399;
    --sc-cal-confirmed-rgb: 52, 211, 153;
    --sc-cal-danger: #f87171;
    --sc-cal-danger-2: #fca5a5;
    --sc-cal-walkin: #f0b757;

    --sc-cal-active-bg: #e2e8f0;
    --sc-cal-active-ink: #0f172a;
    --sc-cal-today: rgba(96, 165, 250, .10);
    --sc-cal-event-ink: #f1f5f9;

    --sc-cal-hatch-cancelled: rgba(148, 163, 184, .55);
    --sc-cal-hatch-noshow: rgba(248, 113, 113, .55);
    --sc-cal-hatch-clinic: rgba(56, 189, 248, .55);

    --sc-msg-pending: #94a3b8;
    --sc-msg-pending-glyph: #fbbf24;
    --sc-msg-delivered: #cbd5e1;

    --sc-cal-shadow: rgba(0, 0, 0, .45);
    --sc-cal-shadow-strong: rgba(0, 0, 0, .55);
    --sc-cal-shadow-drawer: rgba(0, 0, 0, .6);
    --sc-cal-shadow-soft: rgba(0, 0, 0, .3);
    --sc-cal-pulse: rgba(255, 255, 255, .22);
}


/* ── FullCalendar : statut par traitement de la boîte (hue = type, état = style) ──
   Actifs : le contour/la densité montent avec l'avancement (planifié → arrivé → en cours).
   Terminaux : texture/fondu négatif (terminé → annulé → absent). Aucune pastille/icône. */
/* Statut appliqué dans TOUTES les vues (grid/mois/liste) — même langage visuel partout.
   Le fond garde la couleur du TYPE ; la hachure (background-image) se pose par-dessus sans
   l'écraser. Le contenu (nom/motif/tél) est rendu par eventContent AU-DESSUS de la hachure. */
/* Planifié = baseline (fond tinté + contour type), aucun style ajouté. */
/* Arrivé : patient présent → contour plein épais + libellé gras (fond déjà plus dense côté serveur). */
.fc-status-arrived { border-width: 2px !important; font-weight: 600; }
/* En cours : actif maintenant → contour épais + halo qui « respire ». */
.fc-status-open { border-width: 2px !important; animation: sc-rdv-pulse 1.6s ease-in-out infinite; }
@keyframes sc-rdv-pulse {
    0%, 100% { box-shadow: 0 0 0 0 transparent; }
    50% { box-shadow: 0 0 0 3px var(--sc-cal-pulse); }
}
/* Terminaux : terminé = hachure verte (positif, non grisé) ; négatifs = hachure colorée + grisé. */
.fc-status-closed {
    background-image: repeating-linear-gradient(45deg, transparent 0 4px, rgba(var(--sc-cal-confirmed-rgb), .5) 4px 8px) !important;
}
.fc-status-cancelled_patient {
    opacity: 0.6;
    background-image: repeating-linear-gradient(45deg, transparent 0 4px, var(--sc-cal-hatch-cancelled) 4px 8px) !important;
}
.fc-status-no_show {
    opacity: 0.6;
    background-image: repeating-linear-gradient(45deg, transparent 0 4px, var(--sc-cal-hatch-noshow) 4px 8px) !important;
}
.fc-status-cancelled_clinic {
    opacity: 0.6;
    background-image: repeating-linear-gradient(45deg, transparent 0 4px, var(--sc-cal-hatch-clinic) 4px 8px) !important;
}
/* Confirmé (orthogonal au statut) */
.fc-confirmed { border-left: 3px solid var(--sc-cal-confirmed) !important; }
@media print { .fc-status-open { animation: none; } }

/* La couleur du texte d'une carte — décidée ICI, pas par le serveur.
 *
 * Le flux imposait `textColor: '#000'` sur chaque rendez-vous, que FullCalendar
 * écrit en style INLINE : aucune règle CSS ne pouvait le rattraper, et les
 * cartes restaient en texte noir sur fond sombre. Le serveur ne peut pas
 * trancher — le thème est un réglage de POSTE, gardé dans le navigateur.
 *
 * Le fond d'une carte est une TEINTE du type d'acte (même couleur, alpha 20 %) :
 * pâle sur fond clair, sombre sur fond sombre. L'encre suit donc le thème, et
 * une seule variable la porte pour la grille, le mois et la liste.
 */
.fc .fc-event,
.fc .fc-event .fc-event-main,
.fc .fc-event .fc-event-title,
.fc .fc-event .fc-event-time { color: var(--sc-cal-event-ink); }

/* À l'impression, l'encre est noire quel que soit le thème de l'écran : une
   feuille sortie depuis un poste en thème sombre serait illisible. */
@media print {
    .fc .fc-event,
    .fc .fc-event .fc-event-main,
    .fc .fc-event .fc-event-title,
    .fc .fc-event .fc-event-time { color: #000 !important; }
}

/* Mois : événements affichés en blocs pleins (comme grid) et non en points, pour porter
   fond-type + hachure + contenu. Liste : la couleur du type remplit la pastille + le fond. */
.fc-daygrid-block-event .fc-event-time { font-weight: 600; }
.fc-list-event td { position: relative; }
.fc-list-event .fc-list-event-title { padding-top: 6px; padding-bottom: 6px; }

/* Cartes daygrid (mois + liste) — FC remplit déjà le bloc grâce à display:block
   (bordure foncée = couleur du type + fond clair = tint), comme la grille. On ajoute juste
   un peu d'air : coins arrondis, padding, espacement entre les cartes. Classes 100% FC. */
.fc-daygrid-block-event { border-radius: 4px; padding: 3px 6px; white-space: normal; }
.fc-daygrid-event-harness { margin-bottom: 4px; }

/* Popover « +N » du mois : fond OPAQUE + ombre, sinon on voit la grille au travers et les
   cartes (tint semi-transparent) deviennent illisibles. */
.fc .fc-popover {
    background: var(--sc-cal-surface) !important;
    border: 1px solid var(--sc-cal-border-strong);
    border-radius: 8px;
    box-shadow: 0 10px 30px var(--sc-cal-shadow-strong);
    z-index: 1070;
}
.fc .fc-popover-header { background: var(--sc-cal-surface-2); color: var(--sc-cal-ink); }
.fc .fc-popover-body { background: var(--sc-cal-surface); }

/* Badge passager (RDV sans dossier patient) — pastille orange « P ».
   Agenda : ajouté par eventContent (span .sc-passager-badge) dans toutes les vues.
   Hors agenda (kanban/print/salle d'attente) : partial _passager_badge.html. */
.sc-passager-badge {
    display: inline-block;
    background: var(--sc-cal-walkin);
    color: #fff;               /* blanc sur pastille orange : vrai dans les deux thèmes */
    font-size: 0.62rem;
    font-weight: 700;
    line-height: 1.15;
    padding: 1px 6px;
    border-radius: 6px;
    margin-left: 4px;
    vertical-align: middle;
}

/* Contenu custom du RDV (eventContent, TOUTES les vues) : entête heure + nom, tél, motif.
   RÈGLE : le contenu ne dépasse JAMAIS la boîte → tout est clippé / ellipsé. */
.fc-timegrid-event .fc-event-main,
.fc-daygrid-block-event .fc-event-main { overflow: hidden; min-width: 0; }
.fc-rdv-content { overflow: hidden; max-width: 100%; white-space: normal; }
.fc-leave-card { font-weight: 600; font-size: 0.82em; line-height: 1.2; }

/* Flash « enregistré » : le RDV qu'on vient de créer clignote en vert brillant (2 pulses). */
@keyframes sc-flash-new {
    0%   { box-shadow: 0 0 0 0 rgba(var(--sc-cal-confirmed-rgb), 0); filter: none; }
    18%  { box-shadow: 0 0 0 4px rgba(var(--sc-cal-confirmed-rgb), .65), 0 0 24px 7px rgba(var(--sc-cal-confirmed-rgb), .6); filter: brightness(1.14) saturate(1.25); }
    100% { box-shadow: 0 0 0 0 rgba(var(--sc-cal-confirmed-rgb), 0); filter: none; }
}
.sc-flash-new { animation: sc-flash-new 1.5s ease-out 2; position: relative; z-index: 6; border-color: var(--sc-cal-confirmed) !important; }
@media (prefers-reduced-motion: reduce) {
    .sc-flash-new { animation: none; box-shadow: 0 0 0 3px rgba(var(--sc-cal-confirmed-rgb), .6); }
}
.fc-rdv-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 5px; line-height: 1.15; min-width: 0; max-width: 100%; }
.fc-rdv-time { font-weight: 600; font-size: 0.82em; opacity: 0.9; flex: 0 0 auto; }
.fc-rdv-name {
    font-weight: 600;
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* Téléphone : même taille que le motif, juste avant lui. */
.fc-rdv-phone {
    display: flex;
    align-items: center;
    gap: 3px;
    margin-top: 2px;
    max-width: 100%;
    font-size: 0.88em;
    font-variant-numeric: tabular-nums;
    color: inherit;
    text-decoration: none;
}
.fc-rdv-phone svg { width: 12px; height: 12px; flex-shrink: 0; }
.fc-rdv-phone span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.fc-rdv-motif {
    font-size: 0.88em;
    line-height: 1.25;
    opacity: 0.85;
    margin-top: 2px;
    max-width: 100%;
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}
/* Indicateur de statut messagerie sur la carte RDV : logo canal + glyphe d'état.
   Forme = canal (WhatsApp/SMS) ; couleur = état (✗ échec · ✓ reçu · ✓✓ lu · ✅ confirmé). */
.fc-rdv-msg { display: inline-flex; align-items: center; gap: 1px; margin-left: 4px; vertical-align: middle; flex-shrink: 0; }
.fc-rdv-msg .msg-ch { width: 13px; height: 13px; }
.fc-rdv-msg .msg-gl { width: 12px; height: 12px; }
/* Les deux couleurs ci-dessous sont celles de WhatsApp. Une marque ne change
   pas de teinte avec le thème de qui la regarde : elles restent en dur. */
.fc-rdv-msg.st-confirm .msg-ch,
.fc-rdv-msg.st-read .msg-ch,
.fc-rdv-msg.st-deliv .msg-ch { color: #25D366; }        /* vert WhatsApp */
.fc-rdv-msg.st-pending .msg-ch { color: var(--sc-msg-pending); }      /* logo grisé tant que non reçu */
.fc-rdv-msg.st-fail .msg-ch { color: var(--sc-cal-danger); }
.fc-rdv-msg.st-confirm .msg-gl { color: var(--sc-cal-confirmed); }      /* ✅ confirmé */
.fc-rdv-msg.st-read .msg-gl { color: #34b7f1; }         /* ✓✓ lu (bleu WhatsApp) */
.fc-rdv-msg.st-deliv .msg-gl { color: var(--sc-msg-delivered); }        /* ✓ reçu */
.fc-rdv-msg.st-pending .msg-gl { color: var(--sc-msg-pending-glyph); }      /* ⏳ en attente de réception */
.fc-rdv-msg.st-fail .msg-gl { color: var(--sc-cal-danger); }         /* ✗ échec */

/* Garde-fou anti-débordement : l'événement lui-même clippe son contenu. */
.fc-timegrid-event, .fc-daygrid-block-event { overflow: hidden; }

/* Toggle format Grille/Liste masqué quand il ne s'applique pas (mois / vues ressources). */
.fc .fc-button.sc-hidden-btn { display: none !important; }

/* ── Popover survol RDV (lecture rapide, read-only) ── */
#sc-rdv-popover {
    position: absolute;
    z-index: 1085;
    display: none;
    min-width: 200px;
    max-width: 280px;
    background: var(--sc-cal-surface);
    color: var(--sc-cal-ink);
    border: 1px solid var(--sc-cal-border);
    border-radius: 8px;
    box-shadow: 0 8px 28px var(--sc-cal-shadow);
    padding: 10px 12px;
    font-size: 0.8rem;
    pointer-events: none;
}
#sc-rdv-popover .scp-head { font-weight: 700; font-size: 0.9rem; margin-bottom: 1px; }
#sc-rdv-popover .scp-time { color: var(--sc-cal-muted); font-size: 0.78rem; margin-bottom: 6px; }
#sc-rdv-popover .scp-confirm { color: var(--sc-cal-confirmed); font-weight: 600; }
#sc-rdv-popover .scp-row { display: flex; align-items: baseline; gap: 8px; padding: 1px 0; }
#sc-rdv-popover .scp-k { color: var(--sc-cal-muted); min-width: 68px; flex-shrink: 0; }
#sc-rdv-popover .scp-v { font-weight: 500; word-break: break-word; }
#sc-rdv-popover .scp-ic { width: 14px; height: 14px; flex-shrink: 0; color: var(--sc-cal-link); }
#sc-rdv-popover .scp-phone { margin-top: 5px; align-items: center; gap: 5px; }
#sc-rdv-popover .scp-phone .scp-v { color: var(--sc-cal-link); font-weight: 600; font-variant-numeric: tabular-nums; }

/* ═══════════════════════════════════════════════════════════════════════
   AGENDA — responsive tablette / phone
   ═══════════════════════════════════════════════════════════════════════ */
/* Cibles tactiles : boutons FC ≥ 40px sur pointeur grossier (doigt), chairside tablette. */
@media (pointer: coarse) {
    .fc .fc-button { min-height: 40px; }
    .fc-timegrid-event, .fc-daygrid-block-event { min-height: 30px; }
}
/* Panneau « En attente » en OVERLAY (tablette/phone) au lieu de collé 280px. */
@media (max-width: 991.98px) {
    #agenda-container { gap: 0 !important; }
    #booking-panel {
        position: fixed; top: 0; right: 0; bottom: 0; left: auto;
        width: 320px; max-width: 88vw; max-height: 100vh; flex: none !important;
        z-index: 1060; border-radius: 0 !important; overflow-y: auto;
        box-shadow: -6px 0 24px var(--sc-cal-shadow-drawer);
    }
}
/* Phone : toolbar qui wrap proprement + événements/titre compacts mais lisibles. */
@media (max-width: 767.98px) {
    .fc .fc-toolbar.fc-header-toolbar { flex-wrap: wrap; gap: 0.35rem; margin-bottom: 0.5rem; }
    .fc .fc-toolbar-chunk { display: flex; flex-wrap: wrap; align-items: center; gap: 0.25rem; }
    .fc .fc-toolbar-title { font-size: 0.95rem; white-space: nowrap; }
    .fc-rdv-name { font-size: 0.82rem; }
    .fc-rdv-motif, .fc-rdv-phone { font-size: 0.78rem; }
    /* Semaine : colonnes ≥120px (dayMinWidth) → scroll horizontal, RDV lisibles. Entête compact. */
    .fc .fc-col-header-cell-cushion { font-size: 0.68rem; padding: 2px 2px; }
    .fc .fc-timegrid-axis-cushion, .fc .fc-timegrid-slot-label-cushion { font-size: 0.66rem; }

    /* Modal d'ajout : footer « Enregistrer » FIXE (sticky) → pas besoin de scroller pour valider.
       Le corps (tab-content) scrolle, l'entête reste en haut, le footer reste en bas. */
    #addModal .modal-content { max-height: calc(100vh - 1rem); max-height: calc(100dvh - 1rem); }
    #addModal .tab-content { overflow-y: auto; flex: 1 1 auto; min-height: 0; }
    #addModal .modal-footer {
        position: sticky; bottom: 0;
        background: var(--bs-modal-bg, #fff);
        z-index: 3; box-shadow: 0 -2px 10px var(--sc-cal-shadow-soft);
    }
}

/* ── FullCalendar ── */
.fc { font-size: 0.85rem; }
.fc .fc-toolbar-title { font-size: 1.1rem; }
.fc .fc-button { font-size: 0.8rem; padding: 0.25rem 0.5rem; }
.fc .fc-event { cursor: pointer; border-radius: 4px; }
.fc .fc-timegrid-slot { height: var(--sc-slot-height, 34px); }
/* ═══════════════════════════════════════════════════════════════════════
   AGENDA — design system FullCalendar (toolbar, boutons, grille)
   Layout : standard, dans #page_layout comme les autres pages
   ═══════════════════════════════════════════════════════════════════════ */
#agenda-container {
    background: var(--sc-cal-surface);
}
#calendar {
    min-height: 600px;
}

/* ─── Header toolbar : fond doux, séparation claire, 1 seule rangée ─── */
.fc .fc-toolbar.fc-header-toolbar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: nowrap;
    gap: 0.35rem;
    margin-bottom: 0.5rem;
    padding: 0.4rem 0.6rem;
    background: var(--sc-cal-panel);
    border: 1px solid var(--sc-cal-border);
    border-radius: 8px;
}
.fc .fc-toolbar-chunk {
    display: flex;
    align-items: center;
    gap: 0.3rem;
    flex-wrap: nowrap;
}
.fc .fc-toolbar-title {
    font-size: 1.05rem;
    font-weight: 600;
    color: var(--sc-cal-ink-2);
    text-transform: capitalize;
}

/* ─── Boutons FullCalendar : reset + style cohérent ─── */
.fc .fc-button {
    background: var(--sc-cal-surface);
    border: 1px solid var(--sc-cal-border-2);
    color: var(--sc-cal-control-ink);
    font-weight: 500;
    font-size: 0.85rem;
    padding: 0.3rem 0.7rem;
    border-radius: 6px;
    text-transform: none;
    box-shadow: none;
    transition: background 0.15s, border-color 0.15s, color 0.15s;
}
.fc .fc-button:hover {
    background: var(--sc-cal-hover);
    border-color: var(--sc-cal-faint);
    color: var(--sc-cal-ink-2);
}
.fc .fc-button:focus,
.fc .fc-button:not(:disabled):focus {
    box-shadow: 0 0 0 3px rgba(var(--bs-primary-rgb), .15);
}

/* État actif (vue sélectionnée) */
.fc .fc-button-primary:not(:disabled).fc-button-active,
.fc .fc-button-primary:not(:disabled):active {
    background: var(--sc-cal-active-bg);
    border-color: var(--sc-cal-active-bg);
    color: var(--sc-cal-active-ink);
    box-shadow: none;
}

/* prev/next : icon-only compacts */
.fc .fc-prev-button,
.fc .fc-next-button {
    padding: 0.3rem 0.55rem;
}

/* "Today" — discret outline */
.fc .fc-today-button {
    background: var(--sc-cal-surface);
    color: var(--sc-cal-control-ink);
    border-color: var(--sc-cal-border-2);
    text-transform: capitalize;
}
.fc .fc-today-button:disabled {
    background: var(--sc-cal-hover);
    color: var(--sc-cal-faint);
    border-color: var(--sc-cal-border);
}

/* Custom actions — primary "+ RDV", outline pour les autres */
.fc .fc-newRdv-button {
    background: var(--bs-primary);
    border-color: var(--bs-primary);
    color: #fff;
    font-weight: 600;
}
.fc .fc-newRdv-button:hover {
    background: var(--bs-primary);
    border-color: var(--bs-primary);
    color: #fff;
    filter: brightness(0.92);
}
.fc .fc-newEvent-button,
.fc .fc-holidays-button {
    /* style outline — déjà hérité par .fc-button default ci-dessus */
}
/* Icon-only buttons : carré compact */
.fc .fc-msgStatus-button,
.fc .fc-configAgenda-button {
    padding: 0.35rem 0.55rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.fc .fc-msgStatus-button svg,
.fc .fc-configAgenda-button svg {
    display: block;
}

/* Doctor select inline dans la toolbar FC — étroit pour rentrer dans la même ligne */
.fc-doctor-select {
    width: auto !important;
    max-width: 160px;
    min-width: 130px;
    height: 30px;
    padding: 0.2rem 1.6rem 0.2rem 0.5rem;
    font-size: 0.8rem;
    border: 1px solid var(--sc-cal-border-2);
    border-radius: 6px;
    color: var(--sc-cal-control-ink);
    background-color: var(--sc-cal-surface);
}
.fc-doctor-select:focus {
    border-color: var(--sc-cal-faint);
    box-shadow: 0 0 0 3px rgba(var(--bs-primary-rgb), .15);
    outline: none;
}

/* ─── Grid : lignes claires + hiérarchisées ─── */
.fc-theme-standard .fc-scrollgrid {
    border-color: var(--sc-cal-faint);
    border-radius: 6px;
    overflow: hidden;
}
.fc-theme-standard td,
.fc-theme-standard th {
    border-color: var(--sc-cal-border-2);
}
/* Séparation verticale entre jours bien marquée */
.fc-theme-standard .fc-timegrid-col,
.fc-theme-standard .fc-col-header-cell,
.fc-theme-standard .fc-day {
    border-left-color: var(--sc-cal-border-2);
}
/* Heure pleine : ligne nette · sous-slots : ligne fine grise.
   Hauteur pilotée par la config (var posée sur #calendar) → confort ↔ densité. */
.fc .fc-timegrid-slot {
    height: var(--sc-slot-height, 34px);
    border-top-color: var(--sc-cal-border-2);
}
.fc .fc-timegrid-slot-lane.fc-timegrid-slot-minor,
.fc .fc-timegrid-slot-label.fc-timegrid-slot-minor {
    border-top-style: dashed;
    border-top-color: var(--sc-cal-border);
}

/* En-têtes de colonnes (lun. 11/05) */
.fc .fc-col-header-cell {
    background: var(--sc-cal-panel);
}
.fc .fc-col-header-cell-cushion {
    font-weight: 600;
    color: var(--sc-cal-ink-3);
    font-size: 0.82rem;
    padding: 0.5rem 0.3rem;
    text-transform: capitalize;
}

/* Heures de la colonne gauche */
.fc .fc-timegrid-slot-label-cushion,
.fc .fc-timegrid-axis-cushion {
    color: var(--sc-cal-muted);
    font-size: 0.78rem;
    font-weight: 500;
}
.fc .fc-timegrid-axis-frame {
    background: var(--sc-cal-panel-2);
}

/* Aujourd'hui : bandeau bleu très subtil */
.fc .fc-day-today {
    background: var(--sc-cal-today) !important;
}
.fc .fc-day-today .fc-col-header-cell-cushion {
    color: var(--sc-cal-link-2);
}

/* Weekend : gris très léger (au lieu du jaune saturé) */
.fc .fc-day-sat,
.fc .fc-day-sun {
    background: var(--sc-cal-panel-2);
}

/* Indicateur "maintenant" : rouge épais */
.fc-direction-ltr .fc-timegrid-now-indicator-arrow {
    border-color: var(--sc-cal-danger-2);
}
.fc .fc-timegrid-now-indicator-line {
    border-color: var(--sc-cal-danger-2);
    border-width: 2px;
}

/* All modals scrollable */
.modal-dialog-scrollable .modal-body {
    max-height: 70vh;
    overflow-y: auto;
}

/* Pro table hover */
.table-hover tbody tr:hover {
    background-color: rgba(var(--bs-primary-rgb), 0.04) !important;
}

@media (max-width: 991px) {
    .content-inner {
        padding-left: 0.5rem !important;
        padding-right: 0.5rem !important;
    }
}

/* Body text: noir au lieu du gris Hope UI */
:root,
[data-bs-theme="light"] {
    --bs-body-color: #232328;
    --bs-body-color-rgb: 35, 35, 40;
}

/* ────────────────────────────────────────────────────────────
   Odontogramme — anatomique (Odontogram) + compact (sélecteur)
   Portage depuis Endo.tn/frontend — CSS adapté Bootstrap/Plus
──────────────────────────────────────────────────────────── */
.sc-odonto { --sc-tooth-gold: #9a7230; --sc-tooth-green: #1a5c35; --sc-tooth-accent: #c4974a; --sc-tooth-primary: #0d6efd; }
.sc-odonto svg { max-width: 100%; width: auto !important; height: 100% !important; max-height: 100%; display: block; margin: 0 auto; user-select: none; touch-action: manipulation; color: #6c757d; }
.sc-odonto { display: flex; align-items: center; justify-content: center; }
/* Hauteur de l'odontogramme : la règle ci-dessus impose height:100% au SVG, donc
   le conteneur DOIT avoir une hauteur. Plutôt qu'un vh magique, on la dérive de
   la largeur via le rapport du viewBox — le schéma reste proportionné à toutes
   les tailles, du modal étroit à la page pleine. */
/* Hauteur DÉFINIE, pas un rapport d'aspect. La règle ci-dessus impose
   height:100% au SVG : avec un aspect-ratio, la hauteur du conteneur se
   déduirait de sa largeur pendant que la largeur du SVG se déduit de la
   hauteur — la boucle empêche le navigateur de centrer correctement.
   Une hauteur ferme la boucle : le SVG s'y ajuste en largeur (width:auto)
   et le flex du conteneur le centre. */
.sc-odonto--arch { height: 52vh; }
.sc-odonto--row  { height: 20vh; }
/* Wrapper transparent pour que la SVG cloée depuis <template> soit enfant direct de .sc-odonto en layout */
.sc-odonto [data-svg-from] { display: contents; }
.sc-odonto .tooth { cursor: pointer; transition: color 120ms; pointer-events: bounding-box; }
.sc-odonto .tooth path:not(.tooth-fill) { fill: transparent; pointer-events: all; }
.sc-odonto .tooth .tooth-fill { pointer-events: all; opacity: 0.08; transition: opacity 120ms, fill 120ms; }
.sc-odonto .tooth:hover .tooth-fill { opacity: 0.25; }
.sc-odonto .tooth:hover { color: var(--sc-tooth-accent); }

/* Consultation réalisée — gold */
.sc-odonto .tooth.has-cslt { color: var(--sc-tooth-accent); }
.sc-odonto .tooth.has-cslt .tooth-fill { fill: var(--sc-tooth-accent); opacity: .22; }

/* Traitement générique — vert */
.sc-odonto .tooth.has-trt { color: var(--sc-tooth-green); }
.sc-odonto .tooth.has-trt .tooth-fill { fill: var(--sc-tooth-green); opacity: .25; }

/* Sous-types traitement */
.sc-odonto .tooth.has-trt-endo { color: #1a5c55; }
.sc-odonto .tooth.has-trt-endo .tooth-fill { fill: #1a5c55; opacity: .28; }
.sc-odonto .tooth.has-trt-cons { color: #4a7f45; }
.sc-odonto .tooth.has-trt-cons .tooth-fill { fill: #4a7f45; opacity: .28; }
.sc-odonto .tooth.has-trt-chir { color: #7a1f1f; }
.sc-odonto .tooth.has-trt-chir .tooth-fill { fill: #7a1f1f; opacity: .28; }

/* Consult + Traitement */
.sc-odonto .tooth.has-both { color: var(--sc-tooth-accent); }
.sc-odonto .tooth.has-both .tooth-fill { fill: var(--sc-tooth-green); opacity: .45; }

/* Dent active (focus courant) — bleu */
.sc-odonto .tooth.active { color: var(--sc-tooth-primary); }
.sc-odonto .tooth.active .tooth-fill { fill: var(--sc-tooth-primary); opacity: .22; }

/* Sélection multi (filtre / picker formulaire) — gold bord épais + fill visible */
.sc-odonto .tooth.tooth-sel { color: var(--sc-tooth-gold); }
.sc-odonto .tooth.tooth-sel path[stroke] { stroke-width: 3; stroke: var(--sc-tooth-gold); }
.sc-odonto .tooth.tooth-sel .tooth-fill { fill: var(--sc-tooth-gold); opacity: .55; }

/* Lecture seule */
.sc-odonto.readonly .tooth { cursor: default; }
.sc-odonto.readonly .tooth:hover { color: currentColor; }

/* ─── Dentition lactéale (boutons simples) ─── */
.sc-decid { display: flex; flex-direction: column; gap: 4px; padding: 6px 4px; }.sc-decid-tooth {
    min-width: 28px; height: 30px; padding: 2px 4px;
    border: 1.5px solid #dee2e6; background: #fff; color: #495057;
    border-radius: 6px; font-size: 0.75rem; font-weight: 600;
    cursor: pointer; transition: all 120ms;
    display: inline-flex; align-items: center; justify-content: center;
}
.sc-decid-tooth:hover { border-color: #c4974a; color: #9a7230; background: rgba(196,151,74,0.08); }
.sc-decid-tooth.tooth-sel {
    border-color: #9a7230; background: #9a7230; color: #fff; border-width: 2px;
}.sc-decid.readonly .sc-decid-tooth { cursor: default; }
.sc-decid.readonly .sc-decid-tooth:hover { border-color: #dee2e6; color: #495057; background: #fff; }.sc-decid-wrap { overflow: hidden; transition: max-height 200ms ease; }
.sc-decid-wrap.collapsed { max-height: 0; }
.sc-decid-wrap.expanded { max-height: 120px; }

/* ══════════════════════════════════════════════════════════════
 * Odontogram — display states (status + findings)
 * ══════════════════════════════════════════════════════════════ */
/* Status (physique) */
.sc-odonto .tooth.st-absent .tooth-fill { fill: #adb5bd; opacity: 0.5; }
.sc-odonto .tooth.st-absent { color: #adb5bd; opacity: 0.6; }
.sc-odonto .tooth.st-absent path:not(.tooth-fill) { stroke-dasharray: 3,2; }
.sc-odonto .tooth.st-implant .tooth-fill { fill: #6c757d; opacity: 0.35; }
.sc-odonto .tooth.st-implant { color: #495057; }
.sc-odonto .tooth.st-pontic .tooth-fill { fill: #8B5CF6; opacity: 0.3; }
.sc-odonto .tooth.st-pontic { color: #6D28D9; }
.sc-odonto .tooth.st-unerupted { opacity: 0.35; }
.sc-odonto .tooth.st-agenesis { opacity: 0.25; }

/* Findings — pathologies (rouges/oranges) */
.sc-odonto .tooth.has-caries .tooth-fill { fill: #EF4444; opacity: 0.35; }
.sc-odonto .tooth.has-caries { color: #B91C1C; }
.sc-odonto .tooth.has-apical { color: #DC2626; }
.sc-odonto .tooth.has-apical .tooth-fill { fill: #DC2626; opacity: 0.3; }
.sc-odonto .tooth.has-fracture .tooth-fill { fill: #F59E0B; opacity: 0.35; }
.sc-odonto .tooth.has-fracture { color: #D97706; }
.sc-odonto .tooth.has-mobility { color: #F59E0B; }

/* Findings — traitements existants (bleus/verts/violets) */
.sc-odonto .tooth.has-restore .tooth-fill { fill: #3B82F6; opacity: 0.35; }
.sc-odonto .tooth.has-restore { color: #1D4ED8; }
.sc-odonto .tooth.has-crown .tooth-fill { fill: #8B5CF6; opacity: 0.4; }
.sc-odonto .tooth.has-crown { color: #6D28D9; }
.sc-odonto .tooth.has-endo .tooth-fill { fill: #DC2626; opacity: 0.3; }
.sc-odonto .tooth.has-endo { color: #B91C1C; }

/* Combo carie + restauration = tinted */
.sc-odonto .tooth.has-caries.has-restore .tooth-fill { fill: url(#caries-restore-gradient); opacity: 0.5; }

/* Décision (contour coloré) */
.sc-odonto .tooth.dec-treat path:not(.tooth-fill) { stroke: #3B82F6; stroke-width: 2.5; }
.sc-odonto .tooth.dec-extract path:not(.tooth-fill) { stroke: #DC2626; stroke-width: 2.5; stroke-dasharray: 4,2; }
.sc-odonto .tooth.dec-watch path:not(.tooth-fill) { stroke: #F59E0B; stroke-width: 2.5; }

/* Click hint en mode display */
.sc-odonto[data-mode="display"] .tooth { cursor: pointer; }
.sc-odonto[data-mode="display"] .tooth:hover .tooth-fill { opacity: 0.55; }

/* ══════════════════════════════════════════════════════════════
 * Odontogram chart (Premium-style) — PNG base + SVG overlay
 * ══════════════════════════════════════════════════════════════ */
.od-arch-wrap {
    display: grid;
    grid-template-columns: repeat(16, auto);
    gap: 0;
    position: relative;
    width: max-content;
    max-width: 100%;
    margin: 0 auto;
    --od-h-buccal: 120px;
}
.od-arch-wrap .od-midline {
    grid-template-columns: subgrid;
}
/* Mode compact (dans la carte du tab soins) */
.od-schema-compact .od-arch-wrap { --od-h-buccal: 70px; }
.od-schema-compact .od-midline { padding: 2px 0; font-size: 0.6rem; }
/* Hub v2 — card odontogramme : hauteur intermédiaire (lisible mais compact) */
#card-odontogramme .od-arch-wrap { --od-h-buccal: 90px; }
/* Séparation occlusale : invisible — la vraie gencive est rendue par PanoramicOverlay SVG */
#card-odontogramme .od-occlusal-sep {
    height: 4px;
    background: transparent;
    margin: 0;
}
#card-odontogramme .od-row { line-height: 0; }
/* Offcanvas constats : identique au customizer mais sans padding haut */
#odonto-findings-offcanvas .offcanvas-header { padding-top: 0; padding-bottom: 0.5rem; }
#odonto-findings-offcanvas .offcanvas-body { padding-top: 0.5rem; }
#odonto-findings-offcanvas .odp-selected-header { margin-top: 0; }
#card-odontogramme #odonto-chart-wrap { padding: 0 !important; }
.od-row { display: contents; }
.od-row-buccal-up > .od-cell { align-items: flex-end; }
.od-row-buccal-lo > .od-cell { align-items: flex-start; }
/* Visibilité des rangées selon le mode de dentition (display:contents → none) */
.od-schema[data-dentition-mode="primary"] .od-row-buccal-up,
.od-schema[data-dentition-mode="primary"] .od-row-buccal-lo,
.od-schema[data-dentition-mode="permanent"] .od-row-decid-up,
.od-schema[data-dentition-mode="permanent"] .od-row-decid-lo {
    display: none !important;
}
.od-schema[data-dentition-mode="primary"] .od-row-buccal-up > .od-cell,
.od-schema[data-dentition-mode="primary"] .od-row-buccal-lo > .od-cell,
.od-schema[data-dentition-mode="permanent"] .od-row-decid-up > .od-cell,
.od-schema[data-dentition-mode="permanent"] .od-row-decid-lo > .od-cell {
    display: none !important;
}
.od-cell {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    margin: 0;
    background: transparent;
    border: none;
    border-radius: 0;
    cursor: pointer;
    transition: background 0.15s;
    position: relative;
    height: var(--od-h-buccal);
    overflow: hidden;
    /* Indispensable sur mobile : sans cela le browser consomme les touch
       events pour scroll/zoom et les pointerdown/move/up ne sont pas
       dispatchés sur la cellule → click sur dent ne marche pas. */
    touch-action: none;
    /* Désactive aussi le tap highlight bleu/gris de iOS Safari et le 300ms delay */
    -webkit-tap-highlight-color: transparent;
}
.od-cell:hover { background: rgba(var(--bs-primary-rgb), 0.08); }
.od-cell-active { background: rgba(var(--bs-primary-rgb), 0.14); box-shadow: inset 0 -2px 0 var(--bs-primary); }
.od-cell-empty { cursor: default; opacity: 0.15; pointer-events: none; }
.od-buccal-img {
    max-height: 100%;
    max-width: 100%;
    width: auto;
    height: 100%;
    object-fit: contain;
    pointer-events: none;
    user-select: none;
}
.od-svg-overlay {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 2;
}
.od-svg-overlay svg {
    width: 100%;
    height: 100%;
    display: block;
}
/* Midline : rangée des numéros entre les 2 arcades */
.od-midline {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: repeat(16, 1fr);
    padding: 6px 0;
    border-top: 1px solid var(--bs-border-color);
    border-bottom: 1px solid var(--bs-border-color);
    background: var(--bs-light);
    font-size: 0.7rem;
    font-weight: 600;
    color: var(--bs-secondary);
}
.od-midline-num {
    text-align: center;
    border-right: 1px solid var(--bs-border-color-subtle);
}
.od-midline-num:last-child { border-right: none; }
/* Décision — outline dashed coloré + tint léger (style cohérent avec sélection) */
.od-cell[class*="dec-"] { border-radius: 2px; }
.od-cell.dec-treat   { outline: 2px dashed #3B82F6; outline-offset: -1px; background: rgba(59,130,246,0.10); }
.od-cell.dec-extract { outline: 2px dashed #DC2626; outline-offset: -1px; background: rgba(220,38,38,0.10); }
.od-cell.dec-watch   { outline: 2px dashed #F59E0B; outline-offset: -1px; background: rgba(245,158,11,0.10); }
.od-cell.dec-refer   { outline: 2px dashed #8B5CF6; outline-offset: -1px; background: rgba(139,92,246,0.10); }
.od-cell.dec-review  { outline: 2px dashed #6B7280; outline-offset: -1px; background: rgba(107,114,128,0.10); }

/* ══════════════════════════════════════════════════════════════
 * Overlays : panoramic (bridges/mobility/splints) + drawing (% coords)
 * ══════════════════════════════════════════════════════════════ */
.od-arch-wrap { position: relative; }
.od-panoramic-overlay {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
    z-index: 4;
}
.od-drawing-overlay {
    position: absolute;
    /* left/top/width/height posés dynamiquement en JS pour coller à chaque rangée */
    pointer-events: none;  /* désactivé par défaut */
    z-index: 5;
    touch-action: none;
    overflow: hidden;  /* strokes clippés aux bounds de l'overlay */
}
.od-drawing-overlay.active {
    pointer-events: auto;  /* activé en mode drawing */
    cursor: crosshair;
    background: rgba(59,130,246,0.03);  /* légère teinte pour montrer la zone active */
    outline: 1px dashed rgba(59,130,246,0.3);
}

/* Toolbar drawing — flottante en bas du chart */
.od-drawing-toolbar {
    position: absolute;
    bottom: 8px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 10;
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 4px;
    background: rgba(255,255,255,0.95);
    border: 1px solid var(--bs-border-color);
    border-radius: 6px;
    box-shadow: 0 4px 12px rgba(0,0,0,0.08);
    font-size: 0.75rem;
    backdrop-filter: blur(6px);
}
.od-drawing-toolbar .od-tool-panel {
    display: none;
    align-items: center;
    gap: 4px;
    margin-left: 4px;
}
.od-drawing-toolbar.active .od-tool-panel { display: flex; }
.od-drawing-toolbar [data-tool-toggle] { padding: 4px 8px; }
.od-drawing-toolbar.active [data-tool-toggle] {
    background: var(--bs-primary);
    color: #fff;
    border-radius: 4px;
}
.od-tool-btn {
    background: transparent;
    border: none;
    padding: 4px 6px;
    font-size: 0.9rem;
    cursor: pointer;
    color: var(--bs-body-color);
    border-radius: 4px;
}
.od-tool-btn:hover { background: rgba(0,0,0,0.05); }
.od-tool-btn.active { background: var(--bs-primary); color: #fff; }
.od-tool-sep { width: 1px; height: 16px; background: var(--bs-border-color); margin: 0 2px; }
.od-color {
    width: 18px; height: 18px;
    border: 2px solid transparent;
    border-radius: 50%;
    padding: 0;
    cursor: pointer;
}
.od-color.active { border-color: var(--bs-primary); transform: scale(1.15); }
.od-width {
    width: 22px; height: 22px;
    background: transparent;
    border: 1px solid var(--bs-border-color);
    border-radius: 4px;
    font-size: 1rem;
    cursor: pointer;
    line-height: 1;
    padding: 0;
}
.od-width.active { border-color: var(--bs-primary); background: rgba(13,110,253,0.1); }

/* Panoramic overlay text (mobility) */
.od-panoramic-overlay text { user-select: none; }

/* ══════════════════════════════════════════════════════════════
 * Mode fullscreen drawing — chart en plein écran avec backdrop
 * ══════════════════════════════════════════════════════════════ */
.od-schema.od-drawing-fullscreen {
    position: fixed;
    inset: 0;
    z-index: 1060;
    background: rgba(0, 0, 0, 0.85);
    padding: 40px 20px 80px;
    display: flex;
    align-items: center;
    justify-content: center;
}
.od-schema.od-drawing-fullscreen .od-arch-wrap {
    width: auto;
    max-width: 98vw;
    max-height: 92vh;
    margin: 0 auto;
    /* !important pour battre la spécificité de #card-odontogramme .od-arch-wrap */
    --od-h-buccal: 45vh !important;  /* 2 rangées : 90vh total */
}
/* Mixte : 4 rangées visibles → 22vh × 4 = 88vh */
.od-schema.od-drawing-fullscreen[data-dentition-mode="mixed"] .od-arch-wrap {
    --od-h-buccal: 22vh !important;
}
/* Réduit le padding outer en fullscreen pour libérer plus d'espace vertical */
.od-schema.od-drawing-fullscreen {
    padding: 10px 10px 70px;
}
.od-schema.od-drawing-fullscreen .od-drawing-toolbar {
    position: fixed;
    bottom: 20px;
    z-index: 1061;
    background: rgba(255,255,255,1);
    box-shadow: 0 8px 24px rgba(0,0,0,0.3);
}
/* Bouton "Fermer fullscreen" en haut à droite */
.od-fullscreen-close {
    position: fixed;
    top: 16px;
    right: 16px;
    z-index: 1062;
    background: rgba(255,255,255,0.95);
    color: #000;
    border: none;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    cursor: pointer;
    font-size: 1.2rem;
    box-shadow: 0 2px 8px rgba(0,0,0,0.2);
    display: none;
    align-items: center;
    justify-content: center;
}
.od-schema.od-drawing-fullscreen ~ .od-fullscreen-close,
.od-schema.od-drawing-fullscreen .od-fullscreen-close { display: inline-flex; }
.od-schema.od-drawing-fullscreen .od-fullscreen-close {
    position: fixed; top: 16px; right: 16px;
}

/* Cellule sélectionnée — check vert au centre de la dent (n'interfère pas avec décision) */
.od-cell.od-cell-selected {
    position: relative;
}
.od-cell.od-cell-selected::after {
    content: '';
    position: absolute;
    top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    width: 18px; height: 18px;
    background: var(--bs-primary);
    border: 2px solid #fff;
    border-radius: 50%;
    z-index: 6;
    box-shadow: 0 2px 4px rgba(0,0,0,0.20);
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'><polyline points='5 12 10 17 19 7'/></svg>");
    background-size: 70%;
    background-position: center;
    background-repeat: no-repeat;
    pointer-events: none;
}

/* ─── Pédiatrie : modes de dentition ─────────────────────────────────── */

/* Rangées lait : 10 dents (5 par hémi-arcade) positionnées dans le grid 16-cols
   du parent .od-arch-wrap. Les codes lait correspondent aux positions perm 1-5
   donc on les place sur les colonnes grid 4 à 13. */
.od-row-decid-up,
.od-row-decid-lo {
    display: contents;
}
.od-row-decid-up > .od-cell:nth-child(1),
.od-row-decid-lo > .od-cell:nth-child(1) {
    grid-column-start: 4;
}

/* Force chaque rangée perm à commencer à la colonne 1 → garantit qu'elle
   occupe sa propre ligne du grid, même si la rangée lait précédente laisse
   des colonnes 14-16 vides (sinon le grid auto-flow remplirait ces trous). */
.od-row-buccal-up > .od-cell:nth-child(1),
.od-row-buccal-lo > .od-cell:nth-child(1) {
    grid-column-start: 1;
}

/* Séparation occlusale : ligne mince entre les 2 arcades, sans numéros */
.od-occlusal-sep {
    grid-column: 1 / -1;
    height: 1px;
    background: var(--bs-border-color);
    margin: 6px 0;
}

/* Hint retard d'éruption — petit ! orange en coin */
.od-cell.od-cell-eruption-delayed::before {
    content: "!";
    position: absolute;
    top: 2px; left: 2px;
    background: #f97316;
    color: white;
    font-size: 0.6rem;
    font-weight: 700;
    line-height: 1;
    width: 12px; height: 12px;
    text-align: center;
    border-radius: 50%;
    pointer-events: none;
    z-index: 4;
}

/* ══════════════════════════════════════════════════════════════
 * Odontogram — UI chips (Premium-inspired)
 * ══════════════════════════════════════════════════════════════ */
.odp-section {
    margin-bottom: 12px;
    padding-bottom: 10px;
    border-bottom: 1px solid var(--bs-border-color-subtle);
}
.odp-section:last-child { border-bottom: none; margin-bottom: 0; }
.odp-section-title {
    font-size: 0.65rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--bs-secondary);
    margin-bottom: 6px;
    display: flex;
    align-items: center;
    gap: 6px;
}
.odp-chip-row {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    align-items: center;
}
.odp-chip {
    padding: 4px 10px;
    font-size: 0.75rem;
    font-weight: 600;
    border: 1.5px solid var(--bs-border-color);
    border-radius: 4px;
    background: #fff;
    color: var(--bs-body-color);
    cursor: pointer;
    transition: all 0.12s;
    user-select: none;
    line-height: 1.2;
}
.odp-chip:hover {
    border-color: var(--bs-primary);
    color: var(--bs-primary);
    background: rgba(var(--bs-primary-rgb), 0.06);
}
.odp-chip.active {
    background: var(--bs-primary);
    color: #fff;
    border-color: var(--bs-primary);
}
/* Décision : bordure + texte colorés au repos, fond plein quand active */
.odp-chip--danger { border-color: #EF4444; color: #EF4444; }
.odp-chip--danger:hover { border-color: #EF4444; color: #EF4444; background: rgba(239,68,68,0.06); }
.odp-chip--danger.active { background: #EF4444; border-color: #EF4444; color: #fff; }
.odp-chip--warning { border-color: #F59E0B; color: #B45309; }
.odp-chip--warning:hover { border-color: #F59E0B; color: #B45309; background: rgba(245,158,11,0.08); }
.odp-chip--warning.active { background: #F59E0B; border-color: #F59E0B; color: #fff; }
.odp-chip--success { border-color: #10B981; color: #065F46; }
.odp-chip--success:hover { border-color: #10B981; color: #065F46; background: rgba(16,185,129,0.08); }
.odp-chip--success.active { background: #10B981; border-color: #10B981; color: #fff; }
.odp-chip--purple { border-color: #8B5CF6; color: #6D28D9; }
.odp-chip--purple:hover { border-color: #8B5CF6; color: #6D28D9; background: rgba(139,92,246,0.08); }
.odp-chip--purple.active { background: #8B5CF6; border-color: #8B5CF6; color: #fff; }
.odp-chip--info { border-color: #3B82F6; color: #1D4ED8; }
.odp-chip--info:hover { border-color: #3B82F6; color: #1D4ED8; background: rgba(59,130,246,0.08); }
.odp-chip--info.active { background: #3B82F6; border-color: #3B82F6; color: #fff; }
.odp-chip--neutral { border-color: #6B7280; color: #4B5563; }
.odp-chip--neutral:hover { border-color: #6B7280; color: #4B5563; background: rgba(107,114,128,0.08); }
.odp-chip--neutral.active { background: #6B7280; border-color: #6B7280; color: #fff; }

/* Hiérarchie principale : gros boutons État / Pathologie / Traitement / Tests */
.odp-main-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 10px;
    margin-bottom: 8px;
}
.odp-main-grid.odp-main-grid-4 { grid-template-columns: repeat(4, 1fr); }
/* Bouton Tests désactivé quand >1 dent */
.odp-main-btn:disabled,
.odp-main-btn[disabled] {
    opacity: 0.45;
    cursor: not-allowed;
    pointer-events: auto;
}
/* Liste constats existants : 1 row par dent, findings inline, design sobre */
.odp-existing-tooth-row {
    display: flex; align-items: flex-start; gap: 10px;
    padding: 6px 0;
    border-bottom: 1px solid #f3f4f6;
    font-size: 0.78rem;
    color: #1F2937;
}
.odp-existing-tooth-row:last-child { border-bottom: none; }
.odp-existing-tooth-num {
    flex: 0 0 auto; font-weight: 700;
    background: #F3F4F6; color: #1F2937;
    padding: 2px 8px; border-radius: 4px;
    font-size: 0.72rem; line-height: 1.4;
    min-width: 30px; text-align: center;
}
.odp-existing-tooth-items {
    flex: 1; min-width: 0;
    display: flex; flex-wrap: wrap; align-items: center;
    column-gap: 14px; row-gap: 4px;
    line-height: 1.5;
}
.odp-finding-item {
    display: inline-flex; align-items: center; gap: 4px;
    color: #1F2937;  /* override class inheritance */
}
.odp-finding-item.is-pending .odp-finding-label { font-style: italic; color: #b45309; }
.odp-finding-item .odp-finding-label { color: #1F2937; }
.odp-finding-item.is-pending .odp-finding-label { color: #b45309; }
.odp-pending-dot {
    display: inline-block; width: 6px; height: 6px; border-radius: 50%;
    background: #F59E0B;
}

/* Liste constats existants : row compact (single-line, ~22px) */
.odp-existing-row {
    display: flex; align-items: center; gap: 5px;
    padding: 2px 4px; font-size: 0.72rem;
    line-height: 1.3;
    transition: background 0.12s;
}
.odp-existing-row:hover { background: #f9fafb; }
.odp-existing-row.is-pending { background: #fffbeb; }
.odp-existing-row.is-pending .odp-existing-label { font-style: italic; color: #b45309; }
.odp-existing-dot {
    width: 6px; height: 6px; border-radius: 50%;
    background: #d1d5db; flex-shrink: 0;
}
.odp-existing-row.odp-cat-chip--caries .odp-existing-dot { background: #EF4444; }
.odp-existing-row.odp-cat-chip--endo .odp-existing-dot { background: #DC2626; }
.odp-existing-row.odp-cat-chip--fracture .odp-existing-dot { background: #F97316; }
.odp-existing-row.odp-cat-chip--restore .odp-existing-dot { background: #3B82F6; }
.odp-existing-row.odp-cat-chip--prosthetic .odp-existing-dot { background: #8B5CF6; }
.odp-existing-row.odp-cat-chip--perio .odp-existing-dot { background: #10B981; }
.odp-existing-main { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.odp-faces-pill {
    display: inline-block; padding: 0 4px; margin-left: 3px;
    background: #e5e7eb; color: #374151; border-radius: 3px;
    font-size: 0.6rem; font-weight: 600; letter-spacing: 0.04em;
}
.odp-sev-pill {
    display: inline-block; padding: 0 4px; margin-left: 2px;
    background: #fee2e2; color: #b91c1c; border-radius: 3px;
    font-size: 0.6rem; font-weight: 600;
}

/* Sticky save bar : footer de l'offcanvas constats */
.odp-save-bar {
    position: sticky; bottom: 0;
    padding: 12px 16px;
    background: #fff;
    border-top: 1px solid #e5e7eb;
    box-shadow: 0 -4px 12px rgba(0,0,0,0.06);
    z-index: 10;
}
.odp-save-bar.saving #odp-save-btn {
    opacity: 0.6;
    pointer-events: none;
}
.odp-save-pulse {
    width: 8px; height: 8px; border-radius: 50%;
    background: #fff;
    box-shadow: 0 0 0 0 rgba(255,255,255,0.8);
    animation: odp-pulse 1.5s infinite;
}
@keyframes odp-pulse {
    0% { box-shadow: 0 0 0 0 rgba(255,255,255,0.8); }
    70% { box-shadow: 0 0 0 8px rgba(255,255,255,0); }
    100% { box-shadow: 0 0 0 0 rgba(255,255,255,0); }
}
.odp-save-count-pill {
    display: inline-block;
    padding: 2px 8px;
    background: rgba(255,255,255,0.25);
    border-radius: 10px;
    font-size: 0.75rem;
    font-weight: 600;
}

/* Hint quand Tests désactivé (multi-dents) */
.odp-tests-hint {
    padding: 8px 12px;
    background: #fef3c7;
    color: #92400e;
    font-size: 0.72rem;
    border-radius: 4px;
    margin-bottom: 8px;
    border-left: 3px solid #f59e0b;
}

/* Tests cliniques : ligne label + chips alignés */
.odp-test-row {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 4px;
}
.odp-test-label {
    flex: 0 0 60px;
    font-size: 0.7rem;
    font-weight: 600;
    color: #6B7280;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.odp-test-chip {
    min-width: 32px;
    text-align: center;
}
.odp-main-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0.95rem 0.4rem;
    border: 2px solid var(--main-c, #6B7280);
    border-radius: 8px;
    background: #fff;
    color: var(--main-c, #374151);
    font-size: 0.85rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    cursor: pointer;
    transition: all 0.15s;
}
.odp-main-btn:hover { filter: brightness(0.96); }
.odp-main-btn.active { background: var(--main-c, #374151); color: #fff; }
.odp-main-sublist { margin-top: 4px; }

/* Catégories hiérarchiques : 3 gros boutons par section, sublist révélée au click */
.odp-cat-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 8px;
    margin-bottom: 8px;
}
.odp-cat-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0.7rem 0.4rem;
    border: 2px solid var(--cat-c, #6B7280);
    border-radius: 6px;
    background: #fff;
    color: var(--cat-c, #374151);
    font-size: 0.75rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    cursor: pointer;
    transition: all 0.15s;
}
.odp-cat-btn:hover { filter: brightness(0.96); }
.odp-cat-btn.active { background: var(--cat-c, #374151); color: #fff; }
.odp-cat-count { font-weight: 700; opacity: 0.85; }
.odp-cat-sublist { margin-top: 4px; padding-top: 4px; }

/* Chip category buttons (Pathologies/Traitements) — plus costauds */
.odp-cat-chip {
    padding: 6px 12px;
    font-size: 0.8rem;
    font-weight: 600;
    border: 1.5px solid;
    border-radius: 4px;
    background: #fff;
    cursor: pointer;
    transition: all 0.12s;
    position: relative;
}
.odp-cat-chip:hover { filter: brightness(0.97); }
.odp-cat-chip.active {
    color: #fff;
}
.odp-cat-chip--caries { border-color: #EF4444; color: #EF4444; }
.odp-cat-chip--caries.active { background: #EF4444; }
.odp-cat-chip--endo { border-color: #DC2626; color: #DC2626; }
.odp-cat-chip--endo.active { background: #DC2626; }
.odp-cat-chip--fracture { border-color: #F97316; color: #F97316; }
.odp-cat-chip--fracture.active { background: #F97316; }
.odp-cat-chip--restore { border-color: #3B82F6; color: #3B82F6; }
.odp-cat-chip--restore.active { background: #3B82F6; }
.odp-cat-chip--prosthetic { border-color: #8B5CF6; color: #8B5CF6; }
.odp-cat-chip--prosthetic.active { background: #8B5CF6; }
.odp-cat-chip--perio { border-color: #F59E0B; color: #F59E0B; }
.odp-cat-chip--perio.active { background: #F59E0B; }

/* Form expansé sous le chip category */
.odp-finding-form {
    margin-top: 10px;
    padding: 10px;
    border-radius: 6px;
    background: rgba(0,0,0,0.02);
    border-left: 3px solid var(--chip-color, var(--bs-primary));
}
.odp-finding-form-row {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 8px;
}
.odp-finding-form-row:last-child { margin-bottom: 0; }
.odp-finding-form-label {
    font-size: 0.7rem;
    font-weight: 600;
    color: var(--bs-secondary);
    min-width: 70px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
}
.odp-save-btn {
    margin-top: 8px;
    padding: 6px 14px;
    font-size: 0.8rem;
    font-weight: 600;
    border: none;
    border-radius: 4px;
    background: var(--chip-color, var(--bs-primary));
    color: #fff;
    cursor: pointer;
    width: 100%;
}
.odp-save-btn:hover { filter: brightness(1.08); }

/* Selected teeth header */
.odp-selected-header {
    padding: 8px 10px;
    background: rgba(var(--bs-primary-rgb), 0.08);
    border-radius: 4px;
    margin-bottom: 12px;
    display: flex;
    justify-content: space-between;
    align-items: center;
}
.odp-selected-count { font-size: 0.85rem; font-weight: 700; color: var(--bs-primary); }
.odp-selected-teeth { font-size: 0.75rem; color: var(--bs-secondary); }.sc-postit {
    display: inline-flex; flex-direction: column; gap: 2px;
    padding: 4px 8px; border-radius: 4px; font-size: 0.75rem;
    border-left: 3px solid; max-width: 200px;
}
.sc-postit-teeth { font-weight: 600; font-size: 0.7rem; }.sc-postit.c-yellow { background: rgba(245,158,11,.12); border-color: #f59e0b; }
.sc-postit.c-yellow .sc-postit-teeth { color: #f59e0b; }
.sc-postit.c-blue { background: rgba(59,130,246,.12); border-color: #3b82f6; }
.sc-postit.c-blue .sc-postit-teeth { color: #3b82f6; }
.sc-postit.c-green { background: rgba(16,185,129,.12); border-color: #10b981; }
.sc-postit.c-green .sc-postit-teeth { color: #10b981; }
.sc-postit.c-pink { background: rgba(236,72,153,.12); border-color: #ec4899; }
.sc-postit.c-pink .sc-postit-teeth { color: #ec4899; }
.sc-postit.c-orange { background: rgba(249,115,22,.12); border-color: #f97316; }
.sc-postit.c-orange .sc-postit-teeth { color: #f97316; }

/* ══════════════════════════════════════════════════════════════
 * Patient Sidebar Card — composition propre
 * ══════════════════════════════════════════════════════════════ */
.patient-sidebar-card .card-body { font-size: 0.78rem; }

.ps-edit-btn {
    font-size: 0.72rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 4px;
    padding: 4px 8px;
}

/* Section group (Dossier / Coordonnées / Identité / Administratif) */
.ps-section {
    padding: 8px 0;
    border-bottom: 1px solid var(--bs-border-color-subtle);
}
.ps-section:last-child { border-bottom: none; }

.ps-section-header {
    display: flex;
    align-items: center;
    gap: 4px;
    font-size: 0.65rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--bs-primary);
    margin-bottom: 6px;
}
.ps-section-header svg {
    flex-shrink: 0;
    opacity: 0.8;
}

/* Sous-sections (Allergies, Médicaments, Pathologies, Habitudes) */
.ps-subsection {
    margin-bottom: 6px;
}
.ps-subsection:last-child { margin-bottom: 0; }
.ps-subsection-label {
    font-size: 0.6rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--bs-secondary);
    margin-bottom: 2px;
    opacity: 0.75;
}

/* Items — par défaut neutres */
.ps-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 4px;
    padding: 3px 6px;
    border-radius: 3px;
    font-size: 0.78rem;
    line-height: 1.3;
    color: var(--bs-body-color);
    margin-bottom: 2px;
}
.ps-item-empty {
    font-size: 0.72rem;
    color: var(--bs-secondary);
    opacity: 0.6;
    font-style: italic;
    padding: 2px 6px;
}

/* Item à risque — hachure rouge diagonale */
.ps-item-risky {
    background-image: repeating-linear-gradient(
        135deg,
        transparent 0,
        transparent 4px,
        rgba(220, 53, 69, 0.16) 4px,
        rgba(220, 53, 69, 0.16) 8px
    );
    background-color: rgba(220, 53, 69, 0.04);
    border-left: 3px solid #dc3545;
    color: #842029;
    font-weight: 500;
    padding-left: 6px;
}

/* Petit dot rouge pour signaler le risque */
.ps-risk-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: #dc3545;
    box-shadow: 0 0 0 2px rgba(220, 53, 69, 0.2);
    flex-shrink: 0;
}

/* Coordonnées : icon + texte aligné */
.ps-coord {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 2px 0;
    font-size: 0.78rem;
    line-height: 1.3;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}
.ps-coord-icon {
    flex-shrink: 0;
    color: var(--bs-secondary);
    opacity: 0.6;
}
.ps-coord > span {
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Habitudes : badges compacts */
.ps-habit-badge {
    font-size: 0.65rem;
    padding: 2px 6px;
    font-weight: 500;
}

/* Notes section */
.ps-section-notes .ps-notes-text {
    font-size: 0.75rem;
    margin: 0;
    color: var(--bs-body-color);
    background: var(--bs-light);
    padding: 6px 8px;
    border-radius: 4px;
    border-left: 2px solid var(--bs-border-color);
}

/* ════════════════════════════════════════════════════════════════
   Cards Patient (layout v2)
   On utilise Bootstrap au max. Ici uniquement ce qui n'existe pas
   nativement : l'avatar circulaire dimensionné.
   ════════════════════════════════════════════════════════════════ */

.sc-avatar {
    flex-shrink: 0;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-weight: 600;
    line-height: 1;
}
/* Modal xxl : entre modal-xl (1140px) et fullscreen — 88vw large, ~95vh haut */
@media (min-width: 1200px) {
    .modal-xxl { --bs-modal-width: 88vw; max-width: 88vw; }
    .modal-xxl.modal-dialog-scrollable,
    .modal-xxl.modal-dialog-centered { --bs-modal-margin: 0.75rem; }
    .modal-xxl.modal-dialog-scrollable .modal-content { max-height: calc(100vh - 1.5rem); }
}

/* ═══════════════════════════════════════════════════════════════════════
   KANBAN — Salle d'attente (swim lanes par praticien)
   Design system : neutre, sober, inspired by Linear/Notion. Pas d'emoji.
   ═══════════════════════════════════════════════════════════════════════ */
/* ── Les couleurs du tableau du matin, en un seul endroit ──────────────────
 * Le kanban avait déjà six tokens ; les cent soixante autres couleurs étaient
 * écrites à la main, et le thème sombre ne les suivait pas — le tableau restait
 * clair au milieu d'une page noire.
 *
 * Elles vont par PAIRES : un fond discret et l'encre qui se lit dessus. C'est
 * ce couplage qui compte, et c'est lui qu'un remplacement colonne par colonne
 * casse — retourner le fond sans retourner l'encre donne un texte invisible.
 *
 * Les valeurs claires sont exactement les anciennes ; seul le bloc sombre est
 * nouveau. Les tokens vivent sur `:root` et non sur `.kanban-board` : la fiche
 * visiteur et le bandeau de statistiques sont hors du tableau, et ils partagent
 * la même langue.
 */
:root,
[data-bs-theme="light"] {
    --kn-bg: #f6f7f9;                    /* fond du tableau */
    --kn-surface: #fff;                  /* une carte, une fiche */
    --kn-hover: #f1f5f9;
    --kn-border: #e4e6eb;
    --kn-border-soft: #ecedf0;
    --kn-border-2: #cbd5e1;
    --kn-text: #0f172a;
    --kn-text-soft: #64748b;
    --kn-text-muted: #94a3b8;
    --kn-text-2: #475569;
    --kn-text-strong: #1e293b;

    /* Les paires — fond discret, encre qui se lit dessus. */
    --kn-ok-bg: #d1fae5;        --kn-ok-ink: #047857;
    --kn-ok-bg-2: #dcfce7;      --kn-ok-ink-2: #15803d;
    --kn-ok-border: #a7f3d0;    --kn-ok-solid: #16a34a;
    --kn-warn-bg: #fef3c7;      --kn-warn-ink: #b45309;
    --kn-warn-bg-2: #fffbeb;    --kn-warn-ink-2: #92400e;
    --kn-warn-border: #fde68a;  --kn-warn-soft: #fffaf0;
    --kn-bad-bg: #fee2e2;       --kn-bad-ink: #b91c1c;
    --kn-bad-bg-2: #fef2f2;     --kn-bad-soft: #fff7f7;
    --kn-bad-border: #fecaca;   --kn-bad-solid: #dc2626;
    --kn-info-bg: #dbeafe;      --kn-info-ink: #1e40af;
    --kn-info-bg-2: #eff6ff;    --kn-info-border: #93c5fd;

    /* Les accents de colonne : une couleur par étape de la journée. */
    --kn-col-scheduled: #3b82f6;
    --kn-col-arrived: #f59e0b;
    --kn-col-open: #10b981;
    --kn-col-absent: #ef4444;
    --kn-col-cancelled-patient: #f97316;
    --kn-col-cancelled-clinic: #0ea5e9;
    --kn-cell-arrived: #fffbf2;
    --kn-cell-open: #f0fdf6;
    --kn-cell-closed: #f8fafc;

    --kn-visitor-personal: #a855f7;
    --kn-shadow-1: rgba(15, 23, 42, .03);
    --kn-shadow-2: rgba(15, 23, 42, .07);
    --kn-shadow-3: rgba(15, 23, 42, .12);
}

[data-bs-theme="dark"] {
    --kn-bg: #0f172a;
    --kn-surface: #1e293b;
    --kn-hover: #243349;
    --kn-border: #334155;
    --kn-border-soft: #253449;
    --kn-border-2: #475569;
    --kn-text: #f1f5f9;
    --kn-text-soft: #94a3b8;
    --kn-text-muted: #64748b;
    --kn-text-2: #cbd5e1;
    --kn-text-strong: #e2e8f0;

    /* Le fond devient un voile de la couleur, l'encre s'éclaircit : la paire se
       retourne ENSEMBLE, sinon le texte disparaît dans son propre badge. */
    --kn-ok-bg: rgba(16, 185, 129, .18);   --kn-ok-ink: #6ee7b7;
    --kn-ok-bg-2: rgba(22, 163, 74, .22);  --kn-ok-ink-2: #86efac;
    --kn-ok-border: rgba(16, 185, 129, .45); --kn-ok-solid: #16a34a;
    --kn-warn-bg: rgba(217, 119, 6, .20);  --kn-warn-ink: #fcd34d;
    --kn-warn-bg-2: rgba(217, 119, 6, .12); --kn-warn-ink-2: #fbbf24;
    --kn-warn-border: rgba(217, 119, 6, .45); --kn-warn-soft: rgba(217, 119, 6, .10);
    --kn-bad-bg: rgba(220, 38, 38, .22);   --kn-bad-ink: #fca5a5;
    --kn-bad-bg-2: rgba(220, 38, 38, .14); --kn-bad-soft: rgba(220, 38, 38, .08);
    --kn-bad-border: rgba(220, 38, 38, .45); --kn-bad-solid: #f87171;
    --kn-info-bg: rgba(59, 130, 246, .22); --kn-info-ink: #93c5fd;
    --kn-info-bg-2: rgba(59, 130, 246, .12); --kn-info-border: rgba(59, 130, 246, .45);

    --kn-col-scheduled: #60a5fa;
    --kn-col-arrived: #fbbf24;
    --kn-col-open: #34d399;
    --kn-col-absent: #f87171;
    --kn-col-cancelled-patient: #fb923c;
    --kn-col-cancelled-clinic: #38bdf8;
    --kn-cell-arrived: rgba(245, 158, 11, .07);
    --kn-cell-open: rgba(16, 185, 129, .07);
    --kn-cell-closed: rgba(148, 163, 184, .06);

    --kn-visitor-personal: #c084fc;
    --kn-shadow-1: rgba(0, 0, 0, .25);
    --kn-shadow-2: rgba(0, 0, 0, .4);
    --kn-shadow-3: rgba(0, 0, 0, .5);
}

.kanban-board {
    --lane-header-width: 46px;
    --col-min-width: 200px;
    --col-gap: 0.6rem;
    background: var(--kn-bg);
    border-radius: 12px;
    padding: 0.75rem;
    /* Remplit la hauteur dispo : colonnes = zones de dépôt hautes plutôt qu'un board
       flottant au-dessus d'une page vide. */
    display: flex;
    flex-direction: column;
}
/* ── Qui défile, et qui ne défile pas ──────────────────────────────────────
 * C'est LA COLONNE qui défile, jamais la page. Un praticien avec douze
 * patients en attente doit garder ses autres colonnes sous les yeux — s'il
 * faut faire défiler l'écran pour voir « en cours », on ne voit plus jamais
 * qui attend, et c'est la seule question que pose cette page.
 *
 * Les lanes se partagent donc la hauteur disponible, chacune avec un plancher
 * pour rester utilisable quand le cabinet compte quatre praticiens ; en
 * dessous de ce plancher, c'est le TABLEAU qui défile, toujours pas la page.
 */
.kanban-board.sc-fill { overflow-y: auto; }
.kanban-row:not(.kanban-row-header) { flex: 1 1 0; min-height: 13rem; }

/* Chaque cellule est sa propre zone de défilement. `min-height: 0` est
   obligatoire : sans lui, un enfant flex refuse de descendre sous la taille de
   son contenu, la cellule grandit, et c'est le tableau qui défile à sa place. */
.kanban-cell,
.kanban-sub-cell {
    overflow-y: auto;
    min-height: 0;
}

/* Sous-zones (fauteuils / absents) : elles se partagent la hauteur de la
   cellule composite, et défilent chacune de son côté. */
.kanban-cell-composite .kanban-sub-cell { flex: 1 1 0; }
.kanban-row {
    display: grid;
    /* Colonnes flex (1fr) qui s'étendent à 100% largeur. Min-width évite l'écrasement
       et déclenche le scroll horizontal sur petits écrans. */
    grid-template-columns: var(--lane-header-width) repeat(var(--cols, 5), minmax(var(--col-min-width), 1fr));
    gap: var(--col-gap);
    margin-bottom: var(--col-gap);
    align-items: stretch;
}
/* Scroll horizontal seulement si vraiment nécessaire (mobile/petit écran) */
@media (max-width: 1200px) {
    .kanban-board { overflow-x: auto; }
}
.kanban-row-header {
    position: sticky;
    top: 0;
    z-index: 5;
    background: var(--kn-bg);
    padding: 0.25rem 0;
    margin-bottom: 0.4rem;
}

/* Column header */
.kanban-col-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.4rem 0.75rem;
    font-weight: 600;
    font-size: 0.75rem;
    color: var(--kn-text-soft);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    border-bottom: 2px solid transparent;
}
.kanban-col-header .col-count {
    background: var(--kn-shadow-2);
    color: var(--kn-text-soft);
    border-radius: 9999px;
    padding: 0.05rem 0.5rem;
    font-size: 0.7rem;
    font-weight: 600;
    min-width: 22px;
    text-align: center;
    letter-spacing: 0;
}
.kanban-col-header.kanban-col-scheduled { border-bottom-color: var(--kn-col-scheduled); }
.kanban-col-header.kanban-col-arrived   { border-bottom-color: var(--kn-col-arrived); }
.kanban-col-header.kanban-col-open      { border-bottom-color: var(--kn-col-open); }
.kanban-col-header.kanban-col-closed    { border-bottom-color: var(--kn-text-soft); }
.kanban-col-header.kanban-col-absent    { border-bottom-color: var(--kn-col-absent); }

/* Lane header (praticien) */
.kanban-lane-cell {
    background: var(--kn-surface);
    border: 1px solid var(--kn-border);
    border-radius: 10px;
    padding: 0.5rem 0.15rem;
    display: flex;
    align-items: center;
    justify-content: center;
}
.kanban-lane-cell-header { background: transparent; border: none; padding: 0; }
/* Label praticien vertical (texte tourné) → colonne ultra-fine, pas d'avatar. */
.lane-doctor {
    writing-mode: vertical-rl;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    gap: 0.6rem;
    /* PAS de height:100% : en writing-mode vertical, un % de hauteur se résout contre
       un ancêtre ≈ hauteur d'écran → gonflait la cellule label à ~929px, ce qui étirait
       toute la lane. Le centrage est déjà géré par .kanban-lane-cell (flex center). */
}
.lane-doctor-name {
    font-weight: 600;
    font-size: 0.8rem;
    color: var(--kn-text);
    white-space: nowrap;
    line-height: 1;
}
.lane-doctor-count { font-size: 0.68rem; color: var(--kn-text-muted); white-space: nowrap; line-height: 1; }

/* Drop zone simple */
.kanban-cell {
    background: var(--kn-surface);
    border: 1px solid var(--kn-border-soft);
    border-radius: 10px;
    padding: 0.5rem;
    min-height: 90px;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    transition: background 0.15s, border-color 0.15s;
}
.kanban-cell.sortable-drag-over,
.kanban-sub-cell.sortable-drag-over {
    background: var(--kn-info-bg-2);
    border-color: var(--kn-info-border);
    border-style: solid;
}
.kanban-cell-arrived { background: var(--kn-cell-arrived); }
.kanban-cell-open    { background: var(--kn-cell-open); }
.kanban-cell-closed  { background: var(--kn-cell-closed); }

/* Card */
.kanban-card {
    background: var(--kn-surface);
    border: 1px solid var(--kn-border);
    border-left: 3px solid var(--type-color, var(--kn-text-muted));
    border-radius: 8px;
    padding: 0.55rem 0.7rem;
    cursor: grab;
    transition: box-shadow 0.15s, transform 0.1s, border-color 0.15s;
    box-shadow: 0 1px 1px var(--kn-shadow-1);
    font-size: 0.82rem;
    line-height: 1.4;
}
.kanban-card:hover {
    box-shadow: 0 4px 10px var(--kn-shadow-2);
    transform: translateY(-1px);
    border-color: var(--kn-border-2);
}
.kanban-card:active { cursor: grabbing; }
.kanban-card.is-passager {
    background: linear-gradient(to right, var(--kn-warn-bg) 0%, var(--kn-warn-bg-2) 8px, var(--kn-surface) 8px);
}
.kanban-card-ghost { opacity: 0.35; }
.kanban-card-chosen { box-shadow: 0 6px 16px var(--kn-shadow-3); }
.kanban-card-drag { transform: rotate(1.5deg); }

.card-top {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 0.4rem;
    margin-bottom: 0.35rem;
}
.card-patient {
    display: flex;
    align-items: center;
    gap: 0.3rem;
    min-width: 0;
    flex: 1;
}
.card-name {
    font-weight: 600;
    color: var(--kn-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    font-size: 0.84rem;
}
.card-time {
    font-size: 0.72rem;
    color: var(--kn-text-soft);
    font-variant-numeric: tabular-nums;
    background: var(--kn-hover);
    padding: 0.1rem 0.45rem;
    border-radius: 9999px;
    flex-shrink: 0;
    font-weight: 500;
}

/* Icons SVG inline — uniformes 14×14, alignés au texte */
.card-icon {
    width: 13px;
    height: 13px;
    flex-shrink: 0;
    vertical-align: -2px;
}
.card-confirm { color: var(--kn-col-open); }

.card-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
    align-items: center;
    margin: 0.3rem 0;
}
.card-type {
    padding: 0.1rem 0.5rem;
    border-radius: 9999px;
    font-size: 0.7rem;
    font-weight: 500;
    white-space: nowrap;
}
.card-box {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    font-size: 0.7rem;
    color: var(--kn-text-soft);
}

.card-bottom {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: center;
    font-size: 0.72rem;
    color: var(--kn-text-soft);
    margin-top: 0.35rem;
    padding-top: 0.35rem;
    border-top: 1px solid var(--kn-border-soft);
}
.card-phone {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    font-variant-numeric: tabular-nums;
}
.card-badge-p {
    background: var(--kn-col-arrived);
    color: #fff;
    border-radius: 4px;
    padding: 0.08rem 0.4rem;
    font-weight: 600;
    font-size: 0.65rem;
    letter-spacing: 0.03em;
    text-transform: uppercase;
}
.card-waiting, .card-elapsed {
    margin-left: auto;
    font-weight: 500;
    font-variant-numeric: tabular-nums;
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
}
.card-waiting.over-limit { color: var(--kn-bad-solid); font-weight: 600; }
.card-elapsed { color: var(--kn-ok-ink); }

/* Salle d'attente — alertes cliniques / retard / attente dépassée (SdA #1/#2/A) */
.card-med-alert {
    display: flex; align-items: center; gap: 4px;
    margin: -0.1rem 0 0.4rem; padding: 2px 6px;
    background: var(--kn-bad-bg-2); color: var(--kn-bad-ink);
    border-radius: 5px; font-size: 0.72rem; font-weight: 600; line-height: 1.25;
}
.card-med-alert .card-icon { color: var(--kn-bad-solid); }
.card-med-alert-text { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.kanban-card.has-med-alert { border-left-color: var(--kn-bad-solid); }
.card-late-badge {
    margin-left: 5px; padding: 0 5px;
    background: var(--kn-bad-bg); color: var(--kn-bad-ink);
    border-radius: 999px; font-size: 0.66rem; font-weight: 700;
    text-transform: uppercase; letter-spacing: .02em;
}
.kanban-card.is-late { border-left-color: var(--kn-bad-solid); background: var(--kn-bad-soft); }
.kanban-card.wait-exceeded { background: var(--kn-warn-soft); }
.kanban-card.wait-exceeded .card-waiting { color: var(--kn-warn-ink); font-weight: 700; }
.card-action-btn.card-confirmed { color: var(--kn-col-open); }
/* Statut du message (rappel) : petit badge d'angle SUR le bouton message.
   ⋯ envoyé · ✓ reçu · ✓✓ lu · ✕ échec */
.card-msg-wrap { position: relative; display: inline-flex; }
.card-msg-status {
    position: absolute; top: -3px; right: -3px;
    font-size: .62rem; font-weight: 800; line-height: 1; letter-spacing: -1.5px;
    background: var(--kn-surface); border-radius: 5px; padding: 0 1px; pointer-events: none;
}
.card-msg-read { color: var(--kn-info-ink); }
.card-msg-delivered { color: var(--kn-ok-ink); }
.card-msg-sent, .card-msg-queued { color: var(--kn-text-muted); }
.card-msg-failed { color: var(--kn-bad-solid); }
.card-action-btn.card-msg-btn-wa { color: #25d366; }   /* dernier envoi via WhatsApp */
/* Liens contact APPAREIL (wa.me / sms:) près du téléphone */
.card-contact-ic { display: inline-flex; align-items: center; color: var(--kn-text-muted); margin-left: 3px; }
.card-contact-ic:hover { color: var(--kn-text-2); }
.card-contact-wa { color: #25d366; }
.card-contact-wa:hover { color: #128c7e; }
/* Bouton « Arrivé » (action n°1 du guichet sur les cartes Programmés) */
.card-arrived-btn {
    display: inline-flex; align-items: center; gap: 3px;
    border: none; background: var(--kn-ok-bg-2); color: var(--kn-ok-ink-2);
    font-size: .72rem; font-weight: 700; border-radius: 6px;
    padding: 2px 8px; cursor: pointer; line-height: 1.4;
}
.card-arrived-btn:hover { background: var(--kn-ok-solid); color: #fff; }  /* blanc sur vert plein */
.card-arrived-btn .card-icon { width: 12px; height: 12px; }
/* Marqueur « non confirmé » (à relancer) — pendant ambre du ✓ vert confirmé */
.card-unconfirmed { color: var(--kn-warn-ink); display: inline-flex; align-items: center; }

/* Salle d'attente — bandeau VISITEURS (non-cliniques), épinglé en haut, distinct des patients */
.wr-visitors { background: var(--kn-hover); border: 1px dashed var(--kn-border-2); border-radius: 10px; padding: 8px 10px; }
.wr-visitors-head {
    display: flex; align-items: center; gap: 6px;
    font-size: .78rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
    color: var(--kn-text-soft); margin-bottom: 6px;
}
.wr-visitors-count { background: var(--kn-text-soft); color: #fff; border-radius: 999px; padding: 0 7px; font-size: .72rem; }
.wr-visitors-list { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 2px; }
.visitor-card {
    flex: 0 0 auto; min-width: 210px; max-width: 260px;
    background: var(--kn-surface); border: 1px solid var(--kn-border); border-left: 3px solid var(--kn-text-muted);
    border-radius: 8px; padding: 7px 9px; font-size: .8rem;
}
.visitor-card.visitor-delegate { border-left-color: var(--kn-col-scheduled); }
.visitor-card.visitor-supplier { border-left-color: var(--kn-col-arrived); }
.visitor-card.visitor-personal { border-left-color: var(--kn-visitor-personal); }
.visitor-card.visitor-other    { border-left-color: var(--kn-text-soft); }
.visitor-card.vstatus-received { opacity: .72; }
.visitor-top { display: flex; align-items: center; justify-content: space-between; gap: 6px; margin-bottom: 2px; }
.visitor-kind { font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .02em; color: var(--kn-text-soft); }
.visitor-received { font-size: .66rem; font-weight: 700; color: var(--kn-ok-ink); background: var(--kn-ok-bg); border-radius: 999px; padding: 0 6px; }
.visitor-name { font-weight: 600; color: var(--kn-text-strong); }
.visitor-company { font-weight: 400; color: var(--kn-text-soft); }
.visitor-reason { color: var(--kn-text-2); font-size: .76rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.visitor-meta { display: flex; align-items: center; gap: 8px; color: var(--kn-text-muted); font-size: .74rem; margin-top: 3px; }
.visitor-timer { margin-left: auto; font-variant-numeric: tabular-nums; }
.visitor-actions { display: flex; gap: 4px; margin-top: 7px; }
.visitor-actions .btn { padding: 1px 8px; font-size: .74rem; }

/* Empty cell hint */
.kanban-cell:empty::before,
.kanban-sub-cell:has(> .sub-label:only-child)::after {
    content: '';
    display: block;
    height: 1px;
    margin: auto;
}

/* Sub-zones de la colonne Absents/Annulés */
.kanban-cell-composite {
    background: var(--kn-surface);
    border: 1px solid var(--kn-border-soft);
    border-radius: 10px;
    padding: 0.35rem;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    min-height: 90px;
}
.kanban-sub-cell {
    background: var(--kn-cell-closed);
    border: 1px solid transparent;
    border-radius: 7px;
    padding: 0.4rem 0.5rem;
    min-height: 56px;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    transition: background 0.15s, border-color 0.15s;
}
.kanban-sub-cell .sub-label {
    font-size: 0.65rem;
    font-weight: 600;
    color: var(--kn-text-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    margin: -0.05rem 0 0.1rem;
}
.kanban-sub-cancelled_patient { border-left: 3px solid var(--kn-col-cancelled-patient); }
.kanban-sub-cancelled_clinic  { border-left: 3px solid var(--kn-col-cancelled-clinic); }
.kanban-sub-no_show           { border-left: 3px solid var(--kn-bad-solid); }
.kanban-sub-cancelled_patient .sub-label { color: var(--kn-col-cancelled-patient); }
.kanban-sub-cancelled_clinic .sub-label  { color: var(--kn-col-cancelled-clinic); }
.kanban-sub-no_show .sub-label           { color: var(--kn-bad-ink); }

/* Responsive */
@media (max-width: 768px) {
    .kanban-board { --lane-header-width: 42px; --col-min-width: 180px; }
    .lane-doctor-name { font-size: 0.76rem; }
    .card-name { font-size: 0.8rem; }
}

/* ─── Card : motif (clamp 2 lignes) + boutons actions ────────── */
.card-motif {
    font-size: 0.75rem;
    color: var(--kn-text-2);
    line-height: 1.35;
    margin: 0.3rem 0;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    text-overflow: ellipsis;
}
.card-actions {
    margin-left: auto;
    display: inline-flex;
    gap: 0.2rem;
}
.card-action-btn {
    width: 26px;
    height: 26px;
    border: 1px solid var(--kn-border);
    border-radius: 6px;
    background: var(--kn-surface);
    color: var(--kn-text-soft);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: background 0.12s, border-color 0.12s, color 0.12s;
    padding: 0;
}
.card-action-btn svg { width: 14px; height: 14px; }
.card-action-btn:hover { background: var(--kn-hover); border-color: var(--kn-border-2); color: var(--kn-text-strong); }
.card-action-btn.card-pay-paid    { color: var(--kn-ok-ink); border-color: var(--kn-ok-border); }
.card-action-btn.card-pay-partial { color: var(--kn-warn-ink); border-color: var(--kn-warn-border); background: var(--kn-warn-bg-2); }
.card-action-btn.card-pay-unpaid  { color: var(--kn-bad-ink); border-color: var(--kn-bad-border); background: var(--kn-bad-bg-2); }
.card-action-btn.card-pay-no_acts { color: var(--kn-text-muted); }

/* ─── Bannière "Programmation RDV pour X" (agenda en mode pending patient) ─── */
.sc-pending-banner {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    background: var(--kn-warn-bg);
    border: 1px solid var(--kn-warn-border);
    border-radius: 8px;
    padding: 0.55rem 0.85rem;
    margin-bottom: 0.6rem;
    color: var(--kn-warn-ink-2);
    font-size: 0.85rem;
}
.sc-pending-banner svg { color: var(--kn-warn-ink); flex-shrink: 0; }
.sc-pending-banner strong { color: var(--kn-text-strong); }
.sc-pending-banner #sc-pending-cancel { color: var(--kn-warn-ink); text-decoration: underline; }

/* ─── Badge paiement dans card-closed-info (Terminés) ─── */
.card-pay-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.2rem;
    padding: 0.12rem 0.5rem;
    border-radius: 9999px;
    font-size: 0.68rem;
    font-weight: 600;
    line-height: 1.3;
    white-space: nowrap;
}
.card-pay-badge-paid    { background: var(--kn-ok-bg); color: var(--kn-ok-ink); }
.card-pay-badge-partial { background: var(--kn-warn-bg); color: var(--kn-warn-ink); }
.card-pay-badge-unpaid  { background: var(--kn-bad-bg); color: var(--kn-bad-ink); }
.card-pay-badge-no-acts { background: var(--kn-hover); color: var(--kn-text-soft); }

/* ═══════════════════════════════════════════════════════════════════════
   WORKFLOW — Messages / Tasks / Reminders
   Liste sobre, style inbox compact
   ═══════════════════════════════════════════════════════════════════════ */
.wf-list { display: flex; flex-direction: column; gap: 0.5rem; }
.wf-item {
    display: flex;
    gap: 0.75rem;
    align-items: flex-start;
    background: #fff;
    border: 1px solid #e4e6eb;
    border-radius: 10px;
    padding: 0.7rem 0.9rem;
    transition: box-shadow 0.15s;
}
.wf-item:hover { box-shadow: 0 2px 6px rgba(15,23,42,0.05); }
.wf-item-unread { border-left: 3px solid #3a57e8; background: #f8faff; }
.wf-item-avatar {
    width: 36px; height: 36px; border-radius: 8px;
    background: #1e293b; color: #fff;
    display: flex; align-items: center; justify-content: center;
    font-weight: 600; font-size: 0.78rem; text-transform: uppercase;
    flex-shrink: 0;
}
.wf-item-body { flex: 1; min-width: 0; }
.wf-item-head {
    display: flex; justify-content: space-between; align-items: baseline; gap: 0.5rem;
    margin-bottom: 0.15rem;
}
.wf-item-time { font-size: 0.72rem; color: #94a3b8; font-variant-numeric: tabular-nums; }
.wf-item-text { font-size: 0.88rem; color: #1e293b; margin: 0.15rem 0 0.35rem; }
.wf-item-ctx,
.wf-item-meta { font-size: 0.72rem; color: #64748b; }
.wf-item-ctx a, .wf-item-meta a { color: #3a57e8; text-decoration: none; }
.wf-item-ctx a:hover, .wf-item-meta a:hover { text-decoration: underline; }
.wf-item-actions { display: flex; gap: 0.2rem; align-items: flex-start; flex-shrink: 0; }

/* Tâches : priorité (bordure colorée à gauche) */
.wf-task-priority { width: 3px; align-self: stretch; border-radius: 2px; background: #cbd5e1; }
.wf-task-low      .wf-task-priority { background: #94a3b8; }
.wf-task-normal   .wf-task-priority { background: #3b82f6; }
.wf-task-high     .wf-task-priority { background: #f59e0b; }
.wf-task-urgent   .wf-task-priority { background: #dc2626; }
.wf-task-status-done,
.wf-task-status-cancelled { opacity: 0.55; }
.wf-task-status-done .wf-item-head strong { text-decoration: line-through; }
.wf-task-due { font-size: 0.72rem; color: #64748b; }
.wf-task-overdue { color: #dc2626; font-weight: 600; }

/* Reminders */
.wf-reminder-icon {
    width: 36px; height: 36px; border-radius: 8px;
    background: #ede9fe; color: #6d28d9;
    display: flex; align-items: center; justify-content: center; flex-shrink: 0;
}
.wf-reminder-triggered { border-left: 3px solid #dc2626; background: #fff5f5; }
.wf-reminder-triggered .wf-reminder-icon { background: #fee2e2; color: #b91c1c; }
.wf-reminder-dismissed { opacity: 0.6; }

/* ─── Sidebar badges workflow (counts non-lus / pending / triggered) ─── */
.wf-sidebar-badge {
    position: absolute;
    right: 8px;
    top: 50%;
    transform: translateY(-50%);
    background: #dc2626;
    color: #fff;
    border-radius: 10px;
    padding: 0.1rem 0.4rem;
    font-size: 0.65rem;
    font-weight: 700;
    min-width: 18px;
    text-align: center;
    line-height: 1.3;
}
.sidebar .nav-link { position: relative; }

/* ═══════════════════════════════════════════════════════════════════════
   TASKS KANBAN
   ═══════════════════════════════════════════════════════════════════════ */
.task-kanban {
    background: #f6f7f9;
    border-radius: 12px;
    padding: 0.75rem;
}
.task-kanban-row {
    display: grid;
    grid-template-columns: repeat(var(--cols, 4), minmax(220px, 1fr));
    gap: 0.6rem;
    margin-bottom: 0.6rem;
}
.task-kanban-headers { margin-bottom: 0.4rem; }

.task-col-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.4rem 0.75rem;
    font-weight: 600;
    font-size: 0.75rem;
    color: #64748b;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    border-bottom: 2px solid transparent;
}
.task-col-header .col-count {
    background: rgba(15, 23, 42, 0.06);
    color: #64748b;
    border-radius: 9999px;
    padding: 0.05rem 0.5rem;
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0;
}
.task-col-header.task-col-pending      { border-bottom-color: #3b82f6; }
.task-col-header.task-col-in_progress  { border-bottom-color: #f59e0b; }
.task-col-header.task-col-done         { border-bottom-color: #10b981; }
.task-col-header.task-col-cancelled    { border-bottom-color: #64748b; }

.task-col {
    background: #fff;
    border: 1px solid #ecedf0;
    border-radius: 10px;
    padding: 0.5rem;
    min-height: 120px;
    display: flex;
    flex-direction: column;
    gap: 0.45rem;
}
.task-col-in_progress { background: #fffbf2; }
.task-col-done        { background: #f0fdf6; }
.task-col-cancelled   { background: #f8fafc; opacity: 0.85; }
.task-col.sortable-drag-over { background: #eff6ff; border-color: #93c5fd; }

.task-card {
    background: #fff;
    border: 1px solid #e4e6eb;
    border-left: 3px solid #94a3b8;
    border-radius: 8px;
    padding: 0.55rem 0.7rem;
    cursor: grab;
    transition: box-shadow 0.15s, transform 0.1s;
    box-shadow: 0 1px 1px rgba(15,23,42,0.03);
    font-size: 0.82rem;
}
.task-card:hover { box-shadow: 0 4px 10px rgba(15,23,42,0.07); transform: translateY(-1px); }
.task-card:active { cursor: grabbing; }
.task-card-ghost { opacity: 0.35; }
.task-card-chosen { box-shadow: 0 6px 16px rgba(15,23,42,0.12); }
.task-card-drag { transform: rotate(1.5deg); }

.task-priority-low      { border-left-color: #94a3b8; }
.task-priority-normal   { border-left-color: #3b82f6; }
.task-priority-high     { border-left-color: #f59e0b; }
.task-priority-urgent   { border-left-color: #dc2626; }
.task-status-done .task-card-title { text-decoration: line-through; color: #94a3b8; }
.task-status-cancelled { opacity: 0.6; }

.task-card-top { margin-bottom: 0.25rem; }
.task-card-title { color: #0f172a; font-weight: 600; font-size: 0.85rem; }
.task-card-desc { font-size: 0.78rem; color: #475569; margin-bottom: 0.4rem; line-height: 1.35; }
.task-card-bottom {
    display: flex; flex-wrap: wrap; align-items: center;
    gap: 0.3rem; font-size: 0.7rem; color: #64748b;
}
.task-prio-badge {
    padding: 0.08rem 0.4rem;
    border-radius: 9999px;
    font-size: 0.65rem;
    font-weight: 600;
}
.task-prio-high   { background: #fef3c7; color: #b45309; }
.task-prio-urgent { background: #fee2e2; color: #b91c1c; }
.task-prio-low    { background: #f1f5f9; color: #64748b; }
.task-due { font-variant-numeric: tabular-nums; }
.task-overdue { color: #dc2626; font-weight: 600; }
.task-assignee {
    width: 22px; height: 22px;
    border-radius: 6px;
    background: #1e293b; color: #fff;
    display: inline-flex; align-items: center; justify-content: center;
    font-weight: 600; font-size: 0.62rem;
    text-transform: uppercase;
}
.task-patient { color: #3a57e8; text-decoration: none; }
.task-patient:hover { text-decoration: underline; }
.task-delete {
    margin-left: auto;
    background: none;
    border: none;
    color: #94a3b8;
    cursor: pointer;
    font-size: 1.1rem;
    line-height: 1;
    padding: 0 0.25rem;
}
.task-delete:hover { color: #dc2626; }

/* ─── Reminder toasts globaux (notification système) ─── */
#wf-reminder-toasts { display: flex; flex-direction: column; gap: 0.5rem; max-width: 360px; }
.wf-reminder-toast {
    background: #fff;
    border: 1px solid #fde68a;
    border-left: 4px solid #f59e0b;
    border-radius: 10px;
    padding: 0.75rem 0.9rem;
    box-shadow: 0 8px 24px rgba(15,23,42,0.15);
    animation: wfToastSlide 0.25s ease-out;
}
@keyframes wfToastSlide {
    from { transform: translateX(100%); opacity: 0; }
    to   { transform: translateX(0); opacity: 1; }
}
.wf-toast-head {
    display: flex; align-items: center; gap: 0.4rem;
    color: #b45309; font-size: 0.75rem; font-weight: 600;
    text-transform: uppercase; letter-spacing: 0.04em;
    margin-bottom: 0.35rem;
}
.wf-toast-close {
    margin-left: auto;
    background: none; border: none; color: #94a3b8;
    font-size: 1.2rem; line-height: 1; padding: 0 0.2rem; cursor: pointer;
}
.wf-toast-close:hover { color: #1e293b; }
.wf-toast-title { font-weight: 600; color: #0f172a; margin-bottom: 0.15rem; }
.wf-toast-body { font-size: 0.85rem; color: #475569; margin-bottom: 0.5rem; }
.wf-toast-actions { display: flex; gap: 0.4rem; align-items: center; }

/* ═══════════════════════════════════════════════════════════════════════
   CHAT (Messagerie style Messenger : sidebar convs + zone chat)
   ═══════════════════════════════════════════════════════════════════════ */


.chat-bubble-row { display: flex; align-items: flex-end; gap: 0.4rem; }
.chat-mine    { justify-content: flex-end; }
.chat-theirs  { justify-content: flex-start; }
.chat-avatar {
    width: 28px; height: 28px; border-radius: 50%;
    background:var(--bs-secondary); color:var(--bs-white);
    display: flex; align-items: center; justify-content: center;
    font-weight: 600; font-size: 0.65rem; text-transform: uppercase;
    flex-shrink: 0;
}
.chat-bubble {
    max-width: 70%;
    padding: 0.55rem 0.8rem;
    border-radius: 14px;
    line-height: 1.4;
    font-size: 0.88rem;
    word-wrap: break-word;
}
.chat-mine .chat-bubble  { background:var(--bs-primary); color:var(--bs-white); border-bottom-right-radius: 4px; }
.chat-theirs .chat-bubble { background:var(--bs-body-bg); color:var(--bs-emphasis-color); border:1px solid var(--bs-border-color); border-bottom-left-radius: 4px; }
.chat-bubble-author { font-size: 0.7rem; font-weight: 600; color:var(--bs-primary); margin-bottom: 0.15rem; }
.chat-bubble-body { white-space: pre-wrap; }
.chat-bubble-time { font-size: 0.65rem; opacity: 0.65; margin-top: 0.2rem; text-align: right; }
.chat-mine .chat-bubble-time { color: rgba(255,255,255,0.85); }
.chat-theirs .chat-bubble-time { color:var(--bs-tertiary-color); }


@media (max-width: 768px) {
}

/* Toast message (variante bleue du wf-reminder-toast) */
.wf-message-toast { border-color: #93c5fd; border-left-color: #3a57e8; }
.wf-message-toast .wf-toast-head { color: #1e40af; }
.wf-message-toast .wf-toast-head svg { color: #3a57e8; }

/* Task remind badge (cloche) */
.task-remind {
    display: inline-flex;
    align-items: center;
    gap: 0.2rem;
    padding: 0.08rem 0.4rem;
    border-radius: 9999px;
    background: #ede9fe;
    color: #6d28d9;
    font-size: 0.68rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}
.task-remind.is-fired { background: #fee2e2; color: #b91c1c; }

/* ═══ LAB KANBAN — Bootstrap-first. La carte et les colonnes sont 100% en
   classes Bootstrap (card, badge, border-4 border-{color}, text-muted…).
   Résidu irréductible : zone de dépôt d'une colonne vide + états dynamiques
   de drag pilotés par Sortable.js (non exprimables en classes statiques).
   Aucune couleur hardcodée — tokens --bs-* uniquement. ═══ */
.sc-kanban-col { min-height: 8rem; }                                  /* dépôt possible même colonne vide */
.sc-kanban-card { cursor: grab; }
.sc-kanban-card:active { cursor: grabbing; }
.sc-kanban-ghost { opacity: 0.4; }                                    /* carte fantôme pendant le drag */
.sc-kanban-chosen { box-shadow: var(--bs-box-shadow-lg) !important; } /* carte saisie */
.sc-kanban-drag { transform: rotate(1.5deg); }                        /* carte en déplacement */

/* Archives : groupes par mois */
.wf-archive-group + .wf-archive-group { border-top: 1px solid #ecedf0; }
.wf-archive-month {
    padding: 0.5rem 0.85rem;
    font-size: 0.72rem;
    font-weight: 600;
    color: #64748b;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    background: #f8fafc;
    border-bottom: 1px solid #ecedf0;
}
.wf-archive-filters .form-control-sm,
.wf-archive-filters .form-select-sm { min-width: 130px; }

/* Modal Fiche patient : séparateur vertical entre les 2 colonnes (lg+ seulement).
   Sur mobile/tablette les cols s'empilent, donc on cache la bordure pour éviter
   une ligne perdue en haut de la colonne droite. */
@media (min-width: 992px) {
}.sc-bell-btn {
    background: transparent; border: 0; padding: .25rem;
    color: rgba(255, 255, 255, 0.6); transition: color .15s;
    line-height: 1; cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center;
}
.sc-bell-btn:hover { color: #fff; }

/* Panel notifs — position:fixed pour échapper l'overflow sidebar */
.sc-bell-panel {
    position: fixed; z-index: 1080; display: none;
    width: 360px; max-height: 70vh;
    background: #fff; border-radius: 12px;
    box-shadow: 0 12px 36px rgba(0, 0, 0, 0.18);
    border: 1px solid #e2e8f0;
    overflow: hidden;
}
.sc-bell-panel.sc-bell-panel-open { display: block; animation: scBellSlide .18s ease-out; }
@keyframes scBellSlide {
    from { opacity: 0; transform: translateX(-8px); }
    to { opacity: 1; transform: translateX(0); }
}
.sc-bell-panel-body { max-height: 70vh; overflow: hidden; }
.sc-bell-list { max-height: 58vh; overflow-y: auto; margin: 0; padding: 0; list-style: none; }
.sc-bell-item:hover { background: #f8fafc; }
.sc-bell-unread { background: #f0f6ff; }
.sc-bell-unread:hover { background: #e6efff; }
.sc-bell-dot {
    width: 8px; height: 8px; border-radius: 50%;
    flex-shrink: 0; margin-top: .35rem;
}
.x-small { font-size: .72rem; }

/* ─── Toasts notifs instantanés (haut-droite, autodisparait) ──────────── */
.sc-notif-toasts {
    position: fixed; top: 16px; right: 16px;
    z-index: 1090; display: flex; flex-direction: column; gap: 8px;
    pointer-events: none;
}
.sc-notif-toast {
    pointer-events: auto;
    background: #fff; border-radius: 10px;
    box-shadow: 0 8px 28px rgba(0, 0, 0, 0.18);
    border-left: 4px solid var(--bs-primary, #2563eb);
    padding: .75rem 1rem; min-width: 280px; max-width: 360px;
    animation: scNotifSlide .25s ease-out;
    transition: opacity .55s ease, transform .55s ease;
}
.sc-notif-toast-fade { opacity: 0; transform: translateX(20px); }
@keyframes scNotifSlide {
    from { opacity: 0; transform: translateX(20px); }
    to { opacity: 1; transform: translateX(0); }
}
.sc-notif-toast-head {
    display: flex; align-items: center; gap: .5rem;
    font-size: .92rem; color: #0f172a;
}
.sc-notif-toast-icon { font-size: 1rem; }
.sc-notif-toast-body { font-size: .82rem; color: #64748b; margin-top: .25rem; }
.sc-notif-toast-close {
    background: none; border: 0; color: #94a3b8;
    font-size: 1.4rem; line-height: 1; padding: 0 .25rem; cursor: pointer;
}
.sc-notif-toast-close:hover { color: #0f172a; }

/* ── Barre d'action flottante du traitement (clôturer / rouvrir / nouveau) ── */
.sc-treatment-bar {
    position: fixed;
    left: 50%;
    bottom: 1.25rem;
    transform: translateX(-50%);
    z-index: 1030;            /* sous les modals Bootstrap (1055) */
    pointer-events: none;     /* le wrapper laisse passer les clics… */
}
.sc-treatment-bar > *, .sc-treatment-bar > * * { pointer-events: auto; } /* …seuls les boutons captent */
@media (max-width: 575.98px) {
    .sc-treatment-bar { bottom: .75rem; width: max-content; max-width: 94vw; }
}

/* Onglets du modal agenda (Patient / Passager / Événement) — lisibles sur fond
   blanc. Sans ça, le thème colore l'onglet actif en blanc → invisible. */
#addModalTabs { border-bottom: 1px solid var(--bs-border-color); gap: .25rem; }
#addModalTabs .nav-link {
    color: var(--bs-secondary-color);
    background: none;
    border: none;
    border-bottom: 2px solid transparent;
    font-weight: 500;
    padding: .5rem .9rem;
}
#addModalTabs .nav-link:hover { color: var(--bs-primary); }
#addModalTabs .nav-link.active {
    color: var(--bs-primary);
    background: none;
    border-bottom-color: var(--bs-primary);
}

/* ─── Boutons toolbar agenda : icônes 100% CSS (aucune mutation du DOM FC) ───
   Muter ces boutons en JS (innerHTML/textContent) cassait la réconciliation
   preact de FC v6 → toolbar dupliquée au changement de vue. On passe par CSS. */
.fc-msgStatus-button,
.fc-configAgenda-button,
.fc-googleSync-button { font-size: 0 !important; }
.fc-msgStatus-button::before,
.fc-configAgenda-button::before,
.fc-googleSync-button::before {
    content: ''; display: inline-block; width: 16px; height: 16px;
    vertical-align: middle;
    background-repeat: no-repeat; background-position: center; background-size: contain;
}
.fc-msgStatus-button::before {
    background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23444' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z'/></svg>");
}
.fc-configAgenda-button::before {
    background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23444' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='12' cy='12' r='3'/><path d='M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z'/></svg>");
}
.fc-googleSync-button::before {
    background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 48 48'><path fill='%23EA4335' d='M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z'/><path fill='%234285F4' d='M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z'/><path fill='%23FBBC05' d='M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z'/><path fill='%2334A853' d='M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z'/></svg>");
}

/* ─── Filtre praticien + compteur « À placer » dans la toolbar FC ───
   Infos dynamiques via attributs data-* + pseudo-éléments (jamais de mutation
   du contenu des boutons FC → preact intact, pas de toolbar dupliquée). */
.fc-doctorFilter-button::before { content: attr(data-sc-label); }
.fc-bookingPanel-button[data-sc-count]:not([data-sc-count="0"])::after {
    content: " (" attr(data-sc-count) ")";
}
/* Dropdown praticien (élément à nous, hors FC) */
.sc-doctor-dropdown {
    position: absolute; z-index: 3000;
    background: var(--bs-body-bg, #fff);
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: 8px; box-shadow: 0 6px 20px rgba(0,0,0,.12);
    padding: 4px; min-width: 210px; max-height: 320px; overflow-y: auto;
}
.sc-doctor-item {
    display: block; width: 100%; text-align: left;
    background: none; border: none; border-radius: 6px;
    padding: .4rem .6rem; font-size: .85rem; color: var(--bs-body-color); cursor: pointer;
}
.sc-doctor-item:hover { background: var(--bs-secondary-bg, #f1f1f1); }
.sc-doctor-item.active { background: var(--bs-primary); color: #fff; font-weight: 600; }

/* Bouton « aller à une date » (toolbar agenda) — icône calendrier CSS */
.fc-datePicker-button { font-size: 0 !important; }
.fc-datePicker-button::before {
    content: ''; display: inline-block; width: 16px; height: 16px; vertical-align: middle;
    background-repeat: no-repeat; background-position: center; background-size: contain;
    background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23444' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><rect x='3' y='4' width='18' height='18' rx='2' ry='2'/><line x1='16' y1='2' x2='16' y2='6'/><line x1='8' y1='2' x2='8' y2='6'/><line x1='3' y1='10' x2='21' y2='10'/></svg>");
}

/* Jours/plages bloqués (férié/congé) sur l'agenda — fond grisé + libellé rouge */
/* Blocages (férié/congé) : opacité 1 + fond rgba. Sinon l'opacité 0.3 du bg-event
   FC s'applique aux ENFANTS → le texte hérite et paraît gris (impossible à annuler
   depuis l'enfant). On remet opacity:1 et on rend le FOND semi-transparent à la place. */
.fc-bg-event.sc-blocked {
    opacity: 1 !important;
    background-color: rgba(220, 53, 69, 0.14) !important;
}
.fc-bg-event.sc-blocked .fc-blocked-label {
    font-size: .68rem; font-weight: 700; color: #1a1a1a;
    padding: 3px 5px; line-height: 1.1;
    white-space: normal; overflow: hidden;
}

/* ═══════════════════════════════════════════════════════════════════════
   Messagerie staff — bulle flottante (chatb). Réutilise .chat-bubble-* pour
   les bulles de messages (partagé avec la page /workflow/messages/).
   ═══════════════════════════════════════════════════════════════════════ */
.chatb { position: fixed; z-index: 1055; }
/* Coin d'ancrage (déplaçable, mémorisé). Défaut = bas-droite. */
.chatb[data-corner="br"] { right: 20px; bottom: 20px; }
.chatb[data-corner="bl"] { left: 20px; bottom: 20px; }
.chatb[data-corner="tr"] { right: 20px; top: 20px; }
.chatb[data-corner="tl"] { left: 20px; top: 20px; }
/* Panneau + toasts : s'ouvrent vers l'espace dispo selon le coin. */
.chatb[data-corner^="b"] .chatb-panel, .chatb[data-corner^="b"] .chatb-toasts { bottom: 66px; top: auto; }
.chatb[data-corner^="t"] .chatb-panel, .chatb[data-corner^="t"] .chatb-toasts { top: 66px; bottom: auto; }
.chatb[data-corner$="r"] .chatb-panel, .chatb[data-corner$="r"] .chatb-toasts { right: 0; left: auto; }
.chatb[data-corner$="l"] .chatb-panel, .chatb[data-corner$="l"] .chatb-toasts { left: 0; right: auto; }
.chatb[data-corner^="t"] .chatb-toasts { flex-direction: column; }
.chatb-panel {
    position: absolute; right: 0; bottom: 66px; width: 340px; max-width: calc(100vw - 40px);
    height: 460px; max-height: calc(100vh - 120px);
    background:var(--bs-body-bg); border:1px solid var(--bs-border-color); border-radius: 14px;
    box-shadow: 0 12px 40px rgba(15, 23, 42, 0.18);
    display: flex; flex-direction: column; overflow: hidden;
}
.chatb-head {
    display: flex; align-items: center; gap: 6px; padding: 10px 12px;
    border-bottom:1px solid var(--bs-border-color); flex: 0 0 auto;
}
.chatb-close {
    border: none; background: transparent; color:var(--bs-secondary-color); cursor: pointer;
    width: 30px; height: 30px; border-radius: 8px; display: flex; align-items: center; justify-content: center;
}
.chatb-close:hover { background:var(--bs-tertiary-bg); color:var(--bs-emphasis-color); }
.chatb-body { flex: 1 1 auto; overflow-y: auto; min-height: 0; }
.chatb-messages { padding: 10px 12px; display: flex; flex-direction: column; gap: 2px; }
.chatb-input {
    flex: 0 0 auto; display: flex; align-items: flex-end; gap: 6px;
    padding: 8px 10px; border-top:1px solid var(--bs-border-color);
}
.chatb-textarea {
    flex: 1; resize: none; border:1px solid var(--bs-border-color); border-radius: 18px;
    padding: 7px 12px; font-size: 0.86rem; line-height: 1.3; max-height: 90px; outline: none;
}
.chatb-textarea:focus { border-color:var(--bs-primary, var(--bs-primary)); }
.chatb-send {
    flex: 0 0 auto; width: 36px; height: 36px; border-radius: 50%; border: none;
    background:var(--bs-primary, var(--bs-primary)); color:var(--bs-white); cursor: pointer;
    display: flex; align-items: center; justify-content: center;
}
/* Roster */

/* Tabs conversations */
.chatb-tabs { flex: 0 0 auto; border-bottom:1px solid var(--bs-border-color); }
.chatb-tablist { display: flex; gap: 4px; padding: 6px 8px; overflow-x: auto; scrollbar-width: thin; }
.chatb-tab {
    flex: 0 0 auto; display: inline-flex; align-items: center; gap: 5px;
    padding: 5px 11px; border-radius: 16px; border: 1px solid transparent;
    background:var(--bs-tertiary-bg); color:var(--bs-secondary-color); font-size: 0.8rem; font-weight: 500;
    cursor: pointer; white-space: nowrap;
}
.chatb-tab:hover { background:var(--bs-secondary-bg); }
.chatb-tab.is-active { background:var(--bs-primary, var(--bs-primary)); color:var(--bs-white); }
.chatb-tab-label { max-width: 120px; overflow: hidden; text-overflow: ellipsis; }
/* Pourquoi les messages privés n'apparaissent pas : le dire vaut mieux que
   laisser croire à une fonction manquante. */
.chatb-tablist-note {
    padding: 2px 10px 6px; font-size: .68rem; color:var(--bs-tertiary-color);
}
.chatb-tab-dot { width: 7px; height: 7px; border-radius: 50%; background:var(--bs-secondary-bg); flex: 0 0 auto; }
.chatb-tab-dot.is-online { background:var(--bs-success); }
.chatb-tab-unread { width: 7px; height: 7px; border-radius: 50%; background:var(--bs-danger); flex: 0 0 auto; }
.chatb-tab.is-active .chatb-tab-unread { background:var(--bs-white); }

/* Toasts messagerie style Messenger — à côté de la bulle, au-dessus du FAB. */
.chatb-toasts {
    position: absolute; right: 0; bottom: 66px; width: 290px; max-width: calc(100vw - 40px);
    display: flex; flex-direction: column-reverse; gap: 8px; pointer-events: none;
}
.chatb-toast {
    display: flex; align-items: center; gap: 10px; padding: 9px 11px;
    background:var(--bs-body-bg); border-radius: 16px; box-shadow: 0 8px 26px rgba(15, 23, 42, 0.22);
    cursor: pointer; pointer-events: auto;
    opacity: 0; transform: translateY(12px) scale(0.98);
    transition: opacity 0.2s ease, transform 0.2s cubic-bezier(0.22, 1, 0.36, 1);
}
.chatb-toast.is-in { opacity: 1; transform: translateY(0) scale(1); }
.chatb-toast:hover { box-shadow: 0 12px 32px rgba(15, 23, 42, 0.28); }
.chatb-toast-av {
    flex: 0 0 auto; width: 38px; height: 38px; border-radius: 50%;
    background:var(--bs-primary, var(--bs-primary)); color:var(--bs-white);
    display: flex; align-items: center; justify-content: center;
    font-weight: 600; font-size: 0.78rem;
}
.chatb-toast-main { flex: 1; min-width: 0; }
.chatb-toast-name { font-weight: 600; font-size: 0.84rem; color:var(--bs-emphasis-color); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chatb-toast-body { font-size: 0.8rem; color:var(--bs-secondary-color); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chatb-toast-x {
    flex: 0 0 auto; border: none; background: transparent; color:var(--bs-tertiary-color);
    font-size: 1.1rem; line-height: 1; width: 22px; height: 22px; border-radius: 50%; cursor: pointer;
}
.chatb-toast-x:hover { background:var(--bs-tertiary-bg); color:var(--bs-emphasis-color); }


/* ── Cloche notifications dans le footer sidebar + toggles son ── */
.sidebar-footer .sc-bell-btn {
    background: transparent; border: none; color: inherit; cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center;
    padding: 4px; border-radius: 8px; opacity: .85;
}
.sidebar-footer .sc-bell-btn:hover { opacity: 1; background: rgba(255,255,255,.08); }
/* En-tête du panneau cloche (titre + toggle son) */
.sc-bell-panel-head {
    display: flex; align-items: center; justify-content: space-between;
    padding: 10px 12px; border-bottom: 1px solid #eef0f3; font-weight: 600; font-size: .9rem;
}
[data-bs-theme="dark"] .sc-bell-panel-head { border-color: #334155; }
/* Toggle son générique : bell (actif) / bell-off (muet) */
.sc-mute-toggle {
    border: none; background: transparent; cursor: pointer; color: #64748b;
    width: 30px; height: 30px; border-radius: 8px; display: inline-flex; align-items: center; justify-content: center;
}
.sc-mute-toggle:hover { background: #f1f5f9; color: #0f172a; }
.sc-mute-toggle .mute-off { display: none; }
.sc-mute-toggle.is-muted { color: #ef4444; }
.sc-mute-toggle.is-muted .mute-on { display: none; }
.sc-mute-toggle.is-muted .mute-off { display: inline-flex; }
.chatb-head-mute { border: none; background: transparent; color:var(--bs-secondary-color); cursor: pointer;
    width: 30px; height: 30px; border-radius: 8px; display: inline-flex; align-items: center; justify-content: center; }
.chatb-head-mute:hover { background:var(--bs-tertiary-bg); color:var(--bs-emphasis-color); }
[data-bs-theme="dark"] .sc-mute-toggle:hover, [data-bs-theme="dark"] .chatb-head-mute:hover { background:var(--bs-secondary-bg); color:var(--bs-white); }

/* ═══ À faire / Rappels ═══════════════════════════════════════════════ */
/* Ajout rapide */
/* Assigné : bouton-icône + menu de prénoms (non assigné = public, pas d'option « Cabinet ») */
/* Le nom de l'assigné : tant qu'il est vide, il ne doit pas creuser un `gap`
   à côté de l'icône. Bootstrap n'a pas de `:empty`, et c'est le seul cas du
   projet où le style s'accroche à un `data-*` — l'élément n'a aucune classe. */
[data-todo-assign-name]:empty { display: none; }
/* Section */
/* Item */
/* Alerte non-bloquante (popup marquant à l'heure) */
.todo-alert { position: fixed; top: 16px; left: 50%; transform: translateX(-50%) translateY(-16px);
    z-index: 1100; opacity: 0; transition: opacity .2s, transform .2s; }
.todo-alert.in { opacity: 1; transform: translateX(-50%) translateY(0); }
/* Largeur bornée au viewport + liseré haut : ni l'un ni l'autre n'a
   d'utilitaire. */
.todo-alert-card { width: min(420px, calc(100vw - 32px)); border-top:4px solid var(--bs-danger) !important; }

/* ── Bulle en PILULE : deux moitiés (À faire | Messages) ── */
.chatb-pill { display: flex; align-items: stretch; border-radius: 26px; overflow: hidden;
    box-shadow: 0 6px 20px rgba(15,23,42,.25); }
.chatb-pill .chatb-half {
    width: 50px; height: 52px; border: none; background:var(--bs-primary, var(--bs-primary)); color:var(--bs-white);
    display: flex; align-items: center; justify-content: center; cursor: pointer; position: relative;
    transition: background .12s;
}
.chatb-pill .chatb-half:hover { background:color-mix(in srgb, var(--bs-primary) 85%, #000); }
.chatb-half-todo { border-right: 1px solid rgba(255,255,255,.28); }
.chatb-half-badge {
    position: absolute; top: 3px; right: 5px; min-width: 16px; height: 16px; padding: 0 4px;
    border-radius: 8px; background:var(--bs-danger); color:var(--bs-white); font-size: .6rem; font-weight: 700;
    line-height: 16px; text-align: center; border:2px solid var(--bs-body-bg);
}
/* Switcher dans l'en-tête */
.chatb-switch { display: flex; gap: 2px; background:var(--bs-tertiary-bg); border-radius: 9px; padding: 2px; flex: 1; }
.chatb-switch-btn { flex: 1; border: none; background: transparent; padding: 5px 8px; border-radius: 7px;
    font-size: .8rem; font-weight: 600; color:var(--bs-secondary-color); cursor: pointer; }
.chatb-switch-btn.is-active { background:var(--bs-body-bg); color:var(--bs-emphasis-color); box-shadow: 0 1px 2px rgba(0,0,0,.06); }
/* Corps des modes */
.chatb-mode-chat { flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0; }
.chatb-mode-todo { flex: 1 1 auto; overflow-y: auto; min-height: 0; padding: 2px 8px 8px; }

/* ── Segment WhatsApp dans la pilule (au centre, entre À faire et Messages) ── */
.chatb-pill .chatb-half-wa { background:#25d366; border-left: 1px solid rgba(255,255,255,.28); border-right: 1px solid rgba(255,255,255,.28); }
.chatb-pill .chatb-half-wa:hover { background:#1fb855; }
.chatb-half-badge-wa { background:var(--bs-danger); }
.chatb-half-wa.is-new { animation: chatbWaPop .8s ease; }
@keyframes chatbWaPop { 0% { transform: scale(1); } 25% { transform: scale(1.18); } 50% { transform: scale(.98); } 75% { transform: scale(1.1); } 100% { transform: scale(1); } }
.chatb-switch-wa.is-active { background:#25d366 !important; color:var(--bs-white) !important; }

/* ── Mode WhatsApp dans la bulle : liste conversations → fil (style chat) ── */
.chatb-mode-wa { flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0; overflow: hidden; }
.chatb-wa-back { flex: 0 0 auto; align-self: flex-start; border: none; background: transparent; color:var(--bs-primary);
    font-size: .82rem; font-weight: 600; cursor: pointer; padding: 6px 10px; }
.chatb-wa-inner { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; padding: 0 8px 6px; }
/* Liste des conversations */
.chatb-wa-list { flex: 1 1 auto; overflow-y: auto; min-height: 0; display: flex; flex-direction: column; gap: 2px; }
.chatb-wa-list[hidden] { display: none; }
.chatb-wa-conv { text-align: left; border: none; background: transparent; border-radius: 10px;
    padding: 8px 10px; cursor: pointer; width: 100%; }
.chatb-wa-conv:hover { background:var(--bs-tertiary-bg); }
.chatb-wa-conv.is-waiting { background:#ecfdf5; }
.chatb-wa-conv.is-waiting:hover { background:#d1fae5; }
.chatb-wa-conv-row { display: flex; align-items: center; justify-content: space-between; gap: 6px; }
.chatb-wa-conv-name { font-weight: 600; font-size: .86rem; color:var(--bs-emphasis-color); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chatb-wa-wait { flex: 0 0 auto; font-size: .62rem; font-weight: 700; color:#047857; background:#a7f3d0; border-radius: 10px; padding: 1px 7px; }
.chatb-wa-time { flex: 0 0 auto; font-size: .68rem; color:var(--bs-tertiary-color); }
.chatb-wa-conv-last { font-size: .76rem; color:var(--bs-secondary-color); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chatb-wa-empty { text-align: center; color:var(--bs-tertiary-color); padding: 24px; font-size: .84rem; }
/* Fil de conversation (head fixe + messages scroll + input épinglé, comme le chat) */
.chatb-wa-thread { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.chatb-wa-thread[hidden] { display: none; }
.chatb-wa-thread-head { flex: 0 0 auto; padding: 4px 6px 8px; border-bottom:1px solid var(--bs-border-color); margin-bottom: 4px; }
.chatb-wa-thread-name { font-weight: 700; font-size: .9rem; color:var(--bs-emphasis-color); }
.chatb-wa-thread-sub { font-size: .7rem; color:var(--bs-tertiary-color); display: flex; gap: 8px; align-items: center; }
.chatb-wa-win.open { color:#059669; }
.chatb-wa-err { flex: 0 0 auto; background:#fef3c7; color:#92400e; border-radius: 8px; padding: 6px 10px; font-size: .76rem; margin-bottom: 6px; }
.chatb-wa-msgs { flex: 1 1 auto; overflow-y: auto; min-height: 0; display: flex; flex-direction: column; gap: 6px; padding: 2px 4px 6px; }
.chatb-wa-msg { max-width: 82%; padding: 7px 10px; border-radius: 12px; font-size: .84rem; }
.chatb-wa-msg.is-in { align-self: flex-start; background:var(--bs-tertiary-bg); color:var(--bs-emphasis-color); border-bottom-left-radius: 4px; }
.chatb-wa-msg.is-out { align-self: flex-end; background:#d1fae5; color:#064e3b; border-bottom-right-radius: 4px; }
.chatb-wa-msg-body { white-space: pre-wrap; word-break: break-word; }
.chatb-wa-msg-body.is-auto { font-style: italic; opacity: .8; }
.chatb-wa-msg-time { font-size: .62rem; opacity: .65; text-align: right; margin-top: 2px; }
.chatb-wa-read { color:var(--bs-primary); }
.chatb-wa-fail { color:var(--bs-danger); }
.chatb-wa-img { max-width: 180px; max-height: 180px; border-radius: 10px; cursor: zoom-in; display: block; object-fit: cover; }
.chatb-wa-file { font-size: .78rem; }
.chatb-wa-media-wait { font-size: .72rem; font-style: italic; opacity: .7; }
.chatb-wa-form { flex: 0 0 auto; }
.chatb-wa-closed { flex: 0 0 auto; font-size: .76rem; color:var(--bs-secondary-color); background:var(--bs-tertiary-bg); border-radius: 8px; padding: 8px 10px; margin: 4px; }
[data-bs-theme="dark"] .chatb-wa-conv.is-waiting { background: rgba(16,185,129,.12); }
[data-bs-theme="dark"] .chatb-wa-msg.is-out { background:#065f46; color:#d1fae5; }
[data-bs-theme="dark"] .chatb-wa-closed { background:var(--bs-body-bg); }
/* Lightbox image (voir sans quitter la page) */
.chatb-lightbox { position: fixed; inset: 0; z-index: 2000; background: rgba(0,0,0,.82); display: flex;
    align-items: center; justify-content: center; opacity: 0; transition: opacity .18s; cursor: zoom-out; padding: 24px; }
.chatb-lightbox.in { opacity: 1; }
.chatb-lightbox img { max-width: 94vw; max-height: 92vh; border-radius: 10px; box-shadow: 0 10px 50px rgba(0,0,0,.5); }

/* TomSelect vide/non-focus (offcanvas + modal commande) : hauteur alignée sur les inputs Bootstrap */
#stockOff .ts-wrapper .ts-control, #stockOrder .ts-wrapper .ts-control { min-height: calc(1.5em + 0.75rem + 2px); }
/* dropdownParent:'body' (modal commande) → le menu doit passer au-dessus du modal (z-index 1055) */
.ts-dropdown { z-index: 1060; }

.iq-auth-page .btn-primary:hover { background-color: #2e46ba; border-color: #2e46ba; color: #fff; }
.iq-auth-page .btn-outline-secondary { color: #475569; border-color: #cbd5e1; background-color: #fff; }
.iq-auth-page .btn-outline-secondary:hover,
.iq-auth-page .btn-outline-secondary:focus { background-color: #f1f5f9; border-color: #94a3b8; color: #0f172a; }

/* ── Onglets À faire (Cabinet + utilisateurs), comme le chat ── */

/* Barre planning (page dédiée uniquement) : recherche + filtre assigné */

/* ── Board À faire : 2 colonnes (Cabinet public + Privé), centrées, adaptatives ── */

/* ══════════════════════════════════════════════════════════════
 * .sc-filters — champs posés sur une card teintée
 *
 * ABSENT DE BOOTSTRAP : ses form-control sont toujours opaques, si bien
 * que sur une card `bg-primary-subtle` ils découpent des bandes blanches
 * qui cassent la teinte. Ici les champs sont translucides au repos —
 * ils appartiennent au panneau — et redeviennent francs au focus, ce qui
 * fait aussi l'affordance.
 * Tout est exprimé en tokens --bs-* : le thème reste interchangeable.
 * ══════════════════════════════════════════════════════════════ */
.sc-filters .form-control,
.sc-filters .form-select,
.sc-filters .input-group-text,
.sc-filters .ts-wrapper .ts-control {
    background-color: rgba(255, 255, 255, 0.55);
    border-color: rgba(var(--bs-primary-rgb), 0.2);
    color: var(--bs-primary-text-emphasis);
}
.sc-filters .form-control::placeholder {
    color: rgba(var(--bs-primary-rgb), 0.5);
}
.sc-filters .form-control:focus,
.sc-filters .form-select:focus,
.sc-filters .ts-wrapper.focus .ts-control {
    background-color: var(--bs-gray-900);
    border-color: var(--bs-primary);
}
/* Les boutons outline d'une barre de filtres : même logique, un fond très
   léger plutôt que le blanc franc ou le transparent total. */
.sc-filters .btn-outline-primary {
    background-color: rgba(255, 255, 255, 0.35);
}
.sc-filters .btn-outline-primary:hover,
.sc-filters .btn-outline-primary:focus {
    background-color: var(--bs-primary);
}

[data-bs-theme="dark"] .sc-filters .form-control,
[data-bs-theme="dark"] .sc-filters .form-select,
[data-bs-theme="dark"] .sc-filters .input-group-text,
[data-bs-theme="dark"] .sc-filters .ts-wrapper .ts-control,
[data-bs-theme="dark"] .sc-filters .btn-outline-primary {
    background-color: rgba(255, 255, 255, 0.06);
    color: var(--bs-body-color);
}

/* ══════════════════════════════════════════════════════════════
 * Colonnes épinglées d'un tableau
 *
 * ABSENT DE BOOTSTRAP : `.table-responsive` fait défiler le tableau entier,
 * emportant l'identité de la ligne. On scrolle à droite et on ne sait plus
 * de qui on parle, ni où agir.
 * Ici la première colonne (le patient) et la dernière (les actions) restent
 * en place. Le fond doit être explicite : une cellule collante laisse voir
 * les autres à travers si elle est transparente.
 * ══════════════════════════════════════════════════════════════ */
.sc-pin-start,
.sc-pin-end {
    position: sticky;
    z-index: 2;
    background-color: var(--bs-gray-900);
}
.sc-pin-start { left: 0; box-shadow: inset -1px 0 0 var(--bs-border-color); }
.sc-pin-end   { right: 0; box-shadow: inset 1px 0 0 var(--bs-border-color); }

/* Le coin : une cellule d'en-tête ancrée doit tenir dans les DEUX sens.
   HopeUI rend `thead th` collant vers le haut avec un sélecteur bien plus
   spécifique que `.sc-pin-*` ; sans reprendre sa spécificité, nos deux
   colonnes perdaient le `top: 0` et redescendaient au défilement. */
.table-fixed .table-responsive .table thead tr th.sc-pin-start,
.table-fixed .table-responsive .table thead tr th.sc-pin-end {
    position: sticky;
    top: 0;
    z-index: 3;                     /* au-dessus des cellules ancrées du corps */
    background-color: var(--bs-body-bg);
}
/* Le survol doit franchir la colonne épinglée, sinon la ligne se coupe en
   trois au passage de la souris. */
.table-hover > tbody > tr:hover > .sc-pin-start,
.table-hover > tbody > tr:hover > .sc-pin-end {
    background-color: #f4f5f9;
}
[data-bs-theme="dark"] .table-hover > tbody > tr:hover > .sc-pin-start,
[data-bs-theme="dark"] .table-hover > tbody > tr:hover > .sc-pin-end {
    background-color: #2a3047;
}

/* ── Pastille de légende ──────────────────────────────────────────────────
 * Un carré de couleur devant un libellé. Bootstrap sait poser la COULEUR
 * (`bg-primary`, `bg-opacity-50`) mais pas la forme : `p-2` donne un rectangle
 * qui suit la police, et la légende se déforme d'un thème à l'autre.
 */
.sc-swatch {
    display: inline-block;
    width: .7rem;
    height: .7rem;
    border-radius: .2rem;
    flex-shrink: 0;
}

/* ── Bouton à icône seule : toujours le même carré ────────────────────────
 * Un en-tête de card porte ses actions en ICÔNE SEULE (la règle du projet : le
 * libellé n'apparaît que sur un appel à l'action, dans le corps d'une card).
 * Elles n'avaient pourtant pas la même forme d'un endroit à l'autre : icône 13
 * ici, 14 là, 16 ailleurs, `d-inline-flex` présent ou absent — et une icône
 * posée en `inline` prend la hauteur de ligne autour d'elle, ce qui allonge le
 * bouton. On lisait un carré à côté d'un rectangle, pour deux actions du même
 * rang : le micro et le crayon de la card Consultation.
 *
 * Bootstrap n'a pas de bouton icône carré — ses `btn-sm` sont dimensionnés par
 * leur contenu, et un contenu de 13 px n'occupe pas la place d'un de 16.
 * D'où cet utilitaire : une seule géométrie, quelle que soit l'icône dedans.
 */
.sc-btn-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.875rem;
    height: 1.875rem;
    padding: 0;
    flex-shrink: 0;
}
/* Avec un libellé, il redevient un bouton normal : c'est le texte qui donne la
   largeur, et le carré n'a plus de sens. */
.sc-btn-icon.has-label {
    width: auto;
    gap: .35rem;
    padding-inline: .55rem;
}

/* ══════════════════════════════════════════════════════════════════
   LA DICTÉE — une pilule qui flotte, jamais une modale
   ══════════════════════════════════════════════════════════════════
   Le praticien dicte depuis n'importe où : fiche patient, modal Consultation
   déjà ouvert, devis, odontogramme. La pilule se pose PAR-DESSUS sans rien
   fermer — il garde sous les yeux la dent qu'il commente et les champs qu'il
   remplit. Une seconde modale l'aurait obligé à fermer la première, donc à
   perdre le fil.

   Son `z-index` passe au-dessus de la pile Bootstrap (modal = 1055,
   backdrop = 1050) : c'est tout l'intérêt.

   Bootstrap ne sait dire ni « une onde qui suit la voix », ni une pilule
   ancrée au coin de l'écran. Le reste — couleurs, rayons, ombres — vient des
   tokens du projet.
   ══════════════════════════════════════════════════════════════════ */
:root,
[data-bs-theme="light"] {
    --sc-dict-surface: #fff;
    --sc-dict-ink: #232328;
    --sc-dict-muted: rgba(138, 146, 166, .95);
    --sc-dict-border: #eee;
    --sc-dict-soft: #f9f9f9;
    --sc-dict-shadow: rgba(15, 23, 42, .25);
    --sc-dict-wave: var(--bs-primary);
}

[data-bs-theme="dark"] {
    --sc-dict-surface: #1e293b;
    --sc-dict-ink: #e2e8f0;
    --sc-dict-muted: #94a3b8;
    --sc-dict-border: #334155;
    --sc-dict-soft: #0f172a;
    --sc-dict-shadow: rgba(0, 0, 0, .6);
    --sc-dict-wave: #7c93ff;
}

.sc-dictate {
    position: fixed;
    right: 1.25rem;
    bottom: 1.25rem;
    z-index: 1090;                       /* au-dessus des modals Bootstrap */
    display: none;
}
.sc-dictate.is-open { display: block; }

.sc-dictate-pill {
    display: flex;
    align-items: center;
    gap: .75rem;
    width: 24.75rem;
    max-width: calc(100vw - 2.5rem);
    padding: .75rem .875rem;
    background: var(--sc-dict-surface);
    color: var(--sc-dict-ink);
    border: 1px solid var(--sc-dict-border);
    border-radius: 26px;                 /* le rayon de la bulle déjà en place */
    box-shadow: 0 6px 20px var(--sc-dict-shadow);
    animation: sc-dictate-in .18s ease-out;
}
@keyframes sc-dictate-in {
    from { opacity: 0; transform: translateY(.75rem); }
    to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
    .sc-dictate-pill { animation: none; }
}

.sc-dictate-badge {
    flex: 0 0 auto;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--bs-danger);
    color: #fff;                          /* blanc sur pastille pleine */
}
.sc-dictate-badge.is-sending {
    background: rgba(var(--bs-primary-rgb), .12);
    color: var(--bs-primary);
}

.sc-dictate-mid { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: .3rem; }
.sc-dictate-label { font-size: .8125rem; font-weight: 500; }
.sc-dictate-time { font-size: .8125rem; font-variant-numeric: tabular-nums; }
.sc-dictate-time strong { font-weight: 600; }
.sc-dictate-time span { color: var(--sc-dict-muted); font-size: .6875rem; margin-left: .25rem; }

/* Approche de la limite : le compteur vire, sans rien interrompre. */
.sc-dictate-pill.is-near-limit .sc-dictate-time strong { color: var(--bs-warning); }

/* L'onde. Chaque barre suit le volume RÉEL du micro (cf. sc-dictate.js) : c'est
   ce qui dit au praticien qu'on l'entend — et s'il est trop loin. */
.sc-dictate-wave { display: flex; align-items: center; gap: 3px; height: 1.75rem; }
.sc-dictate-wave i {
    display: block;
    flex: 1 1 auto;
    max-width: 4px;
    height: 100%;
    border-radius: 2px;
    background: var(--sc-dict-wave);
    transform: scaleY(.12);
    transform-origin: center;
    transition: transform .07s linear;
}

.sc-dictate-track { height: 3px; border-radius: 2px; background: var(--sc-dict-border); overflow: hidden; }
.sc-dictate-track span {
    display: block;
    width: 40%;
    height: 100%;
    border-radius: 2px;
    background: var(--bs-primary);
    animation: sc-dictate-slide 1.1s ease-in-out infinite;
}
@keyframes sc-dictate-slide {
    0%   { transform: translateX(-100%); }
    100% { transform: translateX(250%); }
}
@media (prefers-reduced-motion: reduce) {
    .sc-dictate-track span { animation: none; width: 100%; opacity: .5; }
}

.sc-dictate-btn {
    flex: 0 0 auto;
    width: 2.5rem;
    height: 2.5rem;
    border: none;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: filter .12s;
}
.sc-dictate-btn:hover { filter: brightness(.94); }
.sc-dictate-cancel { background: var(--sc-dict-soft); color: var(--sc-dict-muted); }
.sc-dictate-ok { background: var(--bs-success); color: #fff; }

/* La pilule qui montre le texte : elle grandit vers le HAUT, elle ne pousse
   rien. Le praticien lit ce que la machine a entendu pendant que l'IA range —
   et peut le reprendre là, sans quitter son écran. */
.sc-dictate-tall {
    align-items: flex-start;
    border-radius: 20px;
    width: 30rem;                        /* plus large dès la relecture */
}
.sc-dictate-tall .sc-dictate-mid { gap: .5rem; }

.sc-dictate-head { display: flex; align-items: center; gap: .4rem; font-size: .75rem; font-weight: 600; }
.sc-dictate-ok-mark { color: var(--bs-success); display: inline-flex; }
.sc-dictate-sub { color: var(--sc-dict-muted); font-weight: 400; margin-left: auto; }

.sc-dictate-text {
    /* Ce texte se RELIT, il ne se survole pas : c'est là que le praticien
       repère « enceintes » à la place d'« absentes ». Trop petit, il ne le
       voit pas et l'erreur file dans sept champs. */
    font-size: .88rem;
    line-height: 1.6;
    max-height: 13rem;
    overflow-y: auto;
    background: var(--sc-dict-soft);
    border-radius: 10px;
    padding: .55rem .65rem;
}

.sc-dictate-input {
    width: 100%;
    font: inherit;
    font-size: .84rem;
    line-height: 1.5;
    color: var(--sc-dict-ink);
    background: var(--sc-dict-soft);
    border: 1px solid var(--bs-primary);
    border-radius: 10px;
    padding: .55rem .65rem;
    resize: vertical;
}

/* En CORRECTION, la pilule s'élargit et respire.
   Relire trois lignes dans une fenêtre de la taille d'un bouton ne permet pas
   de repérer un mot mal entendu — or c'est tout ce qu'on demande ici. Elle
   reste une pilule flottante : le modal derrière n'est toujours pas fermé. */
.sc-dictate-pill.is-editing {
    width: 42rem;
    max-width: calc(100vw - 2.5rem);
}
.sc-dictate-pill.is-editing .sc-dictate-input {
    min-height: 9rem;
    max-height: 55vh;
    font-size: .9rem;
    line-height: 1.65;
}
/* Le texte relu de près : plus grand, plus aéré, et il défile s'il déborde. */
.sc-dictate-pill.is-editing .sc-dictate-text { max-height: none; padding: 0; background: none; }

.sc-dictate-actions { display: flex; gap: .5rem; align-items: center; }
.sc-dictate-link {
    border: none;
    background: transparent;
    padding: 0;
    font: inherit;
    font-size: .78rem;
    color: var(--sc-dict-muted);
    cursor: pointer;
    text-decoration: underline;
    text-underline-offset: 2px;
}
.sc-dictate-link:hover { color: var(--sc-dict-ink); }
.sc-dictate-link.is-primary { color: var(--bs-primary); font-weight: 500; }

.sc-dictate-error { border-color: rgba(var(--bs-danger-rgb), .35); }
.sc-dictate-error .sc-dictate-label { color: var(--bs-danger); }

@media (max-width: 575.98px) {
    .sc-dictate { right: .75rem; left: .75rem; bottom: .75rem; }
    .sc-dictate-pill { width: auto; }
}
