/* ══════════════════════════════════════════════════════════
   ARDAM — Design System du hub (unifié, commun aux deux sites)
   Palette : bleu #174873 / gris #ecf0f1 (référence REPSI)
   Ce fichier est LA référence de style. Les applications
   héritent de ces variables et ne redéfinissent PAS les
   fondamentaux (pas de :root, pas de !important).
   ══════════════════════════════════════════════════════════ */

/* ─── 1. Variables ─────────────────────────────────────────── */
:root {
    /* Couleurs dominantes */
    --hub-blue:        #174873;
    --hub-blue-light:  #3a8abf;
    --hub-blue-hover:  #316f9f;   /* survol des boutons bleus : toujours PLUS CLAIR que --hub-blue */
    --hub-blue-dark:   #0f3450;
    --hub-blue-pale:   #dce7f0;
    --hub-bg:          #ecf0f1;
    --hub-bg-dark:     #d5dbe0;
    --hub-surface:     #ffffff;

    /* Texte */
    --hub-text:        #1a1a2e;
    --hub-text-muted:  #5a6a78;
    --hub-text-light:  #f5f7f9;

    /* Couleurs fonctionnelles (sémantique uniquement, désaturées) */
    --hub-success-bg:  #e4efe8;
    --hub-success:     #2e6b4f;
    --hub-danger-bg:   #f3e4e2;
    --hub-danger:      #a03d33;
    --hub-warning-bg:  #f1ece0;
    --hub-warning:     #8a6d2f;

    /* Mise en forme */
    --hub-radius:      8px;
    --hub-radius-lg:   12px;
    --hub-shadow:      0 2px 8px rgba(23, 72, 115, 0.10);
    --hub-shadow-lg:   0 8px 24px rgba(23, 72, 115, 0.18);
    --hub-font:        'Segoe UI', system-ui, -apple-system, sans-serif;

    /* Alias REPSI */
    --repsi-bg:         var(--hub-bg);
    --repsi-blue:       var(--hub-blue);
    --repsi-blue-light: var(--hub-blue-light);
    --repsi-blue-dark:  var(--hub-blue-dark);
    --repsi-bg-dark:    var(--hub-bg-dark);
    --repsi-text-light: var(--hub-text-light);
}

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

html { scroll-behavior: smooth; }

body {
    margin: 0;
    padding: 0;
    font-family: var(--hub-font);
    font-size: 15px;
    line-height: 1.5;
    color: var(--hub-text);
    background: var(--hub-bg);
    min-height: 100vh;
}

a { color: var(--hub-blue); text-decoration: none; }
a:hover { color: var(--hub-blue-light); text-decoration: underline; }

img { max-width: 100%; }

h1, h2, h3, h4 { color: var(--hub-blue); font-weight: 600; margin: 0 0 0.5em; }

::selection { background: var(--hub-blue); color: #fff; }

* { scrollbar-width: thin; scrollbar-color: var(--hub-bg-dark) transparent; }
*::-webkit-scrollbar { width: 8px; height: 8px; }
*::-webkit-scrollbar-thumb { background: var(--hub-bg-dark); border-radius: 4px; }
*::-webkit-scrollbar-thumb:hover { background: var(--hub-blue-hover); }

/* ─── 3. Structure du hub ──────────────────────────────────── */
.hub-header {
    position: sticky;
    top: 0;
    z-index: 1000;
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 8px 20px;
    background: linear-gradient(180deg, var(--hub-blue) 0%, var(--hub-blue-dark) 100%);
    color: var(--hub-text-light);
    box-shadow: var(--hub-shadow);
}

.hub-header .hub-logo {
    height: 44px;
    width: auto;
    border-radius: 6px;
    background: #fff;
    padding: 3px;
}

.hub-header .hub-title { flex: 1; min-width: 0; }
.hub-header .hub-title h1 {
    margin: 0;
    font-size: 18px;
    line-height: 1.2;
    color: #fff;
    letter-spacing: 0.5px;
}
.hub-header .hub-title .hub-subtitle {
    margin: 0;
    font-size: 11px;
    opacity: 0.75;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.hub-user {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 13px;
    white-space: nowrap;
}
.hub-user .hub-user-name { font-weight: 600; }
.hub-user .hub-user-detail { opacity: 0.7; font-size: 12px; }

.hub-main {
    max-width: 1420px;
    margin: 0 auto;
    padding: 20px;
}

.hub-section-title {
    font-size: 16px;
    font-weight: 600;
    color: var(--hub-blue);
    border-bottom: 2px solid var(--hub-bg-dark);
    padding-bottom: 6px;
    margin: 24px 0 12px;
}
.hub-section-title:first-child { margin-top: 0; }

.hub-footer {
    text-align: center;
    padding: 16px;
    font-size: 12px;
    color: var(--hub-text-muted);
    border-top: 1px solid var(--hub-bg-dark);
    margin-top: 32px;
}
.hub-footer b { color: var(--hub-blue); }

.hub-top-btn {
    position: fixed;
    bottom: 24px;
    right: 24px;
    width: 44px;
    height: 44px;
    border: none;
    border-radius: 50%;
    background: var(--hub-blue);
    color: #fff;
    font-size: 20px;
    cursor: pointer;
    box-shadow: var(--hub-shadow-lg);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.25s, background 0.25s;
    z-index: 1500;
}
.hub-top-btn.visible { opacity: 1; pointer-events: auto; }
.hub-top-btn:hover { background: var(--hub-blue-hover); }

/* ─── 4. Lanceur d'applications ────────────────────────────── */
.hub-launcher {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(148px, 1fr));
    gap: 10px;
}

.hub-app-tile {
    position: relative;
    /* Hauteur FIXE : identique avec ou sans badge (le badge est
       en surimpression et ne modifie pas la mise en page). */
    height: 70px;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: flex-start;
    gap: 10px;
    padding: 8px 10px;
    background: var(--hub-surface);
    border: 1px solid var(--hub-bg-dark);
    border-radius: var(--hub-radius-lg);
    color: var(--hub-blue);
    font-weight: 600;
    font-size: 12px;
    line-height: 1.2;
    text-align: left;
    cursor: pointer;
    transition: transform 0.15s, box-shadow 0.15s, border-color 0.15s;
    font-family: inherit;
    overflow: hidden;
}
.hub-app-tile:hover {
    transform: translateY(-2px);
    box-shadow: var(--hub-shadow-lg);
    border-color: var(--hub-blue-light);
    text-decoration: none;
}
.hub-app-tile img {
    height: 44px;
    width: 44px;
    object-fit: contain;
    flex-shrink: 0;
}
.hub-app-tile > span {
    flex: 1;
    min-width: 0;
    /* 2 lignes max avec ellipsis */
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* Tuile désactivée : non migrée ou droits insuffisants */
.hub-app-tile.disabled {
    opacity: 0.55;
    filter: grayscale(0.8);
    cursor: not-allowed;
}
.hub-app-tile.disabled:hover {
    transform: none;
    box-shadow: none;
    border-color: var(--hub-bg-dark);
}

/* Badge « à migrer » / « pas de droits » en surimpression,
   coin supérieur droit, n'affecte pas la hauteur de la tuile. */
.hub-tile-badge {
    position: absolute;
    top: 4px;
    right: 4px;
    font-size: 9px;
    font-weight: 600;
    color: var(--hub-text-muted);
    background: var(--hub-surface);
    border: 1px solid var(--hub-bg-dark);
    border-radius: 10px;
    padding: 1px 6px;
    box-shadow: 0 1px 3px rgba(0,0,0,0.08);
    pointer-events: none;
    white-space: nowrap;
}

/* ─── Mot de passe oublié (page login) ─────────────────────── */
.hub-login-forgot {
    margin-top: 12px;
    text-align: center;
    font-size: 13px;
}
.hub-login-forgot a {
    color: var(--hub-blue);
    text-decoration: none;
    border-bottom: 1px dotted var(--hub-blue-light);
}
.hub-login-forgot a:hover {
    color: var(--hub-blue-light);
    border-bottom-style: solid;
}

/* ─── Toasts (base — hub.js définit hubToast(msg)) ─────────
   Positionné en bas au centre, une seule notification à la fois.
   Variantes fournies par HubToast.info/success/warning/error. */
#hub-toast {
    position: fixed;
    left: 50%;
    bottom: 24px;
    transform: translate(-50%, 20px);
    background: var(--hub-blue);
    color: #fff;
    padding: 10px 18px;
    border-radius: var(--hub-radius);
    box-shadow: var(--hub-shadow-lg);
    font-size: 14px;
    font-weight: 500;
    max-width: min(560px, calc(100vw - 32px));
    opacity: 0;
    pointer-events: none;
    transition: transform 0.2s, opacity 0.2s;
    z-index: 6000;
}
#hub-toast.visible {
    transform: translate(-50%, 0);
    opacity: 1;
}
#hub-toast.hub-toast--success { background: var(--hub-success);  }
#hub-toast.hub-toast--warning { background: var(--hub-warning);  }
#hub-toast.hub-toast--error   { background: var(--hub-danger);   }
/* hub-toast--info reste sur --hub-blue (défaut) */

