html {
    scrollbar-gutter: stable;
}

header.navbar.sticky-top {
    margin-left: 0 !important;
    margin-right: 0 !important;
    padding-left: 0 !important;
    padding-right: 0 !important;
}

/* Supprime l'icône (croix rouge / coche verte) que Bootstrap injecte sur les champs invalides */
.form-control.is-invalid,
.form-select.is-invalid {
    background-image: none !important;
    padding-right: 0.75rem !important;
}
.form-control.is-valid,
.form-select.is-valid {
    background-image: none !important;
    padding-right: 0.75rem !important;
}

/* Custom compact select group */
.form-selectgroup-sm .form-selectgroup-label {
    padding: 0.125rem 0.5rem;
    font-size: 0.75rem;
    line-height: 1.5;
    min-width: 2.5rem;
}

/* TomSelect — hide selected items, show only input (used for multi-select with custom cards) */
.ts-hidden-items .ts-control > .item { display: none !important; }
.ts-hidden-items .ts-control { display: flex; }
.ts-hidden-items .ts-control > input { flex-grow: 1; width: 100% !important; margin: 0 !important; }

/* Auth pages — loading spinner inside buttons */
.btn-spinner { margin-right: 0.5rem; }
/* Dans un input-group, le message d'erreur est posé après le groupe : il n'est plus frère du
   champ, donc le sélecteur de Bootstrap ne l'atteint pas. On ne l'affiche que là, et seulement
   quand le champ est réellement invalide — une règle sans portée montrerait tous les messages
   en permanence, y compris sur un formulaire vierge. */
.input-group:has(.is-invalid) ~ .invalid-feedback { display: block; }

/* Nav badge compact (ex: counter "le point" dans la navbar) */
.nav-badge-sm { font-size: 0.65rem; padding: 0.2em 0.45em; }

.toast-container { z-index: 1095; }

/* Colonne n° + réf interne des listes : largeur plancher pour aligner le libellé
   d'une ligne à l'autre, sans jamais couper une référence en plein milieu. */
.list-ref-col {
    min-width: 9rem;
    word-break: normal;
    white-space: nowrap;
}

/* Notifications — une notif lue reste dans la cloche, simplement atténuée, jusqu'à ce qu'elle soit retirée */
.notification-item.notification-read .notification-title,
.notification-item.notification-read .text-muted {
    opacity: 0.6;
}

.notification-item .notification-dismiss {
    opacity: 0.4;
    transition: opacity 0.15s ease-in-out;
}

.notification-item .notification-dismiss:hover,
.notification-item .notification-dismiss:focus-visible {
    opacity: 1;
}

/* Toast "Annuler" du retrait d'une notification : au-dessus du dropdown de la cloche */
.notification-undo-container { z-index: 1096; }


/* --- Compacité écrans portables (<= 1400) -------------------------------------
   Les postes en 1366 et 1680 n'ont pas la place que suppose la mise en page :
   ce bloc rend compressible ce qui refusait de l'être. Rien ne change au-dessus
   de 1400, les règles sont soit neutres, soit sous media query. */

/* Un badge de tableau se tronque au lieu de mordre sur la colonne voisine.
   .badge est un inline-flex centré : sans justify-content, la troncature
   ronge le début du texte au lieu de la fin. */
.table td .badge {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    justify-content: flex-start;
}

/* Une référence métier ou une date ne se coupe jamais en plein milieu. */
.no-break {
    overflow-wrap: normal;
    word-break: normal;
    white-space: nowrap;
}

/* Une ancre de contenu ne se cache pas derrière la barre collante. */
[id^="pane-"],
[id^="tab-"] {
    scroll-margin-top: 5rem;
}

/* Les états vides des widgets tiennent dans la hauteur imposée par la grille. */
.grid-stack .empty {
    padding-top: 1.5rem;
    padding-bottom: 1.5rem;
}
.grid-stack .empty-img svg {
    width: 72px;
    height: 72px;
}

