:root {
    color-scheme: light dark;
    --bg: #0f1115;
    --fg: #e8e8ea;
    --muted: #9a9aa2;
    --accent: #6c8cff;
    --card-bg: #1a1d24;
    --border: #2a2e37;
}

* { box-sizing: border-box; }

body {
    margin: 0;
    font-family: var(--font-body, system-ui, -apple-system, "Segoe UI", sans-serif);
    font-weight: 400;
    background: var(--bg-primary, var(--bg));
    color: var(--text-primary, var(--fg));
    line-height: 1.5;
}

h1, h2, h3, h4, h5, h6 {
    font-family: var(--font-heading, inherit);
    font-weight: 700;
}

a { color: var(--accent-primary, var(--accent)); }

/* --- Chrome global (header/footer du site public, thémable via les 8 couleurs Apparence) --- */

.site-header {
    padding: 1rem 2rem;
    display: flex;
    align-items: center;
    gap: 2rem;
}

.site-logo { max-height: 48px; display: block; }

.site-nav {
    display: flex;
    gap: 1.5rem;
}

.site-auth {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-left: auto;
}

/* --- Menus déroulants du header public au survol (2026-08-15, voir le hook
   'nav_dropdown'/core/templates/layout.php/nav-dropdown.js) — survol pur CSS
   sur desktop (.site-nav-dropdown:hover), chevron tactile réservé aux
   écrans sans survol réel (@media (hover: none) plus bas : un appareil
   tactile ET souris ne l'affiche pas, cas rare accepté). Suit le thème du
   module si la page est dans sa zone (.module-scope, voir Core\ModuleThemes) —
   var(--accent-primary)/var(--bg-secondary) sans rien de spécifique ici,
   la cascade CSS suffit déjà. --- */
.site-nav-dropdown {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 0.15rem;
}

.site-nav-dropdown-toggle {
    display: none;
    background: none;
    border: none;
    color: inherit;
    cursor: pointer;
    font-size: 0.7rem;
    line-height: 1;
    padding: 0.25rem;
}

.site-nav-dropdown-panel {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    min-width: 220px;
    background: var(--bg-secondary, var(--card-bg));
    border: 1px solid var(--border-subtle, var(--border));
    border-radius: 8px;
    padding: 0.5rem;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
    z-index: 60;
}

.site-nav-dropdown:hover .site-nav-dropdown-panel,
.site-nav-dropdown.is-open .site-nav-dropdown-panel {
    display: block;
}

.site-nav-dropdown-entry {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.4rem 0.5rem;
    border-radius: 6px;
    color: var(--text-primary, var(--fg));
    text-decoration: none;
    font-size: 0.9rem;
}

.site-nav-dropdown-entry:hover {
    background-color: color-mix(in srgb, var(--accent-secondary, var(--accent)) 15%, transparent);
}

.site-nav-dropdown-avatar {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
}

.site-nav-dropdown-more {
    display: block;
    margin-top: 0.25rem;
    padding: 0.4rem 0.5rem;
    border-top: 1px solid var(--border-subtle, var(--border));
    color: var(--accent-secondary, var(--accent));
    text-decoration: none;
    font-size: 0.85rem;
    font-weight: 700;
}

@media (hover: none) {
    .site-nav-dropdown-toggle {
        display: inline-block;
    }
}

/* --- Bande de navigation secondaire par module (2026-08-15, voir
   Core\ModuleContext/le hook 'module_bar'/core/templates/layout.php) —
   EN PLUS du .site-header ci-dessus, jamais un remplacement : visible sur
   TOUTES les pages publiques d'un module qui la déclare (Univers/Streameurs
   pour commencer), juste en dessous du header global. Vit à l'intérieur du
   même conteneur que porte les variables CSS de portée locale de l'identité
   visuelle du module (voir .module-scope plus bas) : ses propres couleurs
   suivent donc naturellement le thème de CE module, pas besoin de règles
   dédiées ici. Distincte de .jdr-universe-sidebar/.streamer-profile-sidebar
   (navigation fine à l'intérieur d'un univers/profil précis, contexte de
   gestion) : cette bande reste générale, présente même en dehors de tout
   contexte de gestion précis. */
.module-bar {
    background: var(--bg-secondary, var(--card-bg));
    border-bottom: 1px solid var(--border-subtle, var(--border));
    padding: 0.6rem 2rem;
    display: flex;
    align-items: center;
    gap: 1.25rem;
    flex-wrap: wrap;
}

.module-bar-label {
    font-family: var(--font-heading, inherit);
    font-weight: 700;
    color: var(--accent-primary, var(--accent));
}

.module-bar-link {
    color: var(--text-primary, var(--fg));
    text-decoration: none;
    font-size: 0.9rem;
}

.module-bar-link:hover {
    color: var(--accent-secondary, var(--accent));
}

@media (max-width: 768px) {
    .module-bar {
        padding: 0.6rem 1rem;
        gap: 0.75rem;
    }
}

.site-footer {
    text-align: center;
    padding: 2rem;
    border-top: 1px solid var(--border-subtle, var(--border));
    color: var(--text-secondary, var(--muted));
    font-size: 0.85rem;
}

.site-logo-footer { max-height: 32px; margin-bottom: 0.75rem; }

/* Bandeau d'avertissement PERMANENT (2026-08-02, admin/layout.php) tant que le
   mode développement "2FA obligatoire suspendu" est actif (voir
   security.dev_disable_2fa_enforcement) — volontairement plus agressif que
   .flash-banner (fond plein, pas juste une bordure) : ne doit jamais passer
   inaperçu ni se confondre avec un message flash ordinaire. */
