/* Polices auto-hébergées (refonte visuelle 10/2026) — fichiers vendorés
   sous assets/vendor/fonts/, jamais chargées depuis un CDN à l'exécution
   (même convention que Bootstrap). Fichiers "variable" côté Google Fonts :
   un seul .woff2 par famille, plusieurs règles @font-face y pointant
   chacune avec un font-weight différent (le navigateur choisit l'instance
   de graisse voulue dans le fichier variable). Sous-ensemble latin
   uniquement (couvre les accents français usuels, U+00C0-00FF). */
@font-face {
    font-family: "Space Grotesk";
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url("/assets/vendor/fonts/space-grotesk-latin.woff2") format("woff2");
}

@font-face {
    font-family: "Space Grotesk";
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url("/assets/vendor/fonts/space-grotesk-latin.woff2") format("woff2");
}

@font-face {
    font-family: "Space Grotesk";
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url("/assets/vendor/fonts/space-grotesk-latin.woff2") format("woff2");
}

@font-face {
    font-family: "IBM Plex Sans";
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("/assets/vendor/fonts/ibm-plex-sans-latin.woff2") format("woff2");
}

@font-face {
    font-family: "IBM Plex Sans";
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url("/assets/vendor/fonts/ibm-plex-sans-latin.woff2") format("woff2");
}

@font-face {
    font-family: "IBM Plex Sans";
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url("/assets/vendor/fonts/ibm-plex-sans-latin.woff2") format("woff2");
}

:root {
    --bs-primary: #5b8def;
    --bs-primary-rgb: 91, 141, 239;
    /* Vert-sarcelle de la refonte plutôt que le vert Bootstrap par défaut
       — repris automatiquement par tous les badges/boutons text-bg-success/
       btn-success déjà utilisés pour "en ligne" partout dans l'app, sans
       toucher au HTML. */
    --bs-success: #36c2a0;
    --bs-success-rgb: 54, 194, 160;
    --bs-body-font-family: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    --gp-font-display: "Space Grotesk", var(--bs-body-font-family);
    --gp-bottom-nav-h: 3.75rem;
    /* Coins nettement plus arrondis que les 0.375rem par défaut de
       Bootstrap — la maquette utilise ~16px sur les cartes, ~10px sur
       boutons/champs. Cascade automatiquement à tout ce qui référence déjà
       ces variables (boutons, champs, alertes, badges...), voir aussi
       .card et .badge ci-dessous pour les deux composants qui n'en
       dépendent pas directement. */
    --bs-border-radius: 0.5625rem;
    --bs-border-radius-sm: 0.5rem;
    --bs-border-radius-lg: 0.875rem;
    --bs-border-radius-xl: 1rem;
    /* Bootstrap colore <code> en rose vif (--bs-code-color) par défaut —
       la maquette l'affiche en gris-bleu neutre, cohérent avec le reste
       de la palette (adresses de connexion, ports...). */
    --bs-code-color: #8fb4f5;
    /* Valeurs lues directement dans la source de la maquette (Artifact
       Design) plutôt qu'estimées depuis une capture d'écran — la maquette
       empile en fait 4 nuances de surface distinctes en sombre, jamais un
       simple dégradé à 2 tons :
         #0c1016 fond de page < #10151c barre de nav/historique
         < #161b24 carte < #141a22 "creux" (boutons neutres, champs)
       avec une 5e nuance encore plus sombre (#0f141b) pour les puces
       (adresse, pseudo de joueur). */
    --gp-surface-bar: #10151c;
    --gp-surface-sunken-border: #273040;
    --gp-surface-chip: #0f141b;
    --gp-surface-chip-border: #202836;
    --gp-surface-muted-badge: #1a1f27;
    --gp-text-button: #cdd6e0;
    --gp-text-caption: #75829a;
    --gp-text-faint: #5f6b7a;
}

/* Titres/marque en Space Grotesk, graisse 600 comme dans la maquette (la
   graisse 500 par défaut de Bootstrap pour les titres passait inaperçue
   avec cette police) — posé explicitement plutôt que de reprendre un par
   un tous les <h1>-<h6>/.card-title existants. */
h1, h2, h3, h4, h5, h6,
.navbar-brand, .card-title, .font-display {
    font-family: var(--gp-font-display);
    font-weight: 600;
}

/* Bootstrap ne fait pas dépendre .card de --bs-border-radius-lg (sa propre
   variable --bs-card-border-radius a sa propre valeur par défaut) : forcé
   ici explicitement pour retrouver les coins bien arrondis de la maquette
   sans dépendre du nom exact de cette variable interne. */
.card {
    border-radius: var(--bs-border-radius-lg);
}