/* Une colonne de navigation ne s'enroule pas : ses items se dimensionneraient
   sur leur contenu et non sur la colonne, et l'ellipsis tomberait hors cadre. */
#threads-list-container {
    flex-wrap: nowrap;
}

@media (max-width: 1399.98px) {
    /* Cinq filtres dans une colonne de 285 px : on resserre plutôt que de déborder. */
    #ref-type-filter .btn {
        padding-inline: 0.35rem;
        font-size: 0.75rem;
    }
}

/* Le nom de l'organisme devient compressible : il gardait 478 px en dur et
   poussait tout le menu hors de l'écran. Nom complet disponible en infobulle.
   Le plancher évite l'excès inverse : un nom court se faisait écraser en « IPE… »
   alors qu'il tenait entier. */
header.navbar .navbar-brand {
    min-width: 5rem;
    max-width: 14rem;
    flex: 0 1 auto;
    overflow: hidden;
}
header.navbar .navbar-brand a {
    min-width: 0;
    overflow: hidden;
}
header.navbar .navbar-brand span {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 1.05rem;
}
header.navbar .nav-link-title {
    white-space: nowrap;
}

/* Flash de fin d'action posé par l'option `flash` du helper Loading (loading.js). */
.btn-success-flash {
    background-color: var(--tblr-success) !important;
    border-color: var(--tblr-success) !important;
    color: #fff !important;
    transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}

.btn-danger-flash {
    background-color: var(--tblr-danger) !important;
    border-color: var(--tblr-danger) !important;
    color: #fff !important;
    transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}

/* Navbar : les détails du compte (nom + e-mail, deux lignes) n'apparaissent qu'à partir de 1800px.
   En dessous, menu déployé + recherche + compte ne tenaient pas sur une ligne : le bouton de
   recherche se coupait en deux et le nom de l'organisme était tronqué. L'avatar seul suffit, le
   nom complet reste dans le menu déroulant. */
@media (max-width: 1799.98px) {
    .navbar .navbar-user-details {
        display: none !important;
    }
}

/* Placeholder rows and fade-in set by Loading.skeleton / Loading.skeletonHtml (loading.js). */
.skeleton-bar {
    display: block;
    height: .7rem;
    border-radius: .35rem;
    background: linear-gradient(90deg, var(--tblr-border-color) 25%, var(--tblr-bg-surface-secondary, #f6f8fb) 45%, var(--tblr-border-color) 65%);
    background-size: 300% 100%;
    animation: skeleton-shimmer 1.2s ease-in-out infinite;
}
.skeleton-stack { display: flex; flex-direction: column; gap: .9rem; padding: .6rem 0; text-align: left; }
.skeleton-row td { padding-top: .85rem; padding-bottom: .85rem; }
.skeleton-w0 { width: 72%; }
.skeleton-w1 { width: 48%; }
.skeleton-w2 { width: 86%; }
.skeleton-w3 { width: 60%; }
.skeleton-w4 { width: 38%; }
.skeleton-w5 { width: 66%; }
.loading-reveal { animation: loading-reveal .45s ease both; }
@keyframes skeleton-shimmer {
    from { background-position: 100% 0; }
    to   { background-position: 0 0; }
}
@keyframes loading-reveal {
    from { opacity: 0; transform: translateY(4px); }
    to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
    .skeleton-bar, .loading-reveal { animation: none; }
}

/* Images attached to conversation messages, in the pages and the side panels alike. */
.message-images {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    margin-top: .5rem;
}

.message-image-thumb {
    display: block;
    max-width: 160px;
    max-height: 120px;
    border-radius: 4px;
    border: 1px solid rgba(0, 0, 0, .1);
    object-fit: cover;
}

.message-images-draft {
    margin-top: .5rem;
}

.message-images-draft-item {
    position: relative;
}

.message-images-remove {
    position: absolute;
    top: 2px;
    right: 2px;
    padding: .35rem;
    background-color: #fff;
    border-radius: 50%;
    opacity: .9;
}