.admin-dev-mode-banner {
    background: var(--status-danger, #7A2E1A);
    color: #fff;
    padding: 0.75rem 1.5rem;
    margin: 0 0 1rem;
    border-radius: 6px;
    font-size: 0.9rem;
    font-weight: 700;
}

.admin-dev-mode-banner a {
    color: #fff;
    text-decoration: underline;
}

/* Bannière discrète (message flash), pas une alerte agressive */
.flash-banner {
    background: var(--bg-secondary, var(--card-bg));
    border: 1px solid var(--border-subtle, var(--border));
    border-left: 3px solid var(--accent-primary, var(--accent));
    color: var(--text-primary, var(--fg));
    padding: 0.75rem 1.5rem;
    margin: 1rem 2rem;
    border-radius: 6px;
    font-size: 0.9rem;
}

/* --- Vitrine publique --- */

/* .showcase-hero (vitrine) et .page-hero (bandeau de titre générique, voir
   ci-dessous) partagent le même traitement de fond d'écran : overlay
   semi-transparent (pas un fond plein) posé sur un conteneur qui reçoit
   Core\Background::render() directement — un background-color opaque ici
   le recouvrirait entièrement et le rendrait invisible. 65% laisse l'image
   transparaître tout en gardant le titre/sous-titre lisibles ; sans fond
   assigné, le rendu reste visuellement identique à un fond plein classique
   (même teinte, vue à travers un conteneur alors lui-même transparent).
   IMPORTANT : ce traitement ne doit JAMAIS s'étendre au contenu structuré
   sous le titre (grille, listes de cartes...) — voir .page-container plus
   bas, qui ne porte plus aucun fond d'écran depuis ce correctif. */
.showcase-hero, .page-hero {
    text-align: center;
    background-color: color-mix(in srgb, var(--bg-secondary, var(--card-bg)) 65%, transparent);
    background-size: cover;
    background-position: center;
}

.showcase-hero { padding: 6rem 2rem; }
.page-hero { padding: 3rem 2rem; margin-bottom: 2rem; }

.showcase-hero h1, .page-hero h1 { font-size: 2.5rem; margin: 0 0 0.5rem; text-shadow: 0 1px 6px rgba(0, 0, 0, 0.6); }
.showcase-hero .tagline, .page-hero .tagline { color: var(--text-secondary, var(--muted)); font-size: 1.2rem; font-weight: 700; margin: 0; text-shadow: 0 1px 6px rgba(0, 0, 0, 0.6); }
/* Badges dans le titre (statut, serveur...) : garder le texte-shadow pour rester lisibles sur l'image, pas de changement de couleur/fond propre au badge. */
.page-hero h1 .badge, .page-hero h1 .badge-neutral, .page-hero h1 .badge-success, .page-hero h1 .badge-danger, .page-hero h1 .badge-pinned { text-shadow: none; }

.showcase-body {
    max-width: 720px;
    margin: 0 auto;
    padding: 2rem;
}

/* --- Générique, réutilisable par toute page publique d'extension ---
   Ne porte plus de fond d'écran (voir .page-hero ci-dessus) : le contenu
   structuré (grille, listes de cartes) reste toujours sur le fond opaque
   normal du thème, jamais derrière une image qui nuirait à la lisibilité. */
.page-container {
    max-width: 900px;
    margin: 0 auto;
    padding: 2rem;
}

.content-list {
    list-style: none;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.content-list h2, .content-list h3 { margin: 0 0 0.25rem; }

/* Mode d'affichage "liste" de la boutique (voir Core\ModuleSettings, réglage
   shop.display.grid_mode), refondu 2026-07-20 en ligne compacte façon tableau
   même principe que .forum-topic-row/.jdr-simple-row : vignette | nom | prix |
   type | action. Le mode "grille" garde .dashboard-cards + .card tel quel. */
.shop-row-header {
    padding: 0.5rem 1.5rem;
    color: var(--text-secondary, var(--muted));
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

li.shop-product-row, .shop-product-row-grid {
    display: grid;
    grid-template-columns: 48px 1fr 90px 140px 160px;
    align-items: center;
    gap: 1rem;
}

.shop-row-thumb {
    width: 48px;
    height: 48px;
    border-radius: 6px;
    background: var(--bg-primary, var(--bg));
    border: 1px solid var(--border-subtle, var(--border));
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    flex-shrink: 0;
}

.shop-row-thumb img { width: 100%; height: 100%; object-fit: cover; }
.shop-row-thumb svg { width: 22px; height: 22px; color: var(--accent-primary, var(--accent)); }

.shop-row-main { min-width: 0; }
.shop-row-main h3 { margin: 0; }
.shop-row-price { text-align: right; }
.shop-row-action { text-align: right; }
.shop-row-action form { display: inline; }

/* /mes-commandes : même famille .shop-row-header/.shop-row-price/.shop-row-action
   que le catalogue, colonnes propres (numéro | date | statut | total | détail). */
li.shop-order-row, .shop-order-row-grid {
    display: grid;
    grid-template-columns: 1fr 160px 180px 110px 120px;
    align-items: center;
    gap: 1rem;
}

@media (max-width: 768px) {
    .shop-row-header { display: none; }
    li.shop-product-row, .shop-product-row-grid {
        grid-template-columns: 40px 1fr;
        row-gap: 0.4rem;
    }
    .shop-row-price, .shop-row-type, .shop-row-action {
        grid-column: 1 / -1;
        text-align: left;
    }
    .shop-row-price::before { content: "Prix : "; color: var(--text-secondary, var(--muted)); }

    li.shop-order-row, .shop-order-row-grid {
        grid-template-columns: 1fr;
        row-gap: 0.3rem;
    }
}

.meta { color: var(--text-secondary, var(--muted)); font-size: 0.85rem; }

.badge {
    display: inline-block;
    background: var(--border-subtle, var(--border));
    color: var(--text-secondary, var(--muted));
    border-radius: 4px;
    padding: 0.1rem 0.5rem;
    font-size: 0.75rem;
}

/* Statut positif (contenu publié, action confirmée) */
.badge-success {
    display: inline-block;
    background: var(--status-success, #2d6a4f);
    color: var(--text-primary, #fff);
    border-radius: 4px;
    padding: 0.1rem 0.5rem;
    font-size: 0.75rem;
}

/* Statut négatif (erreur, annulation, rejet) */
.badge-danger {
    display: inline-block;
    background: var(--status-danger, #7A2E1A);
    color: var(--text-primary, #fff);
    border-radius: 4px;
    padding: 0.1rem 0.5rem;
    font-size: 0.75rem;
}

/* Neutre (contenu à venir/en attente) — gris volontairement indépendant du thème */
.badge-neutral {
    display: inline-block;
    background: var(--border, #555);
    color: var(--muted, #ccc);
    border-radius: 4px;
    padding: 0.1rem 0.5rem;
    font-size: 0.75rem;
}

.badge-pinned {
    display: inline-block;
    background: var(--accent-primary, var(--accent));
    color: var(--bg-primary, #fff);
    border-radius: 4px;
    padding: 0.1rem 0.5rem;
    font-size: 0.75rem;
}

/* --- Grades (badges de statut cosmétiques, voir Core\GradeService) ---
   La couleur en accent est propre à chaque grade (style inline, background),
   mais la typographie/les coins arrondis suivent la charte générale — même
   famille visuelle que .badge-pinned, jamais var(--accent-primary) en dur ici
   puisque c'est justement la couleur du grade qui doit varier. */
.grade-badges {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 0.25rem;
    vertical-align: middle;
}

.grade-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    background: var(--border-subtle, var(--border));
    color: var(--text-primary, var(--fg));
    border-radius: 4px;
    padding: 0.1rem 0.5rem;
    font-size: 0.75rem;
    font-family: var(--font-body, inherit);
}

.grade-badge img {
    width: 14px;
    height: 14px;
    object-fit: contain;
}

.interest-tags {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 0.25rem;
    margin: 0 0 0.75rem;
}

.interest-tag {
    display: inline-flex;
    align-items: center;
    background: var(--border-subtle, var(--border));
    color: var(--text-primary, var(--fg));
    border-radius: 4px;
    padding: 0.1rem 0.5rem;
    font-size: 0.75rem;
    font-family: var(--font-body, inherit);
}

.subcategory-list {
    list-style: none;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    margin: 1rem 0 1.5rem;
}

.subcategory-list > li {
    background: var(--bg-primary, var(--bg));
    border: 1px solid var(--border-subtle, var(--border));
    border-radius: 6px;
    padding: 0.75rem 1rem;
}

.filters-nav {
    display: flex;
    gap: 1rem;
    margin: 1rem 0;
}

.social-links {
    list-style: none;
    display: flex;
    gap: 1rem;
    padding: 0;
    margin-top: 2rem;
}

.showcase-footer {
    text-align: center;
    padding: 2rem;
    color: var(--text-secondary, var(--muted));
    font-size: 0.85rem;
}

/* --- Composants réutilisables (core + extensions) ---
   Règle de design : --accent-primary (or) ne sert jamais de fond de grande
   surface, uniquement des éléments ponctuels (boutons, bordures, titres). */

.card {
    display: block;
    background: var(--bg-secondary, var(--card-bg));
    border: 1px solid var(--accent-primary, var(--accent));
    border-radius: 8px;
    padding: 1.5rem;
    text-decoration: none;
    color: var(--text-primary, var(--fg));
}

.btn-primary {
    display: inline-block;
    background: var(--accent-primary, var(--accent));
    color: var(--bg-primary, #1a1a1a);
    font-weight: 700;
    border: none;
    border-radius: 999px;
    padding: 0.5rem 1.25rem;
    cursor: pointer;
    text-decoration: none;
    font-family: inherit;
    font-size: 1rem;
}

.btn-secondary {
    display: inline-block;
    background: transparent;
    color: var(--accent-primary, var(--accent));
    border: 1px solid var(--accent-primary, var(--accent));
    border-radius: 999px;
    padding: 0.5rem 1.25rem;
    cursor: pointer;
    text-decoration: none;
    font-family: inherit;
    font-size: 1rem;
    transition: background-color 0.15s ease;
}

.btn-secondary:hover {
    background-color: color-mix(in srgb, var(--accent-secondary, var(--accent)) 20%, transparent);
}

.btn-secondary svg {
    width: 20px;
    height: 20px;
    vertical-align: middle;
}

/* --- Forum : bande de catégorie de premier niveau façon "vrai forum"
   (refonte visuelle 2026-07-19, stats intégrées à la ligne de titre 2026-07-31
   — voir templates/index.php) : icône à gauche, titre+stats+description+
   sous-catégories à droite. Même langage visuel que .card (fond/bordure/
   rayon), disposition en ligne plutôt qu'en grille. Sélecteurs "li.forum-band"/
   "li.forum-topic-row" (pas juste la classe seule) pour primer sur
   .card { display: block } malgré un ordre de cascade sinon défavorable
   (.card est défini plus haut dans ce fichier). Une seule bande existe sur
   cette page (une catégorie de premier niveau, voir ForumController::index()
   $useGrid) — les sous-catégories n'ont plus leur propre bande depuis
   2026-07-31, voir .forum-child-section (repliable, même fichier). */
li.forum-band {
    display: flex;
    gap: 1.25rem;
    background: var(--bg-secondary, var(--card-bg));
    border: 1px solid var(--accent-primary, var(--accent));
    border-radius: 8px;
    padding: 1.5rem;
}

.forum-band-icon {
    flex-shrink: 0;
    width: 56px;
    height: 56px;
    border-radius: 8px;
    background: var(--bg-primary, var(--bg));
    border: 1px solid var(--border-subtle, var(--border));
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}

.forum-band-icon img { width: 100%; height: 100%; object-fit: cover; }
.forum-band-icon svg { width: 28px; height: 28px; color: var(--accent-primary, var(--accent)); }

.forum-band-main {
    flex: 1;
    min-width: 0;
}

.forum-band-main > p { margin: 0 0 0.5rem; }

/* Titre + stats compactes sur la même ligne (2026-07-31) — même principe que
   .forum-child-section > summary : le nom garde la taille/le poids d'un h2,
   les stats/dernier message sont poussés à droite (margin-left: auto) en
   texte discret, jamais leur propre colonne séparée par une bordure. */
.forum-band-title {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    column-gap: 1rem;
    row-gap: 0.25rem;
    font-size: 1.4rem;
    margin: 0 0 0.25rem;
}

.forum-band-title-stats {
    flex: 0 0 auto;
    margin-left: auto;
    color: var(--text-secondary, var(--muted));
    font-weight: 400;
    font-size: 0.85rem;
    white-space: nowrap;
}

.forum-band-title-lastpost {
    flex: 0 1 auto;
    min-width: 0;
    font-size: 0.8rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.forum-subcat-row {
    display: flex;
    align-items: center;
    gap: 0.6rem;
}

.forum-subcat-row svg { width: 18px; height: 18px; color: var(--text-secondary, var(--muted)); flex-shrink: 0; }
.forum-subcat-row a { flex: 1; }

@media (max-width: 768px) {
    li.forum-band { flex-direction: column; }

    .forum-band-title-stats { margin-left: 0; }

    .forum-band-title-lastpost {
        flex-basis: 100%;
        white-space: normal;
    }
}

/* --- Vue en tuiles générique (2026-07-20, introduite pour /forum quand
   plusieurs catégories de premier niveau existent, réutilisée telle quelle
   par /univers pour rester "harmonisée" plutôt que dupliquée — voir
   ForumController::index() $useGrid et JdrController::universes()). Une
   tuile par élément, image de fond (ou fond neutre de la charte si aucune
   n'est assignée, simple background-color sous l'image), nom en évidence
   même traitement typographique que le titre d'en-tête (.page-hero h1 :
   police + ombre portée, taille adaptée au format tuile). */
.tile-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 1.25rem;
}

.tile {
    position: relative;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    min-height: 180px;
    border-radius: 8px;
    border: 1px solid var(--accent-primary, var(--accent));
    background-color: var(--bg-secondary, var(--card-bg));
    background-size: cover;
    background-position: center;
    overflow: hidden;
    text-decoration: none;
    padding: 1rem;
}

/* Voile sombre entre l'image et le texte, pour que le nom reste lisible quelle que soit l'image. */
.tile::before {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(to top, rgba(0, 0, 0, 0.65), transparent 65%);
}

.tile-icon {
    position: absolute;
    top: 0.75rem;
    left: 0.75rem;
    width: 36px;
    height: 36px;
    border-radius: 6px;
    background: var(--bg-primary, var(--bg));
    border: 1px solid var(--border-subtle, var(--border));
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    z-index: 1;
}

.tile-icon img { width: 100%; height: 100%; object-fit: cover; }
.tile-icon svg { width: 20px; height: 20px; color: var(--accent-primary, var(--accent)); }

.tile-name {
    position: relative;
    z-index: 1;
    font-family: var(--font-heading, inherit);
    font-size: 1.6rem;
    color: var(--text-primary, var(--fg));
    text-shadow: 0 1px 6px rgba(0, 0, 0, 0.6);
    margin: 0;
}

.tile-stats {
    position: relative;
    z-index: 1;
    font-size: 0.85rem;
    color: var(--text-secondary, var(--muted));
    text-shadow: 0 1px 4px rgba(0, 0, 0, 0.7);
    margin: 0.25rem 0 0;
}

.tile-badge {
    position: relative;
    z-index: 1;
    align-self: flex-start;
    margin-bottom: 0.5rem;
}

/* Point "non lu" (2026-07-20) : jamais un compteur, jamais affiché à un
   visiteur non connecté (voir ForumController::index() $unreadMap, calculé
   uniquement si Auth::check()). Même emplacement relatif sur la tuile et
   sur la bande classique, juste avant le nom. */
.forum-unread-dot {
    display: inline-block;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--accent-primary, var(--accent));
    box-shadow: 0 0 0 2px var(--bg-primary, var(--bg));
    vertical-align: middle;
    margin-right: 0.5rem;
}

/* --- Forum : liste des sujets façon tableau (page catégorie, 2026-07-19) ---
   Grille à colonnes explicites (état | Sujet | Réponses | Vues | Dernier
   message) partagée par la ligne d'en-tête (.forum-topic-header-row) ET
   chaque ligne de sujet, pour un alignement garanti entre les deux. */
.forum-topic-grid, li.forum-topic-row {
    display: grid;
    grid-template-columns: 24px 1fr 90px 90px 220px;
    align-items: center;
    gap: 1rem;
}

.forum-topic-header-row {
    padding: 0.5rem 1.5rem;
    color: var(--text-secondary, var(--muted));
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.forum-topic-header-row span:nth-child(3),
.forum-topic-header-row span:nth-child(4) { text-align: center; }
.forum-topic-header-row span:nth-child(5) { text-align: right; }

.forum-topic-row-state {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.forum-topic-row-state svg { width: 18px; height: 18px; color: var(--accent-primary, var(--accent)); }

.forum-topic-row-main { min-width: 0; }
.forum-topic-row-main h3 { margin: 0 0 0.15rem; }

.forum-topic-row-replies, .forum-topic-row-views { text-align: center; }

.forum-topic-row-lastpost {
    text-align: right;
    font-size: 0.85rem;
    border-left: 1px solid var(--border-subtle, var(--border));
    padding-left: 1.25rem;
}

.forum-topic-row-lastpost a { display: block; }
.forum-topic-row-lastpost .meta { display: block; }

/* Épinglé : discret sur la ligne (le badge dans le titre porte déjà
   l'accent), un simple liseré suffit à faire ressortir la ligne sans
   remplir toute sa surface d'accent-primary (règle de design ci-dessus). */
li.forum-topic-row.is-pinned { border-left: 3px solid var(--accent-primary, var(--accent)); padding-left: 0.75rem; }

@media (max-width: 768px) {
    .forum-topic-header-row { display: none; }
    .forum-topic-grid, li.forum-topic-row {
        grid-template-columns: 20px 1fr;
        row-gap: 0.4rem;
    }
    .forum-topic-row-replies, .forum-topic-row-views {
        grid-column: 2;
        text-align: left;
    }
    /* La ligne d'en-tête (Sujet/Réponses/Vues/...) est masquée en mobile
       ci-dessus : un simple nombre serait ambigu, on préfixe donc un libellé
       inline ici uniquement (le desktop garde l'en-tête, jamais les deux). */
    .forum-topic-row-replies::before { content: "Réponses : "; color: var(--text-secondary, var(--muted)); }
    .forum-topic-row-views::before { content: "Vues : "; color: var(--text-secondary, var(--muted)); }
    .forum-topic-row-lastpost {
        grid-column: 1 / -1;
        text-align: left;
        border-left: none;
        border-top: 1px solid var(--border-subtle, var(--border));
        padding-left: 0;
        padding-top: 0.5rem;
        margin-top: 0.25rem;
    }
}

/* Étiquette générique au-dessus du titre d'un en-tête de page (2026-07-19,
   "Forum" sur une catégorie/sous-catégorie de forum ; "Univers"/"Campagne"
   sur leurs en-têtes respectifs — réutilisée telle quelle plutôt que
   dupliquée, comme .tile-*). Texte pur, sans cadre ni fond : même police et
   même couleur que le titre juste en dessous (.page-hero h1), simplement
   dans une taille plus petite pour rester un élément secondaire au-dessus
   du titre. */
.hero-tag {
    margin: 0 0 0.5rem;
    font-family: var(--font-heading, inherit);
    font-size: 1.25rem;
    color: var(--text-primary, var(--fg));
    text-shadow: 0 1px 6px rgba(0, 0, 0, 0.6);
}

/* --- Forum : barre de tri/filtre de la page catégorie --- */
.forum-sort-bar {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    align-items: flex-end;
    margin: 1rem 0;
    padding: 1rem;
    background: var(--bg-secondary, var(--card-bg));
    border: 1px solid var(--border-subtle, var(--border));
    border-radius: 8px;
}

.forum-sort-bar label {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    font-size: 0.8rem;
    color: var(--text-secondary, var(--muted));
}

.forum-sort-bar select {
    background: var(--bg-primary, var(--bg));
    color: var(--text-primary, var(--fg));
    border: 1px solid var(--border-subtle, var(--border));
    border-radius: 6px;
    padding: 0.4rem 0.6rem;
    font-family: inherit;
}

.forum-announcements { margin-bottom: 2rem; }

/* Sous-catégorie affichée "en ligne" sur la page de sa catégorie parente
   (2026-07-20, voir ForumController::category() $childSections), repliable
   (2026-07-31) — <details>/<summary> natif avec la bordure/le fond de .card
   (bloc de contenu à part entière, pas un simple panneau d'aide comme le
   sélecteur générique "details" plus bas dans ce fichier). */
.forum-child-sections-toolbar {
    display: flex;
    gap: 0.75rem;
    margin: 1.5rem 0 1rem;
}

details.forum-child-section {
    margin-top: 1rem;
    padding: 1.25rem 1.5rem;
    background: var(--bg-secondary, var(--card-bg));
    border: 1px solid var(--accent-primary, var(--accent));
    border-radius: 8px;
}

details.forum-child-section > summary {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    column-gap: 1rem;
    row-gap: 0.25rem;
    cursor: pointer;
}

details.forum-child-section > summary .forum-child-summary-name {
    color: var(--accent-primary, var(--accent));
    font-weight: 700;
    font-size: 1.1rem;
}

details.forum-child-section > summary .forum-child-summary-desc {
    flex: 1 1 220px;
    min-width: 0;
    color: var(--text-secondary, var(--muted));
    font-weight: 400;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

details.forum-child-section > summary .forum-child-summary-stats {
    flex: 0 0 auto;
    margin-left: auto;
    color: var(--text-secondary, var(--muted));
    font-weight: 400;
    font-size: 0.85rem;
    white-space: nowrap;
}

/* Dernier message discret dans le résumé fermé (2026-07-31, voir
   templates/index.php — vue "bande" d'une seule catégorie de premier niveau) :
   suit .forum-child-summary-stats dans le même groupe aligné à droite, jamais
   sa propre marge auto (un seul point d'ancrage à droite par ligne). */
details.forum-child-section > summary .forum-child-summary-lastpost {
    flex: 0 1 auto;
    min-width: 0;
    font-size: 0.8rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

details.forum-child-section[open] > summary {
    margin-bottom: 1.25rem;
    padding-bottom: 1rem;
    border-bottom: 1px solid var(--border-subtle, var(--border));
}

@media (max-width: 768px) {
    .forum-child-sections-toolbar { flex-wrap: wrap; }

    details.forum-child-section > summary .forum-child-summary-desc {
        flex-basis: 100%;
        white-space: normal;
    }

    details.forum-child-section > summary .forum-child-summary-stats {
        margin-left: 0;
    }

    details.forum-child-section > summary .forum-child-summary-lastpost {
        flex-basis: 100%;
        white-space: normal;
    }
}

/* --- Gaming : annonces d'une section en ligne compacte façon tableau
   (2026-07-20, même principe que .jdr-simple-row/.forum-topic-row) — titre +
   extrait à gauche, date à droite. Sélecteur "li.gaming-announcement-row"
   pour primer sur .card { display: block }. */
.gaming-row-header {
    padding: 0.5rem 1.5rem;
    color: var(--text-secondary, var(--muted));
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.gaming-row-header span:last-child { text-align: right; }

li.gaming-announcement-row, .gaming-announcement-row-grid {
    display: grid;
    grid-template-columns: 1fr 160px;
    align-items: center;
    gap: 1rem;
}

.gaming-row-main { min-width: 0; }
.gaming-row-main h3 { margin: 0 0 0.15rem; }
.gaming-row-excerpt {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    margin: 0;
}

.gaming-row-date { text-align: right; }

@media (max-width: 768px) {
    .gaming-row-header { display: none; }
    li.gaming-announcement-row, .gaming-announcement-row-grid {
        grid-template-columns: 1fr;
        row-gap: 0.3rem;
    }
    .gaming-row-date {
        text-align: left;
        border-top: 1px solid var(--border-subtle, var(--border));
        padding-top: 0.5rem;
        margin-top: 0.25rem;
    }
    .gaming-row-excerpt { white-space: normal; }
}

/* --- Forum : fil de réponses (vue d'un sujet) --- */
.forum-post-header {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
}

/* Message initial légèrement mis en avant (bordure d'accent) par rapport aux réponses. */
.forum-post-first { border-left: 3px solid var(--accent-primary, var(--accent)); }

/* Réponse automatique de sondage de présence (2026-07-31, voir
   ForumController::respondToEvent()) — atténuée/italique, se distingue
   visuellement d'un message écrit par un membre sans sortir du fil normal. */
.forum-post-system {
    font-style: italic;
    opacity: 0.8;
}

.forum-post-system p { color: var(--text-secondary, var(--muted)); }

/* --- Forum : sondage de présence sur un sujet "événement" (2026-07-31,
   voir Extensions\forum\Models\EventResponse) --- */
.forum-event-fieldset {
    border: 1px solid var(--border-subtle, var(--border));
    border-radius: 6px;
    padding: 0.75rem 1rem;
    margin: 1rem 0;
}

.forum-event-checkbox {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

#forum-event-fields {
    margin-top: 0.75rem;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.forum-event-banner, .forum-event-widget {
    margin-bottom: 1.5rem;
}

.forum-event-edit-link { text-align: right; margin: 0 0 0.5rem; }

.forum-event-respond-forms {
    display: flex;
    gap: 0.75rem;
    margin-top: 0.75rem;
}

.forum-event-widget-columns {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1.5rem;
    margin-top: 0.75rem;
}

.forum-event-widget-list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

@media (max-width: 768px) {
    .forum-event-widget-columns { grid-template-columns: 1fr; gap: 1rem; }
    .forum-event-respond-forms { flex-wrap: wrap; }
}

/* --- Installeur (page autonome, hors du layout admin) --- */

.admin-guest {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
}

.admin-login-box {
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 2rem;
    width: 320px;
}

/* --- Admin ---
   Même thème que le site public : les 8 couleurs et les 2 polices Apparence
   s'appliquent ici aussi (voir templates/partials/theme_head.php, inclus par
   admin/layout.php). Les composants réutilisent .card/.btn-*/.badge-* du core,
   pas de CSS dupliqué spécifique à l'admin. */

/* Sidebar en position fixe (pas de sticky+flex : voir historique, cette
   combinaison provoque un rendu cassé sous Chromium) + contenu décalé d'une
   marge égale à sa largeur. Pattern simple, sans dépendance flexbox entre
   les deux. */

.admin-shell {
    min-height: 100vh;
}

.admin-sidebar {
    width: 240px;
    position: fixed;
    top: 0;
    left: 0;
    height: 100vh;
    overflow-y: auto;
    background: var(--bg-secondary, var(--card-bg));
    border-right: 1px solid var(--border-subtle, var(--border));
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    padding: 1.5rem 1rem;
    z-index: 10;
}

.admin-sidebar-nav,
.admin-sidebar-footer {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.admin-sidebar-footer {
    margin-top: 1.5rem;
    padding-top: 1rem;
    border-top: 1px solid var(--border-subtle, var(--border));
}

.admin-sidebar-link {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.6rem 0.75rem;
    border-radius: 6px;
    color: var(--text-primary, var(--fg));
    text-decoration: none;
    font-size: 0.95rem;
}

/* Sidebar admin en contexte module (voir Core\AdminNav::currentModule()) :
   le lien de retour se distingue visuellement du reste (couleur d'accent,
   séparateur en dessous) pour rester repérable même sur une longue liste de
   sous-sections. */
.admin-sidebar-back {
    color: var(--accent-primary, var(--accent));
    margin-bottom: 0.5rem;
    padding-bottom: 0.75rem;
    border-bottom: 1px solid var(--border-subtle, var(--border));
}

.admin-sidebar-module-label {
    margin: 0 0 0.25rem;
    padding: 0 0.75rem;
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-secondary, var(--muted));
}

/* Séparateur avant la sous-liste "Modules" du niveau global (voir layout.php). */
.admin-sidebar-divider {
    border-top: 1px solid var(--border-subtle, var(--border));
    margin: 0.75rem 0 0.5rem;
}

.admin-sidebar-link:hover {
    background-color: color-mix(in srgb, var(--accent-secondary, var(--accent)) 15%, transparent);
}

/* 2026-07-21 : n'existait pas avant (aucune mise en évidence de l'item actif,
   juste :hover) — ajouté pour rester cohérent avec la sidebar de /account,
   qui met en évidence son entrée active sur le même principe. */
.admin-sidebar-link.is-active {
    background: var(--accent-primary, var(--accent));
    color: var(--bg-primary, #1a1a1a);
    font-weight: 700;
}

.admin-sidebar-link svg {
    width: 20px;
    height: 20px;
    flex-shrink: 0;
}

/* Bouton "Vider le cache" (2026-08-02, admin/layout.php -> sidebar footer) :
   un <button> dans un <form>, pas un <a>, pour rester une action POST — reset
   des styles natifs de bouton pour se fondre visuellement dans le reste de
   .admin-sidebar-link (même look, juste un élément HTML différent). */
.admin-sidebar-purge-cache-form {
    margin: 0;
}

.admin-sidebar-purge-cache {
    width: 100%;
    background: none;
    border: none;
    font-family: inherit;
    font-size: 0.95rem;
    text-align: left;
    cursor: pointer;
}

.admin-sidebar-toggle {
    display: none;
}

.admin-sidebar-overlay {
    display: none;
}

.admin-main {
    margin-left: 240px;
    padding: 2rem;
}

@media (max-width: 768px) {
    .admin-sidebar {
        transform: translateX(-100%);
        transition: transform 0.2s ease;
        z-index: 101;
    }

    .admin-sidebar.is-open { transform: translateX(0); }

    .admin-sidebar-toggle {
        display: flex;
        align-items: center;
        justify-content: center;
        position: fixed;
        top: 1rem;
        left: 1rem;
        z-index: 102;
        width: 40px;
        height: 40px;
        background: var(--bg-secondary, var(--card-bg));
        border: 1px solid var(--border-subtle, var(--border));
        border-radius: 6px;
        color: var(--text-primary, var(--fg));
        cursor: pointer;
    }

    .admin-sidebar-toggle svg { width: 20px; height: 20px; }

    .admin-sidebar-overlay.is-visible {
        display: block;
        position: fixed;
        inset: 0;
        background: rgba(0, 0, 0, 0.5);
        z-index: 100;
    }

    .admin-main {
        margin-left: 0;
        padding: 4.5rem 1.5rem 2rem;
    }
}

form label {
    display: block;
    margin-bottom: 1rem;
}

form input[type="text"],
form input[type="password"],
form input[type="email"],
form input[type="url"],
form input[type="file"],
form input[type="datetime-local"],
form textarea {
    display: block;
    width: 100%;
    margin-top: 0.25rem;
    padding: 0.5rem;
    background: var(--bg-secondary, var(--card-bg));
    border: 1px solid var(--border-subtle, var(--border));
    color: var(--text-primary, var(--fg));
    border-radius: 4px;
}

button {
    background: var(--accent-primary, var(--accent));
    color: var(--bg-primary, #fff);
    border: none;
    padding: 0.5rem 1rem;
    border-radius: 4px;
    cursor: pointer;
}

.social-link-row {
    display: flex;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
}

/* Duplication volontaire de .social-link-row (2026-07-21, voir
   templates/account/profile.php) — contexte self-service public plutôt
   qu'admin/vitrine, même convention que jdr-row/forum-topic-row/gaming-row
   déjà dans ce projet. Bouton "Retirer" en plus (liste potentiellement plus
   longue qu'à la vitrine, ajoutée dynamiquement). */
.profile-link-row {
    display: flex;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
}

.profile-link-row input { flex: 1; }

/* Lieux associés d'un personnage (2026-08-01, admin/entity_edit.php) — même
   duplication volontaire que .profile-link-row ci-dessus (select + champ de
   rôle libre au lieu de 2 champs texte, mais même famille visuelle). */
.jdr-location-link-row {
    display: flex;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
}

.jdr-location-link-row select { flex: 1; }
.jdr-location-link-row input { flex: 1; }

/* Lien de contact sans URL reconstructible (Discord, ou plateforme non
   reconnue — voir Core\ContactLinkResolver/templates/profile.php) : texte +
   bouton "Copier" plutôt qu'un <a class="btn-secondary"> comme les autres
   <li> de .social-links, donc pas le même style de "pilule" cliquable. */
.profile-contact-copy {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.color-field {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.25rem;
}

.color-field input[type="color"] {
    width: 48px;
    height: 38px;
    padding: 0.15rem;
    flex-shrink: 0;
}

.color-field input[type="text"] {
    margin-top: 0;
}

.dashboard-cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 1rem;
    margin-top: 1.5rem;
}

.extensions-table {
    width: 100%;
    border-collapse: collapse;
}

.extensions-table th, .extensions-table td {
    text-align: left;
    padding: 0.75rem;
    border-bottom: 1px solid var(--border);
}

.error { color: var(--status-danger, #7A2E1A); }
.success { color: #6bffa0; }

/* Grille mensuelle du calendrier (extension calendar), même logique de
   bordures que .extensions-table mais cellules de hauteur fixe pour
   accueillir plusieurs événements par jour. */
.calendar-grid {
    width: 100%;
    border-collapse: collapse;
    table-layout: fixed;
    /* Fond quasi-opaque propre à la grille (même teinte que .card), pour ne
       jamais laisser transparaître le fond d'écran de page derrière les
       chiffres/badges — indépendant de .page-hero, qui ne couvre de toute
       façon plus que la bande de titre au-dessus de cette grille. */
    background: var(--bg-secondary, var(--card-bg));
}

.calendar-grid th, .calendar-grid td {
    border: 1px solid var(--border-subtle, var(--border));
    padding: 0.5rem;
    vertical-align: top;
    text-align: left;
}

.calendar-grid td { height: 90px; }

.calendar-day-number {
    color: var(--text-secondary, var(--muted));
    font-size: 0.8rem;
}

/* Événement passé (voir CalendarController::isPast) : jamais supprimé, juste
   atténué visuellement dans la grille — reste par ailleurs identique
   (toujours affiché, toujours cliquable si une URL est renseignée). */
.calendar-event-past {
    opacity: 0.6;
}

.calendar-event-past a {
    color: var(--text-secondary, var(--muted));
}

/* --- Médiathèque + composant sélecteur (media-picker), voir media_picker_field() --- */

.media-grid, .media-picker-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    gap: 1rem;
}

.media-grid-item img, .media-picker-item img {
    width: 100%;
    height: 100px;
    object-fit: cover;
    border-radius: 4px;
    display: block;
}

.media-grid-item { padding: 0.75rem; }
.media-grid-item p { margin: 0.35rem 0 0; font-size: 0.85rem; word-break: break-word; }

.media-picker-item {
    cursor: pointer;
    border: 1px solid var(--border-subtle, var(--border));
    border-radius: 6px;
    padding: 0.35rem;
    background: var(--bg-secondary, var(--card-bg));
}

.media-picker-item:hover {
    border-color: var(--accent-primary, var(--accent));
}

.media-picker {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin: 0.5rem 0 1rem;
}

.media-picker-preview {
    width: 64px;
    height: 64px;
    border: 1px solid var(--border-subtle, var(--border));
    border-radius: 4px;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    background: var(--bg-secondary, var(--card-bg));
    flex-shrink: 0;
}

.media-picker-preview img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.media-picker-preview .meta {
    font-size: 0.7rem;
    text-align: center;
    padding: 0.25rem;
}

.media-picker-modal {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.6);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 200;
    padding: 2rem;
}

.media-picker-modal-content {
    background: var(--bg-primary, var(--bg));
    border: 1px solid var(--border-subtle, var(--border));
    border-radius: 8px;
    padding: 1.5rem;
    width: 100%;
    max-width: 720px;
    max-height: 80vh;
    overflow-y: auto;
    position: relative;
}

.media-picker-modal-close {
    position: absolute;
    top: 0.75rem;
    right: 0.75rem;
    background: transparent;
    color: var(--text-primary, var(--fg));
    font-size: 1.5rem;
    line-height: 1;
    padding: 0.25rem 0.5rem;
}

.media-picker-upload-form {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    margin-bottom: 1rem;
}

/* --- Univers/Campagne : listes compactes façon tableau (2026-07-20, même
   principe que .forum-topic-row) — en-tête de colonnes générique réutilisé
   par les entités (avec vignette) ET les timelines/cartes (sans vignette),
   sélecteurs "li.jdr-row" pour primer sur .card { display: block } comme
   pour .forum-topic-row (voir le commentaire équivalent plus haut). */
.jdr-row-header {
    padding: 0.5rem 1.5rem;
    color: var(--text-secondary, var(--muted));
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.jdr-row-header span:last-child { text-align: right; }

li.jdr-entity-row, .jdr-entity-row-grid {
    display: grid;
    grid-template-columns: 48px 1fr 110px;
    align-items: center;
    gap: 1rem;
}

.jdr-row-thumb {
    width: 48px;
    height: 48px;
    border-radius: 6px;
    background: var(--bg-primary, var(--bg));
    border: 1px solid var(--border-subtle, var(--border));
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    flex-shrink: 0;
}

.jdr-row-thumb img { width: 100%; height: 100%; object-fit: cover; }
.jdr-row-thumb svg { width: 22px; height: 22px; color: var(--accent-primary, var(--accent)); }

.jdr-row-main { min-width: 0; }
.jdr-row-main h3 { margin: 0 0 0.15rem; }
.jdr-row-summary {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    margin: 0;
}

.jdr-row-stat { text-align: right; }

li.jdr-simple-row, .jdr-simple-row-grid {
    display: grid;
    grid-template-columns: 1fr 130px;
    align-items: center;
    gap: 1rem;
}

@media (max-width: 768px) {
    .jdr-row-header { display: none; }
    li.jdr-entity-row, .jdr-entity-row-grid {
        grid-template-columns: 40px 1fr;
        row-gap: 0.3rem;
    }
    .jdr-row-stat {
        grid-column: 1 / -1;
        text-align: left;
        border-top: 1px solid var(--border-subtle, var(--border));
        padding-top: 0.5rem;
        margin-top: 0.25rem;
    }
    .jdr-row-summary { white-space: normal; }
    li.jdr-simple-row, .jdr-simple-row-grid {
        grid-template-columns: 1fr;
        row-gap: 0.3rem;
    }
    .jdr-simple-row .jdr-row-stat, .jdr-simple-row-grid .jdr-row-stat { text-align: left; }
}

/* Marqueur "Aujourd'hui" d'une timeline (2026-08-13, voir
   Extensions\jdr\Controllers\TimelineController::show()) — même langage
   visuel que .flash-banner (accent en bordure gauche), pour se distinguer
   clairement des cartes d'événement normales dans la même liste. */
.jdr-timeline-today {
    border-left: 3px solid var(--accent-primary, var(--accent));
    background: var(--bg-secondary, var(--card-bg));
}

/* --- Univers : hiérarchie Lieux/Organisations en arbre repliable (2026-08-01,
   voir templates/entities_tree.php) — même pattern/comportement que les
   sous-catégories du Forum (details/summary, "Tout déplier"/"Tout replier"),
   classes dédiées .jdr-tree-* plutôt que réutilisées (voir .forum-child-section
   dans ce fichier pour l'original ; même duplication volontaire que
   .jdr-location-link-row/.profile-link-row). Un niveau d'imbrication = un
   wrapper .jdr-tree-children de plus, l'indentation s'accumule naturellement. */
.jdr-tree-toolbar {
    display: flex;
    gap: 0.75rem;
    margin: 1rem 0;
}

details.jdr-tree-node {
    margin-top: 0.75rem;
    padding: 1rem 1.25rem;
    background: var(--bg-secondary, var(--card-bg));
    border: 1px solid var(--accent-primary, var(--accent));
    border-radius: 8px;
}

details.jdr-tree-node > summary {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    column-gap: 1rem;
    row-gap: 0.25rem;
    cursor: pointer;
}

details.jdr-tree-node > summary .jdr-tree-summary-name {
    color: var(--accent-primary, var(--accent));
    font-weight: 700;
    font-size: 1.05rem;
}

details.jdr-tree-node > summary .jdr-tree-summary-desc {
    flex: 1 1 220px;
    min-width: 0;
    color: var(--text-secondary, var(--muted));
    font-weight: 400;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

details.jdr-tree-node > summary .jdr-tree-summary-stats {
    flex: 0 0 auto;
    margin-left: auto;
    color: var(--text-secondary, var(--muted));
    font-weight: 400;
    font-size: 0.8rem;
    white-space: nowrap;
}

details.jdr-tree-node[open] > summary {
    margin-bottom: 1rem;
    padding-bottom: 0.75rem;
    border-bottom: 1px solid var(--border-subtle, var(--border));
}

.jdr-tree-children {
    margin: 1rem 0 0 1.5rem;
    padding-left: 1rem;
    border-left: 2px solid var(--border-subtle, var(--border));
}

@media (max-width: 768px) {
    .jdr-tree-toolbar { flex-wrap: wrap; }

    details.jdr-tree-node > summary .jdr-tree-summary-desc {
        flex-basis: 100%;
        white-space: normal;
    }

    details.jdr-tree-node > summary .jdr-tree-summary-stats { margin-left: 0; }

    .jdr-tree-children { margin-left: 0.75rem; padding-left: 0.75rem; }
}

/* --- Cartes interactives JDR (Extensions\jdr\Controllers\MapController) ---
   .map-pin-marker réutilisé à l'identique côté public (.map-viewer-canvas)
   ET côté admin (.map-editor-canvas), pour un seul point à ajuster visuellement. */
.map-viewer-canvas, .map-editor-canvas {
    position: relative;
    display: inline-block;
    max-width: 100%;
}

.map-editor-canvas {
    cursor: crosshair;
}

.map-viewer-image, .map-editor-image {
    display: block;
    max-width: 100%;
    border-radius: 8px;
    border: 1px solid var(--border-subtle, var(--border));
}

.map-pin-marker {
    position: absolute;
    width: 16px;
    height: 16px;
    transform: translate(-50%, -50%);
    background: var(--accent-primary, var(--accent));
    border: 2px solid var(--bg-primary, #000);
    border-radius: 50%;
    box-shadow: 0 0 0 2px var(--accent-primary, var(--accent));
    cursor: pointer;
}

.map-pin-marker.map-pin-static {
    cursor: default;
}

.map-editor-crosshair {
    position: absolute;
    width: 14px;
    height: 14px;
    transform: translate(-50%, -50%);
    border: 2px dashed var(--accent-secondary, var(--accent));
    border-radius: 50%;
    pointer-events: none;
}

/* Info-bulle de pin en CSS pur (:target), sans JS côté public — le lien de
   fermeture pointe vers #map-top (ancre en haut de la page) plutôt que "#"
   nu, pour désactiver le :target sans provoquer de saut de défilement. */
.map-pin-tooltip {
    display: none;
    position: absolute;
    z-index: 5;
    max-width: 260px;
    padding: 0.75rem 1rem;
    transform: translate(-50%, 12px);
    background: var(--bg-secondary, var(--card-bg));
    border: 1px solid var(--accent-primary, var(--accent));
    border-radius: 8px;
    color: var(--text-primary, var(--fg));
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
}

.map-pin-tooltip:target {
    display: block;
}

.map-pin-tooltip-close {
    float: right;
    color: var(--text-secondary, var(--muted));
}

.map-breadcrumb a {
    margin-right: 0.25rem;
}

/* --- Fonds d'écran par page/section (voir Core\Background::render()) ---
   Posé uniquement sur la bande "hero" en haut de page (.page-hero pour
   forum/gaming/calendar, .page-background pour la vitrine) ; jamais sur le
   contenu structuré en dessous (.page-container). Sans image assignée,
   aucun style inline n'est émis et le fond reste celui du thème. */
.page-background, .page-hero {
    min-height: 150px;
}

/* --- 2FA : cadre du QR code TOTP (2026-07-20, correctif) — qrcodejs
   (davidshimjs) dessine ses modules jusqu'au bord du canvas SANS zone
   silencieuse (quiet zone), contrairement à ce qu'exige la norme QR pour une
   lecture fiable par une caméra de téléphone (~4 modules de marge claire
   requis) ; posé sur le thème sombre du site, le motif touchait directement
   le fond, illisible pour la plupart des apps d'authentification malgré un
   code manuel/algorithme TOTP corrects. Fond blanc + marge généreuse fixe
   (24px, largement > 4 modules pour la taille de QR générée ici) ajoutés
   autour du canvas plutôt que dans la librairie vendorisée elle-même. */
.qr-code-frame {
    display: inline-block;
    background: #ffffff;
    padding: 24px;
    border-radius: 8px;
    line-height: 0;
}

/* --- Mon compte : deux colonnes sur desktop (2026-07-21, voir
   templates/account/overview.php) — infos à gauche, réglages à droite,
   empilées verticalement sous 768px comme le reste du site (jdr-row/
   gaming-row/shop-row utilisent le même seuil). */
/* --- Espace "Mon compte" (2026-07-21) : sidebar persistante + contenu,
   style public (pas celui de l'admin) — voir account_sidebar() dans
   core/helpers.php et core/templates/account/*. --- */
.account-shell {
    display: grid;
    grid-template-columns: 220px 1fr;
    gap: 1.5rem;
    align-items: start;
}

.account-sidebar {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    background: var(--bg-secondary, var(--card-bg));
    border: 1px solid var(--border-subtle, var(--border));
    border-radius: 8px;
    padding: 0.5rem;
    position: sticky;
    top: 1rem;
}

.account-sidebar-link {
    display: block;
    padding: 0.6rem 0.75rem;
    border-radius: 6px;
    color: var(--text-primary, var(--fg));
    text-decoration: none;
    font-size: 0.95rem;
}

.account-sidebar-link:hover {
    background-color: color-mix(in srgb, var(--accent-secondary, var(--accent)) 15%, transparent);
}

.account-sidebar-link.is-active {
    background: var(--accent-primary, var(--accent));
    color: var(--bg-primary, #1a1a1a);
    font-weight: 700;
}

.account-content {
    min-width: 0;
}

@media (max-width: 768px) {
    .account-shell {
        grid-template-columns: 1fr;
    }

    /* 2026-07-21 : remplace le défilement horizontal (peu évident à
       découvrir, liens collés les uns aux autres) par un retour à la ligne —
       chaque entrée devient une pastille bordée façon .btn-secondary plutôt
       qu'un simple lien souligné, avec un espacement net entre elles. */
    .account-sidebar {
        flex-direction: row;
        flex-wrap: wrap;
        position: static;
        gap: 0.6rem;
        padding: 0.75rem;
    }

    .account-sidebar-link {
        flex: 0 0 auto;
        white-space: nowrap;
        border: 1px solid var(--border-subtle, var(--border));
        border-radius: 999px;
        padding: 0.55rem 1.1rem;
    }

    .account-sidebar-link:hover {
        border-color: var(--accent-secondary, var(--accent));
    }

    /* La pastille active ressort par un fond plein (déjà défini par
       .account-sidebar-link.is-active ci-dessus) + une bordure assortie,
       jamais juste une couleur de texte différente. */
    .account-sidebar-link.is-active {
        border-color: var(--accent-primary, var(--accent));
    }
}

/* --- Univers (2026-08-01) : sidebar persistante + contenu, même famille que
   .account-shell/.account-sidebar ci-dessus (voir Extensions\jdr\Support\UniverseSidebar) —
   classes dédiées plutôt que réutilisées, même duplication volontaire que
   .jdr-location-link-row/.profile-link-row. --- */
.jdr-universe-shell {
    display: grid;
    grid-template-columns: 220px 1fr;
    gap: 1.5rem;
    align-items: start;
}

.jdr-universe-sidebar {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    background: var(--bg-secondary, var(--card-bg));
    border: 1px solid var(--border-subtle, var(--border));
    border-radius: 8px;
    padding: 0.5rem;
    position: sticky;
    top: 1rem;
}

.jdr-universe-sidebar-link {
    display: block;
    padding: 0.6rem 0.75rem;
    border-radius: 6px;
    color: var(--text-primary, var(--fg));
    text-decoration: none;
    font-size: 0.95rem;
}

.jdr-universe-sidebar-link:hover {
    background-color: color-mix(in srgb, var(--accent-secondary, var(--accent)) 15%, transparent);
}

.jdr-universe-sidebar-link.is-active {
    background: var(--accent-primary, var(--accent));
    color: var(--bg-primary, #1a1a1a);
    font-weight: 700;
}

.jdr-universe-content {
    min-width: 0;
}

@media (max-width: 768px) {
    .jdr-universe-shell {
        grid-template-columns: 1fr;
    }

    .jdr-universe-sidebar {
        flex-direction: row;
        flex-wrap: wrap;
        position: static;
        gap: 0.6rem;
        padding: 0.75rem;
    }

    .jdr-universe-sidebar-link {
        flex: 0 0 auto;
        white-space: nowrap;
        border: 1px solid var(--border-subtle, var(--border));
        border-radius: 999px;
        padding: 0.55rem 1.1rem;
    }

    .jdr-universe-sidebar-link:hover {
        border-color: var(--accent-secondary, var(--accent));
    }

    .jdr-universe-sidebar-link.is-active {
        border-color: var(--accent-primary, var(--accent));
    }
}

/* --- Fiche d'entité (2026-08-13) : sidebar d'ancres + contenu sur UNE SEULE
   page (entity.php ET entity_edit.php), même famille visuelle que
   .jdr-universe-shell ci-dessus mais classes dédiées (voir le commentaire de
   UniverseSidebar.php — duplication volontaire, pas de routes séparées ici,
   juste des ancres #section-xxx dans le même document). --- */
.jdr-entity-shell {
    display: grid;
    grid-template-columns: 220px 1fr;
    gap: 1.5rem;
    align-items: start;
}

.jdr-entity-sidebar {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    background: var(--bg-secondary, var(--card-bg));
    border: 1px solid var(--border-subtle, var(--border));
    border-radius: 8px;
    padding: 0.5rem;
    position: sticky;
    top: 1rem;
}

.jdr-entity-sidebar-link {
    display: block;
    padding: 0.6rem 0.75rem;
    border-radius: 6px;
    color: var(--text-primary, var(--fg));
    text-decoration: none;
    font-size: 0.95rem;
}

.jdr-entity-sidebar-link:hover {
    background-color: color-mix(in srgb, var(--accent-secondary, var(--accent)) 15%, transparent);
}

/* .jdr-entity-body (PAS .jdr-entity-content, déjà pris par le wrapper de
   contenu rendu/secret dans entity.php — évite une collision de nom, voir
   ce commentaire) : la colonne de contenu de la sidebar elle-même. */
.jdr-entity-body {
    min-width: 0;
}

.jdr-entity-section {
    scroll-margin-top: 1rem;
}

.jdr-entity-section + .jdr-entity-section {
    margin-top: 2rem;
    padding-top: 2rem;
    border-top: 1px solid var(--border-subtle, var(--border));
}

@media (max-width: 768px) {
    .jdr-entity-shell {
        grid-template-columns: 1fr;
    }

    .jdr-entity-sidebar {
        flex-direction: row;
        flex-wrap: wrap;
        position: static;
        gap: 0.6rem;
        padding: 0.75rem;
    }

    .jdr-entity-sidebar-link {
        flex: 0 0 auto;
        white-space: nowrap;
        border: 1px solid var(--border-subtle, var(--border));
        border-radius: 999px;
        padding: 0.55rem 1.1rem;
    }

    .jdr-entity-sidebar-link:hover {
        border-color: var(--accent-secondary, var(--accent));
    }
}

/* --- Profil public streameur (2026-08-14) : sidebar d'ancres + contenu sur
   UNE SEULE page (/streamers/{slug}, voir Extensions\streamers\Support\StreamerProfileSidebar),
   même famille visuelle que .jdr-entity-shell ci-dessus mais classes dédiées
   (même duplication volontaire que .jdr-universe-shell/.jdr-entity-shell). --- */
.streamer-profile-shell {
    display: grid;
    grid-template-columns: 220px 1fr;
    gap: 1.5rem;
    align-items: start;
}

.streamer-profile-sidebar {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    background: var(--bg-secondary, var(--card-bg));
    border: 1px solid var(--border-subtle, var(--border));
    border-radius: 8px;
    padding: 0.5rem;
    position: sticky;
    top: 1rem;
}

.streamer-profile-sidebar-link {
    display: block;
    padding: 0.6rem 0.75rem;
    border-radius: 6px;
    color: var(--text-primary, var(--fg));
    text-decoration: none;
    font-size: 0.95rem;
}

.streamer-profile-sidebar-link:hover {
    background-color: color-mix(in srgb, var(--accent-secondary, var(--accent)) 15%, transparent);
}

.streamer-profile-body {
    min-width: 0;
}

.streamer-profile-section {
    scroll-margin-top: 1rem;
}

.streamer-profile-section + .streamer-profile-section {
    margin-top: 2rem;
    padding-top: 2rem;
    border-top: 1px solid var(--border-subtle, var(--border));
}

@media (max-width: 768px) {
    .streamer-profile-shell {
        grid-template-columns: 1fr;
    }

    .streamer-profile-sidebar {
        flex-direction: row;
        flex-wrap: wrap;
        position: static;
        gap: 0.6rem;
        padding: 0.75rem;
    }

    .streamer-profile-sidebar-link {
        flex: 0 0 auto;
        white-space: nowrap;
        border: 1px solid var(--border-subtle, var(--border));
        border-radius: 999px;
        padding: 0.55rem 1.1rem;
    }

    .streamer-profile-sidebar-link:hover {
        border-color: var(--accent-secondary, var(--accent));
    }
}

/* --- Onglets de /account/interests (2026-07-21) : une catégorie visible à la
   fois, bascule purement visuelle en JS (voir templates/account/interests.php) —
   même famille de pastille que .account-sidebar-link pour rester cohérent. --- */
.interest-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin: 1rem 0 1.25rem;
}

.interest-tab {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    background: transparent;
    border: 1px solid var(--border-subtle, var(--border));
    border-radius: 999px;
    padding: 0.5rem 1.1rem;
    color: var(--text-primary, var(--fg));
    font-family: inherit;
    font-size: 0.95rem;
    cursor: pointer;
}

.interest-tab:hover {
    border-color: var(--accent-secondary, var(--accent));
}

.interest-tab.is-active {
    background: var(--accent-primary, var(--accent));
    border-color: var(--accent-primary, var(--accent));
    color: var(--bg-primary, #1a1a1a);
    font-weight: 700;
}

/* Indicateur "catégorie déjà remplie" (2026-07-21) — état enregistré au
   chargement de la page, pas mis à jour en direct pendant la saisie. */
.interest-tab-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--accent-primary, var(--accent));
    flex-shrink: 0;
}

.interest-tab.is-active .interest-tab-dot {
    background: var(--bg-primary, #1a1a1a);
}

.interest-category {
    display: none;
}

.interest-category.is-active {
    display: block;
}

/* Onglets "Remplir manuellement"/"Génération automatique" (2026-08-02,
   extensions/jdr/templates/admin/entity_edit.php) — même principe que
   .interest-category ci-dessus (bascule pure JS), réutilise directement
   .interest-tabs/.interest-tab pour les boutons (déjà stylés, à égalité de
   mise en avant par construction, aucune variante nécessaire). */
.jdr-characteristics-panel {
    display: none;
}

.jdr-characteristics-panel.is-active {
    display: block;
}

/* --- 2FA : liste des codes de secours (2026-07-20, affichée une seule fois à
   l'activation, voir templates/account/2fa_backup_codes.php) --- */
.backup-codes-list {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 0.75rem;
    margin: 1.5rem 0;
}

.backup-codes-list code {
    display: block;
    text-align: center;
    padding: 0.6rem;
    font-size: 1.1rem;
    background: var(--bg-secondary, var(--card-bg));
    border: 1px solid var(--border-subtle, var(--border));
    border-radius: 6px;
}

@media (max-width: 480px) {
    .backup-codes-list { grid-template-columns: 1fr; }
}

/* --- Ordre du menu de navigation, glisser-déposer (2026-07-21, voir
   Core\Controllers\NavigationAdminController, SortableJS vendoré) --- */
.nav-order-list {
    list-style: none;
    padding: 0;
    margin: 1rem 0;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    max-width: 520px;
}

.nav-order-item {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    background: var(--bg-secondary, var(--card-bg));
    border: 1px solid var(--border-subtle, var(--border));
    border-radius: 6px;
    padding: 0.6rem 0.75rem;
}

.nav-order-handle {
    cursor: grab;
    color: var(--text-secondary, var(--muted));
    font-size: 1.1rem;
    line-height: 1;
}

.nav-order-item.sortable-ghost {
    opacity: 0.4;
    border-style: dashed;
}

.nav-order-item.sortable-chosen {
    border-color: var(--accent-primary, var(--accent));
}

/* --- Boutons de connexion externe (2026-07-21, voir oauth_login_buttons()
   dans core/helpers.php) — /login et /demande-de-compte. --- */
.oauth-login-buttons {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    margin: 1rem 0;
}

.oauth-login-buttons .btn-secondary {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
}

.oauth-login-buttons svg {
    width: 18px;
    height: 18px;
}

/* --- Panneaux d'aide repliables (2026-07-21, "Comment obtenir ces
   identifiants ?" — /admin/general, Connexions externes) --- */
details {
    background: var(--bg-secondary, var(--card-bg));
    border: 1px solid var(--border-subtle, var(--border));
    border-radius: 6px;
    padding: 0.5rem 0.75rem;
    margin: 0.5rem 0;
}

details summary {
    cursor: pointer;
    color: var(--accent-primary, var(--accent));
    font-weight: 700;
}

details ol {
    margin: 0.5rem 0 0.25rem;
    padding-left: 1.25rem;
}

details code {
    word-break: break-all;
}

/* --- Overlays de stream, mode HTML personnalisé + éditeur visuel
   (2026-07-21, v2, Extensions\streamers) --- */
.overlay-preview-frame {
    display: block;
    width: 100%;
    max-width: 640px;
    aspect-ratio: 16 / 9;
    border: 1px solid var(--border-subtle, var(--border));
    border-radius: 8px;
    background: #000;
}

/* Canevas de l'éditeur visuel : même principe que .map-editor-canvas
   (positionnement relatif, éléments enfants absolus en %), mais dimensionné
   en ratio 16:9 plutôt que sur une image, et le fond vient de la config
   (background-image inline) plutôt que d'un <img>. */
.overlay-editor-canvas {
    position: relative;
    width: 100%;
    max-width: 640px;
    aspect-ratio: 16 / 9;
    background-color: var(--bg-primary, #0f1115);
    background-size: cover;
    background-position: center;
    border: 1px solid var(--border-subtle, var(--border));
    border-radius: 8px;
    overflow: hidden;
    margin: 0.75rem 0;
}

.overlay-editor-element {
    position: absolute;
    padding: 0.25rem 0.5rem;
    background: rgba(0, 0, 0, 0.35);
    border: 1px dashed var(--accent-secondary, var(--accent));
    color: var(--text-primary, #e8e8ea);
    font-size: 0.85rem;
    cursor: grab;
    user-select: none;
    white-space: nowrap;
}

.overlay-editor-element.selected {
    border-style: solid;
    border-color: var(--accent-primary, var(--accent));
}

.overlay-editor-add-buttons {
    display: flex;
    gap: 0.5rem;
    margin: 0.5rem 0;
}

.overlay-color-swatch {
    width: 24px;
    height: 24px;
    border-radius: 50%;
    border: 1px solid var(--border-subtle, var(--border));
    cursor: pointer;
    padding: 0;
}

/* --- Identité visuelle par module (2026-08-15, voir Core\ModuleThemes/
   admin/module_theme_edit.php) — mêmes pastilles que .overlay-color-swatch
   ci-dessus, classe dédiée plutôt que réutilisée (même duplication
   volontaire que le reste du projet pour ces petits composants). --- */
.module-theme-swatch {
    width: 24px;
    height: 24px;
    border-radius: 50%;
    border: 1px solid var(--border-subtle, var(--border));
    cursor: pointer;
    padding: 0;
}

.module-theme-swatch-default {
    background: transparent;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-style: dashed;
    color: var(--text-secondary, var(--muted));
    font-size: 0.85rem;
    line-height: 1;
}

.module-theme-preview {
    max-width: 480px;
}
