/* ===========================================================================
   HUB DE SERVEURS
   ===========================================================================
   Site à part, mais habillé par le même système : cette feuille ne définit
   AUCUNE couleur, aucune police, aucun rayon. Elle se sert des jetons de
   style.css. Redéfinir « juste un bleu » ici serait le premier pas vers deux
   chartes qui divergent, et le Hub doit rester reconnaissable comme KairoBot
   malgré son domaine séparé.
   =========================================================================== */

/* ----- En-tête -------------------------------------------------------------
   La barre du Hub porte une entrée de plus que celle du site : l'appel à
   l'action « Ajouter mon serveur ». C'est ce qui la faisait déborder — le
   contenu tenait sur les autres pages et pas sur celle-là, sans que la page
   fautive n'explique rien.
   Trois garde-fous, du plus large au plus étroit :
     1. la marque peut rétrécir et se couper proprement plutôt que pousser ;
     2. sous 1200 px l'appel à l'action se réduit à « Ajouter » ;
     3. sous 900 px tout passe dans le menu, ce que style.css prévoit déjà. */
.hub-nav .nav-container { gap: 16px; }
.hub-nav .nav-logo { min-width: 0; flex-shrink: 1; }
.hub-nav .nav-logo > span {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.hub-nav .nav-menu { min-width: 0; flex-wrap: nowrap; }
.hub-cta { white-space: nowrap; flex-shrink: 0; }
.hub-cta-court { display: none; }

@media (max-width: 1200px) and (min-width: 901px) {
  .hub-nav .nav-menu { gap: 14px; }
  /* Le libellé long est le premier à céder : il porte la même information
     que la version courte, et l'icône « + » lève l'ambiguïté. */
  .hub-cta-long { display: none; }
  .hub-cta-court { display: inline; }
}
@media (max-width: 1020px) and (min-width: 901px) {
  /* Dernier cran avant le menu : le suffixe « Hub » disparaît, le logo suffit
     à dire où l'on est. */
  .hub-nav-suffixe { display: none; }
  .hub-nav .nav-menu { gap: 10px; }
}
@media (max-width: 900px) {
  /* Dans le menu déroulant, l'espace ne manque plus : on rétablit le libellé
     complet, qui est plus clair. */
  .hub-cta-long { display: inline; }
  .hub-cta-court { display: none; }
  .hub-cta { width: 100%; justify-content: center; }
}

.hub-wrap { max-width: var(--maxw); margin: 0 auto; padding: 110px 24px 80px; }

/* ----- En-tête ----- */
.hub-hero { text-align: center; margin-bottom: 30px; }
.hub-hero h1 { font-family: 'Space Grotesk', system-ui, sans-serif; font-size: clamp(1.8rem, 5vw, 2.6rem); letter-spacing: -0.02em; }
.hub-hero p { color: var(--text-2); margin-top: 10px; max-width: 60ch; margin-inline: auto; text-wrap: pretty; }
.hub-hero-actions { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin-top: 18px; }

/* ----- Recherche ----- */
.hub-recherche {
  display: flex; align-items: center; gap: 10px;
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: 14px; padding: 12px 16px; margin: 0 auto 14px; max-width: 560px;
  position: relative;
}
.hub-recherche i { color: var(--text-2); }
.hub-recherche input { flex: 1; min-width: 0; background: none; border: none; outline: none; color: var(--text); font: inherit; font-size: 1rem; }
.hub-recherche .hub-vider { background: none; border: none; color: var(--text-2); cursor: pointer; padding: 4px; }

/* Suggestions de tags. Elles se posent PAR-DESSUS, jamais en poussant la page :
   une liste qui déplace le contenu sous le doigt fait rater la cible. */
.hub-sugg {
  position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 30;
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: 12px; overflow: hidden; box-shadow: 0 18px 44px -18px rgba(0,0,0,0.5);
}
.hub-sugg button {
  display: flex; width: 100%; align-items: center; justify-content: space-between; gap: 10px;
  padding: 11px 14px; background: none; border: none; color: var(--text);
  font: inherit; font-size: 0.92rem; cursor: pointer; text-align: left;
}
.hub-sugg button:hover, .hub-sugg button[aria-selected='true'] { background: var(--accent-soft); color: var(--accent); }
.hub-sugg .hub-sugg-n { color: var(--text-2); font-size: 0.8rem; font-variant-numeric: tabular-nums; }

/* ----- Filtres ----- */
.hub-filtres { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; margin-bottom: 10px; }
.hub-chip {
  border: 1px solid var(--border-strong); background: var(--surface); color: var(--text-2);
  border-radius: 999px; padding: 8px 14px; font-size: 0.88rem; font-weight: 500;
  cursor: pointer; transition: color var(--transition), border-color var(--transition), background var(--transition);
}
.hub-chip:hover { color: var(--text); border-color: var(--accent); }
.hub-chip.actif { background: var(--accent-soft); color: var(--accent); border-color: var(--accent); }
.hub-select {
  background: var(--surface); border: 1px solid var(--border-strong); color: var(--text);
  border-radius: 999px; padding: 8px 12px; font: inherit; font-size: 0.88rem;
}
.hub-barre-etat { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; justify-content: center; margin: 16px 0 22px; color: var(--text-2); font-size: 0.9rem; }

/* ----- Grille de fiches ----- */
.hub-grille { display: grid; grid-template-columns: repeat(auto-fill, minmax(290px, 1fr)); gap: 16px; }
.hub-carte {
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--border); border-radius: 14px;
  overflow: hidden; transition: transform var(--transition), border-color var(--transition);
}
.hub-carte:hover { transform: translateY(-3px); border-color: var(--border-strong); }
.hub-banniere { height: 84px; background: var(--surface-2); background-size: cover; background-position: center; }
.hub-carte-tete { display: flex; gap: 12px; padding: 0 16px; margin-top: -26px; align-items: flex-end; }
.hub-icone {
  width: 56px; height: 56px; border-radius: 16px; object-fit: cover; flex-shrink: 0;
  border: 3px solid var(--surface); background: var(--surface-2);
}
.hub-carte-nom { min-width: 0; padding-bottom: 4px; }
.hub-carte-nom h3 { font-size: 1rem; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hub-carte-meta { color: var(--text-2); font-size: 0.82rem; display: flex; gap: 10px; flex-wrap: wrap; margin-top: 2px; }
.hub-carte-corps { padding: 12px 16px 0; flex: 1; }
.hub-desc { color: var(--text-2); font-size: 0.9rem; line-height: 1.5; display: -webkit-box; -webkit-line-clamp: 3; line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.hub-tags { display: flex; flex-wrap: wrap; gap: 6px; padding: 12px 16px; }
.hub-tag {
  border: 1px solid var(--border); background: var(--surface-2); color: var(--text-2);
  border-radius: 999px; padding: 4px 10px; font-size: 0.78rem; cursor: pointer;
}
.hub-tag:hover { color: var(--accent); border-color: var(--accent); }
.hub-carte-pied { display: flex; align-items: center; gap: 10px; padding: 12px 16px; border-top: 1px solid var(--border); }
.hub-bumps { font-variant-numeric: tabular-nums; font-weight: 600; }
.hub-bumps span { color: var(--text-2); font-weight: 400; font-size: 0.85rem; }
.hub-carte-pied .btn { margin-left: auto; }
.hub-badge {
  display: inline-flex; align-items: center; gap: 5px; font-size: 0.76rem; font-weight: 600;
  color: #57c98a; background: rgba(87, 201, 138, 0.12); border: 1px solid rgba(87, 201, 138, 0.3);
  border-radius: 999px; padding: 3px 9px;
}
.hub-cat { display: inline-flex; align-items: center; gap: 5px; }

/* ----- Classements ----- */
.hub-rangs { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 4px; margin-bottom: 18px; }
.hub-rangs::-webkit-scrollbar { height: 0; }
.hub-section-titre { display: flex; align-items: baseline; gap: 10px; margin: 34px 0 14px; }
.hub-section-titre h2 { font-family: 'Space Grotesk', system-ui, sans-serif; font-size: 1.15rem; }
.hub-section-titre p { color: var(--text-2); font-size: 0.86rem; }

/* ----- Tableaux de classement ----- */
.hub-table { width: 100%; border-collapse: collapse; }
.hub-table th { text-align: left; color: var(--text-2); font-size: 0.8rem; font-weight: 600; padding: 10px 12px; border-bottom: 1px solid var(--border); }
.hub-table td { padding: 12px; border-bottom: 1px solid var(--border); }
.hub-table tbody tr:hover { background: var(--surface-2); }
.hub-rang { font-variant-numeric: tabular-nums; font-weight: 700; color: var(--text-2); width: 3rem; }
.hub-rang-1 { color: #f0b232; }
.hub-rang-2 { color: #b9bbc6; }
.hub-rang-3 { color: #cd7f32; }
.hub-qui { display: flex; align-items: center; gap: 10px; min-width: 0; }
.hub-qui img { width: 32px; height: 32px; border-radius: 50%; flex-shrink: 0; background: var(--surface-2); }
.hub-qui b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hub-table-wrap { overflow-x: auto; background: var(--surface); border: 1px solid var(--border); border-radius: 14px; }

/* ----- Page d'un serveur ----- */
.hub-fiche-tete { background: var(--surface); border: 1px solid var(--border); border-radius: 16px; overflow: hidden; }
.hub-fiche-banniere { height: 180px; background: var(--surface-2); background-size: cover; background-position: center; }
.hub-fiche-ident { display: flex; gap: 16px; padding: 0 22px 22px; margin-top: -34px; align-items: flex-end; flex-wrap: wrap; }
.hub-fiche-ident .hub-icone { width: 88px; height: 88px; border-radius: 24px; }
.hub-fiche-titre { flex: 1; min-width: 200px; padding-bottom: 4px; }
.hub-fiche-titre h1 { font-family: 'Space Grotesk', system-ui, sans-serif; font-size: clamp(1.3rem, 4vw, 1.9rem); }
.hub-fiche-actions { display: flex; gap: 10px; flex-wrap: wrap; padding-bottom: 4px; }
.hub-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 12px; margin: 20px 0; }
.hub-stat { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 14px 16px; }
.hub-stat b { display: block; font-size: 1.4rem; font-variant-numeric: tabular-nums; }
.hub-stat span { color: var(--text-2); font-size: 0.82rem; }
.hub-bloc { background: var(--surface); border: 1px solid var(--border); border-radius: 14px; padding: 20px 22px; margin-bottom: 16px; }
.hub-bloc h2 { font-family: 'Space Grotesk', system-ui, sans-serif; font-size: 1.05rem; margin-bottom: 10px; }
.hub-bloc p { color: var(--text-2); line-height: 1.65; white-space: pre-wrap; text-wrap: pretty; }

/* ----- États vides et chargement ----- */
.hub-vide { text-align: center; color: var(--text-2); padding: 48px 20px; }
.hub-vide i { font-size: 2rem; opacity: 0.5; display: block; margin-bottom: 12px; }
.hub-vide h3 { color: var(--text); font-size: 1.05rem; margin-bottom: 6px; }
.hub-squelette { background: var(--surface); border: 1px solid var(--border); border-radius: 14px; height: 230px; animation: hubPulse 1.4s ease-in-out infinite; }
@keyframes hubPulse { 50% { opacity: 0.45; } }

/* ----- Pagination ----- */
.hub-pages { display: flex; justify-content: center; align-items: center; gap: 8px; margin-top: 26px; }
.hub-pages button { min-width: 40px; }

/* ===========================================================================
   Téléphone
   =========================================================================== */
@media (max-width: 640px) {
  .hub-wrap { padding: 92px 14px 60px; }
  .hub-grille { grid-template-columns: 1fr; }
  .hub-filtres { justify-content: flex-start; }
  /* Les filtres défilent latéralement plutôt que d'empiler six lignes de
     pastilles avant le premier serveur. */
  .hub-rangs { margin-inline: -14px; padding-inline: 14px; }
  .hub-fiche-ident { padding-inline: 16px; }
  .hub-fiche-actions { width: 100%; }
  .hub-fiche-actions .btn { flex: 1; justify-content: center; }
  .hub-fiche-banniere { height: 130px; }
}

/* Cibles tactiles : même règle que le dashboard, au doigt et non à la largeur. */
@media (pointer: coarse) {
  .hub-chip, .hub-select, .hub-tag { min-height: 44px; display: inline-flex; align-items: center; }
  .hub-recherche { min-height: 52px; }
  .hub-recherche input { font-size: 1rem; }
  .hub-sugg button { min-height: 48px; }
  .hub-pages button { min-height: 44px; }
}

/* Lien « Voir tout » du titre de section : poussé à droite, et il descend sous
   le titre plutôt que de le comprimer quand la place manque. */
.hub-section-titre { flex-wrap: wrap; }
.hub-voir-tout {
  margin-left: auto; display: inline-flex; align-items: center; gap: 6px;
  color: var(--accent); font-size: 0.88rem; font-weight: 500; white-space: nowrap;
}
.hub-voir-tout:hover { text-decoration: underline; }
@media (max-width: 640px) {
  .hub-voir-tout { margin-left: 0; width: 100%; }
}
@media (pointer: coarse) { .hub-voir-tout { min-height: 44px; } }

/* ----- Menu du compte -------------------------------------------------------
   Cliquer son pseudo ouvre un menu ; ça ne quitte pas le site. C'était un lien
   vers le dashboard du bot : on se retrouvait ailleurs sans l'avoir demandé. */
.hub-moi { position: relative; }
.hub-moi-btn {
  display: inline-flex; align-items: center; gap: 8px;
  background: var(--surface); border: 1px solid var(--border-strong); color: var(--text);
  border-radius: 999px; padding: 5px 12px 5px 6px; font: inherit; font-size: 0.88rem;
  cursor: pointer; max-width: 190px;
  transition: border-color var(--transition);
}
.hub-moi-btn:hover { border-color: var(--accent); }
.hub-moi-btn img { border-radius: 50%; flex-shrink: 0; }
.hub-moi-btn > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hub-moi-caret { font-size: 0.68rem; color: var(--text-2); }
.hub-moi-btn[aria-expanded='true'] .hub-moi-caret { transform: rotate(180deg); }

.hub-moi-menu {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 60;
  min-width: 228px; padding: 6px;
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: 14px; box-shadow: 0 20px 48px -18px rgba(0, 0, 0, 0.55);
  display: flex; flex-direction: column; gap: 2px;
}
.hub-moi-menu a {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px; border-radius: 9px;
  color: var(--text); font-size: 0.9rem; white-space: nowrap;
}
.hub-moi-menu a:hover { background: var(--surface-2); }
.hub-moi-menu a i { width: 16px; color: var(--text-2); }
.hub-moi-menu a:hover i { color: var(--accent); }
.hub-moi-sep { height: 1px; background: var(--border); margin: 4px 2px; }
.hub-moi-sortir { color: #e0556a !important; }
.hub-moi-sortir i { color: #e0556a !important; }

/* Dans le menu déroulant du téléphone, la barre du haut est déjà repliée :
   le menu du compte s'y déplie sur place plutôt que de flotter par-dessus. */
@media (max-width: 900px) {
  .hub-moi, .hub-moi-btn { width: 100%; max-width: none; justify-content: center; }
  .hub-moi-menu { position: static; width: 100%; margin-top: 8px; box-shadow: none; }
}
@media (pointer: coarse) {
  .hub-moi-btn { min-height: 44px; }
  .hub-moi-menu a { min-height: 44px; }
}

/* ===========================================================================
   Mes serveurs
   =========================================================================== */
.hub-mienne-tete { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.hub-mienne-nom { flex: 1; min-width: 160px; }
.hub-mienne-nom h2 { font-size: 1.05rem; }
.hub-mienne-aide { color: var(--text-2); font-size: 0.9rem; margin-top: 10px; }
.hub-motif {
  margin-top: 12px; padding: 12px 14px; border-radius: 10px;
  background: rgba(240, 169, 59, 0.08); border: 1px solid rgba(240, 169, 59, 0.28);
}
.hub-motif b { font-size: 0.82rem; }
.hub-motif p { color: var(--text); margin-top: 4px; white-space: pre-wrap; }

/* ===========================================================================
   Modération
   =========================================================================== */
.hub-grille2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 16px; }
.hub-bloc-action { border-color: rgba(240, 169, 59, 0.35); }
.hub-stat-action b { color: #f0b232; }
.hub-file { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 10px; margin-top: 12px; }
.hub-file-item {
  display: flex; flex-direction: column; gap: 3px; text-align: left;
  padding: 12px 14px; border-radius: 10px; cursor: pointer;
  background: var(--surface-2); border: 1px solid var(--border); color: var(--text); font: inherit;
}
.hub-file-item:hover { border-color: var(--accent); }
.hub-lien { background: none; border: 0; color: var(--accent); font: inherit; cursor: pointer; padding: 0; }
.hub-alerte { color: #e0556a; font-weight: 600; }

/* Graphiques : du SVG écrit à la main. Quelques dizaines de lignes contre trois
   cents kilo-octets de bibliothèque, pour deux courbes et deux répartitions. */
.hub-graph { width: 100%; height: auto; display: block; margin-top: 8px; }
.hub-graph-pied { display: flex; justify-content: space-between; color: var(--text-2); font-size: 0.76rem; margin-top: 4px; }
.hub-barres { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
.hub-barre { display: grid; grid-template-columns: minmax(90px, 1fr) 2fr auto; align-items: center; gap: 10px; font-size: 0.86rem; }
.hub-barre-lbl { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hub-barre-piste { height: 8px; border-radius: 999px; background: var(--surface-2); overflow: hidden; }
.hub-barre-piste > span { display: block; height: 100%; background: var(--accent); border-radius: 999px; }

/* Tiroir de modération : mêmes repères que le tiroir des candidatures. */
.hub-d {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 61;
  width: min(620px, 100vw); display: flex; flex-direction: column;
  background: var(--surface); border-left: 1px solid var(--border);
  box-shadow: -18px 0 48px -18px rgba(0, 0, 0, 0.5);
}
.hub-d-tete { display: flex; align-items: flex-start; gap: 12px; padding: 18px 20px; border-bottom: 1px solid var(--border); }
.hub-d-tete h2 { font-size: 1.05rem; }
.hub-d-tete .rep-ic { margin-left: auto; }
.hub-d-corps { flex: 1; overflow-y: auto; padding: 16px 20px calc(24px + env(safe-area-inset-bottom, 0)); }
.hub-d-corps .hub-bloc { padding: 16px; }
.hub-d-corps h3 { font-family: 'Space Grotesk', system-ui, sans-serif; font-size: 0.95rem; margin-bottom: 10px; }
.hub-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.hub-champ { display: block; font-size: 0.84rem; color: var(--text-2); margin-bottom: 12px; }
.hub-champ input, .hub-champ textarea, .hub-champ select {
  display: block; width: 100%; margin-top: 5px; padding: 10px 12px;
  background: var(--surface-2); border: 1px solid var(--border-strong); border-radius: 9px;
  color: var(--text); font: inherit; font-size: 0.9rem;
}
.hub-champs2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.hub-saisie { padding: 16px 20px; border-bottom: 1px solid var(--border); background: var(--surface-2); }
.hub-saisie textarea {
  width: 100%; margin-top: 8px; padding: 10px 12px; border-radius: 9px;
  background: var(--surface); border: 1px solid var(--border-strong); color: var(--text); font: inherit;
}
.hub-journal { list-style: none; display: flex; flex-direction: column; gap: 10px; }
.hub-journal li { font-size: 0.86rem; padding-bottom: 10px; border-bottom: 1px solid var(--border); }
.hub-journal li:last-child { border-bottom: 0; }
.hub-journal-detail { color: var(--text-2); margin-top: 3px; white-space: pre-wrap; }

@media (max-width: 640px) {
  .hub-d { width: 100vw; top: auto; height: 92dvh; border-left: 0; border-radius: 18px 18px 0 0; }
  .hub-champs2 { grid-template-columns: 1fr; }
  .hub-barre { grid-template-columns: 1fr; gap: 4px; }
}
@media (pointer: coarse) {
  .hub-file-item { min-height: 44px; }
  .hub-champ input, .hub-champ select { min-height: 44px; font-size: max(1rem, 0.9rem); }
}

/* ===========================================================================
   Dépôt d'une fiche
   ===========================================================================
   Une vraie page, pas une fenêtre : l'aperçu vit à côté du formulaire, et une
   modale contenant les deux serait à l'étroit partout. */
.hub-ajout { display: grid; grid-template-columns: minmax(0, 1fr) 330px; gap: 20px; align-items: start; }
.hub-ajout-apercu { position: sticky; top: 100px; }
.hub-ajout-apercu h2 { font-family: 'Space Grotesk', system-ui, sans-serif; font-size: 1rem; }
.hub-ajout-apercu > p { margin-bottom: 12px; }

.hub-choix-serveurs { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 10px; margin-top: 12px; }
.hub-serveur-carte {
  display: flex; align-items: center; gap: 12px; text-align: left;
  padding: 12px 14px; border-radius: 12px; cursor: pointer;
  background: var(--surface-2); border: 1px solid var(--border); color: var(--text); font: inherit;
  transition: border-color var(--transition), transform var(--transition);
}
.hub-serveur-carte:hover { border-color: var(--accent); transform: translateY(-2px); }
.hub-serveur-carte img { border-radius: 12px; flex-shrink: 0; }
.hub-serveur-nom { display: flex; flex-direction: column; min-width: 0; }
.hub-serveur-nom b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hub-serveur-choisi { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.hub-serveur-choisi img { border-radius: 14px; }
.hub-serveur-choisi > div { flex: 1; min-width: 140px; }
.hub-deja { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border); }

.hub-compteur { display: block; text-align: right; color: var(--text-2); font-size: 0.76rem; margin-top: 3px; }
.hub-invit-etat { min-height: 24px; font-size: 0.88rem; }
.hub-ok { color: #57c98a; }
.hub-erreur { color: #e0556a; }
.hub-attention { color: #f0b232; }
.hub-envoi { display: flex; flex-direction: column; gap: 10px; align-items: flex-start; }

/* Le champ de tags porte ses suggestions : elles se posent par-dessus, comme
   dans la recherche, sinon elles décalent le formulaire sous le doigt. */
.hub-champ { position: relative; }

@media (max-width: 900px) {
  .hub-ajout { grid-template-columns: 1fr; }
  /* Sur téléphone l'aperçu passe AVANT le formulaire : c'est ce qu'on veut
     voir changer pendant qu'on écrit, pas trois écrans plus bas. */
  .hub-ajout-apercu { position: static; order: -1; }
}

/* ===========================================================================
   Densité
   ===========================================================================
   Le user veut « pas trop chargé ». Les annuaires de reference entassent
   beaucoup ; on garde leur densite utile — plus de serveurs a l'ecran, un
   bouton « Rejoindre » sur la carte — sans leur surcharge : pas de banniere
   publicitaire, pas de cinquante categories a la file, pas de trois etoiles et
   deux badges sur chaque ligne.

   Ces regles viennent APRES les definitions d'origine et les resserrent ; on
   ne les reecrit pas, pour que le reglage reste lisible d'un coup d'oeil. */
.hub-wrap { padding-top: 96px; padding-bottom: 56px; }
.hub-hero { margin-bottom: 22px; }
.hub-hero h1 { font-size: clamp(1.6rem, 4vw, 2.1rem); }
.hub-hero p { font-size: 0.94rem; }

/* Plus de colonnes : trois sur un ecran courant au lieu de deux et demie. */
.hub-grille { grid-template-columns: repeat(auto-fill, minmax(264px, 1fr)); gap: 14px; }
.hub-banniere { height: 62px; }
.hub-carte-tete { margin-top: -22px; }
.hub-icone { width: 46px; height: 46px; border-radius: 13px; }
.hub-carte-nom h3 { font-size: 0.95rem; }
.hub-carte-meta { font-size: 0.78rem; gap: 8px; }
.hub-carte-corps { padding: 10px 14px 0; }
.hub-desc { font-size: 0.86rem; -webkit-line-clamp: 2; line-clamp: 2; }
.hub-tags { padding: 9px 14px; gap: 5px; }
.hub-tag { font-size: 0.74rem; padding: 3px 8px; }
.hub-carte-pied { padding: 9px 14px; gap: 8px; }
.hub-bumps { font-size: 0.9rem; }
.hub-voir { margin-left: auto; }
.hub-rejoindre { margin-left: 0; }

/* La barre de categories : une seule ligne qui defile, plutot que trois lignes
   de pastilles avant le premier serveur. */
.hub-filtres#hub-categories {
  flex-wrap: nowrap; overflow-x: auto; justify-content: flex-start;
  padding-bottom: 4px; scrollbar-width: none;
}
.hub-filtres#hub-categories::-webkit-scrollbar { height: 0; }
.hub-filtres#hub-categories .hub-chip { flex: none; }

.hub-section-titre { margin: 26px 0 12px; }
.hub-section-titre h2 { font-size: 1.05rem; }

@media (max-width: 640px) {
  .hub-grille { grid-template-columns: 1fr; }
  /* Sur telephone, une carte doit tenir dans le pouce : les deux boutons se
     partagent la largeur au lieu de se tasser a droite. */
  .hub-carte-pied { flex-wrap: wrap; }
  .hub-voir, .hub-rejoindre { flex: 1 1 calc(50% - 4px); justify-content: center; }
  .hub-bumps { flex: 1 0 100%; }
}

/* Voile du menu du compte : invisible, mais il capte tout clic hors du menu.
   Un ecouteur sur le document dependrait de la propagation — il suffirait
   qu'un element en chemin l'arrete pour que le menu reste ouvert. */
.hub-moi-voile { position: fixed; inset: 0; z-index: 55; background: transparent; }

/* L'icone s'alignait sur le BAS du bloc de titre. Des que la ligne
   d'informations passe sur deux lignes — ce qui arrive souvent en colonne
   etroite — elle se decrochait vers le bas et paraissait detachee de son nom.
   Elle s'aligne desormais sur le haut, avec le titre. */
.hub-carte-tete { align-items: flex-start; }
.hub-carte-nom { padding-bottom: 0; }

/* ----- Bump : une indication, plus un bouton -------------------------------
   On ne bumpe plus depuis le site. Ce qui reste dit l'etat et le moyen, sans
   ressembler a un bouton : un element qui a l'air cliquable et ne l'est pas
   est plus agacant qu'une simple phrase. */
.hub-bump-info {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 0; font-size: 0.86rem; color: var(--text-2); cursor: default;
  border: 0; background: none;
}
.hub-bump-info b { color: var(--text); font-weight: 600; }
.hub-bump-info code {
  font-family: 'Space Grotesk', monospace; font-size: 0.85rem;
  background: var(--surface-2); padding: 1px 6px; border-radius: 5px; color: var(--accent);
}
.hub-bump-pret { color: var(--text-2); }
.hub-bump-attente i { color: #f0b232; }
.hub-commentbump { border-color: color-mix(in srgb, var(--accent) 24%, transparent); }
.hub-commentbump code {
  font-family: 'Space Grotesk', monospace; background: var(--surface-2);
  padding: 1px 7px; border-radius: 5px; color: var(--accent);
}

/* Les onglets de classement sont des LIENS, pas des boutons : on doit pouvoir
   les ouvrir dans un nouvel onglet et les partager. Ils reprennent l'apparence
   des pastilles de filtre pour qu'on les reconnaisse comme tels. */
a.hub-chip { text-decoration: none; }
a.hub-chip:hover { color: var(--accent); border-color: var(--accent); }

/* ===========================================================================
   Largeur des ecrans denses
   ===========================================================================
   --maxw vaut 1160 px : c'est la bonne mesure pour une page qui se lit, ou une
   ligne de texte trop longue fatigue. Le panneau de moderation ne se lit pas,
   il se consulte — six compteurs, deux courbes, des tableaux — et 1160 px l'y
   comprime pendant que la barre du haut occupe tout l'ecran, ce qui donne
   l'impression que la page est coupee.
   L'annuaire lui-meme y gagne aussi : une colonne de plus a partir de 1500 px. */
.hub-wrap { max-width: min(1360px, 100%); }
/* Plus specifique que .hub-wrap : a specificite egale, c est l ordre qui
   tranche, et la regle generale ecrasait celle du panneau. */
.hub-wrap.hub-admin-large { max-width: min(1560px, 100%); }
/* Le texte, lui, garde sa mesure de lecture : c'est la largeur des BLOCS qui
   change, pas celle des phrases. */
.hub-hero p, .hub-bloc p { max-width: 70ch; }

@media (min-width: 1500px) {
  .hub-grille { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }
}

/* ===========================================================================
   Tiroir de moderation : le voile et le bouton de fermeture.

   Ces deux elements empruntaient « .rep-voile » et « .rep-ic » a dashboard.css,
   une feuille que les pages du Hub ne chargent PAS. Le resultat ne produisait
   aucune erreur : le voile existait bien dans le DOM, mais sans style il faisait
   zero pixel de haut. Cliquer a cote du tiroir ne fermait donc rien, et il n'y
   avait meme pas d'assombrissement pour signaler qu'on etait dans un panneau
   modal. Le bouton de fermeture, lui, s'affichait avec le style par defaut du
   navigateur.

   Le Hub definit desormais les siens. Une page ne doit pas dependre de la
   feuille d'une AUTRE page pour que ses elements existent visuellement.
   =========================================================================== */
.hub-d-voile {
  position: fixed;
  inset: 0;
  z-index: 60; /* sous le tiroir (61), au-dessus du reste */
  background: rgb(0 0 0 / 0.45);
}
.hub-ic {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 40 px : une cible qu'on atteint au pouce sans viser. */
  width: 40px;
  height: 40px;
  flex: none;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--text-2);
  cursor: pointer;
  transition: color .18s ease, border-color .18s ease, background .18s ease;
}
.hub-ic:hover { color: var(--text); border-color: var(--border-strong); background: var(--surface); }
.hub-ic:focus-visible { outline: none; box-shadow: var(--ring); }
.hub-d-tete .hub-ic { margin-left: auto; }

/* ===========================================================================
   Le Hub definit desormais SES propres « .muted » et « .toast ».

   Les deux etaient empruntes a dashboard.css, que ces pages ne chargent pas.
   Mesure faite dans Chrome sur /hub avant correction :

     .muted  -> couleur rgb(91, 110, 240), soit le bleu d'accent herite du
                contexte, la ou on attendait un gris discret ;
     .toast  -> position: static, fond transparent, place a 5549 px du haut de
                la page.

   Autrement dit : AUCUNE confirmation du Hub n'a jamais ete vue. « Fiche
   publiee », « 3 fiches traitees », les messages d'erreur — tout partait en bas
   du document, sans fond, hors de l'ecran. Une action reussie et une action
   sans effet se ressemblaient donc parfaitement.

   Valeurs alignees sur celles du tableau de bord : c'est le meme produit, les
   memes reperes doivent s'appliquer.
   =========================================================================== */
.muted { color: var(--text-2); font-size: 0.9rem; }

.toast {
  position: fixed;
  bottom: calc(26px + env(safe-area-inset-bottom, 0px));
  left: 50%;
  transform: translateX(-50%) translateY(20px);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: 12px;
  padding: 13px 20px;
  box-shadow: 0 18px 48px -18px rgb(0 0 0 / 0.5);
  opacity: 0;
  pointer-events: none;
  transition: opacity .22s ease, transform .22s ease;
  /* Au-dessus du tiroir (61) et de son voile (60) : une confirmation qui
     s'affiche DERRIERE le panneau ne confirme rien. */
  z-index: 200;
  font-size: 0.92rem;
  display: flex;
  align-items: center;
  gap: 9px;
  max-width: min(92vw, 520px);
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
.toast.ok i { color: #57c98a; }
.toast.error { border-color: #e0556a; }
.toast.error i { color: #e0556a; }
/* L'avatar du menu du compte : rond, comme partout ailleurs. Il n'avait aucune
   regle et s'affichait carre — ses attributs width/height lui donnaient sa
   taille, ce qui masquait l'absence de style. */
.hub-moi-av { border-radius: 50%; object-fit: cover; flex: none; }

/* Les actions destructrices doivent SE VOIR comme telles.
   « .btn-danger » n'existait que dans dashboard.css : sur le panneau du Hub,
   « Refuser », « Suspendre » et « Supprimer definitivement » s'affichaient
   exactement comme « Poser le badge ». Un bouton qui supprime une annonce et un
   bouton anodin ne doivent jamais se ressembler. */
.btn-danger { background: none; border: 1px solid #e0556a55; color: #e0556a; }
.btn-danger:hover { background: #e0556a; color: #fff; border-color: #e0556a; }

/* Le pied du tiroir, avec sa sortie. */
.hub-d-pied { display: flex; justify-content: flex-end; padding-top: 4px; }

/* ===========================================================================
   Ce qui manque pour valider se voit SUR le champ, en rouge.

   Un bouton grise avec une phrase a vingt centimetres de la oblige a
   reconstituer soi-meme le lien entre les deux — et sur un formulaire long, la
   phrase est souvent hors de l'ecran au moment ou l'on regarde le champ fautif.
   =========================================================================== */
.hub-champ-faute > input,
.hub-champ-faute > textarea,
.hub-champ-faute > select {
  border-color: #e0556a;
  /* Un halo tres leger : le bord seul se remarque mal sur fond sombre. */
  box-shadow: 0 0 0 3px rgb(224 85 106 / 0.12);
}
.hub-champ-faute > input:focus,
.hub-champ-faute > textarea:focus {
  outline: none;
  box-shadow: 0 0 0 3px rgb(224 85 106 / 0.22);
}
.hub-champ-note {
  display: block;
  margin-top: 6px;
  color: #e0556a;
  font-size: 0.82rem;
}
/* La couleur ne doit pas etre le SEUL signal : environ un homme sur douze
   distingue mal le rouge du gris environnant. Le triangle le dit aussi. */
.hub-champ-note::before { content: "\26A0  "; }

/* « .hub-bloc p » vaut 0-1-1 et « .hub-manque » 0-1-0 : la premiere gagnait,
   et le resume restait gris pale — c est-a-dire exactement ce qu on ne lit pas.
   Le selecteur est donc qualifie. Meme piege qu avec .hub-wrap face a
   .hub-admin-large : une classe plus recente dans le fichier ne l emporte PAS,
   c est la specificite qui tranche. */
.hub-bloc p.hub-manque,
.hub-manque {
  color: #e0556a;
  font-size: 0.88rem;
  margin-bottom: 10px;
}
/* Le bouton « pas pret » : il reste cliquable — c'est lui qui explique ce qui
   manque — mais il ne se presente pas comme une action prete a partir. Un
   bouton reellement desactive n'emet aucun clic, pas meme sur son conteneur :
   on ne peut donc RIEN expliquer au moment ou la personne essaie. */
.btn.btn-pas-pret { opacity: .55; }
.btn.btn-pas-pret:hover { opacity: .75; }
