:root {
    --bg: #f3f4f6;
    --surface: #ffffff;
    --border: #e2e4e9;
    --text: #1a1d24;
    --text-muted: #6b7280;
    --primary: #2352d9;
    --primary-hover: #1c42b3;
    --primary-bg: #eaf0fe;
    --danger: #d3352c;
    --danger-hover: #b02a23;
    --danger-bg: #fbeaea;
    --success: #157a3d;
    --success-bg: #e5f5ea;
    --warning: #a3610a;
    --warning-bg: #fdf1e0;
    --neutral-bg: #eceef1;
    --radius: .5em;
    --radius-lg: .8em;
    --shadow-sm: 0 1px 2px rgba(20, 22, 30, .06);
    --shadow-md: 0 6px 24px rgba(20, 22, 30, .14);
}

* { box-sizing: border-box; }

body {
    font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
    -webkit-font-smoothing: antialiased;
    max-width: 64em;
    margin: 0 auto;
    padding: 0 1.2em 3em;
    color: var(--text);
    background: var(--bg);
}

h1, h2, h3 { font-weight: 600; letter-spacing: -.01em; }
h1 { font-size: 1.15em; margin: 0; }
h2 {
    font-size: 1.05em;
    margin: 0 0 .9em;
    padding-bottom: .5em;
    border-bottom: 1px solid var(--border);
}
h3 { font-size: 1.1em; margin: 0 0 .9em; }

/* ── Barre du haut ────────────────────────────────────────────────── */

#topbar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 1em;
    margin: 0 -1.2em 1.6em;
    padding: .9em 1.2em;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    box-shadow: var(--shadow-sm);
    position: sticky;
    top: 0;
    z-index: 30;
}

#topbar > div:last-child {
    font-size: .85em;
    color: var(--text-muted);
}
#topbar b { color: var(--text); font-weight: 600; }

#menuBtn {
    background: none;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--text);
    padding: .35em .5em;
    line-height: 0;
}
#menuBtn:hover { background: var(--neutral-bg); }

/* ── Cartes (sections) ───────────────────────────────────────────── */

section {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    padding: 1.4em 1.6em;
    margin-bottom: 1.4em;
}

/* ── Formulaires ──────────────────────────────────────────────────── */

label {
    display: block;
    font-size: .82em;
    font-weight: 600;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: .03em;
    margin: 1em 0 .3em;
}

/* Cases à cocher (rubriques secondaires du formulaire article) — pas le
   style label de champ (majuscules/gras), juste une ligne compacte. */
#artSecondaryCategories {
    display: flex;
    flex-wrap: wrap;
    gap: .3em .9em;
    margin: .3em 0 0;
    max-height: 8em;
    overflow-y: auto;
    padding: .5em .7em;
    border: 1px solid var(--border);
    border-radius: var(--radius);
}
#artSecondaryCategories .checkbox-row {
    display: flex;
    align-items: center;
    gap: .4em;
    font-size: .9em;
    font-weight: normal;
    text-transform: none;
    letter-spacing: normal;
    color: var(--text);
    margin: 0;
}
#artSecondaryCategories .checkbox-row input { width: auto; margin: 0; }

/* ── Clés d'accès (passkeys), modale Utilisateurs ────────────────────── */
.credentials-list { list-style: none; margin: 0; padding: 0; }
.credentials-list li {
    display: flex; align-items: center; justify-content: space-between;
    gap: .6em; padding: .4em 0; border-bottom: 1px solid var(--border);
    font-size: .9em;
}
.credentials-list li:last-child { border-bottom: none; }
.credentials-list li.muted { color: var(--text-muted); justify-content: flex-start; }

/* ── Médiathèque ──────────────────────────────────────────────────── */

#mediaSearch, #mediaPickerSearch, #artSearch { max-width: 24em; margin-top: .8em; }