/* Carte de serveur cliquable (dashboard) — toute la carte mène à la page
   de détail (card-link.js), les boutons/formulaires qu'elle contient
   restent cliquables indépendamment. Juste une bordure qui réagit au
   survol/focus comme indice visuel, pas d'ombre/translation qui
   perturberait les boutons internes. */
.gp-clickable-card {
    cursor: pointer;
    transition: border-color 0.15s ease;
}

.gp-clickable-card:hover {
    border-color: var(--bs-primary);
}

.gp-clickable-card:focus-visible {
    outline: 2px solid var(--bs-primary);
    outline-offset: 2px;
}

/* Badges/pastilles toujours en forme de pilule dans la maquette (jamais le
   rectangle à coins légèrement arrondis que Bootstrap donne par défaut à
   .badge). */
.badge {
    border-radius: var(--bs-border-radius-pill);
}

[data-bs-theme="dark"] {
    /* Séparation carte/fond en sombre (demande de la refonte), couleurs
       reprises telles quelles de la maquette : le fond assombri sous la
       valeur Bootstrap par défaut (#212529), les cartes posées au-dessus
       avec un ton plus clair — jusqu'ici les deux étaient quasi
       identiques, les cartes se distinguaient mal du fond. Bordures
       explicitement plus sombres/subtiles aussi (le gris moyen par défaut
       de Bootstrap en sombre tranchait beaucoup plus que dans la
       maquette). Le clair est déjà suffisamment contrasté par défaut, pas
       de changement nécessaire côté clair. */
    --bs-body-bg: #0c1016;
    --bs-card-bg: #161b24;
    /* Était #1d2430 (plus clair que .card) : la maquette utilise en
       réalité #141a22, plus SOMBRE que .card — c'est la surface "en
       creux" des boutons neutres et des champs de formulaire, pas une
       variante plus claire des cartes. Les panneaux qui semblaient
       utiliser cette nuance (tuiles de stats, info-panel...) sont en
       fait posés directement sur --bs-card-bg dans la maquette, corrigé
       plus bas composant par composant. */
    --bs-tertiary-bg: #141a22;
    --bs-border-color: #232b36;
    --bs-border-color-translucent: rgba(35, 43, 54, 0.6);
    --bs-secondary-color: #8b97a7;
}

/* Toutes les barres de navigation du portail suivent le même schéma :
   un bloc "identité" (logo/nom/badges) à gauche, un bloc "actions"
   (Retour + menu utilisateur) à droite, sans bouton hamburger/collapse.
   Le bloc actions ne doit jamais passer à la ligne (mauvais rendu sur
   mobile) : c'est donc le bloc identité, potentiellement plus long
   (nom de serveur variable), qui cède de la place en se tronquant avec
   des points de suspension plutôt que de forcer tout le bloc actions
   en dessous ou de provoquer un débordement horizontal de la page. */
/* Bootstrap force flex-wrap:nowrap sur .navbar via .navbar-expand (pensé
   pour ne jamais faire passer les liens de nav à la ligne) — ça empêche
   aussi le second .container-fluid (server_brand_meta.php) de passer sous
   le premier, il se retrouve compressé sur la même ligne à droite. On
   rétablit le wrap au niveau de .navbar (chaque .container-fluid fait
   100% de large, donc le second ne peut que passer à la ligne), tout en
   gardant nowrap à l'intérieur de chaque .container-fluid pris
   individuellement (règle suivante) pour que identité/actions restent
   toujours sur une seule ligne entre eux. */
.navbar {
    background: var(--gp-surface-bar);
}

/* Ligne d'identité serveur (server_brand_meta.php, bureau uniquement) :
   revient au fond de PAGE plutôt que la couleur de la barre héritée de
   .navbar — la maquette ne pose aucun fond dessus (seulement un trait de
   séparation), ce qui crée la distinction à deux niveaux entre la barre
   "Portail" et la ligne d'identité en dessous. Sans ce retour, les deux
   lignes se confondaient en une seule barre uniforme. */
.gp-server-identity-row {
    background: var(--bs-body-bg);
    padding-top: 1.125rem;
    padding-bottom: 1.125rem;
}

.navbar.navbar-expand {
    flex-wrap: wrap;
}

.navbar .container-fluid {
    flex-wrap: nowrap;
}

.navbar .container-fluid > :first-child {
    min-width: 0;
}

.navbar .container-fluid > :last-child {
    flex-shrink: 0;
}

#serverDisplayName {
    display: inline-block;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: middle;
}

.output {
    background: #000;
    color: #d7d7d7;
    font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
    font-size: 0.85rem;
    white-space: pre-wrap;
    overflow-x: auto;
    border-radius: var(--bs-border-radius);
    padding: 0.75rem;
    margin: 0;
}