/* ─── Cartes de liste (pattern REPSI généralisé) ───────────
   Utilisé pour toute liste de lignes CRUD (signataires, motifs,
   annuaire, stocks, parcs…). Structure typique :
     <div class="hub-list">
       <div class="hub-list-item">
         <div class="hub-list-item-main"> … </div>
         <div class="hub-list-item-actions"> boutons </div>
       </div>
     </div>
*/
.hub-list { display: flex; flex-direction: column; gap: 8px; }
.hub-list-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 14px;
    background: var(--hub-surface);
    border: 1px solid var(--hub-bg-dark);
    border-radius: var(--hub-radius);
    transition: border-color 0.15s, box-shadow 0.15s;
}
.hub-list-item:hover {
    border-color: var(--hub-blue-light);
    box-shadow: var(--hub-shadow);
}
.hub-list-item-main { flex: 1; min-width: 0; }
.hub-list-item-title { font-weight: 600; color: var(--hub-blue); }
.hub-list-item-sub { font-size: 13px; color: var(--hub-text-muted); }
.hub-list-item-actions {
    display: flex;
    gap: 6px;
    flex-shrink: 0;
}
.hub-list-item-actions .btn {
    min-height: 32px;
    padding: 4px 10px;
    font-size: 0.85rem;
}
.hub-list-empty {
    text-align: center;
    padding: 32px;
    color: var(--hub-text-muted);
    font-size: 14px;
}

/* ─── Badges sémantiques (statuts, tags…) ──────────────────
   Différents des .user-badge qui portent le niveau de droit.
   Ceux-là servent aux données (« en retard », « archivé »…) */
.hub-badge {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 10px;
    font-size: 11px;
    font-weight: 600;
    white-space: nowrap;
    background: var(--hub-bg-dark);
    color: var(--hub-blue);
}
.hub-badge-info    { background: var(--hub-blue-pale); color: var(--hub-blue); }
.hub-badge-success { background: var(--hub-success-bg); color: var(--hub-success); }
.hub-badge-warning { background: var(--hub-warning-bg); color: var(--hub-warning); }
.hub-badge-danger  { background: var(--hub-danger-bg);  color: var(--hub-danger);  }

/* ─── En-tête d'application (logo | titre | badge droit) ────
   Utilisé par toutes les apps (REPSI, template…), rendu au
   choix : en dur en PHP ou via HubHeader.render() en JS.
   Nom / prénom / matricule sont affichés PAR LE HUB, pas ici. */
.app-header {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 16px;
}
.app-header-logo { flex: 0 0 auto; }
.app-header-logo .logo { height: 48px; width: auto; }
.app-header-title { flex: 1; text-align: center; }
.app-header-title h1 {
    font-size: 20px;
    margin: 0;
    line-height: 1.1;
    color: var(--hub-blue);
}
.app-header-title .subtitle {
    font-size: 11px;
    margin: 0;
    color: var(--hub-text-muted);
}
.app-header-badge {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    color: var(--hub-blue);
}