.media-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(9em, 1fr));
    gap: .8em;
    margin-top: 1em;
}
.media-item {
    position: relative;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
    background: var(--surface);
}
.media-item img {
    display: block;
    width: 100%;
    height: 7em;
    object-fit: cover;
}
.media-item .media-caption {
    padding: .4em .5em;
    font-size: .78em;
    color: var(--text-muted);
    line-height: 1.3;
    max-height: 3.2em;
    overflow: hidden;
}
.media-item .media-item-actions {
    position: absolute;
    top: .3em;
    right: .3em;
    display: flex;
    gap: .2em;
}
.media-item .icon-btn {
    background: rgba(255, 255, 255, .92);
    margin: 0;
}
.media-item-pickable {
    cursor: pointer;
    transition: box-shadow .12s;
}
.media-item-pickable:hover { box-shadow: 0 0 0 2px var(--primary); }

/* ── Filtres (filterModal) ────────────────────────────────────────── */

#filterModal { max-width: 32em; }
.filter-hint {
    margin: 0 0 1em;
    font-size: .85em;
    color: var(--text-muted);
    text-transform: none;
    letter-spacing: normal;
}
.filter-checklist {
    display: flex;
    flex-wrap: wrap;
    gap: .4em 1.2em;
    margin-bottom: .4em;
}
.filter-checklist .checkbox-row {
    display: flex;
    align-items: center;
    gap: .4em;
    font-size: .9em;
    font-weight: normal;
    text-transform: none;
    letter-spacing: normal;
    color: var(--text);
    margin: 0;
}
.filter-checklist .checkbox-row input { width: auto; margin: 0; }
/* flex-basis:100% force un saut de ligne dans le flex-wrap ci-dessus —
   groupe Orientation/Ajustements/Style sans avoir à sortir du même
   conteneur flex pour chaque groupe. */
.filter-group-label {
    flex-basis: 100%;
    margin: .6em 0 0;
    font-size: .72em;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--text-muted);
}
.filter-group-label:first-child { margin-top: 0; }
/* Rotation 90°/180° — choix exclusif, un <select> plutôt que des cases
   à cocher indépendantes qui n'auraient pas de sens ensemble (on ne
   choisit jamais "90° horaire" ET "90° antihoraire" à la fois). Miroir
   H/V restent des cases (combinables entre eux et avec une rotation). */
.filter-rotation-field {
    flex-basis: 100%;
    margin: 0 0 .3em;
}
.filter-rotation-field select { margin-top: .2em; }

.filter-panel {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: .8em 1em 1em;
    margin: .8em 0;
    background: var(--neutral-bg);
}
.filter-panel h4 { margin: 0 0 .6em; font-size: .95em; }
.filter-preview {
    display: block;
    width: 100%;
    max-height: 16em;
    object-fit: contain;
    background: #ddd;
    border-radius: var(--radius);
    margin-bottom: .6em;
}
.filter-slider-label {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    margin: .6em 0 .2em;
}
.filter-slider-value {
    font-weight: 600;
    text-transform: none;
    letter-spacing: normal;
    color: var(--text);
}
.filter-panel input[type="range"] { width: 100%; margin: 0; }

#mediaPickerModal { max-width: 40em; }
#mediaPickerGrid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(8em, 1fr));
    gap: .7em;
    max-height: 60vh;
    overflow-y: auto;
    margin-bottom: 1em;
}

/* ── Éditeur riche ────────────────────────────────────────────────────── */