/* Page Console — composition "à plat" de la maquette : pas de carte
   autour, seul le terminal lui-même a un cadre. */
.gp-console {
    display: flex;
    flex-direction: column;
}

.gp-console-output {
    background: #07090d;
    color: #d7d7d7;
    border: 1px solid #1c2430;
    border-radius: var(--bs-border-radius-lg);
    font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
    font-size: 0.71875rem;
    line-height: 1.7;
    white-space: pre-wrap;
    overflow-x: auto;
    overflow-y: auto;
    height: 320px;
    padding: 0.75rem;
    margin: 0;
}

/* Bouton carré icône seule (copier les journaux, envoyer une commande) —
   la maquette n'utilise jamais de bouton texte pour ces deux actions. */
.gp-icon-btn {
    width: 2.625rem;
    height: 2.625rem;
    padding: 0 !important;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}

.log-output {
    height: 320px;
    overflow-y: auto;
}

/* Contraste élevé (Paramètres → Accessibilité) : renforce les contrastes
   texte/fond au-dessus du thème clair ou sombre actif plutôt que d'en
   ajouter un troisième — pas de media query prefers-contrast ici, purement
   un choix explicite de l'utilisateur (voir accessibility.js). */
:root[data-contrast="high"] {
    --bs-body-bg: #000000;
    --bs-body-color: #ffffff;
    --bs-secondary-color: #e0e0e0;
    --bs-border-color: #ffffff;
    --bs-card-bg: #000000;
    --bs-tertiary-bg: #1a1a1a;
}

:root[data-bs-theme="light"][data-contrast="high"] {
    --bs-body-bg: #ffffff;
    --bs-body-color: #000000;
    --bs-secondary-color: #1a1a1a;
    --bs-border-color: #000000;
    --bs-card-bg: #ffffff;
    --bs-tertiary-bg: #e8e8e8;
}

[data-contrast="high"] .card,
[data-contrast="high"] .form-control,
[data-contrast="high"] .form-select,
[data-contrast="high"] .btn {
    border-width: 2px;
}

[data-contrast="high"] .text-secondary {
    color: var(--bs-secondary-color) !important;
}

/* Filtre de chargement (loading-overlay.js) : volontairement blanc dans les
   deux thèmes (demande explicite) plutôt que suivant data-bs-theme — le
   texte reste donc en couleur sombre fixe pour rester lisible dessus. */
.portal-loading-overlay {
    position: fixed;
    inset: 0;
    background: rgba(255, 255, 255, 0.8);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1080;
}

.portal-loading-overlay-content {
    text-align: center;
    color: #212529;
}

.portal-loading-overlay-message {
    margin-top: 0.75rem;
    font-weight: 500;
}

/* Barre d'enregistrement de l'éditeur de configuration (config-editor.js) :
   figée en bas de l'écran (pas seulement en bas du contenu, demande
   explicite) tant qu'il reste des modifications non enregistrées — un peu
   sous le filtre de chargement (z-index 1080) et les modales Bootstrap
   (1055), au-dessus du reste de la page. .container interne pour aligner
   son contenu avec le reste des pages serveur plutôt qu'un simple padding
   horizontal sur la barre elle-même. */
#configSaveBar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1040;
    margin: 0;
    border-radius: 0;
    border-style: solid;
    border-color: var(--bs-border-color);
    border-width: 1px 0 0 0;
    background: var(--bs-card-bg);
}

/* Place sous la barre fixe pour que le dernier champ de la page ne reste
   jamais caché derrière elle une fois affichée (voir le toggle de cette
   classe dans config-editor.js). Sur mobile/tablette, la barre basse
   (.bottom-nav, voir plus loin) occupe déjà le bas de l'écran : la barre
   d'enregistrement remonte au-dessus d'elle plutôt que de se superposer,
   et le padding réservé au bas de la page cumule les deux hauteurs. */
body.has-config-save-bar {
    padding-bottom: 4.5rem;
}

@media (max-width: 991.98px) {
    #configSaveBar {
        bottom: var(--gp-bottom-nav-h);
    }

    body.has-config-save-bar {
        padding-bottom: calc(var(--gp-bottom-nav-h) + 4.5rem);
    }
}

/* Réduire les animations (Paramètres → Accessibilité) : coupe transitions/
   animations CSS quel que soit leur déclencheur (bascule de thème,
   modales Bootstrap, défilement de navigation...) — technique standard
   équivalente à la media query prefers-reduced-motion, mais pilotée par un
   réglage explicite (voir accessibility.js, qui applique aussi ce mode par
   défaut si le système le demande et qu'aucun choix explicite n'a encore
   été fait). */