/* Badge droit — 3 variantes */
.user-badge {
    padding: 3px 10px;
    border-radius: 12px;
    font-size: 12px;
    font-weight: 600;
    white-space: nowrap;
}
.badge-readonly { background: var(--hub-bg-dark); color: var(--hub-blue); }
.badge-write    { background: #2ecc71;            color: #ffffff; }
.badge-admin    { background: #f1c40f;            color: #1a1a2e; }

/* ─── 5. Cartes, formulaires, boutons, tableaux ────────────── */
.hub-card {
    background: var(--hub-surface);
    border: 1px solid var(--hub-bg-dark);
    border-radius: var(--hub-radius-lg);
    box-shadow: var(--hub-shadow);
    padding: 20px;
    margin-bottom: 16px;
}

/* ─── Formulaires (utilisés par HubModal + apps) ──────────── */
.hub-form-group {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-bottom: 12px;
}
.hub-form-group label {
    font-size: 13px;
    font-weight: 600;
    color: var(--hub-blue);
}
.hub-form-group input,
.hub-form-group textarea,
.hub-form-group select {
    padding: 8px 12px;
    background: var(--hub-bg);
    border: 1px solid var(--hub-bg-dark);
    border-radius: var(--hub-radius);
    color: var(--hub-text);
    font-size: 14px;
    font-family: inherit;
}
.hub-form-group input:focus,
.hub-form-group textarea:focus,
.hub-form-group select:focus {
    outline: none;
    border-color: var(--hub-blue-light);
    box-shadow: 0 0 0 2px var(--hub-blue-pale);
}
/* Prime sur la règle globale input[type=…] (page login) déclarée plus
   bas dans ce fichier : sans ceci, nom/prénom/email (text/email)
   restaient à fond blanc dans les modales alors que tel/select/textarea
   étaient gris. Spécificité supérieure = gris garanti. */
.hub-form-group input[type="text"],
.hub-form-group input[type="password"],
.hub-form-group input[type="email"] {
    background: var(--hub-bg);
}
.hub-form-hint {
    font-size: 11px;
    color: var(--hub-text-muted);
}

/* ─── Popup unifiée (HubModal) ─────────────────────────────── */
.hub-modal {
    position: fixed;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(23, 72, 115, 0.35);
    z-index: 5000;
    padding: 16px;
}
.hub-modal-box {
    width: 100%;
    max-width: 500px;
    max-height: 90vh;
    overflow-y: auto;
    background: var(--hub-surface);
    border-radius: var(--hub-radius-lg);
    box-shadow: var(--hub-shadow-lg);
    padding: 22px;
}
.hub-modal-box-large {
    max-width: 720px;
}
.hub-modal-content {
    margin-bottom: 8px;
}
.hub-modal-title {
    font-size: 18px;
    font-weight: 600;
    color: var(--hub-blue);
    margin-bottom: 12px;
}
.hub-modal-actions {
    display: flex;
    gap: 10px;
    justify-content: flex-end;
    margin-top: 6px;
}
.hub-modal-error {
    margin-top: 8px;
    padding: 8px 12px;
    background: var(--hub-danger-bg);
    color: var(--hub-danger);
    border-radius: var(--hub-radius);
    font-size: 13px;
}

.hub-card-title {
    font-size: 16px;
    font-weight: 600;
    color: var(--hub-blue);
    border-bottom: 2px solid var(--hub-bg);
    padding-bottom: 8px;
    margin-bottom: 14px;
}

.btn, button.btn, input[type="submit"].btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    min-height: 34px;
    padding: 6px 16px;
    border: 1px solid #bcd3e6;
    border-radius: var(--hub-radius);
    background: #dfe9f2;
    color: var(--hub-blue, #174873);
    font-family: inherit;
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
    text-decoration: none;
    transition: background 0.2s, transform 0.1s;
}
.btn:hover { background: #cfe0ee; text-decoration: none; }
.btn:active { transform: scale(0.98); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }

/* ── VARIANTES ─────────────────────────────────────────────
   ⚠ Les variantes DOIVENT être écrites « .btn.btn-xxx » (0,2,0).
   Écrites « .btn-xxx » (0,1,0), elles PERDAIENT contre le sélecteur
   de base « button.btn » (0,1,1) : un <button class="btn btn-secondary">
   restait donc BLEU au repos, mais son :hover (plus spécifique)
   s'appliquait → il sautait du bleu foncé au gris clair au survol.
   C'était la cause des survols incohérents.
   Chaque variante s'ÉCLAIRCIT au survol, avec une amplitude comparable.
   (15/07/2026 : les variantes PLEINES d'origine sont retirées — le bloc
   HARMONIE PASTEL ci-dessous est désormais l'unique source.) */

/* ═══ HARMONIE PASTEL COMPLÈTE — TOUS les boutons du hub (15/07/2026,
   Éric : « harmonie complète dans ARDAM »). ═══
   Chaque famille de bouton passe en fond PASTEL clair + texte foncé de
   la même teinte, aligné sur le gris « Annuler ». Vaut pour les boutons
   STANDARDS (HubButtons) comme PERSONNALISÉS des apps, émoji-seul ou
   avec texte, en grille comme en modale. Le survol reste un ton plus
   soutenu. SEULE exception : .btn-ghost (header, transparent sur fond
   bleu foncé — le pastel clair le rendrait illisible), traité à part. */

/* Bleu (btn-primary explicite — la base .btn est déjà pastel bleu) */
.btn.btn-primary {
    background: #dfe9f2; color: var(--hub-blue, #174873);
    border: 1px solid #bcd3e6;
}
.btn.btn-primary:hover { background: #cfe0ee; }

/* Gris (secondary, Annuler / Fermer / Balai 🧹 / Copier 📋) — RÉFÉRENCE */
.btn.btn-secondary        { background: var(--hub-bg-dark); color: var(--hub-text); border: 1px solid #c2c9cf; }
.btn.btn-secondary:hover  { background: #c2c9cf; }

/* Ocre (btn-warning, ✏️ Modifier) */
.btn.btn-warning {
    background: var(--hub-warning-bg, #f1ece0); color: var(--hub-warning, #8a6d2f);
    border: 1px solid #e2d3ad;
}
.btn.btn-warning:hover { background: #e9dec2; }

/* Rouge (btn-danger, 🗑️ Supprimer) */
.btn.btn-danger {
    background: var(--hub-danger-bg, #f3e4e2); color: var(--hub-danger, #a03d33);
    border: 1px solid #e8c4bd;
}
.btn.btn-danger:hover { background: #eed3ce; }

/* Vert (btn-success) */
.btn.btn-success {
    background: #e2efe8; color: var(--hub-success, #2e6b4f);
    border: 1px solid #bcdccb;
}
.btn.btn-success:hover { background: #d3e7dc; }


/* Compatibilité : variantes utilisées SANS la classe .btn — pastel aussi */
.btn-secondary { background: var(--hub-bg-dark); color: var(--hub-text); }
.btn-secondary:hover { background: #c2c9cf; }
.btn-primary { background: #dfe9f2; color: var(--hub-blue, #174873); }
.btn-primary:hover { background: #cfe0ee; }
.btn-success { background: #e2efe8; color: var(--hub-success, #2e6b4f); }
.btn-success:hover { background: #d3e7dc; }
.btn-danger { background: var(--hub-danger-bg, #f3e4e2); color: var(--hub-danger, #a03d33); }
.btn-danger:hover { background: #eed3ce; }
/*  15/07/2026 : sélecteurs renforcés (button.btn.btn-ghost) pour battre
    la base pastel « button.btn » (0,1,1) — sinon le header prenait le
    fond pastel clair et devenait illisible sur le bandeau bleu foncé. */
.btn.btn-ghost, button.btn.btn-ghost, a.btn.btn-ghost {
    background: transparent;
    color: var(--hub-text-light);
    border: 1px solid rgba(255, 255, 255, 0.4);
}
.btn.btn-ghost:hover, button.btn.btn-ghost:hover, a.btn.btn-ghost:hover { background: rgba(255, 255, 255, 0.12); }

input[type="text"], input[type="password"], input[type="email"],
input[type="number"], input[type="date"], input[type="time"],
textarea, select {
    font-family: inherit;
    font-size: 14px;
    color: var(--hub-text);
    background: var(--hub-surface);
    border: 1px solid var(--hub-bg-dark);
    border-radius: var(--hub-radius);
    padding: 7px 10px;
    transition: border-color 0.2s, box-shadow 0.2s;
}
input:focus, textarea:focus, select:focus {
    outline: none;
    border-color: var(--hub-blue-light);
    box-shadow: 0 0 0 3px rgba(23, 72, 115, 0.15);
}

.hub-form-group { margin-bottom: 14px; }
.hub-form-group label {
    display: block;
    font-size: 13px;
    font-weight: 600;
    color: var(--hub-blue);
    margin-bottom: 4px;
}

/* Conteneur scrollable horizontalement : évite qu'une table large
   déborde de l'écran sur mobile (la table garde sa largeur, le
   conteneur défile). Appliqué automatiquement par HubTable. */
.hub-table-wrap { width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; }

table.hub-table { border-collapse: collapse; background: var(--hub-surface); }
table.hub-table th {
    background: var(--hub-blue);
    color: #fff;
    padding: 8px 12px;
    text-align: left;
    font-size: 13px;
}
table.hub-table td {
    padding: 7px 12px;
    border-bottom: 1px solid var(--hub-bg);
    font-size: 14px;
}
table.hub-table tr:hover td { background: var(--hub-blue-pale); }

/* Mémo flags PowerShell (lib/flags_powershell.php — partagé
   Annuaire/Administration ARDIPN54, règle nº 0). */
.hub-note-flags { font-size: 13px; color: var(--hub-text-soft, #555); margin: 4px 0 10px; }
/*  (Éric, 15/07/2026) : sans table-layout fixed, le navigateur
    donnait presque toute la largeur à la colonne services et
    étirait la colonne agents en hauteur. Répartition imposée :
    Valeur étroite, deux colonnes de texte à ÉGALITÉ. */
table.hub-table-flags { width: 100%; table-layout: fixed; }
table.hub-table-flags th:first-child { width: 56px; }
table.hub-table-flags td { vertical-align: top; line-height: 1.45; word-wrap: break-word; }
table.hub-table-flags td:first-child { white-space: nowrap; text-align: center; }

/* HubTable — en-têtes triables + pagination (lib/dama-app.js) */
table.hub-table th.hub-table-sortable { cursor: pointer; user-select: none; }
table.hub-table th.hub-table-sortable:hover { background: var(--hub-blue-hover); }
.hub-table-pager {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    margin-top: 10px;
    font-size: 13px;
    color: var(--hub-text-muted);
}

/* HubAutocomplete — dropdown filtré (lib/dama-app.js) */
.hub-autocomplete {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: 1200;
    max-height: 260px;
    overflow-y: auto;
    background: var(--hub-surface);
    border: 1px solid var(--hub-bg-dark);
    border-radius: 0 0 var(--hub-radius) var(--hub-radius);
    box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15);
}
.hub-autocomplete-item {
    padding: 7px 12px;
    font-size: 14px;
    cursor: pointer;
}
.hub-autocomplete-item:hover,
.hub-autocomplete-item.active {
    background: var(--hub-blue-pale);
    color: var(--hub-blue);
}

.hub-badge {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 12px;
    font-size: 11px;
    font-weight: 600;
    background: var(--hub-bg-dark);
    color: var(--hub-text);
}
.hub-badge-blue { background: var(--hub-blue); color: #fff; }

.hub-msg {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 16px;
    border-radius: var(--hub-radius);
    border: 1px solid var(--hub-bg-dark);
    background: var(--hub-surface);
    margin: 10px auto;
    max-width: 640px;
    font-size: 14px;
}
.hub-msg-success { background: var(--hub-success-bg); color: var(--hub-success); border-color: var(--hub-success); }
.hub-msg-danger  { background: var(--hub-danger-bg);  color: var(--hub-danger);  border-color: var(--hub-danger); }
.hub-msg-warning { background: var(--hub-warning-bg); color: var(--hub-warning); border-color: var(--hub-warning); }
.hub-danger-text { color: var(--hub-danger); font-weight: 600; }

/* ─── 6. Connexion ─────────────────────────────────────────── */
.hub-login-wrap {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 48px 16px;
}
.hub-login-card {
    width: 100%;
    max-width: 400px;
    background: var(--hub-surface);
    border: 1px solid var(--hub-bg-dark);
    border-radius: var(--hub-radius-lg);
    box-shadow: var(--hub-shadow-lg);
    padding: 32px;
}
.hub-login-card h2 {
    text-align: center;
    font-size: 18px;
    margin-bottom: 20px;
}
.hub-login-card input { width: 100%; }
.hub-login-card .btn { width: 100%; margin-top: 6px; }
.hub-login-error {
    display: none;
    margin-top: 12px;
    padding: 10px;
    border-radius: var(--hub-radius);
    background: var(--hub-danger-bg);
    color: var(--hub-danger);
    font-size: 13px;
    text-align: center;
}
.hub-login-rgpd {
    max-width: 640px;
    margin-top: 32px;
    font-size: 12px;
    color: var(--hub-text-muted);
    text-align: center;
    line-height: 1.6;
}

/* ─── 7. Spinner global (pattern REPSI) ────────────────────── */
#hub-spinner {
    position: fixed;
    inset: 0;
    display: none;
    align-items: center;
    justify-content: center;
    flex-direction: column;
    gap: 14px;
    background: rgba(236, 240, 241, 0.75);
    backdrop-filter: blur(2px);
    z-index: 99999;
}
#hub-spinner.active { display: flex; }
#hub-spinner .hub-spinner-ring {
    width: 48px;
    height: 48px;
    border: 5px solid var(--hub-bg-dark);
    border-top-color: var(--hub-blue);
    border-radius: 50%;
    animation: hub-spin 0.8s linear infinite;
}
#hub-spinner .hub-spinner-text {
    font-size: 14px;
    font-weight: 600;
    color: var(--hub-blue);
}
@keyframes hub-spin { to { transform: rotate(360deg); } }

/* ─── 7bis. Composants d'APPLICATION (transverses) ──────────
   Composants réutilisés par TOUTES les applications : onglets,
   barre d'outils, cartes de liste, état vide. Centralisés ici
   (et NON dans chaque app.css) pour rester fidèles à l'esprit
   du hub : le visuel mutualisé vit dans le hub, l'app ne porte
   que son métier. Une app ne doit PAS redéfinir ces classes ;
   elle les utilise telles quelles. Le squelette HTML attendu :
     <div class="nav-tabs"><button class="nav-tab active">…</button></div>
     <div class="tab-content active">…</div>   (bascule display via JS)
     <div class="toolbar"><input class="toolbar-search">…</div>
     <div class="entry-card"><div class="entry-main">…</div>
          <div class="entry-actions">…</div></div>
     <div class="empty-state">…</div>
   ────────────────────────────────────────────────────────── */

/* Onglets */
.nav-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    margin-bottom: 14px;
    border-bottom: 2px solid var(--hub-bg-dark);
}
.nav-tab {
    border: none;
    background: transparent;
    padding: 10px 16px;
    font-family: inherit;
    font-size: 14px;
    font-weight: 600;
    color: var(--hub-text-muted);
    cursor: pointer;
    border-radius: var(--hub-radius) var(--hub-radius) 0 0;
    transition: background 0.15s, color 0.15s;
}
.nav-tab:hover { background: var(--hub-blue-pale); color: var(--hub-blue); }
.nav-tab.active { background: var(--hub-blue); color: #fff; }
/* Onglet actif (fond bleu) : survol unifié comme les boutons bleus,
   sinon il héritait de .nav-tab:hover (bleu pâle → quasi blanc). */
.nav-tab.active:hover { background: var(--hub-blue-hover); color: #fff; }

/* Panneaux d'onglets : visibilité pilotée par la classe .active
   (le JS de l'app ne bascule QUE cette classe, sans toucher au
   style inline). */
.tab-content { display: none; }
.tab-content.active { display: block; }

/* Barre d'outils */
.toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin-bottom: 14px;
}
.toolbar-search { flex: 1; min-width: 150px; max-width: 260px; }

/* Cartes de liste */
.entry-card {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 14px;
    margin-bottom: 8px;
    background: var(--hub-bg);
    border: 1px solid var(--hub-bg-dark);
    border-radius: var(--hub-radius);
    transition: border-color 0.15s, box-shadow 0.15s;
}
.entry-card:hover { border-color: var(--hub-blue-light); box-shadow: var(--hub-shadow); }
.entry-main { flex: 1; min-width: 0; }
.entry-name { font-weight: 600; color: var(--hub-blue); }
.entry-comment { font-size: 13px; color: var(--hub-text-muted); }
.entry-actions { display: flex; gap: 6px; flex-shrink: 0; flex-wrap: wrap; }
.entry-actions .btn { min-height: 32px; padding: 4px 10px; font-size: 0.85rem; }

/* État vide */
.empty-state {
    text-align: center;
    padding: 32px;
    color: var(--hub-text-muted);
    font-size: 14px;
}

/* Fiche détaillée (paires label/valeur), typiquement rendue dans
   la popup globale du hub (HubModal content) → non préfixée. */
.fiche-detail { display: flex; flex-direction: column; gap: 2px; }
.fiche-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 7px 4px;
    border-bottom: 1px solid var(--hub-bg-dark);
}
.fiche-row:last-child { border-bottom: none; }
.fiche-label {
    flex: 0 0 150px;
    font-weight: 600;
    color: var(--hub-text-muted);
    font-size: 13px;
}
.fiche-val { flex: 1; min-width: 0; word-break: break-word; }
/* Bouton d'action en bout de ligne (ex. copier), aligné à droite
   de façon constante d'une ligne à l'autre. */
.fiche-action { flex: 0 0 auto; margin-left: auto; }

/* ─── 8. Impression / responsive ───────────────────────────── */
@media print {
    .no-print, .hub-header, .hub-top-btn, .hub-footer,
    #hub-spinner, #hub-toast { display: none !important; }
    body { background: #fff; }
}

@media (max-width: 720px) {
    .hub-header { flex-wrap: wrap; padding: 8px 12px; row-gap: 8px; }
    /* Le titre prend toute la largeur de la 1re ligne (à côté du
       logo) ; .hub-user passe alors réellement sur la 2e ligne au
       lieu de se superposer au titre. */
    .hub-header .hub-title { flex: 1 1 auto; }
    .hub-header .hub-title h1 { font-size: 15px; }
    .hub-header .hub-subtitle { display: none; }
    .hub-header .hub-user {
        flex: 1 0 100%;
        justify-content: flex-end;
        flex-wrap: wrap;
    }
    .hub-main { padding: 12px; }
    .hub-launcher { grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); }

    /* Composants d'application : cartes de liste en colonne sur mobile */
    .entry-card { flex-direction: column; align-items: stretch; }
    .entry-actions { justify-content: flex-end; }
}

/* ─── Classes utilitaires issues d'ARDIPN54 (repsi…) ──────────
   Intégrées au socle commun ARDAM. Palette identique (mêmes
   variables --hub-*), donc aucun thème par site nécessaire. */
.filter-lieu {
    padding: 6px 10px;
    background: var(--hub-bg);
    border: 1px solid var(--hub-bg-dark);
    border-radius: var(--hub-radius);
    color: var(--hub-text);
    font-size: 13px;
    cursor: pointer;
}
.hub-input {
    padding: 6px 10px;
    background: var(--hub-bg);
    border: 1px solid var(--hub-bg-dark);
    border-radius: var(--hub-radius);
    color: var(--hub-text);
    font-size: 13px;
    font-family: inherit;
}
.hub-input--sm { padding: 4px 8px; border-radius: 6px; }
.hub-input--block { width: 100%; }
.hub-input--mono { font-family: monospace; }
.hub-input:focus {
    outline: none;
    border-color: var(--hub-blue-light);
    box-shadow: 0 0 0 2px var(--hub-blue-pale);
}
.hub-section-title--tight { margin-bottom: 10px; }

/* ─── Badge d'environnement ────────────────────────────── */
.env-badge {
    display: inline-block;
    padding: 2px 10px;
    border-radius: 12px;
    font-size: 11px;
    font-weight: 600;
    color: #fff;
    vertical-align: middle;
    margin-left: 6px;
    letter-spacing: 0.3px;
}

.env-prod {
    background: #d32f2f;
}

.env-preprod {
    background: #f57c00;
}

.env-dev {
    background: #388e3c;
}

.env-switch-btn {
    display: inline-block;
    margin-left: 6px;
    text-decoration: none;
    transition: transform .2s ease;
}
.env-switch-btn:hover { transform: rotate(90deg); }

/* ─── HubRichEditor — éditeur de texte riche ──────────── */
.hub-rich-editor {
    border: 1px solid var(--hub-bg-dark);
    border-radius: var(--hub-radius);
    overflow: hidden;
}
.hub-rich-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 2px;
    padding: 6px 8px;
    background: var(--hub-bg);
    border-bottom: 1px solid var(--hub-bg-dark);
}
.hub-rich-btn {
    width: 30px;
    height: 28px;
    border: none;
    border-radius: 4px;
    background: transparent;
    color: var(--hub-text);
    font-size: 14px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background 0.15s;
}
.hub-rich-btn:hover { background: var(--hub-bg-dark); }
.hub-rich-btn.active { background: var(--hub-blue-pale); color: var(--hub-blue); }
.hub-rich-btn-bold { font-weight: 700; }
.hub-rich-btn-italic { font-style: italic; }
.hub-rich-btn-underline { text-decoration: underline; }
.hub-rich-btn-strike { text-decoration: line-through; }
.hub-rich-btn-clear { font-size: 12px; color: var(--hub-text-muted); }
.hub-rich-sep {
    width: 1px;
    height: 18px;
    background: var(--hub-bg-dark);
    margin: 0 4px;
}
.hub-rich-content {
    padding: 10px 14px;
    background: var(--hub-surface);
    font-size: 14px;
    font-family: inherit;
    line-height: 1.6;
    color: var(--hub-text);
    outline: none;
    overflow-y: auto;
    max-height: 400px;
}
.hub-rich-content:focus {
    box-shadow: inset 0 0 0 2px var(--hub-blue-pale);
}
.hub-rich-content.hub-rich-empty::before {
    content: attr(data-placeholder);
    color: var(--hub-text-muted);
    pointer-events: none;
}
/* Select taille de texte */
.hub-rich-select {
    height: 28px;
    padding: 2px 6px;
    border: 1px solid var(--hub-bg-dark);
    border-radius: 4px;
    background: transparent;
    color: var(--hub-text);
    font-size: 12px;
    cursor: pointer;
    line-height: normal;
}
.hub-rich-select:focus { outline: none; border-color: var(--hub-blue); }
/* Emoji picker */
.hub-rich-emoji-wrap { position: relative; display: inline-flex; }
.hub-rich-emoji-drop {
    display: none;
    position: absolute;
    top: 100%;
    /* Ancré à DROITE du bouton : avec « left:0 », le panneau partait vers
       la droite et SORTAIT du cadre de la modale (le bouton émoji est en
       fin de barre d'outils). */
    right: 0;
    left: auto;
    z-index: 100;
    width: 260px;
    max-width: 90vw;
    max-height: 180px;
    overflow-y: auto;
    background: var(--hub-surface);
    border: 1px solid var(--hub-bg-dark);
    border-radius: var(--hub-radius);
    box-shadow: var(--hub-shadow-lg);
    padding: 6px;
    flex-wrap: wrap;
}
.hub-rich-emoji-drop.open { display: flex; }
.hub-rich-emoji {
    width: 32px;
    height: 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 18px;
    cursor: pointer;
    border-radius: 4px;
    transition: background 0.15s;
}
.hub-rich-emoji:hover { background: var(--hub-bg); }

/* HubRichEditor — menus déroulants (titres, couleur, lien) */
.hub-rich-drop {
    display: none; position: absolute; top: 100%; left: 0; z-index: 100;
    max-width: 90vw;
    background: var(--hub-surface); border: 1px solid var(--hub-bg-dark);
    border-radius: var(--hub-radius); box-shadow: var(--hub-shadow-lg); padding: 6px;
}
.hub-rich-drop.open { display: block; }
.hub-rich-swatches { display: flex; flex-wrap: wrap; gap: 4px; width: 156px; }
.hub-rich-swatch {
    width: 22px; height: 22px; border-radius: 4px; cursor: pointer;
    border: 1px solid rgba(0, 0, 0, 0.15);
}
.hub-rich-swatch:hover { outline: 2px solid var(--hub-blue-light); }
.hub-rich-menu-item { padding: 6px 10px; cursor: pointer; border-radius: 4px; white-space: nowrap; }
.hub-rich-menu-item:hover { background: var(--hub-bg); }
.hub-rich-link-panel { display: flex; gap: 6px; align-items: center; }
.hub-rich-link-panel input {
    width: 200px; padding: 5px 8px; border: 1px solid var(--hub-bg-dark);
    border-radius: 6px; font-family: inherit;
}
.hub-rich-btn-fore { text-decoration: underline; text-decoration-color: #a03d33; }

/* Rendu des titres / images / liens (édition ET affichage lecture seule) */
.hub-rich-content h3, .hub-rich-display h3 { font-size: 1.25em; margin: 8px 0; }
.hub-rich-content h4, .hub-rich-display h4 { font-size: 1.1em;  margin: 6px 0; }
.hub-rich-content img, .hub-rich-display img { max-width: 100%; height: auto; }
.hub-rich-content a, .hub-rich-display a { color: var(--hub-blue); }
/* Listes dans l'éditeur */
.hub-rich-content ul, .hub-rich-content ol { margin: 4px 0 4px 20px; }
.hub-rich-content li { margin-bottom: 2px; }

/* Rendu HTML riche (lecture seule, dans les cards/modales) */
.hub-rich-display { line-height: 1.6; }
.hub-rich-display ul, .hub-rich-display ol { margin: 4px 0 4px 20px; }
.hub-rich-display li { margin-bottom: 2px; }

/* ─── HubParams : limites d'envoi de fichiers ──────────────
   Écran commun affiché dans l'onglet Paramètres des apps. */
.hub-limites { display: flex; flex-direction: column; gap: 8px; }
.hub-limite-row {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}
.hub-limite-row label { flex: 1 1 220px; font-size: 14px; }
.hub-limite-row input { width: 100px; }
.hub-limite-unite { font-weight: 600; color: var(--hub-text-muted); }
/* P7 (12/07/2026) : largeur FIXE du texte « défaut : NNN Mo » pour que
   toutes les cases de saisie restent alignées quelle que soit la
   longueur du défaut (30 vs 400 Mo). */
.hub-limite-defaut { font-size: 12px; color: var(--hub-text-muted); min-width: 118px; }
.hub-limite-info {
    font-size: 12px;
    color: var(--hub-text-muted);
    margin-top: 4px;
}
.hub-limites .btn { align-self: flex-start; margin-top: 6px; }

/* ─── HubClean : nettoyage des fichiers orphelins ──────────── */
.hub-clean-hint { font-size: 13px; color: var(--hub-text-muted); margin: 0 0 10px; line-height: 1.5; }
.hub-clean-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.hub-clean-result { margin-top: 10px; }
.hub-clean-liste { margin: 8px 0 0; padding-left: 18px; font-size: 12px; max-height: 220px; overflow-y: auto; }

/* ─── Cases à cocher et boutons radio ─────────────────────
   ⚠ RÈGLE GÉNÉRALE — une case à cocher ne doit JAMAIS s'étirer.
   Les formulaires déclarent couramment « input { width: 100% } »
   pour aligner les champs texte ; sans l'exception ci-dessous, la
   CASE s'étire elle aussi sur toute la largeur, et le libellé se
   retrouve rejeté loin à sa droite (un grand vide entre les deux).
   Cette règle vaut pour TOUT le hub : ne pas la recopier par app. */
input[type="checkbox"],
input[type="radio"] {
    width: auto !important;
    flex: 0 0 auto;
    margin: 0;
}
/* Un libellé contenant une case reste collé à elle. */
label > input[type="checkbox"],
label > input[type="radio"] {
    margin-right: 6px;
    vertical-align: middle;
}

/* ─── Rapport détaillé (multi-lignes) ──────────────────────
   ⚠ « .hub-msg » est un display:flex, conçu pour un message COURT
   sur une seule ligne. Dès qu'on y met du contenu structuré (<b>,
   <br>, <ul>…), chaque élément devient une COLONNE — le rapport
   s'affiche alors en escalier, illisible.
   Pour tout contenu multi-lignes, utiliser « .hub-rapport ». */
.hub-rapport {
    display: block;
    padding: 12px 16px;
    border-radius: var(--hub-radius);
    border: 1px solid var(--hub-bg-dark);
    background: var(--hub-bg);
    font-size: 13px;
    line-height: 1.6;
}
.hub-rapport ul { margin: 8px 0 0; padding-left: 20px; max-height: 260px; overflow-y: auto; }
.hub-rapport li { margin-bottom: 2px; }
.hub-rapport code { font-size: 12px; }
.hub-rapport.hub-rapport-danger {
    background: var(--hub-danger-bg);
    border-color: var(--hub-danger);
}

/* ─── HubLightbox : image en plein écran ───────────────────── */
.hub-lightbox {
    position: fixed;
    inset: 0;
    /* ⚠ AU-DESSUS des modales (.hub-modal = 5000) : une image ouverte
       DEPUIS une modale (fiche d'un film, d'un aliment…) doit passer
       devant elle. À 2000, elle s'ouvrait DERRIÈRE, donc invisible. */
    z-index: 6000;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.88);
    padding: 24px;
    cursor: zoom-out;
}
.hub-lightbox figure {
    margin: 0;
    max-width: 100%;
    max-height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    cursor: default;
}
.hub-lightbox img {
    max-width: 100%;
    max-height: calc(100vh - 90px);
    border-radius: 6px;
    box-shadow: 0 8px 40px rgba(0, 0, 0, 0.6);
}
.hub-lightbox figcaption {
    color: #fff;
    font-size: 14px;
    text-align: center;
}
.hub-lightbox-close {
    position: absolute;
    top: 14px;
    right: 18px;
    width: 38px;
    height: 38px;
    border: none;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.15);
    color: #fff;
    font-size: 18px;
    cursor: pointer;
}
.hub-lightbox-close:hover { background: rgba(255, 255, 255, 0.3); }

/* ─── Barre d'outils sur MOBILE ────────────────────────────
   ⚠ « .toolbar » est un flex ; ses enfants portent souvent « flex: 1 1 auto »
   (champ de recherche, selects). En passant en colonne sur un petit écran,
   ce flex-grow s'applique à l'axe VERTICAL : chaque champ s'étire en hauteur
   et occupe un tiers de l'écran. Il faut annuler la croissance.
   Même piège que les formulaires — voir la règle « FORMULAIRES SUR MOBILE ». */
@media (max-width: 600px) {
    .toolbar {
        flex-direction: column;
        align-items: stretch;
    }
    .toolbar > * {
        flex: 0 0 auto !important;   /* ne JAMAIS s'étirer en hauteur */
        width: 100%;
    }
    .toolbar-search { min-width: 0; }
}

/* ═══ Sous-onglets universels ARDAM (HubSubTabs) ═══ */
.hub-subtabs { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 14px; }
.hub-subtab {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 14px;
    border: 1px solid var(--hub-bg-dark, #d8dfe5);
    border-radius: 18px;
    background: var(--hub-surface, #fff);
    color: var(--hub-blue, #174873);
    font-family: inherit;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.2s, color 0.2s;
}
.hub-subtab img { width: 20px; height: 20px; object-fit: contain; }
.hub-subtab:hover { background: var(--hub-blue-pale, #e8f0f7); }
.hub-subtab.active { background: var(--hub-blue, #174873); color: #fff; border-color: var(--hub-blue, #174873); }

.hub-btn-icon {
    padding: 6px 14px;
    font-size: 15px;
    line-height: 1;
    min-width: 48px;
    /*  15/07/2026 (Éric) : LARGEUR COMMUNE — tous les boutons émoji-seul
        ont la même largeur généreuse (celle des « Annuler »), plus de
        variantes compactes. Hauteur commune, icône centrée. */
    min-height: 34px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

/* Combo (select ARDAM) : même rendu que les inputs */
.hub-modal-combo { cursor: pointer; }

/* ═══ Fiche en mode LECTURE (règle ARDAM) : présentation naturelle,
   aucun champ de saisie. Utilisé par les fiches de toutes les apps. ═══ */
.prc-lecture-fiche { padding: 2px 4px; }
.prc-l-entete {
    display: flex;
    align-items: center;
    gap: 14px;
    padding-bottom: 12px;
    margin-bottom: 14px;
    border-bottom: 1px solid var(--hub-bg-dark, #d8dfe5);
}
.prc-l-entete img, .prc-l-icone { width: 44px; height: 44px; object-fit: contain; font-size: 34px; }
.prc-l-titre { font-size: 18px; font-weight: 700; color: var(--hub-blue, #174873); }
.prc-l-sous  { font-size: 13px; color: var(--hub-text-soft, #5a6b7a); }
.prc-l-etat  { margin-left: auto; }
.prc-l-grille {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 4px 28px;
    margin-bottom: 8px;
}
.prc-l-grille1 { grid-template-columns: 1fr; }

/*  14/07/2026 (Éric, sur smartphone) : la grille tentait DEUX
    colonnes dès 220px chacune — sur un écran de 380px, le libellé
    mangeait 110px et la valeur se cassait LETTRE PAR LETTRE à la
    verticale (« A-l-c-o-o-l-s »). Sous 700px : UNE colonne, et le
    libellé cesse d'imposer sa largeur. Le CSS des fiches étant
    unique (règle nº 0), ce correctif vaut pour les DIX apps. */
@media (max-width: 700px) {
    .prc-l-grille { grid-template-columns: 1fr; gap: 2px 0; }
    .prc-l-paire  { flex-wrap: wrap; }
    .prc-l-lab    { min-width: 0; flex: 0 0 auto; }
    .prc-l-val    { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
}
.prc-l-paire { display: flex; gap: 10px; padding: 5px 0; border-bottom: 1px dotted var(--hub-bg-dark, #e3e9ee); font-size: 13px; }
.prc-l-lab { flex: 0 0 150px; color: var(--hub-text-soft, #5a6b7a); font-weight: 600; }
.prc-l-val { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.prc-l-bloc { margin: 12px 0 0; font-size: 13px; }
.prc-l-bloc .prc-l-lab { display: block; margin-bottom: 4px; }
.prc-l-texte {
    background: var(--hub-blue-pale, #f2f6fa);
    border-radius: 8px;
    padding: 10px 12px;
    white-space: pre-wrap;       /* jamais tronqué */
    overflow-wrap: anywhere;
}
.stk-photo-clic img { cursor: zoom-in; }

/* Bouton 📋 discret dans les fiches en lecture */
.prc-l-val .hub-btn-icon, .prc-l-sous .hub-btn-icon, .prc-l-titre .hub-btn-icon,
.prc-l-bloc .prc-l-lab .hub-btn-icon {
    padding: 1px 7px;
    font-size: 12px;
    min-width: auto;
    margin-left: 6px;
    vertical-align: middle;
}

/* Lignes cliquables (fiche en lecture) */
.hub-table-cliquable { cursor: pointer; }
.hub-table-cliquable:hover { background: var(--hub-blue-pale, #eef4f9); }

/* Pagination (règle ARDAM : 50 lignes max partout) */
.hub-pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    margin: 14px 0 4px;
    /*  16/07 tard (Éric, capture) : les zones paginées sont souvent
        des GRILLES ou des maçonneries — sans ceci, chaque barre
        occupait une CELLULE (1er et dernier élément) au lieu de
        s'étendre au-dessus et en dessous de la grille. */
    grid-column: 1 / -1;     /* conteneur display:grid  */
    column-span: all;        /* conteneur columns (maçonnerie) */
    flex-basis: 100%;        /* conteneur flex-wrap */
    width: 100%;
    font-size: 13px;
    /*  8terdecies (16/07 tard, Éric) : les zones paginées sont souvent
        des GRILLES de cartes — sans ceci, la barre devenait un item de
        grille (1re et dernière cellule). Pleine ligne au-dessus et
        en-dessous, quel que soit le conteneur (grid, flex-wrap, flux). */
    grid-column: 1 / -1;
    width: 100%;
    color: var(--hub-text-soft, #5a6b7a);
}

/* Toolbar unifiée (recherche + 🧹 + extras) */
.hub-toolbar {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 10px;
    flex-wrap: wrap;
}
.hub-toolbar-search { flex: 1; min-width: 180px; }

/* ═══ Balai intégré des cases HubAutocomplete (règle du 12/07/2026) ═══
   Injecté par lib/hub-app.js (_injecterBalai) : span.hub-ac-wrap
   enveloppe la case, le 🧹 est accolé à droite DANS la case.
   Le wrap sert aussi d'ancre position:relative au menu déroulant. */
.hub-ac-wrap { position: relative; display: inline-flex; max-width: 100%; vertical-align: middle; }
.hub-ac-wrap > input { width: 100%; min-width: 0; padding-right: 28px; }
/* Dans une modale, la case occupe toute la largeur de son champ. */
.hub-modal-content .hub-ac-wrap { width: 100%; }
.hub-ac-balai {
    position: absolute; right: 3px; top: 50%; transform: translateY(-50%);
    border: none; background: none; cursor: pointer;
    font-size: 12px; line-height: 1; padding: 3px; opacity: .55;
}
.hub-ac-balai:hover { opacity: 1; }

/* ═══ REPÈRE VISUEL DES 3 MODES DE CHAMP À CHOIX (15/07/2026, Éric) ═══
   Icône à GAUCHE + fine bordure gauche colorée, posées par
   _poserMode() du hub. Subtil mais immédiatement lisible.
     .hub-ac-filtre  🔍 neutre — recherche (n'écrit rien)
     .hub-ac-ferme   🔒 bleu   — choix imposé (référentiel fini)
     .hub-ac-ouvert  ➕ vert   — création possible (popup « créer ? ») */
.hub-ac-mode {
    position: absolute; left: 7px; top: 50%; transform: translateY(-50%);
    font-size: 12px; line-height: 1; pointer-events: none; opacity: .7;
}
.hub-ac-wrap.hub-ac-filtre > input,
.hub-ac-wrap.hub-ac-ferme  > input,
.hub-ac-wrap.hub-ac-ouvert > input,
.hub-ac-wrap.hub-ac-libre  > input {
    padding-left: 26px;
    border-left-width: 3px;
    border-left-style: solid;
}
.hub-ac-wrap.hub-ac-filtre > input { border-left-color: var(--hub-border, #cbd5e0); }
.hub-ac-wrap.hub-ac-ferme  > input { border-left-color: var(--hub-blue-light, #3a8abf); }
.hub-ac-wrap.hub-ac-ouvert > input { border-left-color: var(--hub-success, #2e6b4f); }
/* 'libre' : vert CLAIR + POINTILLÉ = saisie libre sans référentiel
   (pas de popup), à distinguer du vert plein 'ouvert' qui, lui,
   déclenche la création dans un référentiel. */
.hub-ac-wrap.hub-ac-libre > input {
    border-left-style: dotted;
    border-left-color: #8fc4a8;
}

/* K4 (12/07/2026) : cases à cocher des modales FIELDS — case collée au
   texte, une seule ligne, label cliquable. */
.hub-check { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; font-weight: 600; }
.hub-check input[type="checkbox"] { margin: 0; flex: 0 0 auto; }

/* 12/07/2026 : note furtive (🛡️ intégrité) — coin bas GAUCHE, canal
   séparé du toast principal (#hub-toast, bas droite). */
#hub-toast-discret {
    position: fixed;
    left: 14px;
    bottom: 14px;
    z-index: 9998;
    padding: 5px 10px;
    border-radius: 6px;
    background: rgba(23, 72, 115, .82);
    color: #fff;
    font-size: 12px;
    opacity: 0;
    pointer-events: none;
    transform: translateY(6px);
    transition: opacity .25s, transform .25s;
}
#hub-toast-discret.visible { opacity: .92; transform: translateY(0); }


/* AD1 (13/07/2026, règle GÉNÉRALE) : dans une fiche en lecture, les
   boutons 📋 sont ALIGNÉS en colonne (poussés au bord droit de la
   ligne), au lieu de flotter à des positions variables selon la
   longueur des valeurs. Vaut pour les paires prc-l-* du hub. */
.prc-l-paire { display: flex; align-items: center; gap: 6px; }
.prc-l-paire .prc-l-val { flex: 1 1 auto; min-width: 0; }
.prc-l-paire > .hub-btn-icon[data-hub-copie],
.prc-l-paire .prc-l-val > .hub-btn-icon[data-hub-copie] { margin-left: auto; flex: 0 0 auto; }

/* GH2 → (15/07/2026, Éric) : HARMONIE COMPLÈTE — tous les boutons
   émoji-seul du hub ont désormais LA MÊME largeur, celle des boutons
   « Annuler » (plus large). On NE réduit plus ✏️/🗑️ : la réduction
   d'antan est retirée. Seul 📋 Copier reste discret (petit). */
.hub-btn-icon[data-hub-copie] {
    padding: 4px 7px;
    font-size: 13px;
    min-width: 30px;
}

/* ══ Formulaire de fiche — COMPOSANT TRANSVERSE (13/07/2026) ══
   .stk-champ / .stk-note étaient dupliquées dans Stocks, Parcs et
   Cartes SIM (non préfixables : le contenu des fiches vit dans la
   MODALE, hors racine d'app). PROMUES ici — source unique ; les
   copies locales ont été retirées. Nom « stk- » conservé (héritage
   Stocks) pour ne pas renommer des dizaines d'occurrences. */
.stk-champ { margin-bottom: 10px; position: relative; }
.stk-champ label { display: block; font-weight: 600; font-size: 13px; margin-bottom: 3px; }
.stk-champ label small { font-weight: normal; color: var(--hub-text-soft, #5a6b7a); }
.stk-champ .hub-input { width: 100%; box-sizing: border-box; }
.stk-note { color: var(--hub-text-soft, #5a6b7a); font-size: 13px; margin: 6px 0 12px; }

/* ─── PIÈGE [hidden] TUÉ (13/07/2026) ────────────────────────
   Toute règle display (ex .btn{display:inline-flex},
   .ann-multibar{display:flex}) NEUTRALISE l'attribut hidden :
   deux bugs réels le 13/07 (➕ Agent visible pour tous, barre
   « 0 coché » affichée à vide). Règle canonique : hidden GAGNE,
   toujours, partout. */
[hidden] { display: none !important; }


/* ─── 16/07/2026 (Éric) — RÈGLE ARDAM §5.8sexies ÉTENDUE :
   SOUS-ONGLETS SYSTÉMATIQUES. Dès qu'un onglet contient PLUS DE
   2 blocs pleine largeur (≥ 3), OU 5 petits blocs et plus (les
   cartes colonnisées de ⚙️ Paramètres / 🛠️ Maintenance), il passe
   en sous-onglets HubSubTabs — un thème à la fois, plus de pages
   interminables. Patron NORMALISÉ :
     <div id="xx-subtabs"></div>
     <div class="hub-sous" id="xx-sous-<id>"> …cartes… </div>
   + app.js : HubSubTabs.render(el('xx-subtabs'), defs,
              HubSubTabs.blocs('#xx-app', 'xx-sous-'));
   L'onglet porteur reçoit la classe hub-avec-sous : les COLONNES
   des onglets de réglages descendent alors au bloc VISIBLE (sinon
   le bandeau et les wrappers seraient eux-mêmes colonnisés). */
.hub-sous { display: none; }
.hub-sous.active { display: block; }
#tab-parametres.tab-content.active.hub-avec-sous,
#tab-maintenance.tab-content.active.hub-avec-sous { columns: auto; }
/*  16/07 soir (Éric) — RÈGLE ARDAM « OCCUPER LA LARGEUR » :
    un sous-onglet à CARTE UNIQUE prend TOUTE la largeur (ses
    sections internes se colonnisent via .hub-cols ci-dessous) ;
    le flux maçonnerie (columns) ne s'applique qu'aux sous à
    PLUSIEURS cartes — la classe hub-sous-multi est posée par
    HubSubTabs.blocs() (pas de :has(), postes anciens). */
#tab-parametres > .hub-sous.active,
#tab-maintenance > .hub-sous.active { display: block; }
#tab-parametres > .hub-sous.active.hub-sous-multi,
#tab-maintenance > .hub-sous.active.hub-sous-multi {
    columns: 2 360px;
    column-gap: 14px;
}

/*  .hub-cols — colonnes INTERNES d'une carte (règle « occuper la
    largeur ») : sections côte à côte, une seule colonne quand
    l'écran rétrécit. Remplace les stk-cols/otl-cols locaux. */
.hub-cols { display: flex; gap: 28px; flex-wrap: wrap; }
.hub-cols > div { flex: 1 1 280px; min-width: 0; }

/* ─── 13/07/2026 (Éric, 2049) — RÈGLE ARDAM : les onglets de
   RÉGLAGES (⚙️ Paramètres de CHAQUE app, 🛠️ Maintenance des deux
   Administrations) s'affichent en COLONNES. ⚠ Le GRID (essai 2031)
   aligne les cartes par RANGÉE : une carte haute (les grades !)
   creusait un grand blanc à côté d'une carte courte. On passe donc
   en MULTI-COLONNES (flux « maçonnerie ») : chaque carte se pose à
   la suite, dans la colonne la plus courte — plus de trous. Une
   seule colonne quand la fenêtre rétrécit. La règle vit ICI :
   aucune app n'a rien à faire. ─── */
#tab-parametres.tab-content.active,
#tab-maintenance.tab-content.active,
.ann-params-grille, .hub-params-grille {
    display: block;
    columns: 2 360px;          /* 2 colonnes tant que ≥ 360px chacune */
    column-gap: 14px;
}
/*  Une carte ne se coupe JAMAIS entre deux colonnes. */
#tab-parametres .hub-card, #tab-maintenance .hub-card,
.ann-params-grille > *, .hub-params-grille > * {
    break-inside: avoid;
    -webkit-column-break-inside: avoid;
    page-break-inside: avoid;
    margin-bottom: 14px;
}
/*  Grille interne d'une app qui groupait déjà ses cartes (Repsi :
    .settings-grid) : elle garde SA disposition et prend TOUTE la
    largeur — elle ne doit pas être compressée dans une colonne. */
#tab-parametres > .ann-params-grille,
#tab-parametres > .hub-params-grille,
#tab-maintenance > .ann-params-grille { columns: auto; }
#tab-parametres > .settings-grid,
#tab-maintenance > .settings-grid {
    column-span: all; -webkit-column-span: all; columns: auto;
}
/*  Une carte qui contient un TABLEAU (rapport de maintenance, liste
    large) est illisible à mi-écran : classe hub-pleine-largeur. */
#tab-parametres > .hub-card.hub-pleine-largeur,
#tab-maintenance > .hub-card.hub-pleine-largeur {
    column-span: all; -webkit-column-span: all;
}
/*  Les zones de RÉSULTAT (tableaux de maintenance) sortent du flux
    en colonnes : elles reprennent TOUTE la largeur. */
#tab-maintenance > [id$="-result"],
#tab-parametres > [id$="-resultat"],
#tab-parametres > .hub-pleine-largeur,
#tab-maintenance > .hub-pleine-largeur {
    column-span: all;
    -webkit-column-span: all;
}
@media (max-width: 820px) {
    #tab-parametres.tab-content.active,
    #tab-maintenance.tab-content.active,
    .ann-params-grille, .hub-params-grille { columns: 1; }
}


/* ─── 14/07/2026 (règle nº 0) : LE style unique des STATS et des
   ANOMALIES. Il était recopié dans cinq CSS d'apps (ann-stat-*,
   prc-stat-*, adm-stat-*, stk-*, sim-*) : les anciennes classes
   restent en ALIAS ci-dessous, le temps que les apps basculent. */
.hub-stat-cartes { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 14px; }
.hub-stat-carte {
    background: var(--hub-bg-card, #fff); border: 1px solid var(--hub-border, #d8dfe5);
    border-radius: 10px; padding: 10px 16px; min-width: 130px; text-align: center;
}
.hub-stat-carte b { display: block; font-size: 1.5rem; color: #174873; }
.hub-stat-carte span { font-size: 0.8rem; color: var(--text-secondary, #555); }
.hub-stat-carte.hub-stat-alerte { border-color: #e6b3b3; background: #fdf3f3; }
.hub-stat-carte.hub-stat-alerte b { color: #a33; }
.hub-stat-bloc {
    background: var(--hub-bg-card, #fff); border: 1px solid var(--hub-border, #d8dfe5);
    border-radius: 10px; padding: 12px 16px;
}
.hub-stat-ligne {
    display: grid; grid-template-columns: 280px 1fr 44px; gap: 8px;
    align-items: center; font-size: 13px; margin-bottom: 5px;
}
.hub-stat-barre { background: #eef3f8; border-radius: 4px; height: 12px; overflow: hidden; }
.hub-stat-barre > div { background: #7fb3dd; height: 100%; }
.hub-ano-total { margin: 6px 0 10px; font-size: 0.95rem; }
.hub-ano-table { margin-top: 8px; }
@media (max-width: 700px) {
    .hub-stat-ligne { grid-template-columns: 150px 1fr 40px; }
}


/*  14/07/2026 : mails et téléphones CLIQUABLES partout (mailto: /
    tel:) — la copie reste au bouton 📋. */
.hub-mail, .hub-tel { color: inherit; text-decoration: underline dotted; }
.hub-mail:hover, .hub-tel:hover { text-decoration: underline; }

/*  §5.5 : une carte entière est cliquable → le curseur le dit, et
    les boutons d'action gardent le leur. */
.entry-card { cursor: pointer; }
.entry-card .entry-actions { cursor: default; }


/*  14/07/2026 (Éric, capture Adhérents) : les boutons 📋 suivaient la
    VALEUR — donc en escalier, un par largeur de texte. Ils s'alignent
    désormais À DROITE de la cellule, comme l'ICasso : la valeur à
    gauche, le bouton en bout. Vaut pour TOUTES les fiches (HubFiche). */
.prc-l-val {
    display: flex; align-items: center; gap: 8px;
    justify-content: space-between; flex: 1 1 auto; min-width: 0;
    /*  (Éric, A5) : un élément en flex-basis:100% (ex. le bouton
        Activer/Désactiver) doit pouvoir passer À LA LIGNE — sans
        wrap, il restait collé à droite et écrasait le texte. */
    flex-wrap: wrap;
}
.prc-l-val > .hub-btn-icon { flex: 0 0 auto; margin-left: auto; }


/*  14/07/2026 (Éric, D16) : la colonne d'ACTIONS d'une grille est
    alignée à DROITE — sinon les lignes qui ont MOINS de boutons
    (action conditionnelle : visible(row)) décalent les autres. */
.hub-table td:last-child { text-align: right; white-space: nowrap; }


/* ─── HubTuto (14/07/2026) : la fenêtre FLOTTANTE d'accueil et le
   petit [?] qui la relance. ─────────────────────────────────── */
.hub-tuto-boite {
    position: fixed; right: 20px; bottom: 78px; z-index: 9000;
    width: min(340px, calc(100vw - 40px));
    background: var(--hub-bg-card, #fff);
    border: 1px solid var(--hub-border, #d8dfe5);
    border-radius: 14px; padding: 16px 18px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.18);
    animation: hub-tuto-in 0.18s ease-out;
}
@keyframes hub-tuto-in { from { opacity: 0; transform: translateY(8px); } }
.hub-tuto-boite h4 { margin: 0 0 8px; display: flex; align-items: center; gap: 8px; }
.hub-tuto-boite h4 img { width: 26px; height: 26px; object-fit: contain; }
.hub-tuto-boite p { margin: 0 0 12px; font-size: 0.9rem; line-height: 1.45; }
.hub-tuto-num { font-size: 0.75rem; color: var(--text-secondary, #667); margin-bottom: 4px; }
.hub-tuto-actions { display: flex; flex-wrap: wrap; gap: 6px; justify-content: flex-end; }
.hub-tuto-actions .btn { font-size: 0.82rem; padding: 5px 12px; }
.hub-tuto-btn {
    position: fixed; right: 20px; bottom: 20px; z-index: 8999;
    width: 42px; height: 42px; border-radius: 50%;
    border: 1px solid var(--hub-border, #d8dfe5);
    background: var(--hub-bg-card, #fff); color: #174873;
    font-size: 1.2rem; font-weight: 700; cursor: pointer; opacity: 0.75;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.12);
}
.hub-tuto-btn:hover { opacity: 1; }
/*  La tuile présentée est MISE EN AVANT. */
.hub-tuto-cible {
    outline: 3px solid #7fb3dd; outline-offset: 4px;
    border-radius: 12px; transition: outline-color 0.2s;
}
/*  Ancrée à côté d'une tuile (position calculée en JS) : la boîte
    passe en coordonnées absolues, avec une petite transition. */
.hub-tuto-boite@media (max-width: 700px) {
    /*  Sur mobile, la boîte reste ANCRÉE EN BAS, pleine largeur : la
        placer à côté d'une tuile n'aurait aucun sens (et la ferait
        sortir de l'écran). Le !important neutralise le calcul JS. */
    .hub-tuto-boite {
        right: 12px !important; left: 12px !important;
        top: auto !important; bottom: 70px !important;
        width: auto;
    }
}


/* ─── 14/07/2026 : PROFILS DE COMMUNS. Le CSS était resté dans
   l'annuaire alors que l'onglet a DÉMÉNAGÉ dans l'Administration
   (d'où : fiches non séparées, boutons collés). Il monte au HUB —
   il n'a rien de spécifique à une app. ─────────────────────────── */
.ann-profil {
    border: 1px solid var(--hub-border, #d8dfe5);
    border-radius: 10px; padding: 12px 14px; margin-bottom: 12px;
    background: var(--hub-bg-card, #fff);
}
.ann-profil h4 { margin: 0 0 8px; }
.ann-pr-ligne {
    display: grid; grid-template-columns: 1fr 150px 36px;
    gap: 8px; align-items: center; padding: 3px 0;
}
/*  La ligne d'ajout : le combo, le ➕, puis les actions — ESPACÉS. */
.ann-profil .ann-pr-ajout {
    display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
    margin-top: 10px; padding-top: 10px;
    border-top: 1px dotted var(--hub-border, #d8dfe5);
}
.ann-profil .ann-pr-ajout .btn { white-space: nowrap; }
.ann-check {
    display: inline-flex; align-items: center; gap: 6px;
    font-size: 13px; white-space: nowrap;
}


/* ─── 14/07/2026 (Éric) — RÈGLE ARDAM DE CONTRASTE, valable pour
   TOUT le hub : une ZONE de liste est BLANCHE (--hub-surface), et
   les FICHES/CARTES qu'elle contient sont GRIS CLAIR (--hub-bg).
   Jamais l'inverse, jamais gris sur gris (Adhérents), jamais blanc
   sur blanc (Films). Les cartes se détachent, la lecture est
   immédiate. ────────────────────────────────────────────────── */
.hub-card .entry-card,
.hub-card .ann-profil,
.hub-card .crs-carte,
.hub-card .flm-carte,
.hub-card .veg-carte {
    background: var(--hub-bg);
    border: 1px solid var(--hub-bg-dark);
}
.hub-card .entry-card:hover,
.hub-card .ann-profil:hover,
.hub-card .crs-carte:hover {
    border-color: var(--hub-blue-light);
    box-shadow: var(--hub-shadow);
}
/*  Le profil de communs suit la même règle (il était BLANC sur blanc). */
.ann-profil { background: var(--hub-bg); border: 1px solid var(--hub-bg-dark); }

/* Multi-numéros (15/07/2026) : les liens tel: restent groupés en flux
   texte même quand la valeur de fiche (.prc-l-val) est un conteneur flex. */
.hub-tel-multi { display: inline; white-space: normal; }

/* ─── 16/07/2026 — ÉDITORIAL (règle nº 0) : classes partagées des
   apps « articles riches » (astronomie_damamme) et des rendus
   éditoriaux apparentés (biographies d'artistika_damamme). Deux
   apps en avaient besoin : elles montent au hub. ─── */
.edito-entete { display: flex; justify-content: space-between; align-items: flex-start; gap: 8px; }
.edito-titre { font-size: 1.15em; font-weight: 700; }
.edito-soustitre { color: var(--hub-text-soft, #5a6b7a); font-style: italic; margin-top: 2px; }
.edito-image {
    display: block;
    max-width: min(520px, 100%);
    border-radius: var(--hub-radius, 8px);
    margin: 10px 0;
    cursor: zoom-in;
}
.edito-article .hub-rich-display { margin-top: 8px; }
.edito-input-pos { width: 90px; }

/* ─── 16/07/2026 (Éric) — APERÇU IMMÉDIAT des fichiers choisis
   (règle hub, délégation globale dans lib/hub-app.js). */
.hub-apercu-fichier { margin-top: 6px; display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.hub-apercu-fichier img {
    max-height: 120px; max-width: 200px; border-radius: var(--hub-radius, 8px);
    border: 1px solid var(--hub-bg-dark); object-fit: contain;
}
.hub-apercu-fichier span { color: var(--hub-text-soft, #5a6b7a); font-size: 13px; }

/* ─── 16/07/2026 (Éric) — HubListeCfg ORDONNABLE (glisser-déposer). */
.hub-lc-poignee { width: 1%; cursor: grab; color: var(--hub-text-soft, #5a6b7a); user-select: none; }
tr.hub-lc-survol td { border-top: 2px solid var(--hub-accent, #174873); }

/* ─── 16/07/2026 (Éric) — HubPhotos (règle 8undecies) : bloc photos
   NORMALISÉ des formulaires ➕/✏️ — principale + supplémentaires,
   vignettes cliquables (visionneuse), ✕ retirer, ⭐ promouvoir. */
.hub-photos-zone {
    display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
}
.hub-photo-item { position: relative; display: inline-flex; }
.hub-photo-item img {
    max-height: 90px; max-width: 140px; border-radius: var(--hub-radius, 8px);
    border: 1px solid var(--hub-bg-dark); object-fit: cover; cursor: zoom-in;
    /*  16/07 soir (Éric) : une image au fichier cassé s'écrasait à
        0×0 → ⭐ et ✕ superposés = « vignette fantôme ». Plancher de
        taille : l'entrée reste visible et gérable (✕ pour la retirer). */
    min-width: 64px; min-height: 64px; background: var(--hub-bg-dark);
}
.hub-photo-cassee img { border-style: dashed; opacity: .55; }
.hub-photo-cassee::after {
    content: '🖼️❓'; position: absolute; inset: 0;
    display: flex; align-items: center; justify-content: center;
    font-size: 22px; pointer-events: none;
}
.hub-photo-item-princ img {
    max-height: 140px; max-width: 220px;
    border: 2px solid var(--hub-accent, #174873);
}
.hub-photo-item .hub-photo-x,
.hub-photo-item .hub-photo-star {
    position: absolute; top: -6px; border: none; border-radius: 50%;
    width: 22px; height: 22px; line-height: 20px; font-size: 12px;
    cursor: pointer; padding: 0; box-shadow: 0 1px 3px rgba(0,0,0,.35);
}
.hub-photo-item .hub-photo-x   { right: -6px; background: var(--hub-danger, #a33); color: #fff; }
.hub-photo-item .hub-photo-star { left: -6px;  background: #fff; }