.editor-toolbar {
    display: flex;
    flex-wrap: wrap;
    gap: .3em;
    margin: .3em 0 0;
    padding: .4em;
    border: 1px solid var(--border);
    border-bottom: none;
    border-radius: var(--radius) var(--radius) 0 0;
    background: var(--neutral-bg);
}
.editor-toolbar button {
    margin: 0;
    padding: .3em .6em;
    font-size: .85em;
}
.editor-toolbar .toolbar-sep {
    width: 1px;
    align-self: stretch;
    margin: .2em .2em;
    background: var(--border);
}
.rich-editor {
    min-height: 40vh;
    max-height: 55vh;
    overflow-y: auto;
    padding: .8em 1em;
    border: 1px solid var(--border);
    border-radius: 0 0 var(--radius) var(--radius);
    background: var(--surface);
    font-family: inherit;
    font-size: .95em;
    line-height: 1.5;
}
.rich-editor:focus { outline: none; border-color: var(--primary); }
.rich-editor img { max-width: 100%; cursor: pointer; }
.rich-editor img.cms-featured { outline: 3px solid var(--success); outline-offset: 2px; }
.rich-editor figure { margin: .8em 0; }
/* Titre (gras, taille normale) + credit (small, plus petit/gris) —
   voir insertMediaIntoBody, dashboard.js. Le titre ne doit PAS hériter
   d'un style atténué : seul le credit (élément <small>) l'est. */
.rich-editor figcaption { font-size: .92em; margin-top: .3em; }
.rich-editor figcaption small { display: block; color: var(--text-muted); font-size: .88em; margin-top: .15em; }
.rich-editor blockquote {
    margin: .8em 0;
    padding-left: 1em;
    border-left: 3px solid var(--border);
    color: var(--text-muted);
}

input, select, textarea {
    display: block;
    width: 100%;
    box-sizing: border-box;
    margin: .3em 0 0;
    padding: .55em .7em;
    font-size: .95em;
    font-family: inherit;
    color: var(--text);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}
input:focus, select:focus, textarea:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--primary-bg);
}
input[readonly] { background: var(--neutral-bg); color: var(--text-muted); }
input[type="checkbox"] { width: auto; }

textarea { min-height: 8em; font-family: ui-monospace, monospace; font-size: .88em; }
#artModal textarea#artBody { min-height: 50vh; }

fieldset {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    margin: 1em 0 0;
    padding: 0 1em 1em;
}
legend { font-size: .82em; font-weight: 600; color: var(--text-muted); padding: 0 .3em; }

/* Champs repliables de la modale article (artFieldsDetails) — laisse
   plus de place visible à l'éditeur une fois titre/rubriques/tags
   remplis, sans perdre l'accès (juste un clic pour redéplier). */
#artFieldsDetails {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    margin: 1em 0 0;
    padding: .2em .9em .9em;
}
#artFieldsDetails summary {
    cursor: pointer;
    font-size: .82em;
    font-weight: 600;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: .03em;
    padding: .7em 0;
}
#artFieldsDetails summary:hover { color: var(--text); }
#artFieldsDetails label:first-of-type { margin-top: 0; }

/* ── Boutons ──────────────────────────────────────────────────────── */

button {
    font-family: inherit;
    font-size: .9em;
    font-weight: 600;
    cursor: pointer;
    border-radius: var(--radius);
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text);
    padding: .5em 1em;
    margin: 0 .3em .3em 0;
    transition: background-color .12s, border-color .12s, color .12s;
}
button:hover { background: var(--neutral-bg); }
button:disabled { opacity: .55; cursor: default; }

.btn-primary, button[type="submit"] {
    background: var(--primary);
    border-color: var(--primary);
    color: #fff;
}
.btn-primary:hover, button[type="submit"]:hover { background: var(--primary-hover); border-color: var(--primary-hover); }

.btn-danger {
    background: var(--surface);
    border-color: var(--danger);
    color: var(--danger);
}
.btn-danger:hover { background: var(--danger-bg); }

/* Confirmation "avertissement" (ex: archiver) — distincte de btn-danger
   (rouge, réservé aux actions vraiment irréversibles comme supprimer) :
   archiver ne détruit rien, juste dépublié si l'article l'était. */
.btn-warning {
    background: var(--surface);
    border-color: var(--warning);
    color: var(--warning);
}
.btn-warning:hover { background: var(--warning-bg); }

/* Boutons d'action pictos (lignes de tableau/liste) — icône + title en
   tooltip plutôt que du texte, pour rester lisible avec beaucoup
   d'actions par ligne ("je veux du pro"). */
.icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2em;
    height: 2em;
    padding: 0;
    margin: 0 .15em;
    border: 1px solid transparent;
    background: none;
    color: var(--text-muted);
    border-radius: var(--radius);
    vertical-align: middle;
}
.icon-btn:hover { background: var(--neutral-bg); color: var(--text); }
.icon-btn svg { width: 1.05em; height: 1.05em; display: block; }
.icon-btn.danger:hover { background: var(--danger-bg); color: var(--danger); }
.icon-btn.primary:hover { background: var(--primary-bg); color: var(--primary); }
.actions-cell { white-space: nowrap; }

/* ── Tableau des articles ─────────────────────────────────────────── */

table { border-collapse: collapse; width: 100%; margin: 0; font-size: .92em; }
th {
    text-align: left;
    font-size: .78em;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--text-muted);
    padding: 0 .6em .6em;
    border-bottom: 2px solid var(--border);
}
td { padding: .7em .6em; border-bottom: 1px solid var(--border); vertical-align: middle; }
tbody tr:hover { background: var(--bg); }
tbody tr:last-child td { border-bottom: none; }

/* Table → cartes empilées sous ~40em : 5 colonnes (titre/statut/mis à
   jour/publié le/actions) ne tiennent pas sur un écran de téléphone
   sans écraser le texte ou forcer un scroll horizontal. Le <thead> se
   masque, chaque <tr> devient une carte, chaque <td> une ligne
   étiquetée via data-label (posé par renderArticleRow, dashboard.js). */
@media (max-width: 40em) {
    thead { display: none; }
    table, tbody, tr, td { display: block; width: 100%; }
    tbody tr {
        border: 1px solid var(--border);
        border-radius: var(--radius);
        margin-bottom: .7em;
        padding: .3em .8em;
        background: var(--surface);
    }
    tbody tr:hover { background: var(--surface); }
    td {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 1em;
        padding: .5em 0;
        border-bottom: 1px solid var(--border);
        text-align: right;
    }
    td:last-child { border-bottom: none; }
    td::before {
        content: attr(data-label);
        font-size: .78em;
        font-weight: 600;
        text-transform: uppercase;
        letter-spacing: .04em;
        color: var(--text-muted);
        text-align: left;
    }
    td.actions-cell { justify-content: flex-end; }
    td.actions-cell::before { display: none; }
}

.status-pill {
    display: inline-block;
    padding: .2em .7em;
    border-radius: 1em;
    font-size: .8em;
    font-weight: 600;
    white-space: nowrap;
}
.status-draft { background: var(--neutral-bg); color: var(--text-muted); }
.status-in_review { background: var(--warning-bg); color: var(--warning); }
.status-published { background: var(--success-bg); color: var(--success); }
.status-archived { background: var(--neutral-bg); color: var(--text-muted); }

/* ── Liste des rubriques ──────────────────────────────────────────── */

#catList { list-style: none; margin: 0; padding: 0; }
#catList li {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .6em;
    padding: .6em 0;
    border-bottom: 1px solid var(--border);
}
#catList li:last-child { border-bottom: none; }
#catList li > span:first-child { flex: 1; min-width: 0; }

/* ── Modales ──────────────────────────────────────────────────────── */

dialog {
    border: none;
    border-radius: var(--radius-lg);
    padding: 1.5em;
    box-sizing: border-box;
    width: 94vw;
    max-width: 64em;
    max-height: 90vh;
    overflow: auto;
    box-shadow: var(--shadow-md);
}
dialog::backdrop { background: rgba(20, 22, 30, .45); }

#confirmModal { max-width: 26em; }
#confirmModal p { margin: 0 0 1.2em; font-size: .98em; }