[data-motion="reduce"] *,
[data-motion="reduce"] *::before,
[data-motion="reduce"] *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
}

/* Pastille de statut (refonte visuelle 10/2026) : posée en plus des
   classes text-bg-success/text-bg-secondary déjà gérées par status-poll.js
   (voir [data-status-badge] dans index.php, servers/status.php et
   status-poll.js::applyStatus()) — aucun changement du mécanisme JS, qui
   continue de piloter la couleur via ces mêmes classes Bootstrap ; cette
   règle ajoute juste un point coloré avant le texte du badge. */
.status-dot {
    display: inline-flex;
    align-items: center;
    gap: 0.4em;
}

.status-dot::before {
    content: "";
    width: 0.5em;
    height: 0.5em;
    border-radius: 50%;
    background: currentColor;
    flex-shrink: 0;
}

/* Halo pulsant sur la pastille "en ligne" (repris de la maquette) — pas
   sur "arrêté", un état statique n'a pas besoin d'attirer l'œil. Respecte
   le réglage d'accessibilité "Réduire les animations" comme tout le reste
   (voir la règle [data-motion="reduce"] plus haut, qui neutralise déjà
   n'importe quelle animation par son nom). */
.status-dot.text-bg-success::before {
    animation: gp-status-pulse 2.2s infinite;
}

@keyframes gp-status-pulse {
    0% {
        box-shadow: 0 0 0 0 rgba(var(--bs-success-rgb), 0.55);
    }
    70% {
        box-shadow: 0 0 0 6px rgba(var(--bs-success-rgb), 0);
    }
    100% {
        box-shadow: 0 0 0 0 rgba(var(--bs-success-rgb), 0);
    }
}

/* Bandeau de comptage du tableau de bord (nombre de serveurs en ligne/
   arrêtés) — purement informatif, calculé depuis $serverStatuses déjà
   chargé par index.php, aucun nouvel appel gamectl. */
.status-summary-strip {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
}

.status-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.35rem 0.75rem;
    border-radius: 999px;
    background: var(--bs-tertiary-bg);
    border: 1px solid var(--bs-border-color);
    font-size: 0.8125rem;
    /* Dans la maquette, seule la pastille change de couleur selon l'état
       (en ligne/arrêté/en démarrage) — le texte reste toujours neutre,
       jamais teinté en entier comme le ferait un simple text-success. */
    color: var(--gp-text-button);
}

.gp-pill-dot {
    display: inline-block;
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 50%;
    flex-shrink: 0;
}

/* Barre de navigation basse, mobile et tablette (< 992px) — la navbar du
   haut suffit à partir de lg, voir bottom_nav.php. L'onglet actif est
   déterminé côté client (bottom-nav.js) : ce composant vit hors de
   <main>, une navigation pjax (page-nav.js) ne le retouche donc jamais,
   un surlignage qui ne dépendrait que du rendu PHP initial resterait
   périmé après avoir changé d'onglet sans rechargement complet. */
.bottom-nav {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1030;
    height: var(--gp-bottom-nav-h);
    display: flex;
    background: var(--gp-surface-bar);
    border-top: 1px solid var(--bs-border-color);
}

.bottom-nav-item {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.15rem;
    font-size: 0.6875rem;
    color: var(--bs-secondary-color);
    text-decoration: none;
}

.bottom-nav-item.active {
    color: var(--bs-primary);
    font-weight: 600;
}

/* Réserve la hauteur de la barre basse pour que le dernier élément de
   chaque page ne reste jamais caché derrière elle — même raisonnement que
   body.has-config-save-bar ci-dessus, mais inconditionnel puisque la
   barre basse est toujours présente (pas seulement pendant une édition
   en cours) sur toute page authentifiée en dessous de lg. */
@media (max-width: 991.98px) {
    body {
        padding-bottom: var(--gp-bottom-nav-h);
    }
}

/* Mise en page de servers/status.php : deux blocs flex INDÉPENDANTS côte
   à côte à partir de md (contenu + actions), chacun vertical en interne
   — pas un seul flexbox partagé où l'aside se retrouvait compressée.
   Empilés (actions sous le contenu) en dessous de md. */
.gp-status-columns {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.gp-status-main {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

@media (min-width: 768px) {
    .gp-status-columns {
        flex-direction: row;
        flex-wrap: nowrap;
        align-items: flex-start;
        gap: 1.5rem;
    }

    /* Déséquilibre volontaire (999 contre 1) : dans la maquette, le
       contenu absorbe presque tout l'espace supplémentaire et l'aside
       reste proche de sa largeur de base (240px) quelle que soit la
       largeur de l'écran. Avec deux flex-grow égaux comme avant,
       l'aside s'élargissait en lockstep avec le contenu sur un grand
       écran — bien plus large que dans la maquette. */
    .gp-status-main {
        flex: 999 1 420px;
    }
}

/* Panneau d'actions de la page de détail serveur (servers/status.php) :
   en colonne à droite du contenu à partir de md (comme la barre latérale
   de Paramètres), empilé sous le contenu en dessous — les formulaires/
   boutons eux-mêmes (data-btn-start/stop/restart, CSRF...) sont
   inchangés, seul leur conteneur change de mise en page. La mise en
   colonne ne doit s'appliquer qu'à partir de md : en dessous, l'aside
   passe sous le contenu en pleine largeur et les boutons doivent rester
   en ligne (Arrêter/Redémarrer/Backup côte à côte) — sans cette
   media query ils s'empilaient verticalement même sur mobile. */
.detail-actions-aside {
    flex: 1 1 240px;
    min-width: 220px;
}

.detail-actions-list {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.detail-actions-list > form {
    flex: 1 1 0;
}

@media (min-width: 768px) {
    .detail-actions-list {
        flex-direction: column;
        flex-wrap: nowrap;
    }

    .detail-actions-list > form {
        display: block;
        width: 100%;
        flex: none;
    }
}

/* En dessous de md : la maquette mobile n'a pas de carte "Actions" du
   tout, juste une ligne de 3 boutons pleine largeur directement dans le
   flux de la page (titre "Actions" et légende "Dernière sauvegarde"
   masqués au-dessus, voir le d-none d-md-block posé dans le HTML) —
   fond/bordure/marge interne de la carte retirés, boutons un peu plus
   hauts pour rester faciles à toucher. */
@media (max-width: 767.98px) {
    /* Sélecteur à deux classes (plutôt que .gp-status-actions seule) pour
       l'emporter, à spécificité égale, sur .detail-actions-aside {
       background: var(--bs-card-bg); ... } défini plus bas dans ce
       fichier — sans ça l'ordre source lui aurait donné la main malgré
       cette media query. */
    .detail-actions-aside.gp-status-actions {
        background: transparent;
        border: none;
        padding: 0;
        min-width: 0;
    }

    .gp-status-actions .btn {
        min-height: 2.75rem;
    }
}

/* Bascule à 3 états (Aucun/Lecture/Écriture) de servers/access.php — même
   contrat serveur qu'avant (un simple submit du formulaire contenant le
   module concerné), seul l'habillage visuel change par rapport aux
   anciens <select> à soumission automatique. */
.segmented-toggle {
    display: inline-flex;
    max-width: 100%;
    gap: 2px;
    padding: 2px;
    border-radius: var(--bs-border-radius-pill);
    background: var(--gp-surface-chip);
    border: 1px solid var(--gp-surface-chip-border);
}

.segmented-toggle button {
    border: none;
    background: transparent;
    flex: 1 1 auto;
    min-width: 0;
    padding: 0.25rem 0.5rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    border-radius: var(--bs-border-radius-pill);
    font-size: 0.8125rem;
    color: var(--bs-secondary-color);
}

.segmented-toggle button.active {
    background: var(--bs-card-bg);
    color: var(--bs-body-color);
    font-weight: 600;
}

/* Léger halo derrière la carte de connexion (login.php/register.php) —
   purement cosmétique, n'affecte pas la lisibilité du formulaire. */
body.gp-login-bg {
    background-image: radial-gradient(ellipse 900px 600px at 50% 36%, rgba(var(--bs-primary-rgb), 0.12), transparent);
    background-repeat: no-repeat;
}

/* --- Deuxième passe de fidélité à la maquette (10/2026) ---
   Les composants ci-dessus (pastille, barre basse, panneau d'actions,
   bascule segmentée) étaient déjà posés, mais Bootstrap continuait
   d'imposer ses propres aplats de couleur pleine (boutons, onglets actifs,
   barre latérale active) là où la maquette voulait un traitement
   beaucoup plus discret — surchargé ci-dessous via les variables
   --bs-btn-*/--bs-nav-tabs-*/--bs-list-group-* déjà exposées par
   Bootstrap, jamais en réécrivant les composants. */

/* Boutons neutres : fond "en creux" (#141a22) + bordure visibles dès le
   repos — vérifié directement dans la source de la maquette (Retour,
   Copier, Annuler, Redémarrer, Sauvegarder, Désarchiver...) : AUCUN
   bouton n'y est transparent au repos, contrairement à ce qu'une
   capture d'écran avait suggéré lors d'une passe précédente. .btn-secondary
   et .btn-outline-secondary sont donc rendus strictement identiques —
   Bootstrap distingue les deux variantes, la maquette non. */
.btn-secondary,
.btn-outline-secondary {
    --bs-btn-color: var(--gp-text-button);
    --bs-btn-bg: var(--bs-tertiary-bg);
    --bs-btn-border-color: var(--gp-surface-sunken-border);
    --bs-btn-hover-color: var(--bs-body-color);
    --bs-btn-hover-bg: var(--bs-card-bg);
    --bs-btn-hover-border-color: var(--gp-surface-sunken-border);
    --bs-btn-active-color: var(--bs-body-color);
    --bs-btn-active-bg: var(--bs-card-bg);
    --bs-btn-active-border-color: var(--gp-surface-sunken-border);
}

/* Boutons "danger" : même raisonnement — teinte rouge sombre dès le
   repos (Arrêter, Supprimer...), .btn-outline-danger identique à
   .btn-danger. */
.btn-danger,
.btn-outline-danger {
    --bs-btn-color: #e2928c;
    --bs-btn-bg: #201218;
    --bs-btn-border-color: #3a2530;
    --bs-btn-hover-color: #e2928c;
    --bs-btn-hover-bg: #2a1920;
    --bs-btn-hover-border-color: #3a2530;
    --bs-btn-active-color: #e2928c;
    --bs-btn-active-bg: #2a1920;
    --bs-btn-active-border-color: #3a2530;
}

/* Boutons désactivés (ex: Redémarrer/Arrêter sur une carte "en
   démarrage") : nuance dédiée, encore plus neutre, plutôt que la simple
   opacité réduite par défaut de Bootstrap. */
.btn:disabled, .btn.disabled {
    background: #12161d !important;
    border-color: #202836 !important;
    color: #54606e !important;
    opacity: 1;
}

/* Texte sombre sur fond bleu plutôt que le blanc par défaut de Bootstrap
   — la maquette utilise #0c1016 (couleur du fond de page) sur
   --bs-primary partout (Générer le lien, Créer le serveur, Mettre à
   jour...), meilleur contraste que du blanc sur ce bleu assez clair.
   S'applique aussi à .btn-outline-primary une fois rempli (survol/actif),
   où le fond devient --bs-primary via --bs-btn-hover-bg ci-dessous. */
.btn-primary,
.btn-outline-primary {
    --bs-btn-color: var(--bs-body-bg);
    --bs-btn-hover-color: var(--bs-body-bg);
    --bs-btn-active-color: var(--bs-body-bg);
}

.btn-outline-primary {
    --bs-btn-bg: transparent;
    --bs-btn-color: var(--bs-primary);
    --bs-btn-hover-bg: var(--bs-primary);
}

/* Onglets de page serveur (Statut/Console/Configuration...) : trait sous
   l'onglet actif plutôt que l'encadré "collé au contenu" par défaut de
   Bootstrap (--bs-nav-tabs-link-active-border-color mélange les bords
   avec --bs-body-bg, ce qui ne donne rien à cette échelle de couleurs). */
.nav-tabs {
    --bs-nav-tabs-link-active-bg: var(--bs-card-bg);
    --bs-nav-tabs-link-active-border-color: transparent;
    --bs-nav-tabs-link-active-color: var(--bs-body-color);
    --bs-nav-tabs-border-color: transparent;
}

.nav-tabs .nav-link.active {
    box-shadow: inset 0 -2px 0 var(--bs-primary);
    font-weight: 600;
}

/* Barre latérale de Paramètres : liens "fantômes" indépendants plutôt
   qu'un .list-group Bootstrap (boîte connectée avec une bordure entre
   chaque élément) — la maquette montre chaque entrée comme un lien
   discret, seule l'entrée active ressort avec un fond + une bordure. */
.gp-sidebar-link {
    display: block;
    padding: 0.5rem 0.75rem;
    border-radius: var(--bs-border-radius);
    color: var(--bs-secondary-color);
    font-size: 0.84375rem;
    text-decoration: none;
    border: 1px solid transparent;
}

.gp-sidebar-link:hover {
    color: var(--bs-body-color);
}

.gp-sidebar-link.active {
    background: var(--bs-card-bg);
    border-color: var(--bs-border-color);
    color: var(--bs-body-color);
    font-weight: 600;
}

.gp-sidebar-divider {
    margin: 0.4rem 0.1rem;
    border: none;
    border-top: 1px solid var(--bs-border-color);
}

/* Champs de formulaire : fond distinct du corps de page — jusqu'ici
   identique à --bs-body-bg (valeur utilisée telle quelle par .form-control/
   .form-select dans Bootstrap), les champs se fondaient dans le fond. */
.form-control, .form-select {
    background-color: var(--bs-tertiary-bg);
    border-color: var(--gp-surface-sunken-border);
}

.form-control:focus, .form-select:focus {
    background-color: var(--bs-tertiary-bg);
    border-color: var(--gp-surface-sunken-border);
}

/* Boutons de la barre de navigation (Retour, menu utilisateur) : tous en
   pilule et de même hauteur, pour une barre cohérente plutôt qu'un
   mélange d'un bouton à coins arrondis "classique" (Retour) et d'un
   bouton pilule (menu utilisateur) côte à côte. Scopé à .navbar pour ne
   pas arrondir les boutons outline-secondary ailleurs dans l'app
   (Copier, Annuler...), qui gardent les coins "classiques" du reste des
   formulaires. */
.navbar .btn {
    border-radius: var(--bs-border-radius-pill);
}

/* Grille de pastilles de statistiques (CPU/RAM/Monde) — servers/status.php
   et son miroir JS, status-poll.js::renderDetail(). Tuiles posées sur une
   troisième nuance de surface (--bs-tertiary-bg, plus claire que la carte
   qui les contient) pour bien les détacher du reste du contenu de la
   carte, comme dans la maquette. */
.stat-tile-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.75rem;
}

@media (min-width: 768px) {
    .stat-tile-grid {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}

.stat-tile {
    background: var(--bs-card-bg);
    border: 1px solid var(--bs-border-color);
    border-radius: var(--bs-border-radius-lg);
    padding: 0.85rem 1rem;
}

/* Marges internes resserrées sous md — la maquette mobile utilise des
   tuiles/panneaux sensiblement plus compacts que le bureau (11-13px de
   marge interne contre 16-18px), pas juste la même carte redimensionnée
   par le texte qui rapetisse. */
@media (max-width: 767.98px) {
    .stat-tile {
        padding: 0.6875rem;
        border-radius: var(--bs-border-radius);
        text-align: center;
    }

    .info-panel {
        padding: 0.8125rem;
        border-radius: var(--bs-border-radius);
    }
}

.stat-label {
    font-size: 0.75rem;
    color: var(--bs-secondary-color);
    margin-bottom: 0.3rem;
}

.stat-value {
    font-family: var(--gp-font-display);
    font-weight: 600;
    font-size: 1.5rem;
}

.stat-caption {
    font-size: 0.75rem;
    color: var(--bs-secondary-color);
    margin-top: 0.2rem;
}

/* Panneaux "Joueurs connectés"/"Ports" sous la grille de statistiques —
   même traitement de surface que .stat-tile, pour que la carte Statut se
   lise comme plusieurs blocs distincts plutôt qu'une liste de texte brut. */
.info-panel {
    background: var(--bs-card-bg);
    border: 1px solid var(--bs-border-color);
    border-radius: var(--bs-border-radius-lg);
    padding: 1rem 1.1rem;
}

/* Pastilles de pseudo (liste des joueurs connectés) — nuance la plus
   sombre de la maquette (#0f141b), pas --bs-body-bg. */
.chip-pill {
    display: inline-block;
    padding: 0.3rem 0.75rem;
    border-radius: var(--bs-border-radius-pill);
    background: var(--gp-surface-chip);
    border: 1px solid var(--gp-surface-chip-border);
    font-size: 0.8125rem;
}

/* Adresse/port affichés comme une puce distincte plutôt qu'un simple
   <code> en ligne dans le texte. */
.address-chip {
    display: inline-block;
    background: var(--gp-surface-chip);
    border: 1px solid var(--gp-surface-chip-border);
    border-radius: var(--bs-border-radius-sm);
    padding: 0.4rem 0.65rem;
    font-size: 0.8125rem;
}

/* Pastille "P" en dégradé — marque du portail dans la navbar (remplace
   logo-portal-small.png, jamais utilisé dans la maquette : un badge en
   dégradé primary→success avec l'initiale, pas une image). */
.gp-portal-badge {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 30px;
    height: 30px;
    border-radius: var(--bs-border-radius);
    background: linear-gradient(145deg, var(--bs-primary), var(--bs-success));
    color: var(--bs-body-bg);
    font-family: var(--gp-font-display);
    font-weight: 700;
    font-size: 0.875rem;
}

.gp-portal-badge-lg {
    width: 46px;
    height: 46px;
    border-radius: var(--bs-border-radius-lg);
    font-size: 1.25rem;
}

/* Pastille colorée avec l'initiale du jeu (src/views/game_badge.php) —
   remplace les logos PNG vendorés pour l'affichage, jamais utilisés dans
   la maquette (badge "M"/"P" teinté par jeu à la place). */
.gp-game-badge {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    border-radius: var(--bs-border-radius);
    font-family: var(--gp-font-display);
    font-weight: 700;
    font-size: 0.875rem;
}

/* Carte de serveur archivé : fond encore plus sombre + bordure en
   pointillés, pour se distinguer visuellement des cartes actives sans
   répéter leur traitement "pleine couleur". */
.gp-archived-card {
    background: #12161d;
    border: 1px dashed var(--bs-border-color);
}

/* Cartes du tableau de bord : 20px de marge interne (1.25rem), plus que
   le 1rem par défaut de .card-body. */
.gp-dashboard-card .card-body {
    padding: 1.25rem;
}

/* Texte secondaire discret (sous-titre de jeu, dossier réel, légendes de
   carte...) — nuance la plus utilisée dans la maquette pour ce rôle
   (#75829a), différente de --bs-secondary-color (#8b97a7, un peu plus
   clair, réservé aux libellés de type "CPU"/"RAM"). */
.gp-card-caption {
    font-size: 0.75rem;
    color: var(--gp-text-caption);
}

.gp-text-faint {
    color: var(--gp-text-faint) !important;
}

/* Mini-barres CPU/RAM — cartes du tableau de bord (index.php) et tuiles
   de servers/status.php (status_detail.php / status-poll.js). */
.mini-bar {
    height: 5px;
    border-radius: 4px;
    background: var(--bs-border-color);
    overflow: hidden;
}

.mini-bar > div {
    height: 100%;
    background: var(--bs-primary);
}

/* Libellé Public/Local uniquement d'un port (panneau Ports) : texte
   coloré simple plutôt qu'un badge-pilule, pour rester discret à côté de
   plusieurs lignes de ports. */
.port-visibility {
    font-weight: 600;
    font-size: 0.75rem;
}

/* En-tête de tableau discret (ex: Comptes dans Paramètres) — petites
   majuscules espacées plutôt que le texte en taille normale par défaut
   de Bootstrap pour <thead>. */
.gp-table-head-row th {
    font-size: 0.6875rem;
    color: var(--gp-text-faint);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-weight: 600;
    background: var(--bs-tertiary-bg);
    border-bottom-color: var(--bs-border-color);
}

/* Pastilles de statut "teintées" (fond sombre + texte vif) pour des
   statuts de données (compte actif/en attente d'invitation...), à ne pas
   confondre avec .status-dot qui reste un aplat plein — ce dernier sert
   uniquement au statut en direct d'un serveur (en ligne/arrêté/en
   démarrage), un repère plus visible et volontairement différent. */
.chip-status-active {
    background: #102922;
    color: #58d9b4;
}

.chip-status-pending {
    background: #2a2010;
    color: #e8a23c;
}

/* Panneau d'actions de la page de détail serveur : même traitement de
   surface que .info-panel (carte distincte), pour matcher la maquette où
   les actions vivent dans leur propre bloc visuellement délimité plutôt
   que flottantes à côté du contenu. */
.detail-actions-aside {
    background: var(--bs-card-bg);
    border: 1px solid var(--bs-border-color);
    border-radius: var(--bs-border-radius-lg);
    padding: 1rem 1.1rem;
}

/* À partir de md (panneau en colonne à droite, voir plus haut) : icône à
   gauche du libellé, en ligne, bouton pleine largeur — vérifié dans la
   source de la maquette (une passe précédente, basée sur une capture
   d'écran, avait empilé icône/libellé par erreur). Pas de traitement
   "fantôme" particulier ici : .btn-secondary/.btn-danger utilisés tels
   quels suffisent déjà (fond en creux uniforme, voir plus haut). */
@media (min-width: 768px) {
    .detail-actions-aside .btn-secondary,
    .detail-actions-aside .btn-success,
    .detail-actions-aside .btn-danger {
        width: 100%;
        justify-content: flex-start;
    }
}

/* En-tête repliable d'une section de servers/config.php (Propriétés/
   Gamerules) : la page peut compter une centaine de champs, repliée par
   défaut pour que la page reste courte au premier coup d'œil plutôt que
   de tout afficher d'un bloc. Un simple <button> stylé pour ressembler à
   l'ancien en-tête statique (icône + libellé), pas un composant .btn —
   le plugin Collapse de Bootstrap (déjà vendoré) gère le dépliage/aria
   sans JS supplémentaire. */
.gp-config-section-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    width: 100%;
    padding: 0;
    margin-bottom: 0.5rem;
    border: none;
    background: transparent;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.gp-config-section-toggle:focus-visible {
    outline: 2px solid var(--bs-primary);
    outline-offset: 2px;
}

.gp-config-chevron {
    flex-shrink: 0;
    color: var(--bs-secondary-color);
    transition: transform 0.15s ease;
}

.gp-config-section-toggle:not(.collapsed) .gp-config-chevron {
    transform: rotate(180deg);
}

.gp-config-section-toggle .badge {
    font-weight: 600;
}