#regenProgressModal { max-width: 26em; }
.progress-track {
    height: .7em;
    border-radius: 1em;
    background: var(--neutral-bg);
    overflow: hidden;
    margin: .3em 0 .6em;
}
.progress-fill {
    height: 100%;
    width: 0%;
    background: var(--primary);
    border-radius: 1em;
    transition: width .3s ease;
}
/* Publication d'un seul article : pas de progression chiffrée côté
   serveur pour cette opération (pas de gate partagé avec la
   republication en masse — publier un article ne doit jamais attendre
   qu'une republication en masse se termine), juste un indicateur
   "ça travaille" pendant la requête. */
.progress-fill.indeterminate {
    width: 40% !important;
    animation: progress-indeterminate 1.1s ease-in-out infinite;
}
@keyframes progress-indeterminate {
    0%   { margin-left: 0%; }
    50%  { margin-left: 58%; }
    100% { margin-left: 0%; }
}
#regenProgressText { margin: 0 0 .2em; font-weight: 600; }
.progress-label { margin: 0; color: var(--text-muted); font-size: .88em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.progress-hint { margin: .8em 0 0; color: var(--text-muted); font-size: .8em; }
#regenStatusPill { font-size: .78em; padding: .3em .8em; }

#linkCheckModal { max-width: 32em; }
#linkCheckModal p { margin: 0 0 .8em; font-size: .95em; }
#linkCheckList {
    margin: 0 0 1.2em;
    padding-left: 1.3em;
    font-size: .88em;
    color: var(--danger);
    word-break: break-all;
}
#linkCheckList li { margin-bottom: .4em; }

/* ── Divers ───────────────────────────────────────────────────────── */

#artSentinel, #mediaSentinel, #mediaPickerSentinel { height: 1px; }

.badge {
    background: var(--warning);
    color: #fff;
    border-radius: 1em;
    padding: .1em .65em;
    font-size: .72em;
    font-weight: 700;
    vertical-align: middle;
}

.views-badge {
    color: var(--text-muted);
    font-size: .78em;
    font-variant-numeric: tabular-nums;
}

.muted { color: var(--text-muted); }

/* ── Statistiques (2026-08-19) ───────────────────────────────────────── */
.stats-tabs { display: flex; gap: .4em; margin: 1em 0 0; border-bottom: 1px solid var(--border); }
.stats-tab {
    padding: .5em 1em; border: none; background: none; border-radius: 0;
    border-bottom: 2px solid transparent; font-size: .9em; color: var(--text-muted);
    margin-bottom: -1px;
}
.stats-tab.active { color: var(--primary); border-bottom-color: var(--primary); font-weight: 600; }
.stats-chart-card {
    background: #fff;
    border: 1px solid var(--border);
    border-radius: .6em;
    padding: 1em 1.2em;
    margin: 1em 0 1.4em;
}
.stats-chart-head {
    display: flex; align-items: center; justify-content: space-between;
    gap: 1em; margin-bottom: .3em;
}
.stats-chart-head h3 { margin: 0; font-size: 1em; word-break: break-all; }
.stats-chart-head-actions { display: flex; align-items: center; gap: .5em; flex: none; }
/* `margin` réinitialisé ici — la règle générique `select` (plus haut,
   pensée pour un champ de formulaire seul sous son <label>) lui donne
   `margin: .3em 0 0`, qui décale visuellement le champ vers le bas par
   rapport à #statsChartRefresh malgré `align-items:center` sur le
   conteneur flex parent (le décalage vient de la boîte de marge, pas du
   contenu). `height` alignée sur celle du bouton (2em) pour que les
   deux aient exactement le même gabarit, pas juste centrés l'un par
   rapport à l'autre. */
#statsRangeSelect { width: auto; margin: 0; padding: .3em .5em; font-size: .85em; height: 2em; }
#statsChartRefresh {
    display: flex; align-items: center; justify-content: center;
    width: 2em; height: 2em; padding: 0; flex: none;
}
.stats-chart-legend {
    display: flex; gap: 1.2em; font-size: .8em; color: var(--text-muted);
    margin-bottom: .6em;
}
.stats-dot {
    display: inline-block; width: .7em; height: .7em; border-radius: 50%;
    margin-right: .3em; vertical-align: middle;
}
.stats-dot-views { background: var(--primary); }
.stats-dot-visitors { background: var(--warning); }
/* position:relative — ancre l'infobulle (#statsChartTooltip, position
   absolute) sur ce conteneur, pas sur toute la page. aspect-ratio (pas
   une hauteur fixe en px) + pas de preserveAspectRatio="none" côté SVG
   (dashboard.js) : le viewBox est mis à l'échelle UNIFORMÉMENT en x/y
   quelle que soit la largeur réelle affichée, sinon les <circle> sont
   étirés en ellipses (signalé "les ronds ne sont pas ronds"). */
#statsChartWrap { position: relative; }
.stats-chart-svg { width: 100%; aspect-ratio: 680 / 200; display: block; overflow: visible; }
.stats-axis { stroke: var(--border); stroke-width: 1; }
.stats-grid { stroke: var(--border); stroke-width: .5; stroke-dasharray: 2 3; }
.stats-axis-label { font-size: 9px; fill: var(--text-muted); }
.stats-line { fill: none; stroke-width: 2.25; stroke-linecap: round; stroke-linejoin: round; }
.stats-line-views { stroke: var(--primary); }
.stats-line-visitors { stroke: var(--warning); }
.stats-point-views { fill: var(--primary); }
.stats-point-visitors { fill: var(--warning); }
.stats-area-views { fill: url(#statsAreaGrad); }
.stats-area-stop-start { stop-color: var(--primary); stop-opacity: .22; }
.stats-area-stop-end { stop-color: var(--primary); stop-opacity: 0; }
/* Survol interactif (2026-08-19, demandé explicitement — "se balader
   dessus pour avoir les chiffres") — .stats-hover-rect capte la souris/
   le doigt (transparent, pleine surface) ; crosshair/points en
   surbrillance restent cachés (display:none inline) jusqu'à ce que
   attachStatsChartHover les révèle. */
.stats-hover-rect { fill: transparent; cursor: crosshair; }
.stats-crosshair { stroke: var(--text-muted); stroke-width: 1; stroke-dasharray: 3 3; pointer-events: none; }
.stats-hl { stroke: #fff; stroke-width: 1.5; pointer-events: none; }
.stats-hl-views { fill: var(--primary); }
.stats-hl-visitors { fill: var(--warning); }
.stats-chart-tooltip {
    position: absolute; top: 4px; transform: translateX(-50%);
    background: #1a1a1a; color: #fff; padding: .4em .65em;
    border-radius: .35em; font-size: .75em; line-height: 1.4;
    white-space: nowrap; pointer-events: none; box-shadow: 0 2px 8px rgba(0,0,0,.25);
    z-index: 5;
}
.stats-chart-xlabels {
    display: flex; justify-content: space-between; font-size: .75em;
    color: var(--text-muted); margin-top: .2em;
}
/* Carte de fréquentation (2026-08-19) — portage du world.svg de
   stats.amalgame.me, viewBox 1000×1000 (voir cms_dashboard.am). */
#statsMapWrap { position: relative; }
/* Aussi haute que le graphe (2026-08-19, demandé explicitement — "vue
   que l'on va pouvoir zoomer" : pas besoin de voir la carte carrée
   entière d'un coup, zoomer/recentrer suffit) — même ratio que
   STATS_CHART_W/H (dashboard.js) plutôt que le ratio 1:1 natif du SVG ;
   preserveAspectRatio par défaut (xMidYMid meet) recadre proprement
   dans cette boîte plus large que haute. */
.stats-map-svg {
    width: 100%; aspect-ratio: 680 / 200; display: block; background: #eef2f9;
    border-radius: .3em; cursor: grab; touch-action: none;
}
/* vector-effect:non-scaling-stroke (2026-08-19, signalé "le trait des
   frontieres est vraiment trop gros") — SANS ça, l'épaisseur du trait
   est en unités carte (viewBox) : zoomer réduit statsMapVb.w, donc la
   MÊME épaisseur occupe une part croissante de l'écran, jusqu'à devenir
   énorme une fois recentré sur une petite zone. Avec, l'épaisseur reste
   CONSTANTE EN PIXELS ÉCRAN quel que soit le zoom — exactement ce que
   font les vrais outils d'analytics cartographiques. */
.stats-map-svg path { stroke: #cdd5e3; stroke-width: .5; vector-effect: non-scaling-stroke; }
/* Position/transform propres (2026-08-19, signalé "le tooltip s'affiche
   en haut et pas pres de la souris") — la carte suit la souris en X ET
   Y (dashboard.js pose left/top en px), contrairement au tooltip du
   graphe (centré en haut, X seul compte pour une série temporelle). */
#statsMapTooltip { top: auto; transform: none; }
/* Le contour reste fin à l'écran (non-scaling-stroke, même raison que
   les frontières) ; le RAYON lui-même est recalculé en JS à chaque zoom
   (statsMapDotRadius) — vector-effect ne s'applique qu'au trait, pas à
   la géométrie du cercle. */
.stats-city-dot { fill: rgba(163, 97, 10, .75); stroke: #fff; stroke-width: .5; vector-effect: non-scaling-stroke; }
.stats-map-mode {
    padding: .3em .7em; font-size: .82em; border: 1px solid var(--border);
    background: #fff; border-radius: .3em;
}
.stats-map-mode.active { background: var(--primary); color: #fff; border-color: var(--primary); }
.stats-map-zoom {
    width: 2em; height: 2em; padding: 0; display: flex; align-items: center;
    justify-content: center; font-size: 1em; font-weight: 700; line-height: 1;
}
.stats-map-attribution { font-size: .72em; color: var(--text-muted); margin: .5em 0 0; }
.stats-map-attribution a { color: inherit; }
.stats-pages-list { list-style: none; margin: 0; padding: 0; }
.stats-page-btn {
    display: flex; align-items: center; justify-content: space-between;
    gap: 1em; width: 100%; padding: .5em 0; border: none; border-bottom: 1px solid var(--border);
    background: none; font: inherit; text-align: left; cursor: pointer;
}
.stats-page-btn:hover { color: var(--primary); }
/* Ligne actuellement scopée dans le graphe (2026-08-19, remplace le
   rappel textuel du chemin dans #statsChartTitle — celui-ci cassait
   l'affichage mobile avec un chemin long). Posée/retirée en JS par le
   handler délégué de clic (#viewStats) et resetStatsChart(). */
.stats-page-btn.stats-row-active {
    background: var(--primary-bg);
    color: var(--primary);
    padding-left: .6em;
    padding-right: .6em;
    border-radius: var(--radius);
}
.stats-page-path { word-break: break-all; }
.stats-page-views {
    flex: none; color: var(--text-muted); font-size: .85em;
    font-variant-numeric: tabular-nums;
}
.stats-page-static-row {
    display: flex; align-items: center; justify-content: space-between;
    gap: 1em; padding: .5em 0; border-bottom: 1px solid var(--border);
}
/* Élargie (2026-08-19, "trop petite") — 32em rendait les chemins de
   page (souvent longs) illisibles, coupés sur plusieurs lignes.
   Liste interne scrollable plutôt que de laisser le <dialog> entier
   grandir indéfiniment (jusqu'à 90vh, règle générique `dialog` plus
   haut) — plus confortable dès qu'un bot a beaucoup de pages. */
#botsModal { max-width: 46em; width: 100%; }
#botsModal .stats-pages-list { max-height: 55vh; overflow-y: auto; }
#botsDetailBack { margin-bottom: .6em; }

.toast {
    position: fixed;
    left: 50%;
    bottom: 24px;
    transform: translateX(-50%);
    z-index: 80;
    background: var(--surface);
    border: 1px solid var(--border);
    border-left: 4px solid var(--text-muted);
    color: var(--text);
    font-size: .92em;
    padding: .7em 1.2em;
    border-radius: var(--radius);
    box-shadow: var(--shadow-md);
    max-width: 90vw;
    text-align: center;
}
.toast.ok { border-left-color: var(--success); }
.toast.err { border-left-color: var(--danger); }

#navMenu {
    display: none;
    flex-direction: column;
    align-items: stretch;
    gap: .15em;
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 20;
    min-width: 12em;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: .4em;
    margin-top: .5em;
    background: var(--surface);
    box-shadow: var(--shadow-md);
}

#navMenu a, #navMenu button {
    color: var(--text);
    text-decoration: none;
    text-align: left;
    background: none;
    border: none;
    border-radius: var(--radius);
    padding: .5em .6em;
    margin: 0;
    font-size: .92em;
    font-weight: 500;
}
#navMenu a:hover, #navMenu button:hover { background: var(--neutral-bg); }
#navMenu #logoutBtn { color: var(--danger); }
/* Sépare les liens d'usage courant (Articles/Médiathèque) des liens
   d'administration (Rubriques/Types/Utilisateurs, admin seulement) —
   remplace le rendu 3D par défaut du navigateur par un simple trait
   plat, cohérent avec le reste du design flat de ce menu. */
#navMenu hr {
    width: 100%;
    border: none;
    border-top: 1px solid var(--border);
    margin: .3em 0;
}

/* ── Bureau : #navMenu en colonne latérale fixe ──────────────────────
   Sous 64em, comportement 100% inchangé (tiroir #navMenu en position
   absolute sous le burger, tel qu'avant — aucune règle ci-dessus n'est
   touchée). Au-dessus de ce seuil, le burger disparaît et le MÊME
   <nav id="navMenu"> (même DOM, mêmes liens, aucun changement JS)
   devient une colonne fixe toujours visible. `!important` sur
   `display` est nécessaire : showView() (dashboard.js) remet
   `navMenu.style.display = 'none'` en ligne après chaque clic de lien
   (fermeture du tiroir sur mobile) — sans !important la colonne
   disparaîtrait au premier clic sur bureau aussi.
   `left` reproduit le calcul de centrage du <body> (max-width + margin
   auto) pour que la colonne s'aligne exactement sous le coin gauche du
   bandeau du haut, y compris sur les très grands écrans (voir la media
   query 90em plus bas dans ce fichier, qui élargit body à 100em). */
@media (min-width: 64em) {
    #menuBtn { display: none; }

    #navMenu {
        display: flex !important;
        position: fixed;
        top: 3.9em;
        bottom: 0;
        left: max(0px, (100vw - 64em) / 2);
        width: 13em;
        margin-top: 0;
        border: none;
        border-right: 1px solid var(--border);
        border-radius: 0;
        box-shadow: none;
        padding: 1em .8em;
        overflow-y: auto;
        z-index: 15;
    }

    #dashboardMain { margin-left: 13em; padding-left: 1.4em; }
}

/* Pensé PWA (mobile/tablette) à l'origine — plafond de 64em conservé
   par défaut, mais un poste de bureau avec un grand écran a bien plus
   de place pour configurer/rédiger (demandé explicitement 2026-08-19).
   Élargit juste les deux plafonds existants (corps de page + modales,
   dont l'éditeur d'article) au-delà d'un certain seuil, aucun impact
   sous ce seuil (mobile/PWA étroite inchangée). Le `left` de la colonne
   #navMenu (voir plus haut) recalcule le même centrage avec ce nouveau
   plafond de 100em — sinon la colonne resterait alignée sur l'ancien
   centrage à 64em, désynchronisée du bandeau du haut au-delà de ce
   seuil. */
@media (min-width: 90em) {
    body { max-width: 100em; }
    dialog { max-width: 88em; }
    #navMenu { left: max(0px, (100vw - 100em) / 2); }
}
