/* ════════════════════════════════════════════════════════════════════════
 * MyESEF — Composants réutilisables
 * Naming : .block, .block--modifier, .block__element
 * Charge tokens.css + base.css AVANT ce fichier.
 *
 *   .card               — conteneur encadré ombré
 *   .panel              — boîte d'info sur fond subtil
 *   .btn .btn--primary  — boutons
 *   .field              — wrapper label + input
 *   .input              — input/textarea/select
 *   .checkbox           — case à cocher labellisée
 *   .alert              — bandeau d'alerte (info/success/warning/error)
 *   .link               — liens UI (différencier des liens dans texte)
 *   .badge              — pastille (rôles, statuts)
 *   .chip-code          — token monospace mis en valeur
 *   .stack              — utilitaire : pile verticale gap-based
 *   .muted              — texte secondaire
 * ════════════════════════════════════════════════════════════════════════ */

/* ── UI non-sélectionnable ───────────────────────────────────────────────
   Empêche la sélection du texte sur les composants interactifs (CTA, pills,
   toggles, nav, onglets, etc.). Les pages de contenu (lecture, éditeur)
   gardent la sélection normale puisque ces classes n'y sont pas posées. */
.btn, .btn-pill, .btn-client, .icon-btn, .pill-action,
.lang-tabs, .lang-tab,
.v1-toggle,
.dropdown summary, .dropdown__item, .user-pill,
.app-header__nav a, .app-header__brand,
.archive-row, .project-card,
.action-bar, .action-bar__btn, .action-bar__close,
.dropbox-overlay__header, .dropbox-overlay__tabs, .dropbox-overlay__tab, .dropbox-overlay__close,
.editor-side__acc-head,
.link--button, .back-link,
.toc-row__chevron, .toc-row__tools,
.checkbox {
    user-select: none;
    -webkit-user-select: none;
}

/* ── Icônes "mask" (SVG externes qui héritent de currentColor) ───────── */
/* Utilisation :
     <span class="icon-mask" style="--icon: url('chemin/icon.svg');"></span>
   La couleur de l'icône suit le `color` de l'élément parent — utile pour
   les états :hover, focus, role-based, etc. */
.icon-mask {
    display: inline-block;
    width: 20px; height: 20px;
    background-color: currentColor;
    -webkit-mask: var(--icon) center / contain no-repeat;
    mask: var(--icon) center / contain no-repeat;
}

/* ── Typo utilitaires ────────────────────────────────────────────────── */
/* Applique la police Windsor sur n'importe quel élément. Utilitaire pur :
   ne touche ni à la taille ni au poids — à composer avec d'autres classes. */
.windsor { font-family: var(--font-display); }

/* ── Brand (titre marque MyESEF) ─────────────────────────────────────── */
/* Pose juste la taille / le poids / les marges. La police est ajoutée via
   `.windsor` dans le template (composition). Ainsi `.brand` reste utilisable
   sans Windsor si on veut un jour un titre de carte standard volumineux.
   Taille réduite à fs-2xl (26px) pour que « Bienvenue sur MyESEF » tienne
   sur une seule ligne dans la carte d'auth (max-width 420px), en Windsor
   comme en fallback Arial Narrow. */
.brand {
    font-weight: 650;
    font-size: var(--fs-2xl);
    margin: 0;
    white-space: nowrap;
}
/* Combinaison `.brand.windsor` — utilisée pour l'accroche « bienvenue sur
 * MyESEF ». Poids un peu plus marqué que le default `.brand` pour ressortir
 * sur la carte d'auth. */
.brand.windsor { font-weight: 750; }

/* ── Card ────────────────────────────────────────────────────────────── */
.card {
    background: var(--c-bg-elevated);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-md);
}
/* Variante avec filet noir — utilisée sur les pages admin pour matcher
   la charte du dashboard (users-card, account-card…). */
.card--bordered { border-color: var(--c-text); }

/* Header titré à l'intérieur d'une .card (ex: page mon compte). Reprend
   les proportions du .users-card__title pour cohérence visuelle. */
.account__header { margin-bottom: var(--sp-6); }
.account__title {
    margin: 0;
    font-size: var(--fs-2xl);
    font-weight: 800;
    color: var(--c-text);
}
/* La règle générique `.section { border-top: 1px solid }` posait un
   filet juste sous le titre « mon compte » (la section qui suit n'est
   plus le `:first-child` à cause du header inséré au-dessus). On annule
   ce filet pour la section qui suit immédiatement le account__header. */
.account__header + .section {
    border-top: 0;
    padding-top: 0;
}
.card__header {
    padding: var(--sp-8) var(--sp-8) var(--sp-4);
    text-align: center;
}
.card__body   { padding: var(--sp-4) var(--sp-8) var(--sp-8); }
.card__footer { padding: var(--sp-4) var(--sp-8); border-top: 1px solid var(--c-border); }

.card__title {
    font-size: var(--fs-xl);
    font-weight: 600;
    margin: 0 0 var(--sp-1);
}
.card__subtitle {
    color: var(--c-text-muted);
    font-size: var(--fs-sm);
    margin: var(--sp-2) 0 0;     /* léger gap par rapport au brand qui le précède */
}

/* ── Panel (boîte d'info sur fond subtil) ────────────────────────────── */
.panel {
    background: var(--c-bg-subtle);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-lg);
    padding: var(--sp-4);
}
.panel--dashed { border-style: dashed; }

/* ── Buttons ─────────────────────────────────────────────────────────── */
.btn {
    display: inline-flex; align-items: center; justify-content: center;
    gap: var(--sp-2);
    appearance: none;
    border: 1px solid transparent;
    border-radius: 999px;                   /* pill, en cohérence avec la charte CTA */
    padding: 10px var(--sp-5);
    font: inherit; font-weight: 500;
    cursor: pointer;
    background: transparent;
    color: inherit;
    transition: background var(--tr-base), border-color var(--tr-base),
                color var(--tr-base), transform var(--tr-fast);
    text-decoration: none;
}
.btn:active { transform: translateY(1px); }
.btn:focus-visible { outline-offset: 3px; }

/* Pas de soulignement natif des liens sur les boutons (`<a class="btn">`). */
.btn:hover { text-decoration: none; }

/* CTA primaire — suit la charte unifiée : outline NOIR au repos
   (bordure + texte noir, fond transparent, texte gras), puis au hover
   fond couleur CLIENT + texte BLANC. Sur les pages déconnectées il
   n'y a pas de couleur client → `--c-client` retombe sur `--c-text`
   (charbon) via le defaut du token, donc l'hover est noir, ce qui
   reste cohérent avec « fond couleur principale + texte blanc ». */
.btn--primary {
    background: transparent;
    color: var(--c-text);
    border-color: var(--c-text);
    font-weight: 600;
}
.btn--primary:hover {
    background: var(--c-client);
    color: var(--c-text-on-dark);
    border-color: var(--c-client);
}

/* CTA secondaire : filet GRIS CLAIR (--c-text-faded), texte GRIS MOYEN
   (--c-text-muted, plus foncé pour la lisibilité). Hover identique au
   primaire (fill couleur client + texte blanc). */
.btn--ghost {
    background: transparent;
    border-color: var(--c-text-faded);
    color: var(--c-text-muted);
    font-weight: 600;
}
.btn--ghost:hover {
    background: var(--c-client);
    color: var(--c-text-on-dark);
    border-color: var(--c-client);
}

.btn--danger {
    background: var(--c-error-fg);
    color: var(--c-text-on-dark);
}

.btn--block { width: 100%; }

/* ── Field (label + input) ───────────────────────────────────────────── */
.field { display: flex; flex-direction: column; gap: var(--sp-1); }
.field__label {
    font-size: var(--fs-sm);
    font-weight: 500;
    color: var(--c-text-muted);
}
.field__hint {
    font-size: var(--fs-xs);
    color: var(--c-text-muted);
    margin: 0;
}

.input {
    appearance: none; -webkit-appearance: none;
    width: 100%;
    border: 1px solid var(--c-border);
    border-radius: var(--radius-md);
    padding: 10px var(--sp-3);
    font: inherit;
    color: inherit;
    background: var(--c-bg-elevated);
    transition: border-color var(--tr-base), box-shadow var(--tr-base);
}
/* Les <select> avec .input perdent leur flèche native (appearance:none).
   On la remet via un SVG en background à droite + padding-right pour
   réserver la place. */
select.input {
    padding-right: calc(var(--sp-3) + 20px);
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8' fill='none' stroke='%235b5b5b' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'><polyline points='1 1.5 6 6.5 11 1.5'/></svg>");
    background-repeat: no-repeat;
    background-position: right var(--sp-3) center;
    background-size: 12px 8px;
    cursor: pointer;
}
.input:focus {
    outline: none;
    border-color: var(--c-text);
    box-shadow: 0 0 0 3px rgba(0,0,0,.06);
}
.input--code {
    text-align: center;
    font-size: var(--fs-xl);
}

/* ── OTP input (N cases d'un chiffre) ────────────────────────────────── */
.otp-input {
    display: flex;
    gap: var(--sp-2);
    justify-content: center;
}
.otp-input__cell {
    width: 48px; height: 56px;
    text-align: center;
    font-size: 1.5rem;                /* 24px en base 16 — one-off OTP */
    font-weight: 600;
    border: 1px solid var(--c-border);
    border-radius: var(--radius-md);
    background: var(--c-bg-elevated);
    color: inherit;
    transition: border-color var(--tr-base), box-shadow var(--tr-base);
    appearance: none; -webkit-appearance: none;
}
.otp-input__cell:focus {
    outline: none;
    border-color: var(--c-text);
    box-shadow: 0 0 0 3px rgba(0,0,0,.06);
}

/* Wrapper auto-injecté par password-toggle.js autour des input[type=password] */
.password-wrap { position: relative; display: block; }
.password-wrap .input--with-toggle { padding-right: 42px; }
.password-toggle {
    position: absolute;
    right: 6px; top: 50%;
    transform: translateY(-50%);
    width: 32px; height: 32px;
    display: inline-flex; align-items: center; justify-content: center;
    background: transparent;
    border: 0;
    color: var(--c-text-muted);
    cursor: pointer;
    border-radius: var(--radius-sm);
    transition: color var(--tr-base), background var(--tr-base);
}
.password-toggle:hover         { color: var(--c-text); background: var(--c-bg-subtle); }
.password-toggle.is-active     { color: var(--c-text); }

/* ── Checkbox ────────────────────────────────────────────────────────── */
.checkbox {
    display: inline-flex; align-items: center; gap: var(--sp-2);
    font-size: var(--fs-sm);
    color: var(--c-text-muted);
    cursor: pointer; user-select: none;
}
.checkbox input { accent-color: var(--c-text); }

/* ── Alert ───────────────────────────────────────────────────────────── */
.alert {
    border-radius: var(--radius-md);
    padding: var(--sp-3) var(--sp-3);
    font-size: var(--fs-sm);
    border: 1px solid transparent;
}
.alert--info    { background: var(--c-info-bg);    color: var(--c-info-fg);    border-color: rgba(29,77,150,.18); }
.alert--success { background: var(--c-success-bg); color: var(--c-success-fg); border-color: rgba(30,111,58,.18); }
.alert--warning { background: var(--c-warning-bg); color: var(--c-warning-fg); border-color: rgba(107,79,0,.18); }
.alert--error   { background: var(--c-error-bg);   color: var(--c-error-fg);   border-color: rgba(138,26,20,.18); }

/* ── Links ───────────────────────────────────────────────────────────── */
.link {
    color: var(--c-text-muted);
    font-size: var(--fs-sm);
    text-decoration: none;
}
.link:hover { color: var(--c-text); text-decoration: underline; }
.link--block  { display: inline-block; text-align: center; }
/* Variante quand .link est porté par un <button> (style natif strippé) */
.link--button {
    background: transparent; border: 0; padding: 0;
    font: inherit; cursor: pointer;
}
.link--button:disabled,
.link--button[aria-disabled="true"] {
    cursor: not-allowed;
    opacity: 0.5;
    color: var(--c-text-muted);
    text-decoration: none;
}
.link--button:disabled:hover { color: var(--c-text-muted); text-decoration: none; }

/* ── Badge (pastille) ────────────────────────────────────────────────── */
.badge {
    display: inline-block;
    padding: 2px var(--sp-2);
    border-radius: 999px;
    font-size: var(--fs-xs);
    font-weight: 500;
    line-height: 1.4;
}
.badge--super-admin { background: var(--role-super-admin-bg); color: var(--role-super-admin-fg); }
.badge--admin       { background: var(--role-admin-bg);       color: var(--role-admin-fg); }
.badge--editor      { background: var(--role-editor-bg);      color: var(--role-editor-fg); }
.badge--reviewer    { background: var(--role-reviewer-bg);    color: var(--role-reviewer-fg); }
.badge--viewer      { background: var(--role-viewer-bg);      color: var(--role-viewer-fg); }

/* ── Chip-code (token monospace mis en valeur) ───────────────────────── */
.chip-code {
    display: inline-block;
    background: var(--c-bg-elevated);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-sm);
    padding: 4px var(--sp-2);
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    word-break: break-all;
}

/* ── Table simple ────────────────────────────────────────────────────── */
.table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.75rem;          /* 12px — cellules compactes */
}
.table th, .table td {
    text-align: left;
    padding: var(--sp-3);
    border-bottom: 1px solid var(--c-border);
}
.table th {
    font-weight: 500;
    color: var(--c-text-muted);
    font-size: 0.6875rem;        /* 11px — têtière discrète */
}
.table tr:last-child td { border-bottom: 0; }
.table td.is-actions { text-align: right; }

/* ── Select (cohérent avec .input) ───────────────────────────────────── */
.select {
    appearance: none; -webkit-appearance: none;
    width: 100%;
    border: 1px solid var(--c-border);
    border-radius: var(--radius-md);
    padding: 10px var(--sp-3);
    padding-right: var(--sp-8);
    font: inherit; color: inherit;
    background: var(--c-bg-elevated)
        url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none' stroke='%236b6b6b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='1 1 6 6 11 1'/></svg>")
        no-repeat right 12px center;
    transition: border-color var(--tr-base), box-shadow var(--tr-base);
}
.select:focus {
    outline: none;
    border-color: var(--c-text);
    box-shadow: 0 0 0 3px rgba(0,0,0,.06);
}

/* ── Form row : libellé statique + valeur (pour champs non-éditables) ── */
.form-row {
    display: flex; flex-direction: column; gap: var(--sp-1);
    padding: var(--sp-2) 0;
}
.form-row__label { font-size: var(--fs-sm); color: var(--c-text-muted); }
.form-row__value { font-size: var(--fs-md); }

/* ── Grille de cartes projets ────────────────────────────────────────── */
/* > 1760px : 3 cartes / ligne — en dessous : 2 — mobile : 1. */
.project-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--sp-6);
}
@media (max-width: 1760px) { .project-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 720px)  { .project-grid { grid-template-columns: 1fr; } }

/* Carte projet : le <a> englobe tout — clic = ouvrir le projet */
.project-card {
    display: flex; flex-direction: column;
    justify-content: space-between;
    min-height: 260px;
    padding: var(--sp-6) var(--sp-8);
    background: var(--c-bg-elevated);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    color: inherit;
    text-decoration: none;
    transition: box-shadow var(--tr-base), transform var(--tr-base);
}
.project-card:hover {
    box-shadow: var(--shadow-md);
    text-decoration: none;
    transform: translateY(-1px);
}

/* Header : titre à gauche, année en gros à droite en couleur client */
.project-card__head {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: var(--sp-4);
}
.project-card__title {
    font-size: var(--fs-2xl);
    font-weight: 650;
    margin: 0;
    line-height: var(--lh-tight);
    color: var(--c-text);
    max-width: 10em;          /* force le wrap des titres longs */
}
.project-card__year {
    font-size: var(--fs-2xl);
    font-weight: 650;
    color: var(--c-client);
    line-height: var(--lh-tight);
    flex-shrink: 0;
}

/* Footer carte : flèche en bas-gauche ; au hover, le label "ouvrir le
   projet" s'élargit depuis la gauche (max-width 0 → contenu), et pousse
   la flèche vers la droite par le jeu du flex layout. */
/* CTA pill : flèche (et label révélé au hover) dans une pastille bordée
   noir, qui passe au fond couleur client + texte blanc au hover de la
   carte — même comportement que le pill « dropbox ». */
.project-card__cta {
    display: inline-flex; align-items: center;
    align-self: flex-start;
    color: var(--c-text);                  /* noir au repos */
    background: transparent;
    border: 1px solid currentColor;        /* filet noir via currentColor */
    border-radius: 999px;
    padding: 4px 12px;
    font-size: var(--fs-lg);               /* plus gros (18px) */
    font-weight: 600;
    transition: background var(--tr-base), color var(--tr-base),
                border-color var(--tr-base);
}
.project-card__cta-label {
    max-width: 0;
    opacity: 0;
    overflow: hidden;
    white-space: nowrap;
    padding-right: 0;
    transition: max-width 280ms ease, opacity 280ms ease, padding-right 280ms ease;
}
.project-card:hover .project-card__cta-label,
.project-card:focus-visible .project-card__cta-label {
    max-width: 240px;
    opacity: 1;
    padding-right: var(--sp-3);           /* gap qui pousse la flèche */
}
.project-card:hover .project-card__cta,
.project-card:focus-visible .project-card__cta {
    background: var(--c-client);
    color: var(--c-text-on-dark);
    border-color: var(--c-client);
}

/* ── Liste plate (archives) ──────────────────────────────────────────── */
.archive-list {
    display: flex; flex-direction: column;
}
.archive-row {
    display: flex; align-items: center; justify-content: space-between;
    padding: var(--sp-4) 0;
    border-bottom: 1px solid var(--c-border);
    color: var(--c-text-muted);
    text-decoration: none;
    transition: color var(--tr-base), border-color var(--tr-base);
}
.archive-row:hover {
    color: var(--c-text);
    border-bottom-color: var(--c-text);
    text-decoration: none;
}
.archive-row__label { font-size: 1.25rem; }
.archive-row__arrow {
    width: 26px; height: 26px;
    color: var(--c-text-faded);            /* gris clair par défaut */
    transition: color var(--tr-base);
}
.archive-row:hover .archive-row__arrow {
    color: var(--c-text);                  /* noir au hover */
}

/* ── Bouton pill (action secondaire encadrée) ────────────────────────── */
/* Défaut : fond transparent, bordure + texte noirs.
   Hover : bascule sur la couleur client (fond + bordure) + texte blanc. */
.btn-pill {
    display: inline-flex; align-items: center; gap: var(--sp-1);
    padding: 8px var(--sp-4);
    border: 1px solid var(--c-text);
    border-radius: 999px;
    background: transparent;
    color: var(--c-text);
    font-size: var(--fs-sm);
    font-weight: 600;
    cursor: pointer;
    text-decoration: none;
    transition: background var(--tr-base), border-color var(--tr-base),
                color var(--tr-base);
}
.btn-pill:hover {
    background: var(--c-client);
    border-color: var(--c-client);
    color: var(--c-text-on-dark);
    text-decoration: none;
}

/* ── État vide ───────────────────────────────────────────────────────── */
.empty-state {
    text-align: center;
    padding: var(--sp-12) var(--sp-6);
    color: var(--c-text-muted);
}

/* ── Dropdown réutilisable (<details>) ───────────────────────────────── */
.dropdown { position: relative; }
.dropdown summary {
    list-style: none;
    cursor: pointer;
}
.dropdown summary::-webkit-details-marker { display: none; }
.dropdown[open] summary { /* color: var(--c-text); */ }

.dropdown__panel {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    min-width: 220px;
    background: var(--c-bg-elevated);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    padding: var(--sp-2);
    z-index: 20;
}
.dropdown__panel--right { left: auto; right: 0; }
.dropdown__label {
    padding: var(--sp-2) var(--sp-3);
    margin: 0;
    font-size: var(--fs-xs);
    color: var(--c-text-muted);
    text-transform: uppercase;
}
.dropdown__list {
    display: flex; flex-direction: column;
    margin: 0;
}
.dropdown__item {
    display: flex; align-items: center; gap: var(--sp-2);
    width: 100%;
    padding: var(--sp-2) var(--sp-3);
    background: transparent;
    border: 0;
    border-radius: var(--radius-md);
    color: var(--c-text);
    font: inherit; font-size: var(--fs-sm);
    text-align: left;
    text-decoration: none;
    cursor: pointer;
    transition: background var(--tr-base);
}
.dropdown__item:hover { background: var(--c-bg-subtle); text-decoration: none; }
.dropdown__item.is-active { font-weight: 600; }
/* Variante destructive (suppression, révocation). Rouge même au repos. */
.dropdown__item--danger { color: var(--c-accent, #ea0000); }
.dropdown__item--danger:hover { background: var(--c-error-bg); }

.client-dot {
    display: inline-block;
    width: 10px; height: 10px;
    border-radius: 50%;
    background: var(--c-text);
    flex-shrink: 0;
}

/* ── User pill (avatar + nom + dropdown) ─────────────────────────────── */
.user-pill summary { list-style: none; }
.user-pill__summary {
    display: inline-flex; align-items: center; gap: var(--sp-2);
    padding: 3px var(--sp-4) 3px 3px;     /* presse l'avatar contre la bordure */
    border-radius: 999px;
    border: 1px solid var(--c-border);
    background: var(--c-bg-elevated);
    text-transform: lowercase;            /* tout en minuscules dans la pill */
    transition: border-color var(--tr-base);
}
.user-pill__summary:hover { border-color: var(--c-border-strong); }
.user-pill__name {
    font-size: var(--fs-md);              /* plus gros (16px) */
    font-weight: 650;                     /* gras */
    color: var(--c-text);                 /* noir */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;                         /* autorise le shrink en flex */
}

/* En mobile, contraindre la largeur max du nom pour déclencher l'ellipsis
   sur les noms longs (sans ça, le nom pousse la pill et déborde du header). */
@media (max-width: 900px) {
    .user-pill__name { max-width: 100px; }
}

/* ── Section (à l'intérieur d'une card multi-blocs) ──────────────────── */
.section { padding: var(--sp-6) 0; border-top: 1px solid var(--c-border); }
.section:first-child { padding-top: 0; border-top: 0; }
.section:last-child  { padding-bottom: 0; }
.section__title    { font-size: 1.25rem; font-weight: 650; margin: 0 0 var(--sp-1); }
.section__subtitle { color: var(--c-text-muted); font-size: var(--fs-sm); margin: 0 0 var(--sp-4); }

/* ── Stack utility (pile verticale gap-based) ────────────────────────── */
.stack         { display: flex; flex-direction: column; gap: var(--sp-4); }
.stack--tight  { gap: var(--sp-2); }
.stack--loose  { gap: var(--sp-6); }

/* ════════════════════════════════════════════════════════════════════════
   PAGE PROJET — chemin de fer + action bar + overlay dropbox
   ════════════════════════════════════════════════════════════════════════ */

/* ── Onglet attaché au haut d'une carte (pattern partagé) ─────────────
 * Utilisé par .lang-tab (versions linguistiques d'un projet) et
 * .dropbox-overlay__tab (reçus / envoyés). Ces deux classes n'apportent
 * plus que leur padding + border-radius spécifiques ; toute la logique
 * d'état (fond, texte, hover, actif) est mutualisée ici.
 *
 * Onglet inactif :
 *   - fond quasi transparent (léger teint neutre) pour laisser respirer
 *     l'arrière-plan de la page
 *   - texte gris clair pour ne pas concurrencer l'onglet actif
 * Onglet actif :
 *   - fond blanc, texte noir, bordure noire qui fusionne avec la carte
 *     en cachant la border-bottom (transparent). */
.card-tab {
    border: 1px solid rgba(0, 0, 0, 0.4);     /* haut + côtés — visible mais discret (inactif) */
    border-bottom: 1px solid var(--c-text);   /* le bas noir plein continue la bordure de la carte */
    background: rgba(255, 255, 255, 0.25);
    color: rgba(0, 0, 0, 0.5);
    text-decoration: none;
    cursor: pointer;
    transition: background var(--tr-base), color var(--tr-base);
}
.card-tab:hover {
    background: var(--c-bg-elevated);
    color: var(--c-text);
    text-decoration: none;
}
.card-tab.is-active {
    background: var(--c-bg-elevated);
    color: var(--c-text);
    border-color: var(--c-text);              /* noir noir sur tout l'onglet actif */
    border-bottom-color: transparent;         /* sauf le bas, fusionne avec le card */
}

/* ── Onglets langue (FR / EN) attachés au haut de la carte projet ────── */
.lang-tabs {
    display: flex;
    gap: 4px;
    margin-bottom: -1px;        /* chevauche la border-top de la carte */
    position: relative;
    z-index: 1;
}
.lang-tab {
    padding: 11px 32px;
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    font-size: var(--fs-sm);
    font-weight: 600;
}

/* ── Carte projet : conteneur de toute la liste ──────────────────────── */
/* Pas de radius en haut-GAUCHE (les onglets s’y posent sans cassure) ;
   le haut-droit garde l’arrondi pour rester cohérent avec les 3 autres. */
.project-shell {
    background: var(--c-bg-elevated);
    border: 1px solid var(--c-text);          /* noir noir tout autour */
    border-radius: 0 var(--radius-lg) var(--radius-lg) var(--radius-lg);
    padding: var(--sp-6) var(--sp-6) var(--sp-8);
    position: relative;
}

/* ════════════════════════════════════════════════════════════════════════
 *  LEXIQUE CLIENT — page /lexicons
 *  Réutilise .project-shell + .card-tab. Ajoute la barre alphabet, les
 *  ancres de groupe (A, B, C…) et les lignes de terme avec 4 flags toggle.
 * ════════════════════════════════════════════════════════════════════════ */
.lexicon-shell__title {
    margin: 0 0 var(--sp-5);
    font-size: var(--fs-2xl);
    font-weight: 650;
    color: var(--c-text);
    line-height: var(--lh-tight);
}

.lexicon-nav {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-1);
    /* Sticky sous le header global : reste visible pendant qu'on scrolle
     * la liste de termes. Extension aux bords du shell via marge négative
     * + padding pour que le fond couvre bord à bord (pas de bande de
     * contenu qui apparaîtrait sous le sp-6 latéral). */
    position: sticky;
    top: var(--header-height);
    z-index: 5;
    margin: 0 calc(-1 * var(--sp-6)) var(--sp-4);
    padding: var(--sp-3) var(--sp-6);
    background: var(--c-bg-elevated);
    border-bottom: 1px solid var(--c-border);
}
.lexicon-nav__add {
    margin-right: var(--sp-3);
}
.lexicon-nav__letter {
    display: inline-flex;
    align-items: center; justify-content: center;
    min-width: 28px; height: 28px;
    padding: 0 var(--sp-1);
    border: 1px solid var(--c-text);
    border-radius: 999px;
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--c-text);
    text-decoration: none;
    opacity: 0.8;
    transition: background var(--tr-base), opacity var(--tr-base);
    user-select: none;
    -webkit-user-select: none;
}
.lexicon-nav__letter:hover {
    background: var(--c-text);
    color: var(--c-text-on-dark);
    border-color: var(--c-text);
    opacity: 1;
    text-decoration: none;
}
/* Lettre du groupe COURANT dans le scroll (ScrollSpy — cf. lexicon-editor.js).
 * Rendu proche du hover pour rester discret : fond client + texte blanc. */
.lexicon-nav__letter.is-active {
    background: var(--c-client);
    color: var(--c-text-on-dark);
    border-color: var(--c-client);
    opacity: 1;
}
.lexicon-nav__letter.is-active:hover {
    background: var(--c-client);
    color: var(--c-text-on-dark);
    border-color: var(--c-client);
}

.lexicon-group {
    display: inline-flex;
    align-items: center; justify-content: center;
    width: 32px; height: 32px;
    margin: var(--sp-4) 0 var(--sp-2);
    background: var(--c-text);
    color: var(--c-text-on-dark);
    border-radius: 999px;
    font-size: var(--fs-sm);
    font-weight: 700;
    /* Offset pour l'anchor-scroll : le sticky nav + header ne doivent pas
     * masquer la cible. `--lexicon-nav-h` est mesurée en JS
     * (lexicon-editor.js) et posée en inline var sur .lexicon-shell —
     * fallback 60px si JS pas encore chargé. */
    scroll-margin-top: calc(var(--header-height) + var(--lexicon-nav-h, 60px) + var(--sp-2));
}

.lexicon-list__empty {
    margin: var(--sp-4) 0;
    color: var(--c-text-muted);
}

.lexicon-term {
    display: flex;
    align-items: baseline;                    /* aligne le texte et les pastilles sur la baseline */
    flex-wrap: wrap;                          /* passe à la ligne si l'espace manque */
    gap: var(--sp-2);
    padding: 4px 0;
    border-bottom: 1px solid var(--c-border);
    min-height: 32px;
}
.lexicon-term__text {
    /* Pas de flex:1 : le texte prend sa largeur naturelle, les pastilles
     * suivent directement à côté (spec « à la suite du terme »). */
    max-width: 100%;
    font-size: 1rem;
    color: var(--c-text);
    padding: 4px 6px;
    border-radius: var(--radius-sm);
    outline: none;
    white-space: pre-wrap;
    word-break: break-word;
}
.lexicon-term__text[contenteditable]:hover {
    background: var(--c-bg-subtle);
}
.lexicon-term__text[contenteditable]:focus {
    background: var(--c-bg-subtle);
    box-shadow: 0 1px 0 var(--c-text);
}
.lexicon-term__text[data-placeholder]:empty::before {
    content: attr(data-placeholder);
    color: var(--c-text-faded);
}

/* Style VISUEL du terme selon ses flags — data-flag-* posés sur .lexicon-term
 * par le rendu Twig et par lexicon-editor.js.syncRow() après un PATCH. */
.lexicon-term[data-flag-italic="1"] .lexicon-term__text { font-style: italic; }
.lexicon-term[data-flag-bold="1"]   .lexicon-term__text { font-weight: 700; }
.lexicon-term[data-flag-contextual="1"] .lexicon-term__text {
    color: rgba(0, 0, 0, 0.5);                  /* gris 50 % */
}

.lexicon-term__flags {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    /* Masquées au repos : révélées quand la ligne est survolée ou qu'un
     * champ interne (le span éditable) est focus. Le style visuel du
     * terme (italique / gras / soulignement contextuel) reste visible
     * en permanence, donc l'utilisateur voit toujours l'état sans les
     * boutons. */
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--tr-fast);
}
.lexicon-term:hover .lexicon-term__flags,
.lexicon-term:focus-within .lexicon-term__flags {
    opacity: 1;
    pointer-events: auto;
}

.lexicon-flag {
    display: inline-flex;
    align-items: center; justify-content: center;
    width: 26px; height: 26px;
    padding: 0;
    background: transparent;
    border: 1px solid rgba(0, 0, 0, 0.5);       /* gris 50 % au repos */
    border-radius: 999px;
    color: rgba(0, 0, 0, 0.5);                  /* gris 50 % au repos */
    font-family: inherit;
    font-size: 0.75rem;
    font-weight: 700;
    cursor: pointer;
    /* Pas d'opacité sur le bouton entier : elle grisonnerait aussi la
     * tooltip (::before / ::after) qui hérite. On grise via color/border
     * pour laisser les pseudos rester à pleine intensité au hover. */
    transition: background var(--tr-fast), color var(--tr-fast),
                border-color var(--tr-fast);
    user-select: none;
    -webkit-user-select: none;
    position: relative;                         /* ancre de la tooltip */
}
/* Tooltip flottante au-dessus du bouton — lit `aria-label`. Même pattern
 * que `.icon-action` : petit label couleur client + flèche pointant vers
 * le bouton. Apparaît uniquement au hover / focus visible. */
.lexicon-flag::before,
.lexicon-flag::after {
    position: absolute;
    left: 50%;
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--tr-base);
    z-index: 30;
}
.lexicon-flag::before {
    content: attr(aria-label);
    /* Overlap de 1 px avec la base du triangle pour éliminer le hairline
     * gap sub-pixel qui apparaît sur certaines lignes selon le device
     * pixel ratio. La couleur identique fait fusion invisible. */
    bottom: calc(100% + 5px);
    transform: translateX(-50%);
    background: var(--c-client);
    color: var(--c-text-on-dark);
    font-size: 0.6875rem;
    font-weight: 600;
    line-height: 1.4;
    box-sizing: content-box;
    padding: 4px 8px;
    border-radius: var(--radius-sm);
    white-space: nowrap;
}
.lexicon-flag::after {
    content: "";
    top: -6px;
    transform: translateX(-50%);
    border: 5px solid transparent;
    border-top-color: var(--c-client);          /* triangle pointant vers le bas */
}
.lexicon-flag:hover::before,
.lexicon-flag:hover::after,
.lexicon-flag:focus-visible::before,
.lexicon-flag:focus-visible::after {
    opacity: 1;
}
.lexicon-flag em, .lexicon-flag strong {
    font-size: inherit;
    line-height: 1;
}
.lexicon-flag:hover {
    background: var(--c-text);
    color: var(--c-text-on-dark);
    border-color: var(--c-text);
}
.lexicon-flag.is-active,
.lexicon-flag.is-active:hover {
    background: var(--c-client);
    color: var(--c-text-on-dark);
    border-color: var(--c-client);
}
.lexicon-flag .icon-mask {
    width: 16px; height: 16px;
}
/* Verrouillé = contextual (?) est ON → les 3 autres flags sont désactivés
 * visuellement, click reste possible et réactive automatiquement le flag
 * côté serveur (règle métier gérée par la repo). */
.lexicon-flag.is-locked {
    opacity: 0.35;
}
.lexicon-flag.is-locked:hover {
    opacity: 0.65;
}
/* Corbeille : icône plus visible, sans cercle bordé, gris clair au repos,
 * rouge au hover. Reset des styles bordure/background hérités de .lexicon-flag. */
.lexicon-flag--delete {
    width: 28px; height: 28px;
    background: transparent;
    border: 0;
    color: var(--c-text-muted);                 /* gris moyen — lisible malgré l'opacité 0.5 */
}
.lexicon-flag--delete .icon-mask {
    width: 18px; height: 18px;
}
.lexicon-flag--delete:hover {
    background: transparent;
    border: 0;
    color: var(--c-error-fg, #8a1a14);
}

/* ── Panneau slide-in droit pour ajout en masse de termes ──────────────
 * Même pattern que .dropbox-overlay (backdrop translucide + carte fixée
 * à droite avec slide transform). Contenu : checkboxes flags + textarea. */
.lexicon-add-backdrop {
    position: fixed; inset: 0;
    background: rgba(255, 255, 255, 0.85);
    opacity: 0;
    pointer-events: none;
    transition: opacity 240ms ease;
    z-index: 150;
}
.lexicon-add-backdrop.is-open {
    opacity: 1;
    pointer-events: auto;
}
.lexicon-add-panel {
    position: fixed;
    top: 50%;
    right: 0;
    transform: translate(100%, -50%);
    width: min(720px, calc(100vw - 2.5rem));
    height: calc(100% - 5rem);
    max-height: 900px;
    background: var(--c-bg-elevated);
    border: 1px solid var(--c-text);
    border-right: 0;
    border-radius: var(--radius-xl) 0 0 var(--radius-xl);
    box-shadow: none;
    transition: transform 240ms ease, box-shadow 160ms ease;
    z-index: 200;
    display: flex; flex-direction: column;
    overflow: hidden;
}
.lexicon-add-panel.is-open {
    transform: translate(0, -50%);
    box-shadow: var(--shadow-lg);
}
.lexicon-add-panel__header {
    display: flex; align-items: center;
    padding: var(--sp-5) var(--sp-6);
    gap: var(--sp-3);
}
.lexicon-add-panel__title {
    margin: 0;
    flex: 1;
    font-size: var(--fs-lg);
    font-weight: 700;
    color: var(--c-text);
}
.lexicon-add-panel__close {
    display: inline-flex;
    align-items: center; justify-content: center;
    width: 32px; height: 32px;
    padding: 0;
    background: transparent;
    border: 1px solid var(--c-border);
    border-radius: 999px;
    color: var(--c-text);
    cursor: pointer;
    transition: background var(--tr-fast), border-color var(--tr-fast);
}
.lexicon-add-panel__close:hover {
    background: var(--c-client);
    color: var(--c-text-on-dark);
    border-color: var(--c-client);
}
.lexicon-add-panel__body {
    flex: 1;
    min-height: 0;
    display: flex; flex-direction: column;
    gap: var(--sp-4);
    padding: 0 var(--sp-6) var(--sp-5);
    overflow-y: auto;
}
.lexicon-add-panel__flags {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-2);
    margin-top: var(--sp-2);
}
/* Boutons toggle pour les options bulk — même charte que .lexicon-flag
 * (bordure noire, opacité 80 % au repos, actif = fond noir/texte blanc),
 * mais avec icône EN LÉGENDE puis label texte à droite. */
.lexicon-bulk-flag {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    padding: 6px 12px 6px 8px;
    background: transparent;
    border: 1px solid var(--c-text);
    border-radius: 999px;
    color: var(--c-text);
    font-family: inherit;
    font-size: var(--fs-sm);
    font-weight: 600;
    cursor: pointer;
    opacity: 0.5;
    transition: background var(--tr-fast), color var(--tr-fast),
                opacity var(--tr-fast);
    user-select: none;
    -webkit-user-select: none;
}
.lexicon-bulk-flag:hover {
    opacity: 1;
}
.lexicon-bulk-flag.is-active {
    background: var(--c-text);
    color: var(--c-text-on-dark);
    opacity: 1;
}
.lexicon-bulk-flag.is-active:hover {
    background: var(--c-client);
    color: var(--c-text-on-dark);
    border-color: var(--c-client);
}
.lexicon-bulk-flag .icon-mask {
    width: 16px; height: 16px;
    flex-shrink: 0;
}
.lexicon-add-panel__textarea {
    min-height: 240px;
    font-family: inherit;
    line-height: 1.5;
    resize: none;                               /* pas de resize user */
}

/* ── Ligne d'item du chemin de fer (projet / chapitre / sous-chapitre) ─ */
.toc-row {
    display: grid;
    grid-template-columns: 1fr auto;     /* contenu + outils à droite */
    align-items: start;                  /* outils calés sur la 1re ligne */
    gap: var(--sp-4);
    padding: 6px var(--sp-2);            /* interlignage resserré */
    border-radius: var(--radius-md);
    transition: background var(--tr-base);
}
/* Hover bg uniquement sur les rows réellement cliquables (data-editor-url
   présent = chapitre top-level ou sous-chapitre avec version) et sur la
   row projet. Les sous-chapitres grisés (--empty) n'ont ni cursor pointer
   ni hover bg → ils restent visuellement « morts ». */
.toc-row[data-editor-url]:hover,
.toc-row--project:hover { background: var(--c-bg-subtle); }
.toc-row--empty { cursor: default; }

/* Le main contient : chevron/spacer, numéro, titre, ET toggle V1 + status
   pour les sous-chapitres → toggle + date collés au titre, pas à droite.
   align-items: flex-start → si le titre wrap sur 2 lignes, le numéro,
   le toggle et la date restent alignés avec la 1re ligne du titre. */
.toc-row__main {
    display: flex; align-items: flex-start;
    gap: var(--sp-3);
    min-width: 0;
}
.toc-row__title {
    font-size: var(--fs-md);
    color: var(--c-text);
    line-height: var(--lh-tight);     /* 1.25 — titres serrés sur multi-lignes */
}
/* Numéro de chapitre/sous-chapitre — min-width FIXE en rem pour aligner
   les titres verticalement même si les font-sizes diffèrent entre niveaux. */
.toc-row__number {
    display: inline-block;
    min-width: 2.75rem;         /* ≈ 44px — colonne resserrée numéro → titre */
    flex-shrink: 0;
    color: var(--c-text);
    font-weight: 500;
}
.toc-row__chevron {
    flex-shrink: 0;
    width: 28px; height: 28px;
    border-radius: 50%;
    background: var(--c-text);
    color: var(--c-text-on-dark);
    border: 0;
    display: inline-flex; align-items: center; justify-content: center;
    cursor: pointer;
    transition: transform var(--tr-base);
}
/* Par défaut le chevron pointe vers le BAS (état "fermé") ; ouvert il
   pivote de 180° pour pointer vers le haut. */
.toc-row__chevron[aria-expanded="true"] { transform: rotate(180deg); }
.toc-row__spacer { display: inline-block; width: 28px; flex-shrink: 0; }
/* Petit air supplémentaire entre chevron/spacer et le numéro qui suit.
   margin-top léger pour aligner visuellement le centre du chevron avec
   la baseline de la première ligne du titre (qui est plus bas que le
   top du flex container à cause du leading de la police). */
.toc-row__chevron,
.toc-row__spacer { margin-right: var(--sp-2); margin-top: 4px; }

/* Même décalage pour le toggle V1 et le status sur les top-level chapters
   (titre 1.5rem) → la baseline reste alignée avec celle du titre. */
.toc-row--top .v1-toggle,
.toc-row--top .toc-status { margin-top: 7px; }
/* Sur les sous-chapitres (titre 1.375rem), un décalage plus léger suffit. */
.toc-row--sub .v1-toggle,
.toc-row--sub .toc-status { margin-top: 6px; }

/* Outils à droite (pill fichiers partagés + checkbox) — alignés sur
   la baseline de la 1re ligne du titre via margin-top. */
.toc-row--top .toc-row__tools { margin-top: 2px; }
.toc-row--sub .toc-row__tools { margin-top: 4px; }
.toc-row--project .toc-row__tools { margin-top: 3px; }

/* Conteneur des outils à droite (déclaré ici pour pouvoir l'override
   en media query — précédemment en inline style ce n'était pas possible). */
.toc-row__tools {
    display: inline-flex;
    gap: var(--sp-3);
    align-items: center;
}

/* Wrap-break : caché par défaut (grand écran), devient un séparateur
   forcé en flex-wrap quand on passe en layout petit écran. */
.toc-row__wrap-break { display: none; }

/* Responsive : en dessous de 900px on aplatit toute la row en un seul
   flex-wrap. La checkbox reste en haut-droite sur la ligne du titre ;
   le wrap-break force le passage à la ligne suivante pour les meta
   (toggle + status + pill). */
@media (max-width: 900px) {
    .toc-row {
        display: flex;
        flex-wrap: wrap;
        align-items: flex-start;
        column-gap: var(--sp-3);
        row-gap: var(--sp-1);         /* options proches du titre */
        padding-bottom: var(--sp-3);  /* marge après la ligne d'options */
    }
    /* display:contents → enfants de main et tools deviennent enfants
       directs de .toc-row → on peut les ordonner librement. */
    .toc-row__main,
    .toc-row__tools { display: contents; }

    /* Ordre d'affichage des items en flex-wrap :
       L1 : chevron(1) - number(2) - title(3) - checkbox(4 right-aligned)
       wrap-break(5) force le passage à L2
       L2 : toggle(6) - status(7) - pill(8) */
    .toc-row__chevron,
    .toc-row__spacer  { order: 1; }
    .toc-row__number  { order: 2; }
    .toc-row__title   { order: 3; flex: 1 1 0; min-width: 0; }
    .toc-check        { order: 4; margin-left: auto; }
    .toc-row__wrap-break {
        order: 5;
        display: block;
        flex-basis: 100%;
        height: 0;
    }
    /* Pseudo-élément qui sert d'indent sur la ligne 2 → la 1re option
       (toggle V1) tombe exactement sous le texte du titre :
       chevron(28) + margin(8) + gap(12) + number(44) = 92px.
       Le gap auto entre l'indent et la 1re option (12px) amène à 104px,
       qui correspond à la position X du titre sur la ligne 1. */
    .toc-row::after {
        content: "";
        order: 6;
        display: block;
        width: calc(28px + var(--sp-2) + var(--sp-3) + 2.75rem);
        flex-shrink: 0;
        height: 0;
    }
    .v1-toggle        { order: 7; }
    .toc-status       { order: 8; }
    .pill-action      { order: 9; }

    /* Plus de décalages baseline en layout vertical. */
    .toc-row--top .v1-toggle,
    .toc-row--top .toc-status,
    .toc-row--sub .v1-toggle,
    .toc-row--sub .toc-status,
    .toc-row--top .toc-row__tools,
    .toc-row--sub .toc-row__tools,
    .toc-row--project .toc-row__tools { margin-top: 0; }

    /* Petit filet entre sous-chapitres pour aérer la hiérarchie en
       mobile (où chaque row prend plusieurs lignes). */
    .toc-children > .toc-row--sub:not(:first-child) {
        border-top: 1px solid var(--c-border);
    }

    /* En mobile la pill "fichiers partagés" est masquée (place limitée).
       L'utilisateur peut toujours y accéder via une autre UX future. */
    .pill-action[data-dropbox-target] { display: none; }

    /* Row projet : pas d'indent ::after (il n'y a ni chevron ni number),
       et la pill réglages se cale à gauche sous le titre. */
    .toc-row--project::after { display: none; }
    .toc-row--project__settings { margin-left: 0; margin-top: 0; }
}

/* Variantes selon niveau */
.toc-row--project .toc-row__title {
    font-size: var(--fs-2xl);
    font-weight: 650;
    color: var(--c-text);
}
/* Pill « réglages » à droite du titre projet — en desktop il décale et
   s'aligne sur la baseline du titre via un petit margin-top. */
.toc-row--project__settings {
    margin-left: var(--sp-4);
    margin-top: 7px;
}
.toc-row--top .toc-row__title,
.toc-row--top .toc-row__number {
    font-size: 1.5rem;          /* 24px — top-level légèrement + grand */
    font-weight: 650;
    color: var(--c-client);
}
.toc-row--sub .toc-row__title,
.toc-row--sub .toc-row__number {
    font-size: 1.375rem;        /* 22px */
    color: var(--c-text);
    font-weight: 300;           /* Light — Inter VF supporte 100-900 */
}

/* Sous-chapitre sans version → grisé (placeholder) */
.toc-row--empty .toc-row__title,
.toc-row--empty .toc-row__number {
    color: var(--c-text-faded);
}

/* Pas d'indentation par profondeur — toutes les lignes (chapitres + sous-
   chapitres) commencent au même X grâce au chevron/spacer 28px + min-width
   du numéro. La hiérarchie visuelle vient de la typo (orange/dark, taille). */

/* Séparateur horizontal entre groupes (après le dernier enfant d'un chapitre) */
.toc-group {
    border-bottom: 1px solid var(--c-border);
    padding-top: var(--sp-3);
    padding-bottom: var(--sp-3);
}
.toc-group:last-child { border-bottom: 0; }

/* ── Toggle V1 (lock/unlock) ─────────────────────────────────────────── */
.v1-toggle {
    position: relative;
    display: inline-block;
    width: 44px; height: 20px;
}
.v1-toggle__input {
    position: absolute; opacity: 0;
    width: 100%; height: 100%;
    margin: 0; cursor: pointer;
}
.v1-toggle__track {
    display: block;
    width: 100%; height: 100%;
    border-radius: 999px;
    background: var(--c-text-faded);
    transition: background var(--tr-base);
    position: relative;
}
/* Le numéro de version (V1, V2…) s'affiche DANS le knob — qui devient une
   pastille ovale assez large pour le texte. */
.v1-toggle__knob {
    position: absolute;
    top: 2px; left: 2px;
    width: 22px; height: 16px;
    border-radius: 999px;
    background: var(--c-bg-elevated);
    color: var(--c-text);
    font-size: 9px;
    font-weight: 500;
    line-height: 16px;
    text-align: center;
    text-transform: uppercase;
    transition: transform var(--tr-base);
}
.v1-toggle__input:checked + .v1-toggle__track {
    background: var(--c-client);
}
.v1-toggle__input:checked + .v1-toggle__track .v1-toggle__knob {
    /* Slide à droite : track(44) − knob(22) − 2×offset(2) = 18 */
    transform: translateX(18px);
}
.v1-toggle__input:disabled + .v1-toggle__track { cursor: not-allowed; opacity: 0.7; }

/* Tooltip au hover du toggle (rouge si verrouillé, vert si en cours) */
.v1-toggle__tip {
    position: absolute;
    bottom: calc(100% + 6px); left: 50%;
    transform: translateX(-50%);
    background: #e02828;
    color: #fff;
    font-size: 0.6875rem;
    font-weight: 600;
    padding: 3px 8px;
    border-radius: var(--radius-sm);
    white-space: nowrap;
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--tr-base);
}
.v1-toggle__tip::after {
    content: "";
    position: absolute;
    top: 100%; left: 50%;
    transform: translateX(-50%);
    border: 4px solid transparent;
    border-top-color: #e02828;
}
.v1-toggle:hover .v1-toggle__tip { opacity: 1; }

/* Bascule rouge/vert + bascule "verrouillé" / "en cours" selon :checked,
   sans JS — les deux labels sont toujours dans le DOM. */
.v1-toggle__tip-on  { display: none; }
.v1-toggle__tip-off { display: inline; }
.v1-toggle__input:checked ~ .v1-toggle__tip {
    background: #1ea356;
}
.v1-toggle__input:checked ~ .v1-toggle__tip::after { border-top-color: #1ea356; }
.v1-toggle__input:checked ~ .v1-toggle__tip .v1-toggle__tip-off { display: none; }
.v1-toggle__input:checked ~ .v1-toggle__tip .v1-toggle__tip-on  { display: inline; }

/* ── Pill actions (CTA outline) ──────────────────────────────────────── */
/* Règle UI globale :
   • CTA primaire (défaut) → filet NOIR + texte NOIR
   • CTA secondaire (.pill-action--small) → filet GRIS + texte GRIS
   • HOVER (les deux) → fond couleur client + texte BLANC + filet client */
.pill-action {
    display: inline-flex; align-items: center;
    gap: 4px;
    padding: 3px 6px 3px 5px;
    border-radius: 999px;
    background: transparent;              /* repos : transparent (laisse passer le fond) */
    border: 1px solid currentColor;       /* suit la couleur du texte */
    color: var(--c-text);                  /* primary : noir */
    font-size: 0.75rem;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
    transition: background var(--tr-base), color var(--tr-base),
                border-color var(--tr-base);
}
.pill-action:hover {
    background: var(--c-client);
    color: var(--c-text-on-dark);        /* texte blanc */
    border-color: var(--c-client);       /* override currentColor pour border client pleine */
    text-decoration: none;
}
.pill-action__icon { width: 24px; height: 24px; }

/* Variante "small" — secondaire : filet gris clair (--c-text-faded),
   texte gris moyen (--c-text-muted) pour la lisibilité. Hover hérité. */
.pill-action--small {
    padding: 2px 6px 2px 4px;
    gap: 4px;
    font-size: 0.6875rem;
    color: var(--c-text-muted);
    border-color: var(--c-text-faded);
}
.pill-action--small .pill-action__icon { width: 14px; height: 14px; }

/* Variante « dropbox » (surnom interne — le label utilisateur est
   « fichiers partagés »). État REPOS : seul le picto cloud est visible
   (bouton compact ~46px). État HOVER : la pill s'élargit pour révéler le
   label « fichiers partagés » (+ éventuel count) en fondu, avec un fond
   couleur client et le texte blanc. La largeur est animée via max-width
   pour pouvoir transitionner depuis/vers la valeur « contenu seul ». */
.pill-action.pill-action--dropbox {
    /* Au repos : couleur gris clair (= « pas de nouveau fichier »).
       La variante --has-files repasse en noir pour attirer l'œil. */
    color: var(--c-text-faded);
    border-color: var(--c-text-faded);
    padding: 4px 12px 4px 10px;
    gap: 6px;
    height: 33px;
    font-size: var(--fs-sm);
    /* Au repos : juste assez de largeur pour l'icône + padding. */
    max-width: 46px;
    overflow: hidden;
    transition: max-width 220ms ease, background var(--tr-base),
                color var(--tr-base), border-color var(--tr-base);
}
/* Bouton noir (plus marqué) dans trois cas :
   1. Présence de fichiers reçus (.pill-action--dropbox--has-files)
   2. La row chapitre qui le contient est survolée (data-editor-url)
   3. La row projet (en haut du chemin de fer) est survolée */
.pill-action.pill-action--dropbox.pill-action--dropbox--has-files,
.toc-row[data-editor-url]:hover .pill-action--dropbox,
.toc-row--project:hover .pill-action--dropbox {
    color: var(--c-text);
    border-color: var(--c-text);
}
.pill-action.pill-action--dropbox .pill-action__icon {
    width: 22px; height: 18px;
    flex-shrink: 0;                /* l'icône reste toujours pleinement visible */
}
/* Hover direct du pill → fond couleur client + texte blanc. Les
   variantes scopées row chapitre / row projet ont une spécificité plus
   haute pour battre les règles de « survol de la row » quand les deux
   s'appliquent. */
.pill-action.pill-action--dropbox:hover,
.toc-row[data-editor-url]:hover .pill-action.pill-action--dropbox:hover,
.toc-row--project:hover .pill-action.pill-action--dropbox:hover {
    max-width: 220px;
    background: var(--c-client);
    color: var(--c-text-on-dark);
    border-color: var(--c-client);
}
/* Label + count : masqués au repos via opacity, fade-in après le début
   de l'expansion (petit delay pour que la pill ait commencé à s'élargir). */
.pill-action--dropbox__label,
.pill-action--dropbox__count {
    white-space: nowrap;
    opacity: 0;
    transition: opacity 160ms ease;
}
.pill-action--dropbox__label { font-weight: 600; }
.pill-action--dropbox__count { font-weight: 500; opacity: 0; }
.pill-action.pill-action--dropbox:hover .pill-action--dropbox__label,
.pill-action.pill-action--dropbox:hover .pill-action--dropbox__count {
    opacity: 1;
    transition-delay: 80ms;
}
.pill-action.pill-action--dropbox:hover .pill-action--dropbox__count { opacity: 0.7; }

/* ── Status text (il y a 3h, en cours de modification) ──────────────── */
.toc-status {
    display: inline-flex; align-items: center;
    gap: 4px;
    font-size: var(--fs-xs);
    color: var(--c-text-faded);
    white-space: nowrap;
}

/* ── Checkbox custom (carré arrondi, couleur client quand coché) ─────── */
.toc-check {
    appearance: none; -webkit-appearance: none;
    width: 18px; height: 18px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--c-border-strong);
    background: #F2F2F2;                /* fond gris discret quand décochée */
    cursor: pointer;
    position: relative;
    flex-shrink: 0;
    transition: background var(--tr-base), border-color var(--tr-base);
}
.toc-check:checked {
    background: var(--c-client);
    border-color: var(--c-client);
}
.toc-check:checked::after {
    content: "";
    position: absolute;
    left: 5px; top: 1px;
    width: 5px; height: 10px;
    border: solid var(--c-text-on-dark);
    border-width: 0 2px 2px 0;
    transform: rotate(45deg);
}
/* État indeterminate (mis via JS quand certains enfants sont cochés mais
   pas tous) → trait horizontal blanc sur fond couleur client. */
.toc-check:indeterminate {
    background: var(--c-client);
    border-color: var(--c-client);
}
.toc-check:indeterminate::after {
    content: "";
    position: absolute;
    left: 3px; right: 3px;
    top: 50%;
    height: 2px;
    margin-top: -1px;
    background: var(--c-text-on-dark);
    border-radius: 1px;
}

/* ── Lien retour ──────────────────────────────────────────────────────── */
.back-link {
    display: flex; align-items: center; gap: var(--sp-2);
    width: max-content;                        /* garde la taille du contenu
                                                  mais reste block-level → la
                                                  margin verticale agit */
    color: var(--c-text-muted);
    font-size: var(--fs-xs);                  /* légèrement réduit (13px) */
    font-weight: 650;
    text-decoration: none;
    margin-top: calc(-1 * var(--sp-6));        /* remonte près du header */
    margin-bottom: var(--sp-10);               /* plus d'air avant les onglets */
}
.back-link:hover { color: var(--c-text); text-decoration: none; }

/* ── Action bar (apparaît quand au moins 1 case cochée) ──────────────── */
/* La barre orange occupe TOUTE la largeur du viewport (full bleed).
   Le CONTENU interne (boutons + close) est aligné avec le project-shell
   via un padding horizontal qui suit le rail de .app-main. */
.action-bar {
    position: fixed;
    top: var(--header-height);
    left: 0; right: 0;
    background: var(--c-client);
    color: var(--c-text-on-dark);
    padding: var(--sp-3) var(--content-rail);
    display: flex; align-items: center;
    gap: var(--sp-8);
    z-index: 10;
    transform: translateY(-100%);
    transition: transform 240ms ease;
    min-height: 56px;
    /* Pas de box-shadow : quand la bar est cachée (translateY -100%) elle se
       loge juste au-dessus du header, et l'ombre descendante déborderait
       sous le bord inférieur du header — créant une fausse ombre portée
       qui changerait l'apparence du header sur la page projet uniquement. */
}
.action-bar.is-open { transform: translateY(0); }
/* En mobile, la barre d'actions s'étend sur toute la largeur (mêmes
   padding latéraux que .app-main mobile = sp-6). */
@media (max-width: 900px) {
    .action-bar { padding-left: var(--sp-6); padding-right: var(--sp-6); }
}
.action-bar__btn {
    display: inline-flex; align-items: center; gap: var(--sp-2);
    background: transparent; border: 0;
    color: inherit;
    font-size: var(--fs-md);
    font-weight: 500;
    line-height: 1.2;            /* serre les lignes quand le texte wrappe */
    text-align: left;             /* texte fer à gauche sur 2+ lignes */
    cursor: pointer;
    text-decoration: none;
}
.action-bar__btn:hover { opacity: 0.85; text-decoration: none; }
.action-bar__close {
    margin-left: auto;
    width: 36px; height: 36px;
    flex-shrink: 0;                         /* taille fixe, jamais resizée */
    border-radius: 50%;
    background: transparent;
    border: 1px solid var(--c-text-on-dark);  /* bordure blanche */
    color: inherit;
    cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center;
    transition: background var(--tr-base);
}
.action-bar__close:hover { background: rgba(255,255,255,0.15); }

/* ── Overlay dropbox (panneau flottant droite) ───────────────────────── */
/* Panneau qui flotte avec des marges visibles plutôt que de coller aux
   bords. Tous les coins arrondis. Backdrop clair (whitewash) pour ne pas
   assombrir le projet derrière. */
/* ── Context menu (clic droit sur les chapitres) ─────────────────────── */
.ctx-menu {
    position: fixed;
    top: 0; left: 0;
    width: 260px;                           /* largeur fixe → toujours
                                                identique quel que soit le
                                                titre ou les actions. */
    background: var(--c-bg-elevated);
    border: 1px solid var(--c-text);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    padding: var(--sp-2);
    z-index: 300;
    user-select: none;
}
.ctx-menu[hidden] { display: none; }
.ctx-menu__title {
    display: flex; align-items: baseline;
    gap: var(--sp-3);                       /* écart numéro ↔ titre */
    margin: 0 0 var(--sp-2);
    padding: 0 var(--sp-2) var(--sp-2);
    font-size: 0.8125rem;                   /* 13px */
    color: var(--c-text);                   /* noir */
    font-weight: 650;
    border-bottom: 1px solid var(--c-border);
}
.ctx-menu__title-name {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
    flex: 1;
}
.ctx-menu__title-num { flex-shrink: 0; }
.ctx-menu__title-num[hidden] { display: none; }
.ctx-menu__list {
    list-style: none; padding: 0; margin: 0;
}
/* Charte alignée sur le menu utilisateurs : items resserrés, filet gris
   sous chaque sauf le dernier, pas de pictos (retirés du twig), texte
   bord à bord (le ctx-menu n'a pas de roue à éviter, contrairement
   au user-actions menu). */
.ctx-menu__item {
    display: flex; align-items: center;
    width: 100%;
    padding: var(--sp-2) var(--sp-3);
    background: transparent;
    border: 0;
    border-bottom: 1px solid var(--c-border);
    border-radius: 0;
    color: var(--c-text);
    font: inherit;
    font-size: var(--fs-xs);
    text-align: left;
    cursor: pointer;
    transition: background var(--tr-base);
    box-sizing: border-box;
}
.ctx-menu__list li:last-child .ctx-menu__item { border-bottom: 0; }
.ctx-menu__list li:first-child .ctx-menu__item {
    border-top-left-radius:  calc(var(--radius-md) - 1px);
    border-top-right-radius: calc(var(--radius-md) - 1px);
}
.ctx-menu__list li:last-child .ctx-menu__item {
    border-bottom-left-radius:  calc(var(--radius-md) - 1px);
    border-bottom-right-radius: calc(var(--radius-md) - 1px);
}
.ctx-menu__item:hover { background: var(--c-bg-subtle); }

/* Container externe : fond gris + bordure noire, collé à droite.
   Coins arrondis à gauche uniquement (le bord droit touche le viewport). */
.dropbox-overlay {
    position: fixed;
    top: 50%;
    right: 0;
    transform: translate(100%, -50%);                 /* hors écran à droite */
   width: calc(100vw - 2.5rem);                /* défaut 1081-1400px */
    max-width: 800px;
    height: calc(100% - 5rem);
    max-height: 900px;
    background: var(--c-bg);                          /* gris */
    border: 1px solid var(--c-text);                  /* noire */
    border-right: 0;                                  /* collé au viewport */
    border-radius: var(--radius-xl) 0 0 var(--radius-xl);
    /* Pas d'ombre portée quand le panel est replié — sinon la blur de
       shadow-lg déborde vers la gauche et reste visible dans le viewport
       même quand le panel est hors écran. L'ombre n'apparaît que sur
       .is-open, avec une transition courte. */
    box-shadow: none;
    transition: transform 240ms ease, box-shadow 160ms ease;
    z-index: 200;
    display: flex; flex-direction: column;
    overflow: hidden;
}
.dropbox-overlay.is-open {
    transform: translate(0, -50%);
    box-shadow: var(--shadow-lg);
}
/* Responsive width — par défaut 80vw capé à 1400. */
@media (min-width: 1501px) {
    .dropbox-overlay { width: 50vw;  max-width: 1400px;  }
}

.dropbox-overlay__backdrop {
    position: fixed; inset: 0;
    background: rgba(255, 255, 255, 0.85);   /* whitewash uniformisé à 85% */
    opacity: 0;
    pointer-events: none;
    transition: opacity 240ms ease;
    z-index: 150;
}
.dropbox-overlay__backdrop.is-open {
    opacity: 1;
    pointer-events: auto;
}

.dropbox-overlay__header {
    display: flex; align-items: center; gap: var(--sp-3);
    padding: var(--sp-5) var(--sp-6);
    /* pas de border-bottom : la séparation visuelle est portée par les
       onglets et la carte blanche en dessous. */
}
.dropbox-overlay__title { font-size: var(--fs-xl); font-weight: 650; margin: 0; flex: 1; }
.dropbox-overlay__actions { display: inline-flex; gap: var(--sp-2); }
/* Boutons « envoyer fichier / dossier » : alignés sur la hauteur de la
   croix de fermeture (36px), donc plus généreux que les pill-action du
   chemin de fer. */
.dropbox-overlay__actions .pill-action {
    height: 36px;
    padding: 0 var(--sp-3);
    font-size: var(--fs-sm);
}
.dropbox-overlay__close {
    width: 36px; height: 36px;
    border-radius: 50%;
    border: 1px solid var(--c-text);            /* filet noir au repos */
    background: transparent;
    cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center;
    color: var(--c-text);                        /* icône noire */
    transition: background var(--tr-base), color var(--tr-base),
                border-color var(--tr-base);
}
/* Hover : suit la même charte que les autres CTA → bg couleur client +
   icône blanche + bordure client. */
.dropbox-overlay__close:hover {
    background: var(--c-client);
    color: var(--c-text-on-dark);
    border-color: var(--c-client);
}

/* Onglets attachés à la carte blanche — hérite tout de .card-tab, ne
 * spécifie que les dimensions/typo propres. */
.dropbox-overlay__tabs {
    display: flex; gap: 4px;
    padding: 0 var(--sp-6);
    margin-bottom: -1px;                     /* chevauche la border-top du card */
    position: relative;
    z-index: 1;
}
.dropbox-overlay__tab {
    padding: 8px var(--sp-5);
    border-radius: var(--radius-md) var(--radius-md) 0 0;
    font-size: var(--fs-sm);
    font-weight: 650;
}

/* Carte blanche qui contient la liste des fichiers, attachée aux tabs. */
.dropbox-overlay__card {
    flex: 1;
    margin: 0 var(--sp-6) var(--sp-6);
    background: var(--c-bg-elevated);
    border: 1px solid var(--c-text);
    border-radius: 0 var(--radius-lg) var(--radius-lg) var(--radius-lg);
    overflow-y: auto;
    min-height: 0;                              /* permet le scroll dans flex */
    /* Scrollbar fine et discrète, style iOS. Firefox : */
    scrollbar-width: thin;
    scrollbar-color: rgba(0,0,0,.22) transparent;
}
/* Chrome / Safari / Edge — webkit. */
.dropbox-overlay__card::-webkit-scrollbar {
    width: 6px;
}
.dropbox-overlay__card::-webkit-scrollbar-track {
    background: transparent;
}
.dropbox-overlay__card::-webkit-scrollbar-thumb {
    background: rgba(0,0,0,.18);
    border-radius: 999px;
    transition: background var(--tr-base);
}
.dropbox-overlay__card:hover::-webkit-scrollbar-thumb {
    background: rgba(0,0,0,.32);
}
.dropbox-overlay__body {
    padding: var(--sp-4) var(--sp-5);
}
/* Note : la définition principale de `.dropbox-overlay__select-all` vit
   plus bas avec le bloc de `.dropbox-overlay__list-head` — elle est
   alignée pixel-parfait sur les checkboxes des rows individuels. */
/* L'attribut `hidden` est respecté → "tout cocher" disparaît quand il
   n'y a aucun fichier (JS le retirera quand la liste sera peuplée). */
.dropbox-overlay__select-all[hidden] { display: none; }
.dropbox-overlay__empty {
    text-align: center; padding: var(--sp-10) 0;
    color: var(--c-text-muted);
    font-size: var(--fs-sm);
}

/* ── Liste des fichiers (contenu réel de l'onglet) ────────────────────
   Rendue par public/js/dropbox-overlay.js. Un item = icône (32×32) +
   nom + meta (taille · date · auteur) + actions (télécharger + suppr). */
/* Listing façon `data-table` : row = ligne, filets entre entrées, hover
   subtle. Le row entier est un `<a>` cliquable → téléchargement direct.
   Seul un bouton corbeille en fin de ligne pour la suppression. */
.dropbox-overlay__files {
    list-style: none;
    margin: 0;
    padding: 0;
    /* Pas de container encadré : chaque row porte son propre filet bas.
       Fond transparent pour laisser le body de l'overlay respirer. */
}
/* Layout row : [checkbox] [icon] [name ................ meta] [trash].
   UNE SEULE bordure bottom grise sur chaque row (aucune bordure top /
   left / right). Au hover : la bordure bottom devient noire (couleur
   texte). Rows compactes. */
.dropbox-overlay__file {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    padding: 0 var(--sp-1);         /* 4px horizontal, discret */
    border: 0;
    border-bottom: 1px solid var(--c-border);
    transition: border-color var(--tr-base), background var(--tr-base);
}
/* Ré-affirmé sur le dernier item pour éviter tout override implicite
   (ex: `ul > li:last-child { border: 0 }` d'un reset global). */
.dropbox-overlay__file:last-child {
    border-bottom: 1px solid var(--c-border);
}
.dropbox-overlay__file:last-child:hover,
.dropbox-overlay__file:hover {
    border-bottom-color: var(--c-text);
    background: var(--c-bg-subtle);
}
.dropbox-overlay__file-check {
    display: inline-flex;
    align-items: center;
    padding: 6px;
    cursor: pointer;
    flex-shrink: 0;
}
/* Checkbox custom : fond client couleur pleine + check BLANC quand
   cochée, quelle que soit la teinte client. On désactive le rendu
   natif (appearance: none) et on dessine tout à la main pour garantir
   le contraste (accent-color délègue le check color au browser, qui
   choisit noir si le client color est clair). */
.dropbox-overlay__file-check input[type="checkbox"],
[data-dropbox-select-all] {
    appearance: none;
    -webkit-appearance: none;
    width: 16px; height: 16px;
    margin: 0;
    cursor: pointer;
    background: transparent;
    border: 1.5px solid var(--c-text-muted);
    border-radius: 3px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background var(--tr-base), border-color var(--tr-base);
    flex-shrink: 0;
    position: relative;
}
.dropbox-overlay__file-check input[type="checkbox"]:checked,
[data-dropbox-select-all]:checked,
[data-dropbox-select-all]:indeterminate {
    background: var(--c-client);
    border-color: var(--c-client);
}
.dropbox-overlay__file-check input[type="checkbox"]:checked::after,
[data-dropbox-select-all]:checked::after {
    /* Coche BLANCHE dessinée en CSS pur (SVG via mask). */
    content: '';
    width: 10px; height: 10px;
    background: #fff;
    -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'><polyline points='20 6 9 17 4 12'/></svg>") center/contain no-repeat;
            mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'><polyline points='20 6 9 17 4 12'/></svg>") center/contain no-repeat;
}
/* État indéterminé (mixte) : petit tiret blanc au lieu du check. */
[data-dropbox-select-all]:indeterminate::after {
    content: '';
    width: 8px; height: 2px;
    background: #fff;
    border-radius: 1px;
}
.dropbox-overlay__file-row {
    flex: 1;
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: 8px 0;
    color: var(--c-text);
    text-decoration: none;
    cursor: pointer;
    transition: background var(--tr-base);
    min-width: 0;             /* pour l'ellipsis dans .file-name */
}
.dropbox-overlay__file-row:hover { text-decoration: none; }
/* Info (nom seul) occupe l'espace restant, meta poussée à droite. */
.dropbox-overlay__file-info {
    flex: 1;
    min-width: 0;
    display: flex; flex-direction: row; align-items: baseline;
    gap: var(--sp-2);
}
.dropbox-overlay__file-meta {
    flex-shrink: 0;
    color: var(--c-text-muted);
    font-size: 0.75rem;
    white-space: nowrap;
    /* Colonne à largeur fixe + texte aligné à gauche → toutes les dates
       s'alignent verticalement quel que soit le contenu. min-width couvre
       le pire cas ("aujourd'hui à 14:32" ≈ 130px). text-align: left pour
       garder l'alignement gauche demandé. */
    min-width: 140px;
    text-align: left;
    margin-left: auto;   /* pousse la colonne à droite quand name est court */
}
.dropbox-overlay__file-icon {
    width: 22px; height: 22px;
    color: var(--c-text-muted);
    flex-shrink: 0;
}
.dropbox-overlay__file-name {
    flex: 1;
    min-width: 0;
    font-size: var(--fs-sm);
    font-weight: 550;
    color: var(--c-text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* Corbeille — bouton icône seule à droite. Discret au repos, rouge au
   hover pour signaler la charge d'action. Le row parent est un <a>, on
   force `pointer-events: auto` par sécurité pour rester cliquable. */
.dropbox-overlay__file-trash {
    appearance: none;
    background: transparent;
    border: 0;
    padding: 6px;
    color: var(--c-text-muted);
    cursor: pointer;
    border-radius: var(--radius-sm);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    transition: color var(--tr-base), background var(--tr-base);
    pointer-events: auto;
}
.dropbox-overlay__file-trash:hover {
    color: #c53030;
    background: color-mix(in srgb, #c53030 10%, transparent);
}

/* En-tête sticky de la liste : "tout cocher" à GAUCHE, actions bulk à
   DROITE (via margin-left: auto). L'en-tête reste visible pendant le
   scroll. Pas de bordure du tout — les filets sont sur les rows.
   min-height garantit une hauteur constante que les boutons bulk soient
   affichés ou non → pas de shift des rows en dessous quand on coche. */
.dropbox-overlay__list-head {
    position: sticky; top: 0; z-index: 1;
    display: flex;
    align-items: center;
    /* Mêmes padding L/R que `.dropbox-overlay__file` pour que la case
       "tout cocher" s'aligne pixel-parfait avec les cases individuelles
       en dessous. `gap` du parent + padding interne du label = mêmes
       offsets sur tous les niveaux. */
    padding: var(--sp-2) var(--sp-1);
    gap: var(--sp-2);
    min-height: 44px;
    background: var(--c-bg-elevated);
}
.dropbox-overlay__list-head[hidden] { display: none; }
.dropbox-overlay__select-all {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    /* Padding interne identique à `.dropbox-overlay__file-check` — la
       checkbox du "tout cocher" se cale au MÊME pixel que celle de
       chaque row en dessous. */
    padding: 6px;
    font-size: var(--fs-sm);
    color: var(--c-text-muted);
    cursor: pointer;
}

/* Bulk actions dans l'en-tête — poussées à droite via margin-left: auto.
   Visibles seulement quand au moins un fichier est coché. */
.dropbox-overlay__bulk {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
}
.dropbox-overlay__bulk[hidden] { display: none; }
.dropbox-overlay__bulk-btn {
    appearance: none;
    padding: 4px var(--sp-3);
    height: 28px;
    border-radius: 999px;
    border: 1px solid var(--c-text);
    background: transparent;
    color: var(--c-text);
    font-size: var(--fs-xs);
    font-weight: 550;
    cursor: pointer;
    transition: background var(--tr-base), color var(--tr-base), border-color var(--tr-base);
}
.dropbox-overlay__bulk-btn:hover {
    background: var(--c-client);
    color: var(--c-text-on-dark);
    border-color: var(--c-client);
}
.dropbox-overlay__bulk-btn--danger:hover {
    background: #c53030;
    color: var(--c-text-on-dark);
    border-color: #c53030;
}

/* Bouton plein couleur client (pour "envoyer un fichier" etc.) */
.btn-client {
    display: inline-flex; align-items: center; gap: var(--sp-2);
    padding: 8px var(--sp-4);
    border-radius: 999px;
    background: var(--c-client);
    color: var(--c-text-on-dark);
    border: 0;
    font-size: var(--fs-sm);
    font-weight: 500;
    cursor: pointer;
    text-decoration: none;
}
.btn-client:hover { opacity: 0.9; text-decoration: none; }

/* ════════════════════════════════════════════════════════════════════════
 *  ÉDITEUR — page /editor/{chapter_uid}
 *  Layout 3 colonnes (sidebar gauche + carte éditeur + sidebar droite)
 *  + sélecteur de chapitre + pills V2/historique au centre du header.
 * ════════════════════════════════════════════════════════════════════════ */

/* ── Bloc central du header (positionné en absolute pour rester centré
   indépendamment de la largeur de la nav et des tools). ──────────────── */
.app-header__center {
    position: absolute;
    left: 50%;
    top:  50%;
    transform: translate(-50%, -50%);
    display: inline-flex; align-items: center;
    gap: var(--sp-3);
    z-index: 1;                         /* sous les details/dropdowns ouverts */
}
/* En desktop étroit (sous 1200px), le centre risque de chevaucher
   la nav ou les tools — on le masque proprement. */







/* ── Sélecteur de chapitre (dropdown <details>) ────────────────────── */
.chapter-switcher__summary {
    display: inline-flex; align-items: center;
    gap: var(--sp-3);
    cursor: pointer;
    list-style: none;
    padding: var(--sp-2) var(--sp-3);
    border-radius: var(--radius-md);
    color: var(--c-text);
     color: var(--c-client);
    user-select: none;

}
.chapter-switcher__summary::-webkit-details-marker { display: none; }
.chapter-switcher__summary:hover { background: var(--c-bg-subtle); }
.chapter-switcher__num   { color: var(--c-client); font-weight: 650; }
.chapter-switcher__title {
    
    font-weight: 650;
    display: inline-block;   /* nécessaire pour que max-width + overflow s'appliquent sur ce <span> */
    max-width: 25vw;
    max-width: calc(35vw - 9rem);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: bottom;  /* aligne proprement avec le numéro sur la même baseline */
}


@media (max-width: 1500px) {
    .app-header__center { 
        position: static;
        display: flex;
        top: auto;
        left: 0;
        right: auto;
        transform: none;

}


.chapter-switcher__title {
    max-width: 15vw;
    max-width: calc(20vw - 3rem);
}
}



/* Sous 1280px : le bloc central du header sortirait de sa zone. On le
   masque HERE et on affiche à la place le doublon `.chapter-switcher-
   inline` placé dans `.editor-topbar` (cf. show.twig). Comme ça il
   scrolle naturellement avec le contenu (pas fixed) plutôt que de
   rester collé au viewport. */
@media (max-width: 1280px) {
    .app-header__center { display: none; }
}

/* ── Doublon inline du sélecteur de chapitre (rangée back-link) ────
   Rendu inconditionnellement dans le DOM (partial), affiché
   uniquement < 1280px. Au-dessus, on garde le bloc du header. */
.editor-topbar {
    display: flex;
    align-items: center;
    gap: var(--sp-4);
    /* Reprend les marges verticales que porte normalement .back-link,
       pour que la ligne « retour au projet + switcher » ait la même
       respiration que le back-link seul en > 1280px. */
    margin-top: calc(-1 * var(--sp-6));
    margin-bottom: var(--sp-10);
}
.editor-topbar .back-link {
    /* Le wrapper porte les marges maintenant, on retire celles du
       back-link (sinon double margin). Width auto → prend juste sa
       taille de contenu (chevron + texte). */
    margin-top: 0;
    margin-bottom: 0;
    width: auto;
}
.chapter-switcher-inline {
    display: none;                    /* caché par défaut (desktop) */
    margin-left: auto;                /* pousse à droite du back-link */
    align-items: center;
    gap: var(--sp-3);
    /* Version légèrement compactée par rapport au header : texte
       un poil plus petit pour respecter la respiration du rail. */
    font-size: var(--fs-sm);
}
.chapter-switcher-inline .chapter-switcher__title {
    max-width: 30vw;
}
.chapter-switcher-inline .chapter-switcher__summary {
    padding: var(--sp-1) var(--sp-2);
}
.chapter-switcher-inline .pill-version {
    font-size: 0.75rem;               /* -1px vs le header pour rester discret */
}
@media (max-width: 1280px) {
    .chapter-switcher-inline { display: inline-flex; }
}







.chapter-switcher[open] .chapter-switcher__summary svg { transform: rotate(180deg); }
.chapter-switcher__summary svg { transition: transform var(--tr-base); }

.chapter-switcher__panel {
    left: 50%; right: auto;
    transform: translateX(-50%);
    min-width: 320px; max-width: 480px;
    max-height: 60vh; overflow-y: auto;
}
.chapter-switcher__row {
    display: flex; align-items: baseline;
    gap: var(--sp-3);
}
.chapter-switcher__panel-num   { min-width: 2.75rem; }
.chapter-switcher__panel-title { min-width: 0; }

/* Top-level : couleur client, gras, taille standard (comme le dashboard). */
.chapter-switcher__row--top .chapter-switcher__panel-num,
.chapter-switcher__row--top .chapter-switcher__panel-title {
    color: var(--c-client);
    font-weight: 650;
    font-size: var(--fs-md);
}

/* Sous-titres : plus petits, plus légers, charbon. */
.chapter-switcher__row--sub .chapter-switcher__panel-num,
.chapter-switcher__row--sub .chapter-switcher__panel-title {
    color: var(--c-text);
    font-weight: 300;
    font-size: var(--fs-sm);
}

/* Chapitre non accessible (pas de version) : affiché mais non cliquable. */
.chapter-switcher__row--empty {
    opacity: 0.4;
    cursor: not-allowed;
    pointer-events: none;
}

/* ── Pills V2 / historique (header center) ─────────────────────────── */
.pill-version {
    display: inline-flex; align-items: center;
    padding: var(--sp-2) var(--sp-4);
    border-radius: 999px;
    font-size: var(--fs-sm);
    font-weight: 600;
    cursor: pointer;
    border: 1px solid transparent;
    transition: background var(--tr-base), color var(--tr-base),
                border-color var(--tr-base);
    white-space: nowrap;
    text-decoration: none;
}
.pill-version:hover { text-decoration: none; }

/* Suivent la règle UI globale (cf. .pill-action) :
   • « passer en V2 » = CTA primaire → filet NOIR + texte NOIR
   • « voir l'historique » = CTA secondaire → filet GRIS + texte GRIS
   • hover des deux → fond couleur client + texte BLANC */
.pill-version--promote {
    background: transparent;
    color: var(--c-text);
    border-color: var(--c-text);
}
.pill-version--history {
    background: transparent;
    color: var(--c-text-muted);                  /* texte un peu plus foncé que le filet */
    border-color: var(--c-text-faded);
}
.pill-version--promote:hover:not(:disabled),
.pill-version--history:hover:not(:disabled) {
    background: var(--c-client);
    color: var(--c-text-on-dark);
    border-color: var(--c-client);
}
/* État désactivé (chapitre verrouillé → impossible de passer en V2). */
.pill-version:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}
/* La pill « historique » désactivée signifie « aucune version antérieure »
   (le chapitre n'a que la working). Opacité très faible pour l'atténuer
   visuellement sans la retirer du flux — l'user comprend qu'elle
   existe mais qu'elle n'a rien à afficher. Après un bump Ajax, le JS
   retire l'attribut disabled et la pill redevient pleine (100%). */
.pill-version--history:disabled {
    opacity: 0.15;
    cursor: not-allowed;
    pointer-events: none;
}

/* ── Row du chemin de fer entièrement cliquable ────────────────────── */
.toc-row[data-editor-url] {
    cursor: pointer;
    border-radius: var(--radius-md);
    transition: background var(--tr-base);
}
.toc-row[data-editor-url]:hover { background: var(--c-bg-subtle); }

/* ── Layout 3 colonnes de la page éditeur ──────────────────────────── */
.editor-shell {
    display: grid;
    grid-template-columns: 300px 1fr 300px;
    gap: var(--sp-4);
    align-items: start;
    margin-top: var(--sp-6);
}
/* Entre 900 et 1799px : aside droit masqué, aside gauche regroupe
   alors les 4 sections d'accordéon (les 2 « narrow-only » sont révélées).
   Compound selectors (.editor-side.editor-side--right) pour battre la
   spécificité de la règle commune `.editor-side { display: flex }`
   définie plus loin dans le fichier. */
@media (max-width: 1799px) {
    .editor-shell { grid-template-columns: 300px 1fr; }
    .editor-side.editor-side--right { display: none; }
}
@media (min-width: 1800px) {
    .editor-side--left .editor-side__acc--narrow-only,
    .editor-side--left .editor-side__panel--narrow-only {
        display: none;
    }
}
/* Au-dessus de 1850px on élargit les sidebars à 360px pour profiter
   de la place disponible (mode confort écran XL). */
@media (min-width: 1850px) {
    .editor-shell { grid-template-columns: max(320px,20%) 1fr max(320px,20%); }
}
/* Sous 900px (mobile) : 1 colonne, TOUTES les sides complètement
   masquées (gauche ET droite). Même trick de spécificité. */
@media (max-width: 900px) {
    .editor-shell { grid-template-columns: 1fr; }
    .editor-side.editor-side--left,
    .editor-side.editor-side--right { display: none; }
}

/* ── Cartes éditeur + sides : structure commune ────────────────────── */
.editor-card,
.editor-side {
    background: var(--c-bg-elevated);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);                 /* légère ombre portée */
    display: flex; flex-direction: column;
    min-width: 0;
}
/* `overflow: hidden` UNIQUEMENT sur les sides — la carte éditeur doit
   le garder retiré pour que la toolbar interne puisse `position: sticky`
   (un ancêtre avec overflow:hidden casserait le sticky). */
.editor-side { overflow: hidden; }

/* Ancrage explicite des sidebars dans leur cellule grid respective.
 * Sans ça, les panneaux overlay (recherche, lexique) qui posent
 * `grid-column: 1; grid-row: 1` explicitement forceraient la sidebar
 * auto-placée à sortir vers (1,2) et créer une colonne supplémentaire —
 * en la déclarant explicitement au même endroit, elles cohabitent
 * simplement dans la même cellule et se superposent par z-index. */
.editor-side--left  { grid-column: 1; grid-row: 1; }
.editor-side--right { grid-column: 3; grid-row: 1; }
@media (max-width: 1799px) {
    /* Sous 1799px la 3e colonne disparaît → la right sidebar est cachée
     * de toute façon (display: none), donc pas besoin de forcer son
     * placement. La left reste sur col 1. */
}

/* Seule la carte éditeur a une bordure (les sides juste l'ombre).
   La bordure TOP est portée par le toolbar (border-top), pas par le card,
   parce que le box-shadow gris du toolbar (cache anti-scroll) recouvre
   précisément cette zone. Visuellement c'est identique : le toolbar est
   bordé en haut ET en bas, la carte continue de droite/gauche/bas. */
.editor-card {
    border: 1px solid var(--c-text);
    border-top: 0;
}
/* Les sides grandissent en fonction de leur contenu (hauteur auto),
   avec un plancher à 200px et un plafond = hauteur viewport − header
   − un peu d'air. Quand le contenu dépasse, scroll vertical dans
   .editor-side__acc-body de la section ouverte.
   Sticky comme la toolbar éditeur : restent ancrées juste sous le
   header pendant qu'on scrolle l'éditeur. */
.editor-side {
    max-height: calc(100vh - var(--header-height) - var(--sp-8));
    position: sticky;
    top: calc(var(--header-height) + var(--sp-4));
}

/* ── Accordéon vertical des sidebars éditeur ──────────────────────────
 * Chaque section = <details data-side-section="X"> contenant :
 *   • <summary class="editor-side__acc-head">       (header cliquable)
 *   • <div class="editor-side__acc-body">           (body scrollable)
 *
 * Exclusivité (une seule section ouverte par aside) + persistance de
 * l'état ouvert par aside : gérées côté JS (editor-side-tabs.js).
 *
 * Layout : les sections fermées ne prennent que la hauteur de leur summary
 * (flex-shrink: 0). La section OUVERTE prend tout l'espace restant
 * (flex: 1) — son body scrolle si le contenu dépasse. */
.editor-side__acc {
    display: flex;
    flex-direction: column;
    flex-shrink: 0;                    /* ne rétrécit jamais côté aside */
    min-height: 0;
    border-bottom: 1px solid var(--c-border);
}
.editor-side__acc:last-child { border-bottom: 0; }
.editor-side__acc[open] {
    /* Aucun flex-basis / grow imposé : la section prend la hauteur naturelle
     * de son summary + body. Le body est capé par un `max-height` calculé
     * en JS (cf. editor-side-tabs.js → applyBodyMaxHeight) — au-delà, il
     * scrolle via `overflow-y: auto`. On évite ainsi les quirks de flex
     * sur <details> qui ne propageaient pas le shrink correctement. */
    overflow: hidden;
}

/* Header cliquable — même palette que l'ancien header gris. */
.editor-side__acc-head {
    list-style: none;                          /* hide default marker */
    padding: 10px var(--sp-4);
    background: var(--c-bg-subtle);
    color: var(--c-text-muted);
    font-size: var(--fs-sm);
    font-weight: 600;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-2);
    cursor: pointer;
    user-select: none;
    -webkit-user-select: none;
    flex-shrink: 0;
    transition: background var(--tr-base), color var(--tr-base);
}
.editor-side__acc-head::-webkit-details-marker { display: none; }
.editor-side__acc-head:hover { color: var(--c-text); }
.editor-side__acc[open] > .editor-side__acc-head {
    background: var(--c-bg-elevated);
    color: var(--c-text);
    border-bottom: 1px solid var(--c-border);
}
.editor-side__acc-title {
    flex: 1;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.editor-side__acc-caret {
    width: 12px;
    height: 8px;
    flex-shrink: 0;
    transition: transform var(--tr-fast);
}
.editor-side__acc[open] > .editor-side__acc-head .editor-side__acc-caret {
    transform: rotate(180deg);
}

/* Body : hauteur naturelle jusqu'au max-height posé en inline par le JS
 * (calcul = hauteur de l'aside − somme des summaries). Au-delà, scroll
 * interne via `overflow-y: auto`. */
.editor-side__acc-body {
    max-height: none;                  /* écrasé par style inline JS */
    overflow-y: auto;
    padding: var(--sp-2) var(--sp-3);
}

/* ── Toolbar de l'éditeur (même gris clair que le header des sides) ─
   Sticky sous le header global, avec un gap de 16px (= sp-4, même valeur
   que le gap entre les colonnes du shell) sous le header pour respirer.
   Bordure basse noire (cohérence avec la bordure noire de la carte).
   Le masquage du contenu scrollant dans le gap est réalisé par un
   box-shadow plein de 16px projeté vers le haut avec la couleur du
   fond de page : pas d'élément DOM, donc la bordure du card est
   préservée et le shadow blend invisiblement avec la marge supérieure
   page-bg de l'editor-shell à la position naturelle. */
.editor-toolbar {
    display: flex; align-items: center;
    flex-wrap: wrap;
    gap: var(--sp-1);                  /* groupes plus serrés */
    padding: var(--sp-3) var(--sp-4);
    background: var(--c-bg-subtle);
    border:    1px solid var(--c-text);    
    position: sticky;
    top: calc(var(--header-height) + var(--sp-4));
    z-index: 5;
    border-top-left-radius:  calc(var(--radius-lg) - 1px);
    border-top-right-radius: calc(var(--radius-lg) - 1px);
    margin: 0 -1px;
    /* Deux ombres solides (blur 0) cumulées :
       1. Cache anti-scroll 16px en haut (couleur fond de page) — masque
          le contenu qui scrolle dans le gap entre header et toolbar.
       2. Halo 2px gris (fond de page) sur tous les côtés via spread,
          décalé légèrement vers le haut → donne une ombre portée
          discrète qui détache visuellement le toolbar de la carte. */
    box-shadow:
        0 calc(-1 * var(--sp-6)) 0 0 var(--c-bg),
        0 -2px 0 2px var(--c-bg);
}
/* Tant que la toolbar n'est pas marquée is-ready (= après mountEditor),
   on masque tout son contenu — sinon les boutons apparaissent au load
   avant que les handlers JS ne soient câblés, ce qui clignote et permet
   à l'utilisateur de cliquer dans le vide. */
.editor-toolbar > * { opacity: 0; pointer-events: none; }
.editor-toolbar.is-ready > * { opacity: 1; pointer-events: auto; }

.editor-toolbar__group {
    display: inline-flex; align-items: center;
    gap: 2px;
}
.editor-toolbar__sep {
    width: 1px;
    align-self: stretch;
    background: var(--c-border);
    margin: 4px 3px;                   /* léger air autour du filet,
                                          en plus du gap sp-1 du toolbar */
}

/* ── Label « document en lecture seule » ──────────────────────────────
   Texte rouge nu (pas de pill/badge), affiché à côté des boutons toujours
   visibles (commentaires + recherche) DÈS QUE la toolbar est en mode
   lecture seule — peu importe la cause (chapitre verrouillé OU pas de
   permission d'écriture). Caché par défaut. */
.editor-toolbar__lock-label {
    display: none;
    margin-left: var(--sp-3);
    font-size: var(--fs-xs);
    font-weight: 600;
    color: var(--c-accent, #ea0000);
    white-space: nowrap;
}
.editor-toolbar--readonly .editor-toolbar__lock-label {
    display: inline-block;
}

/* ── Mode lecture seule (rôle sans write OU chapitre verrouillé) ───────
   Masque tous les groupes du toolbar SAUF ceux marqués --always (les
   commentaires + la recherche, qui restent toujours accessibles), et
   les séparateurs (qui n'ont plus de sens). */
.editor-toolbar--readonly .editor-toolbar__group:not(.editor-toolbar__group--always),
.editor-toolbar--readonly .editor-toolbar__sep {
    display: none;
}
/* Le bouton « mode révision » est dans le groupe --always (à côté du
   commentaire, pour rester dispo en écriture même quand tout le reste
   de la toolbar est masqué) mais il n'a de SENS qu'en mode écriture :
   activer un mode capture sur un doc verrouillé ne servirait à rien.
   On le masque donc explicitement quand le doc est en read-only. */
.editor-toolbar--readonly [data-cmd="track-changes"] {
    display: none;
}
.editor-toolbar__btn {
    display: inline-flex; align-items: center; justify-content: center;
    width: 32px; height: 32px;
    background: transparent;
    border: 0;
    border-radius: var(--radius-sm);
    color: var(--c-text);
    cursor: pointer;
    /* Pas de bordure au repos — apparaît au hover (noir léger) ou à
     * l'actif (client). Transition sur box-shadow pour le fondu. */
    transition: background var(--tr-base), box-shadow var(--tr-base);
    padding: 0;
}
/* Hover : fond blanc (vs bg-subtle de la toolbar) + fine bordure noir
 * 25 % pour matérialiser le bouton. S'applique à tous les boutons
 * toolbar non désactivés ; l'actif a son propre :hover client plus bas. */
.editor-toolbar__btn:hover:not(:disabled) {
    background: color-mix(in srgb, var(--c-text) 5%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--c-text) 8%, transparent);
}
.editor-toolbar__btn:disabled {
    color: var(--c-text-muted);
    cursor: default;
    opacity: 0.45;
}
/* Bouton actif (mark/heading appliqué au curseur).
 * Combinaison bg client en opacité réduite + bordure pleine + icône colorée
 * → reste lisible même si la couleur client est très foncée (proche du noir).
 * box-shadow inset plutôt que border pour éviter le shift de 1px au repos. */
.editor-toolbar__btn.is-active {
    background: color-mix(in srgb, var(--c-client) 5%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--c-client) 60%, transparent);
    color: var(--c-client);
}
.editor-toolbar__btn.is-active:hover {
    background: color-mix(in srgb, var(--c-client) 10%, transparent);
}

/* Exception : le bouton « mode révision » en état actif prend un
   signalement plus fort (bg client PLEIN + bordure client + icône
   blanche). Rationale UX : c'est un mode global qui change la sémantique
   de chaque édition — il doit sauter aux yeux beaucoup plus que les
   toggles habituels (gras, italique…) qui n'affectent que la sélection. */
.editor-toolbar__btn[data-cmd="track-changes"].is-active {
    background: var(--c-client);
    box-shadow: inset 0 0 0 1px var(--c-client);
    color: #fff;
}
.editor-toolbar__btn[data-cmd="track-changes"].is-active:hover {
    background: color-mix(in srgb, var(--c-client) 88%, black);
}
/* Contributeur / rôle sans permission : le bouton est disabled côté JS
   (pas cliquable) mais reste à opacité PLEINE. L'info importante à
   véhiculer est « le mode est ON / OFF » — dimmer masquerait ce signal
   au user qui n'a pas le droit d'agir mais doit tout de même savoir
   l'état courant du doc. */
.editor-toolbar__btn[data-cmd="track-changes"]:disabled {
    opacity: 1;
    cursor: not-allowed;
    color: var(--c-text);
}
.editor-toolbar__btn[data-cmd="track-changes"].is-active:disabled {
    color: #fff;
}

/* ── Split-button (style Word : toggle principal + caret variants) ──────
 *  Cohérent avec la charte toolbar : aucune bordure au repos. La
 *  structure (bordure wrapper + filet séparateur) n'apparaît qu'au hover.
 *  En mode actif, le filet séparateur disparaît pour que les deux halves
 *  forment visuellement un seul bloc plein. */
.editor-toolbar__split {
    display: inline-flex;
    align-items: stretch;
    /* position: relative pour que le dropdown panel des variants se
     * positionne par rapport à la wrapper (gauche = bord gauche du bouton
     * principal), pas par rapport au caret seul. */
    position: relative;
    /* Bordure transparente au repos — réserve la place sans tracer
     * de filet, évite le shift de layout au hover. */
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    transition: border-color var(--tr-base), background var(--tr-base);
}
/* Neutralise le position:relative natif de .dropdown pour que le panel
 * remonte jusqu'à .editor-toolbar__split comme containing block. */
.editor-toolbar__split .dropdown { position: static; }
.editor-toolbar__split .dropdown__panel {
    left: 0;                              /* aligné sur le bouton principal */
    top: calc(100% + 6px);
}
/* Coins arrondis partagés : main (gauche) + caret (droite). */
.editor-toolbar__split .editor-toolbar__split-main {
    border-radius: var(--radius-sm) 0 0 var(--radius-sm);
}
.editor-toolbar__split .editor-toolbar__btn--caret {
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
    width: 18px;                   /* caret juste un poil élargi (flèche reste 8px) */
    transition: background var(--tr-base);
}
/* Hover sur le caret uniquement (zone cliquable distincte du bouton
 * principal) : fond noir 8 %. Fonctionne même en état actif. */
.editor-toolbar__split .editor-toolbar__btn--caret:hover {
    background: color-mix(in srgb, var(--c-text) 8%, transparent);
}
.editor-toolbar__split .editor-toolbar__btn--caret .icon-mask {
    width: 8px; height: 8px;       /* flèche réduite */
}
/* Évite que le <summary> caret hérite des styles list-marker natifs */
.editor-toolbar__btn--caret { list-style: none; }
.editor-toolbar__btn--caret::-webkit-details-marker { display: none; }

/* Hover du split inactif : fond blanc + fine bordure noir 25 % posée
 * sur la wrapper (les deux halves apparaissent comme un seul widget
 * encadré). On supprime alors la bordure individuelle des inner buttons
 * pour éviter le double tracé. */
.editor-toolbar__split:hover {
    background: color-mix(in srgb, var(--c-text) 5%, transparent);
    border-color: color-mix(in srgb, var(--c-text) 8%, transparent);
}
.editor-toolbar__split .editor-toolbar__btn:hover {
    box-shadow: none;     /* la wrapper porte la bordure pour le split */
}

/* État actif : wrapper bg + bordure client, deux halves fusionnent
 * visuellement (pas de filet séparateur), box-shadow inset des inner
 * buttons neutralisé (sinon double bordure). */
.editor-toolbar__split:has(.editor-toolbar__split-main.is-active) {
    background: color-mix(in srgb, var(--c-client) 5%, transparent);
    border-color: color-mix(in srgb, var(--c-client) 60%, transparent);
}
.editor-toolbar__split:has(.editor-toolbar__split-main.is-active):hover {
    background: color-mix(in srgb, var(--c-client) 10%, transparent);
}
/* Le main devient transparent (le bg client vient de la wrapper) ; le
 * caret GARDE son fond noir 8 % posé par sa règle de base — c'est ce
 * qui le distingue visuellement en permanence du bouton principal. */
.editor-toolbar__split:has(.editor-toolbar__split-main.is-active) .editor-toolbar__split-main {
    background: transparent;
    box-shadow: none;
    color: var(--c-client);
}
.editor-toolbar__split:has(.editor-toolbar__split-main.is-active) .editor-toolbar__btn--caret {
    box-shadow: none;
    color: var(--c-client);
}

/* Panel des variants — grille de tuiles carrées avec mini-preview de la
 * liste rendue (style Word). Le navigateur dessine nativement la puce ou
 * la numérotation via list-style-type, on n'a qu'à styler le conteneur. */
.editor-toolbar__variants--grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--sp-2);
    padding: var(--sp-2);
    min-width: 0;
}
.editor-toolbar__variant {
    aspect-ratio: 1;
    width: 60px;
    background: var(--c-bg-elevated);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-sm);
    padding: 6px 6px 6px 18px;     /* place pour la puce/numéro à gauche (ordered) */
    cursor: pointer;
    transition: border-color var(--tr-base), background var(--tr-base);
}
/* Variante bullet : un seul caractère puce centré dans le carré, pas de
 * mini-liste (les puces non ordonnées sont visuellement explicites). Tuile
 * + symbole plus petits que les variants ordonnées (qui doivent contenir
 * une mini-liste). */
.editor-toolbar__variant--bullet {
    width: 40px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
}
.variant-bullet {
    font-size: 14px;
    line-height: 1;
    color: var(--c-text);
}
/* Ajustements typographiques par caractère :
 *   - cercle (○) un cran plus fin (caractère un peu massif visuellement)
 *   - carré (▪) un cran plus gros (caractère visuellement plus petit) */
.variant-bullet--circle { font-size: 13px; }
.variant-bullet--square { font-size: 22px; }

/* Pour le panneau de variants BULLET : 4 tuiles sur une seule ligne
 * (dash + disc + circle + square). Override du grid 3-cols par défaut. */
.editor-toolbar__variants--grid[data-cmd-group="list_bullet"] {
    grid-template-columns: repeat(4, auto);
}
.editor-toolbar__variant:hover {
    border-color: color-mix(in srgb, var(--c-text) 25%, transparent);
    background: var(--c-bg-elevated);
}
.editor-toolbar__variant.is-active {
    border-color: color-mix(in srgb, var(--c-client) 60%, transparent);
    background: color-mix(in srgb, var(--c-client) 5%, transparent);
}
.editor-toolbar__variant.is-active .variant-preview,
.editor-toolbar__variant.is-active .variant-bullet {
    color: var(--c-client);
}

/* Mini-preview interne : 3 li, chacune avec une petite barre horizontale
 * (le span) qui simule du texte. Les markers (puces/numéros) viennent
 * naturellement du navigateur via list-style-type sur le ul/ol. */
.variant-preview {
    margin: 0;
    padding-left: 0;
    list-style-position: outside;
    font-size: 9px;
    line-height: 1;
    color: var(--c-text-muted);
}
.variant-preview li {
    margin-bottom: 4px;
}
.variant-preview li:last-child { margin-bottom: 0; }
.variant-preview span {
    display: inline-block;
    width: 28px;
    height: 3px;
    background: currentColor;
    opacity: 0.5;
    border-radius: 1px;
    vertical-align: middle;
}
.editor-toolbar__btn svg              { width: 18px; height: 18px; display: block; }
.editor-toolbar__btn .icon-mask       { width: 18px; height: 18px; }
.editor-toolbar__btn--text {
    font-size: var(--fs-md);
    font-weight: 650;
    font-family: var(--font-sans);
    line-height: 1;
}
.editor-toolbar__btn--text sup,
.editor-toolbar__btn--text sub {
    font-size: 0.65em;
    font-weight: 600;
}

/* ── Contenu éditeur (lorem statique pour la Phase 1) ──────────────── */
.editor-content {
    padding: var(--sp-8) var(--sp-10) var(--sp-12);
    color: var(--c-text);
    line-height: var(--lh-base);
    font-size: var(--fs-md);
}
.editor-content p {
    margin: 0 0 var(--sp-4);
}
.editor-h1, .editor-h2 {
    display: flex; align-items: baseline;
    gap: var(--sp-6);
    margin: 0 0 var(--sp-5);
}
.editor-h1 { font-size: 1.875rem; font-weight: 800; }    /* 30px */
.editor-h2 { font-size: 1.5rem;   font-weight: 800; margin-top: var(--sp-6); }
.editor-h-num  { color: var(--c-client); min-width: 2.5rem; }
.editor-h-text { color: var(--c-client); }

/* Lien « cassé » illustratif (ancre rouge barrée) du mock. */
.editor-link-broken {
    color: var(--c-accent);
    text-decoration: line-through;
}

/* NB : règles du CONTENU édité (.ProseMirror) → public/css/editor-prose.css
 * (séparée car partagée avec l'export HTML, voir le memo associé).
 *
 * Ici ne restent que les styles du CHROME éditeur : overlay de connexion,
 * toolbar, side panels, etc. */

/* ── État de connexion : overlay « connexion en cours » + opacité
 *    réduite sur l'éditeur et les 2 side panels. Le header reste intact
 *    (l'overlay est dans .editor-shell, pas au-dessus du header). On utilise
 *    :has() pour remonter l'état depuis #editor-root jusqu'au shell parent. */
.editor-shell { position: relative; }

.editor-shell__status {
    position: absolute;
    top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    z-index: 5;
    padding: var(--sp-3) var(--sp-5);
    background: var(--c-bg-elevated);
    color: var(--c-text);
    font-size: var(--fs-sm);
    font-weight: 600;
    border: 1px solid var(--c-border);
    border-radius: var(--radius-md);
    box-shadow: 0 4px 16px rgba(0,0,0,0.08);
    pointer-events: none;
    display: none;             /* affiché conditionnellement plus bas */
}
.editor-shell__status-connecting,
.editor-shell__status-disconnected { display: none; }

/* État connecting : opacité réduite sur card + sides, overlay visible. */
.editor-shell:has(#editor-root[data-connection="connecting"]) .editor-card,
.editor-shell:has(#editor-root[data-connection="connecting"]) .editor-side {
    opacity: 0.3;
    transition: opacity var(--tr-base, 0.2s);
    pointer-events: none;
}
.editor-shell:has(#editor-root[data-connection="connecting"]) .editor-shell__status { display: block; }
.editor-shell:has(#editor-root[data-connection="connecting"]) .editor-shell__status-connecting { display: inline; }

/* État disconnected : même grisé + libellé différent (reconnexion en cours). */
.editor-shell:has(#editor-root[data-connection="disconnected"]) .editor-card,
.editor-shell:has(#editor-root[data-connection="disconnected"]) .editor-side {
    opacity: 0.3;
    transition: opacity var(--tr-base, 0.2s);
    pointer-events: none;
}
.editor-shell:has(#editor-root[data-connection="disconnected"]) .editor-shell__status { display: block; }
.editor-shell:has(#editor-root[data-connection="disconnected"]) .editor-shell__status-disconnected { display: inline; }

/* État connected : transition douce vers opacité pleine. */
.editor-shell .editor-card,
.editor-shell .editor-side {
    transition: opacity var(--tr-base, 0.2s);
}

/* ── Compteur de présence — fixed en bas de viewport, centré ───────────
 *  <details class="dropdown editor-presence"> :
 *    summary = pill ovale avec icône user + libellé compteur
 *    panel   = liste des connectés (avatar, nom, rôle, mailto) qui
 *              S'OUVRE VERS LE HAUT (bottom: 100% + offset)
 *
 *  Position fixed = reste visible quel que soit le scroll de l'éditeur.
 *  Z-index 50 pour passer devant le contenu et derrière une éventuelle
 *  modale (qui sera > 100). Fermeture sur clic extérieur ou Escape via
 *  dropdown-outside-close.js (handler global déjà en place).
 */
.editor-presence {
    position: fixed;
    bottom: 24px;
    left: 50%;
    transform: translateX(-50%);
    width: max-content;
    z-index: 50;
}

/* Empêche le badge curseur d'un participant (positionné absolute) de
 * déclencher une scrollbar horizontale au niveau du document quand il
 * déborde la largeur de l'éditeur. `clip` clipper seulement visuellement,
 * sans créer de scroll container (compatible avec position: sticky). */
body.page-editor { overflow-x: clip; }
.editor-presence__button {
    list-style: none;                         /* retire la flèche native <summary> */
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    padding: var(--sp-1) var(--sp-3);
    background: var(--c-bg-elevated);
    border: 1px solid var(--c-border);
    border-radius: 999px;
    font-size: var(--fs-sm);
    color: var(--c-text-secondary);
    transition: background var(--tr-base), color var(--tr-base),
                border-color var(--tr-base);
}
.editor-presence__button::-webkit-details-marker { display: none; }
/* Neutralise la règle globale .dropdown[open] summary { color: var(--c-text) }
 * (sinon le texte devient noir une fois la liste ouverte). On garde le
 * texte secondaire à l'état repos+open, et on passe en blanc au hover. */
.editor-presence[open] .editor-presence__button {
    color: var(--c-text-secondary);
}
/* Spécificité bumpée (.class .class:hover) pour battre la règle globale
 * .dropdown[open] summary qui s'imposerait sinon en état open+hover. */
.editor-presence .editor-presence__button:hover,
.editor-presence .editor-presence__button:focus-visible {
    background: var(--c-client);
    color: white;
    border-color: var(--c-client);
}
.editor-presence__button svg {
    width: 16px; height: 16px;
    flex-shrink: 0;
}

.editor-presence__panel {
    position: absolute;
    /* Reset le `top` posé par la règle globale .dropdown__panel — sinon
     * top + bottom simultanés provoquent un comportement contradictoire,
     * et `top` gagne → le panel s'ouvrirait vers le bas. */
    top: auto;
    bottom: calc(100% + var(--sp-2));
    left: 50%;
    transform: translateX(-50%);
    min-width: 340px;
    max-width: 420px;
    padding: var(--sp-3);
    background: var(--c-bg-elevated);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-md);
    box-shadow: 0 8px 24px rgba(0,0,0,0.12);
    z-index: 10;
}
.editor-presence__title {
    margin: 0 0 var(--sp-2);
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--c-text-muted);
}
.editor-presence__list {
    list-style: none;
    margin: 0; padding: 0;
    display: flex; flex-direction: column;
    gap: var(--sp-2);
    max-height: 320px;
    overflow-y: auto;
}
.editor-presence__user {
    display: flex; align-items: center; gap: var(--sp-3);
    padding: var(--sp-2);
    border-top: 1px solid var(--c-border);   /* filet séparateur clair */
    border-radius: var(--radius-sm);
    transition: background var(--tr-base);
}
.editor-presence__user:first-child { border-top: 0; }
/* Hover : fond gris subtil pour souligner la cible (cible de clic = enveloppe
 * mailto). Le filet du top reste visible. */
.editor-presence__user:hover { background: var(--c-bg-subtle); }
/* Charte CTA : filet gris discret au repos + bg couleur client + icône
 * blanche au hover, transition douce. */
.editor-presence__contact {
    margin-left: auto;
    width: 32px; height: 32px;
    display: inline-flex; align-items: center; justify-content: center;
    color: var(--c-text-muted);
    background: transparent;
    border: 1px solid var(--c-text-muted);
    border-radius: 50%;
    transition: background var(--tr-base), color var(--tr-base),
                border-color var(--tr-base);
    flex-shrink: 0;
}
.editor-presence__contact:hover {
    background: var(--c-client);
    color: white;
    border-color: var(--c-client);
}
.editor-presence__contact .icon-mask { width: 16px; height: 16px; }
.editor-presence__avatar {
    flex-shrink: 0;
    width: 40px; height: 40px;
    border-radius: 50%;
    background: var(--c-user-bg, #d0e4fd);
    color: var(--c-user-fg, #066ef0);
    display: flex; align-items: center; justify-content: center;
    font-size: 0.85rem; font-weight: 650;
    overflow: hidden;
}
.editor-presence__avatar img {
    width: 100%; height: 100%;
    object-fit: cover;
    display: block;
}
.editor-presence__info {
    display: flex; flex-direction: column;
    line-height: 1.25;
    min-width: 0;
}
.editor-presence__info strong {
    font-weight: 600;
    font-size: var(--fs-md);
    color: var(--c-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.editor-presence__info small {
    color: var(--c-user-fg, var(--c-text-muted));
    font-size: var(--fs-xs);
}

/* ── Modale « renommer le chapitre » ───────────────────────────────────
 *  Overlay blanc à 85% (charte) + card centrée. Pas de transition pour
 *  rester instantanée (focus immédiat sur l'input). Z-index élevé pour
 *  passer devant la presence pill (z-index 50). */
.editor-rename-modal {
    position: fixed;
    inset: 0;
    z-index: 200;
    background: rgba(255, 255, 255, 0.85);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--sp-4);
}
.editor-rename-modal__card {
    width: 100%;
    max-width: 460px;
    background: var(--c-bg-elevated);
    /* Bordure noire + ombre légère standard — convention uniforme sur
       toutes les modales de l'app (cf. .versions-modal, .editor-confirm-modal). */
    border: 1px solid var(--c-text);
    border-radius: var(--radius-lg);
    box-shadow: 0 8px 24px rgba(0,0,0,0.12);
    padding: var(--sp-5) var(--sp-6) var(--sp-4);
    display: flex; flex-direction: column;
    gap: var(--sp-3);
}
.editor-rename-modal__title {
    margin: 0;
    font-size: var(--fs-lg);
    font-weight: 650;
    color: var(--c-text);
}
.editor-rename-modal__label {
    font-size: var(--fs-sm);
    color: var(--c-text-secondary);
    margin: 0;
}
.editor-rename-modal__input {
    width: 100%;
    font-size: var(--fs-md);
}
.editor-rename-modal__error {
    margin: 0;
    color: var(--c-accent, #ea0000);
    font-size: var(--fs-sm);
}
.editor-rename-modal__actions {
    display: flex; justify-content: flex-end;
    gap: var(--sp-2);
    margin-top: var(--sp-2);
}

/* ── Modale de confirmation générique (remplace window.confirm) ─────────
 *  Même structure visuelle que la modale rename : overlay blanc à 85%,
 *  card centrée. Variante --danger pour le bouton de confirmation quand
 *  l'action est destructive (suppression de commentaire, par ex.). */
.editor-confirm-modal {
    position: fixed;
    inset: 0;
    /* 400 pour passer au-dessus du modal versions (z-index 300).
       Un confirm est TOUJOURS déclenché depuis un autre contexte —
       il doit rester le plus haut z-index de l'app. */
    z-index: 400;
    background: rgba(255, 255, 255, 0.85);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--sp-4);
}
.editor-confirm-modal__card {
    width: 100%;
    max-width: 420px;
    background: var(--c-bg-elevated);
    /* Bordure noire (couleur texte) pour détacher franchement la card
       de l'overlay clair. Convention partagée avec .versions-modal
       pour toutes les modales de l'app. */
    border: 1px solid var(--c-text);
    border-radius: var(--radius-lg);
    /* Ombre standard légère — même pattern que .versions-modal__card et
       les dropdowns/popovers de l'app. */
    box-shadow: 0 8px 24px rgba(0,0,0,0.12);
    padding: var(--sp-5) var(--sp-6) var(--sp-4);
    display: flex; flex-direction: column;
    gap: var(--sp-3);
}
.editor-confirm-modal__title {
    margin: 0;
    font-size: var(--fs-lg);
    font-weight: 650;
    color: var(--c-text);
}
.editor-confirm-modal__message {
    margin: 0;
    font-size: var(--fs-sm);
    color: var(--c-text-muted);
    line-height: 1.5;
}
.editor-confirm-modal__actions {
    display: flex; justify-content: flex-end;
    gap: var(--sp-2);
    margin-top: var(--sp-2);
}
/* Min-width sur les boutons du modal confirm — sinon un label court comme
   « ok » rend le bouton presque circulaire à cause du border-radius, ce qui
   fait bizarre. 84px garantit une forme rectangulaire lisible même pour les
   labels d'un ou deux mots. */
.editor-confirm-modal__actions .btn {
    min-width: 84px;
}
/* Pas d'outline focus sur les boutons du modal — le contexte modal est
   déjà clair, l'outline par défaut décalé de 3px fait un halo disgracieux
   autour du bouton primary quand il reçoit le focus initial (btnConfirm.focus()). */
.editor-confirm-modal__actions .btn:focus,
.editor-confirm-modal__actions .btn:focus-visible {
    outline: none;
}
/* ── Overlay hover sur tableau ─────────────────────────────────────
   Matérialise le tableau survolé par une bordure couleur client + 2
   flèches (au-dessus/en-dessous) qui insèrent un paragraphe avant/après
   le tableau. `pointer-events: none` sur l'overlay lui-même pour ne
   PAS bloquer les clics vers les cellules ; les 2 flèches réactivent
   `pointer-events: auto` pour rester interactives. */
/* Bordure de survol autour du tableau — filet noir léger. Passe en
   client + épaisseur double quand le tableau est SÉLECTIONNÉ (via la
   case à cocher en haut-gauche → NodeSelection PM). */
.pm-table-hover-overlay {
    position: absolute;
    /* Doit passer SOUS la toolbar éditeur sticky (z-index: 5) quand le
       tableau scrolle jusqu'à croiser la toolbar — sinon l'outline et
       les flèches d'insertion débordent visuellement par-dessus les
       boutons. Reste au-dessus du flow normal du document (aucun
       z-index concurrent < 3 côté contenu). La toolbar flottante
       tableau (z-index: 101) continue de passer par-dessus, elle. */
    z-index: 3;
    pointer-events: none;             /* laisse passer les clics aux cellules */
    outline: 1px solid var(--c-text, #111);
    outline-offset: 2px;
    border-radius: 2px;
    transition: outline-color var(--tr-base), outline-width var(--tr-base);
}
.pm-table-hover-overlay[hidden] { display: none; }
/* Tableau sélectionné (NodeSelection via la case à cocher) : signal
   fort — filet COULEUR CLIENT 2 px pour se distinguer clairement du
   simple survol (filet noir 1 px). Cohérent avec les autres states
   actifs de la UI (client comme signal principal). */
.pm-table-hover-overlay.is-selected {
    outline: 2px solid var(--c-client);
}

/* Case à cocher en haut-gauche : coche = NodeSelection sur la table.
   Style Word / CKEditor : petit carré blanc bordé noir → carré client
   avec ✓ blanc quand coché. */
.pm-table-hover-overlay__select {
    position: absolute;
    top: -12px;
    left: -12px;
    pointer-events: auto;
    width: 20px;
    height: 20px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    appearance: none;
    -webkit-appearance: none;
    background: #fff;
    color: transparent;             /* ✓ invisible tant que non coché */
    border: 1.5px solid var(--c-text, #111);
    border-radius: 3px;
    cursor: pointer;
    box-shadow: 0 1px 4px rgba(0,0,0,0.12);
    transition: background var(--tr-base), border-color var(--tr-base), color var(--tr-base);
}
.pm-table-hover-overlay__select::before {
    /* Coche ✓ SVG-like via caractère — apparaît quand .is-checked. */
    content: '✓';
    font-size: 14px;
    line-height: 1;
    font-weight: 650;
}
.pm-table-hover-overlay__select:hover {
    border-color: var(--c-client);
}
/* Checkbox cochée quand l'overlay parent est en `.is-selected`
   (NodeSelection sur le tableau). Même couleur client que la bordure —
   un seul état source (`.is-selected` sur l'overlay), pas de double-tracking. */
.pm-table-hover-overlay.is-selected .pm-table-hover-overlay__select {
    background: var(--c-client);
    border-color: var(--c-client);
    color: #fff;
}

/* Boutons ronds — même charte que .editor-chapter-title__handle :
   28px blanc / bordure noire / icône noire au repos, bg client + icône
   blanche au hover. Boîte pointer-events: auto pour rester cliquable
   pendant que l'overlay parent est en pointer-events: none. */
.pm-table-hover-overlay__arrow {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    pointer-events: auto;
    width: 28px;
    height: 28px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    appearance: none;
    -webkit-appearance: none;
    background: #fff;
    color: var(--c-text, #111);
    border: 1px solid var(--c-text, #111);
    border-radius: 50%;
    cursor: pointer;
    box-shadow: 0 1px 4px rgba(0,0,0,0.12);
    transition: background var(--tr-base), color var(--tr-base), border-color var(--tr-base);
}
.pm-table-hover-overlay__arrow:hover {
    background: var(--c-client);
    color: #fff;
    border-color: var(--c-client);
}
.pm-table-hover-overlay__arrow--above { top:    -14px; }
.pm-table-hover-overlay__arrow--below { bottom: -14px; }
.pm-table-hover-overlay__arrow .icon-mask { width: 14px; height: 14px; }
/* Disabled : voisin déjà paragraphe vide → inutile d'ajouter un autre.
   Cachée entièrement (comme les handles du chapter_title). */
.pm-table-hover-overlay__arrow:disabled { display: none; }

/* ── Floating toolbar tableau — quand curseur dans une cellule ────
   Mêmes règles visuelles que la toolbar image (32×32, hover 1px inset,
   is-active client). */
.editor-table-toolbar {
    position: absolute;
    z-index: 101;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--sp-2);
    max-width: calc(100vw - 32px);
    background: var(--c-bg-subtle); /* même gris clair que la toolbar principale */
    border: 1px solid var(--c-text);
    border-radius: var(--radius-md);
    box-shadow: 0 8px 24px rgba(0,0,0,0.12);
    padding: var(--sp-1) var(--sp-2);
    user-select: none;
}
.editor-table-toolbar[hidden] { display: none; }
.editor-table-toolbar__group {
    display: flex;
    align-items: center;
    gap: 2px;
}
.editor-table-toolbar__group + .editor-table-toolbar__group {
    border-left: 1px solid var(--c-border);
    padding-left: var(--sp-2);
}
.editor-table-toolbar__btn {
    appearance: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    padding: 0;
    background: transparent;
    border: 0;
    border-radius: var(--radius-sm);
    color: var(--c-text);
    cursor: pointer;
    font: inherit;
    font-size: 11px;
    line-height: 1;
    transition: background var(--tr-base), box-shadow var(--tr-base), color var(--tr-base);
}
.editor-table-toolbar__btn:hover {
    background: color-mix(in srgb, var(--c-text) 5%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--c-text) 8%, transparent);
}
.editor-table-toolbar__btn.is-active {
    background: color-mix(in srgb, var(--c-client) 5%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--c-client) 60%, transparent);
    color: var(--c-client);
}
.editor-table-toolbar__btn.is-active:hover {
    background: color-mix(in srgb, var(--c-client) 10%, transparent);
}
/* Bouton disabled — sert notamment aux boutons merge/split qui ne
   sont pas toujours applicables (ex: pas de multi-sélection = pas de
   fusion possible). */
.editor-table-toolbar__btn:disabled {
    color: var(--c-text-muted);
    opacity: 0.4;
    cursor: default;
    pointer-events: none;
}
.editor-table-toolbar__btn--danger { color: #c4302b; }
.editor-table-toolbar__btn--danger:hover {
    background: rgba(196, 48, 43, 0.08);
    box-shadow: inset 0 0 0 1px rgba(196, 48, 43, 0.3);
}
/* Icônes SVG dans les boutons — 18×18 (charte toolbars éditeur). */
.editor-table-toolbar__btn .icon-mask {
    width: 18px;
    height: 18px;
}

/* Option « supprimer » d'un dropdown en rouge (delete row / delete col).
   Sélecteur doublé pour battre la specificité du `.editor-image-toolbar__option
   { color: var(--c-text) }` déclaré plus bas dans le fichier. */
.editor-image-toolbar__option.editor-image-toolbar__option--danger {
    color: #c4302b;
}
.editor-image-toolbar__option.editor-image-toolbar__option--danger:hover {
    background: rgba(196, 48, 43, 0.08);
    color: #c4302b;
}

/* ── Grid picker « insérer un tableau » (style Word) ───────────────
   Popover flottant ancré sous le bouton toolbar. Grille de cellules,
   hover pour choisir N×M, click pour insérer. */
.editor-table-picker {
    z-index: 300;
    background: var(--c-bg-elevated);
    border: 1px solid var(--c-text);
    border-radius: var(--radius-md);
    box-shadow: 0 8px 24px rgba(0,0,0,0.12);
    padding: var(--sp-2);
    user-select: none;
}
.editor-table-picker__grid {
    display: grid;
    grid-template-columns: repeat(var(--picker-cols, 10), 20px);
    gap: 2px;
}
.editor-table-picker__cell {
    width: 20px;
    height: 20px;
    background: rgba(0, 0, 0, 0.06);          /* gris très clair au repos */
    border: 1px solid rgba(0, 0, 0, 0.10);
    box-sizing: border-box;
    border-radius: 2px;
    cursor: pointer;
    transition: background var(--tr-base), border-color var(--tr-base);
}
/* Cellule dans la sélection en cours (calcul « toutes celles jusqu'à
   la cellule survolée ») → fond client atténué + bordure client pleine.
   Cohérent avec les autres states is-active du reste de la UI. */
.editor-table-picker__cell.is-active {
    background: color-mix(in srgb, var(--c-client) 20%, transparent);
    border-color: var(--c-client);
}
.editor-table-picker__label {
    margin-top: var(--sp-2);
    text-align: center;
    font-size: var(--fs-xs);
    color: var(--c-text-muted);
}

/* ── Modale dimensions image — étend la confirm-modal ──────────────
   2 champs (W / H) avec un bouton cadenas central. Padlock locked =
   les 2 dimensions sont liées par le ratio natif de l'image. */
.editor-image-dimensions-modal__card { max-width: 360px; }
.editor-image-dimensions-modal__fields {
    display: flex;
    align-items: flex-end;
    gap: var(--sp-2);
}
.editor-image-dimensions-modal__field {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
}
.editor-image-dimensions-modal__field-label {
    font-size: var(--fs-xs);
    color: var(--c-text-muted);
}
.editor-image-dimensions-modal__field-input {
    width: 100%;
    padding: 6px 8px;
    border: 1px solid var(--c-border);
    border-radius: var(--radius-sm);
    font: inherit;
    font-size: var(--fs-sm);
    background: var(--c-bg);
    color: var(--c-text);
}
.editor-image-dimensions-modal__field-input:focus {
    outline: none;
    border-color: var(--c-client);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--c-client) 25%, transparent);
}
/* Cadenas purement décoratif : indique que largeur/hauteur sont liées
   par le ratio natif. Pas interactif. */
.editor-image-dimensions-modal__lock {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    margin-bottom: 1px;       /* aligne sur la baseline des inputs */
    color: var(--c-text-muted);
}
.editor-image-dimensions-modal__lock .icon-mask {
    width: 16px;
    height: 16px;
}

.editor-confirm-modal__confirm--danger {
    background: var(--c-error-fg);
    color: var(--c-text-on-dark);
    border: 1px solid var(--c-error-fg);
}
.editor-confirm-modal__confirm--danger:hover {
    background: color-mix(in srgb, var(--c-error-fg) 85%, #000);
    border-color: color-mix(in srgb, var(--c-error-fg) 85%, #000);
}

/* ── ContextMenu : panel toujours visible ancré au bloc courant ─────────
 *  Pas de trigger : le panel apparaît dès que le curseur entre dans un
 *  bloc éligible. Position calculée en JS : bord DROIT du panel = 10 px
 *  à gauche du bord gauche de .ProseMirror (centrée max-width 1100).
 *  Position verticale via `transform: translateY` (GPU compositing) pour
 *  éviter le jitter Chrome quand la toolbar sticky se réaligne au scroll.
 *  Z-index 100 → au-dessus de tout sauf modales (z-index 200). */
.editor-contextmenu {
    position: absolute;
    top: 0;                          /* ancre, le transform fait le décalage Y */
    z-index: 100;
    min-width: 220px;
    background: var(--c-bg-elevated);
    border: 1px solid var(--c-text);
    border-radius: var(--radius-md);
    box-shadow: 0 8px 24px rgba(0,0,0,0.12);
    padding: var(--sp-2);
    will-change: transform;
}

/* ─── Floating toolbar : image sélectionnée ──────────────────────────
   Apparaît au-dessus (ou en-dessous si pas la place) de l'image
   sélectionnée. Position calculée par floating-toolbar.js et écrite en
   style inline `top` / `left` relatifs à `.editor-shell`. */
.editor-image-toolbar {
    position: absolute;
    z-index: 101;                   /* au-dessus du ContextMenu */
    display: flex;
    align-items: center;
    flex-wrap: wrap;                /* repli sur 2 lignes si trop large */
    gap: var(--sp-2);
    max-width: calc(100vw - 32px);
    background: var(--c-bg-subtle); /* même gris clair que la toolbar principale */
    border: 1px solid var(--c-text);
    border-radius: var(--radius-md);
    box-shadow: 0 8px 24px rgba(0,0,0,0.12);
    padding: var(--sp-1) var(--sp-2);
    user-select: none;
}
.editor-image-toolbar__group[hidden] { display: none; }
.editor-image-toolbar[hidden] { display: none; }
.editor-image-toolbar__group {
    display: flex;
    align-items: center;
    gap: 2px;
}
.editor-image-toolbar__group + .editor-image-toolbar__group {
    /* Filet séparateur entre groupes (align / largeur / suppr). */
    border-left: 1px solid var(--c-border);
    padding-left: var(--sp-2);
}
/* Boutons identiques à la toolbar éditeur (charte unifiée) :
   carré 32×32, transparent au repos, légère bordure inset + bg au
   hover, et en is-active : bg client teinté + bordure client + couleur
   icône passée en --c-client (l'icon-mask hérite via background-color =
   currentColor). */
.editor-image-toolbar__btn {
    appearance: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    padding: 0;
    background: transparent;
    border: 0;
    border-radius: var(--radius-sm);
    color: var(--c-text);
    cursor: pointer;
    font: inherit;
    font-size: var(--fs-xs);
    line-height: 1.1;
    gap: 4px;
    transition: background var(--tr-base), box-shadow var(--tr-base), color var(--tr-base);
}
.editor-image-toolbar__btn:hover:not(:disabled) {
    background: color-mix(in srgb, var(--c-text) 5%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--c-text) 8%, transparent);
}
.editor-image-toolbar__btn.is-active {
    background: color-mix(in srgb, var(--c-client) 5%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--c-client) 60%, transparent);
    color: var(--c-client);
}
.editor-image-toolbar__btn.is-active:hover {
    background: color-mix(in srgb, var(--c-client) 10%, transparent);
}
/* Dropdown largeur : on accepte une largeur > 32px pour pouvoir
   afficher « 100 % », « 320 px », etc. Square strict cassé ici. */
.editor-image-toolbar__btn--width {
    width: auto;
    min-width: 56px;
    padding: 0 8px;
}
.editor-image-toolbar__btn--delete {
    color: #c4302b;
}
.editor-image-toolbar__btn--delete:hover {
    background: rgba(196, 48, 43, 0.08);
    box-shadow: inset 0 0 0 1px rgba(196, 48, 43, 0.3);
}
/* Icônes (masquées via icon-mask). */
.editor-image-toolbar__btn .icon-mask {
    width: 18px;
    height: 18px;
}

/* ── Dropdown « style image » (CKEditor-style) ────────────────────── */
.editor-image-toolbar__group--dropdown {
    position: relative;       /* ancre du popover */
}
.editor-image-toolbar__btn--dropdown {
    gap: 2px;
    /* Boutons dropdown = un poil plus larges qu'un carré 32×32 : il faut
       accueillir l'icône principale + le caret sans les tasser. */
    width: auto;
    min-width: 44px;
    padding: 0 6px;
}
/* Légère respiration entre l'icône principale et le caret du dropdown. */
.editor-image-toolbar__btn--dropdown .editor-image-toolbar__caret {
    margin-left: 4px;
}
/* Caret du dropdown — même taille que dans la toolbar éditeur (8×8).
   Pas d'opacité custom : le caret utilise le même `currentColor` que
   l'icône principale (icon-mask), donc suit exactement les mêmes
   états inactive / is-active / disabled que le reste du bouton. */
.editor-image-toolbar__caret.icon-mask {
    width: 8px;
    height: 8px;
}
.editor-image-toolbar__popover {
    position: absolute;
    top: 100%;
    left: 0;
    margin-top: 4px;
    min-width: 220px;
    background: var(--c-bg-elevated);
    border: 1px solid var(--c-text);
    border-radius: var(--radius-md);
    box-shadow: 0 8px 24px rgba(0,0,0,0.12);
    padding: 4px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    z-index: 102;             /* au-dessus du panel parent */
    white-space: nowrap;      /* labels sur 1 ligne (les modes long
                                 comme « insérer une colonne à droite »
                                 doivent tenir sans se casser en 2 lignes) */
}
.editor-image-toolbar__popover .editor-image-toolbar__option-label {
    white-space: nowrap;
}
.editor-image-toolbar__popover[hidden] { display: none; }
.editor-image-toolbar__option {
    appearance: none;
    background: transparent;
    border: 0;
    text-align: left;
    padding: 6px 10px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    color: var(--c-text);
    font: inherit;
    font-size: var(--fs-xs);
    display: flex;
    align-items: center;
    gap: 8px;
    transition: background var(--tr-base);
}
.editor-image-toolbar__option:hover {
    background: color-mix(in srgb, var(--c-text) 6%, transparent);
}
.editor-image-toolbar__option.is-active {
    background: color-mix(in srgb, var(--c-client) 18%, transparent);
    font-weight: 600;
}
.editor-image-toolbar__option .icon-mask {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
}
.editor-image-toolbar__option-label {
    flex: 1;
    min-width: 0;
}
/* (Réservé) Masque le ContextMenu quand un commentaire est actif —
   la classe `body.has-active-comment` n'est plus posée par le JS
   actuel (les états is-active/is-resolved sont gérés par des PM
   Decorations sur la mark elle-même, pas par un toggle global).
   Garde la règle si jamais on remet un toggle global plus tard. */
body.has-active-comment .editor-contextmenu {
    display: none !important;
}
/* Filet séparateur entre groupes visibles. On utilise `~` (general
 * sibling) plutôt que `+` (adjacent) pour SAUTER les groupes masqués :
 * ex. blocktype VISIBLE, blockstyles HIDDEN, heading-numbering VISIBLE
 * → heading-numbering reçoit bien le filet en se basant sur blocktype
 * comme « précédent visible », même si blockstyles est entre les deux. */
.editor-contextmenu__group:not([hidden]) ~ .editor-contextmenu__group:not([hidden]) {
    margin-top: var(--sp-2);
    padding-top: var(--sp-2);
    border-top: 1px solid var(--c-border);
}

/* CellSelection multi-lignes/cellules : le menu reste visible mais on
   masque tous les groupes qui n'ont pas de sens pour un range de cellules
   (styles de bloc, type de bloc, numérotation heading, styles de caractère).
   Seuls rowStyle et trameColor restent utilisables. */
.editor-contextmenu--cell-selection .editor-contextmenu__group--blocktype,
.editor-contextmenu--cell-selection .editor-contextmenu__group--styles,
.editor-contextmenu--cell-selection .editor-contextmenu__group--heading-num,
.editor-contextmenu--cell-selection .editor-contextmenu__group--charstyles {
    display: none;
}
/* En mode CellSelection, rowStyle devient le PREMIER groupe visible :
   pas de filet séparateur au-dessus (les groupes masqués ci-dessus
   sont en display:none donc invisibles, mais la règle générique
   `:not([hidden]) ~ :not([hidden])` ne fait pas la différence entre
   [hidden] et display:none — on force explicitement le reset ici).
   Sélecteur DOUBLÉ (chaque classe apparaît deux fois) pour matcher la
   spécificité de la règle générique (0,4,0 avec les deux `:not([hidden])`)
   et gagner via l'ordre de déclaration. */
.editor-contextmenu.editor-contextmenu--cell-selection
    .editor-contextmenu__group.editor-contextmenu__group--row-style {
    margin-top: 0;
    padding-top: 0;
    border-top: 0;
}
.editor-contextmenu__group-title {
    font-size: 0.7rem;
    font-weight: 650;
    text-transform: none;          /* bas de casse comme tout le reste UI */
    color: color-mix(in srgb, var(--c-text-muted) 65%, transparent);
    padding: 0 var(--sp-2) var(--sp-1);
}
.editor-contextmenu__item {
    display: block;
    width: 100%;
    text-align: left;
    background: transparent;
    border: 0;
    padding: var(--sp-2) var(--sp-3);
    border-radius: var(--radius-sm);
    font-size: var(--fs-sm);
    color: var(--c-text);
    cursor: pointer;
    transition: background var(--tr-base), color var(--tr-base);
}
.editor-contextmenu__item:hover:not(:disabled) {
    background: color-mix(in srgb, var(--c-text) 5%, transparent);
}
.editor-contextmenu__item.is-active {
    background: color-mix(in srgb, var(--c-client) 8%, transparent);
    color: var(--c-client);
    font-weight: 600;
}
.editor-contextmenu__item:disabled {
    color: var(--c-text-muted);
    cursor: default;
    opacity: 0.5;
}

/* ── Block type select : dropdown stylé (trigger + liste) ──────────── */
.editor-blocktype-select { position: relative; }
.editor-blocktype-select__trigger {
    list-style: none;
    cursor: pointer;
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--sp-2);
    padding: var(--sp-2) var(--sp-3);
    background: transparent;
    color: var(--c-text);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-sm);
    font-size: 0.95rem;
    font-weight: 500;
    transition: background var(--tr-base), border-color var(--tr-base);
}
.editor-blocktype-select__trigger::-webkit-details-marker { display: none; }
.editor-blocktype-select__trigger:hover {
    background: color-mix(in srgb, var(--c-text) 5%, transparent);
    border-color: color-mix(in srgb, var(--c-text) 25%, transparent);
}
.editor-blocktype-select[open] .editor-blocktype-select__trigger {
    border-color: color-mix(in srgb, var(--c-text) 40%, transparent);
}
.editor-blocktype-select__current {
    flex: 1;
    text-align: left;
    font-weight: 600;          /* type courant un peu plus marqué */
}
.editor-blocktype-select__caret {
    color: var(--c-text-muted);
    width: 10px; height: 10px;
    flex-shrink: 0;
}
.editor-blocktype-select__list {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    min-width: 100%;
    padding: var(--sp-1);
    background: var(--c-bg-elevated);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-md);
    box-shadow: 0 8px 24px rgba(0,0,0,0.12);
    z-index: 1;
}

/* Sections « style de paragraphe » / « style de titre » — listing vertical
 * d'options sans preview (l'application est invisible au repos puisque le
 * style par défaut ne pose pas de classe). */
.editor-contextmenu__styles-list {
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.editor-contextmenu__item--style {
    font-size: var(--fs-sm);
}

/* ── Palette « couleur de trame » (contextmenu) ────────────────────
   6 swatches carrés côte-à-côte : 5 grays du clair au foncé + 1
   couleur client. Le swatch actif porte un anneau couleur client. */
.editor-contextmenu__trame-swatches {
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 2px 0;
}
.editor-contextmenu__trame-swatch {
    appearance: none;
    width: 22px;
    height: 22px;
    padding: 0;
    border: 1px solid rgba(0, 0, 0, 0.08);
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: transform var(--tr-base), box-shadow var(--tr-base);
}
.editor-contextmenu__trame-swatch:hover {
    transform: scale(1.08);
}
.editor-contextmenu__trame-swatch.is-active {
    box-shadow: 0 0 0 2px var(--c-client);
    border-color: transparent;
}
/* Palette prédéfinie — cohérente avec les classes CSS `.is-trame--<X>`
   du rendu tableau. Les 5 premiers sont de vrais gris du très clair au
   très foncé (le premier = défaut). Les 2 derniers sont client-clair
   et client à 100 %. */
.editor-contextmenu__trame-swatch--gray-1       { background: #f2f2f2; }
.editor-contextmenu__trame-swatch--gray-2       { background: #d9d9d9; }
.editor-contextmenu__trame-swatch--gray-3       { background: #a6a6a6; }
.editor-contextmenu__trame-swatch--gray-4       { background: #595959; }
.editor-contextmenu__trame-swatch--gray-5       { background: #262626; }
.editor-contextmenu__trame-swatch--client-light { background: color-mix(in srgb, var(--c-client, #666) 22%, transparent); }
.editor-contextmenu__trame-swatch--client       { background: var(--c-client, #666); }
.editor-contextmenu__item--restart {
    margin-top: 4px;
    padding-top: var(--sp-2);
    border-top: 1px solid var(--c-border);
    color: var(--c-text-muted);
}
.editor-contextmenu__item--restart.is-active {
    color: var(--c-client);
    font-weight: 600;
}

/* Items « bloc » : preview stylé qui ressemble à la cible (Notion-like).
 * Chaque preview prend la couleur client + une taille progressive selon
 * le niveau heading. */
.editor-contextmenu__item--block {
    padding: var(--sp-2) var(--sp-3);
    line-height: 1.2;
}
.editor-contextmenu__preview {
    display: inline-block;
    color: inherit;
}
.editor-contextmenu__preview--paragraph {
    font-size: 0.9rem;
    font-weight: 400;
    color: var(--c-text);
}
.editor-contextmenu__preview--h1 {
    font-size: 1.4rem;
    font-weight: 800;
    color: var(--c-client);
}
.editor-contextmenu__preview--h2 {
    font-size: 1.2rem;
    font-weight: 800;
    color: var(--c-client);
}
.editor-contextmenu__preview--h3 {
    font-size: 1.05rem;
    font-weight: 650;
    color: var(--c-client);
}
.editor-contextmenu__preview--h4 {
    font-size: 0.95rem;
    font-weight: 650;
    color: var(--c-client);
}
.editor-contextmenu__preview--h5 {
    font-size: 0.9rem;
    font-weight: 650;
    color: var(--c-client);
}
.editor-contextmenu__preview--h6 {
    font-size: 0.85rem;
    font-weight: 650;
    color: var(--c-client);
    font-style: italic;
}

/* Previews « Note X » — mêmes proportions que les titres réguliers mais
 * décalées d'un cran vers le bas (Note 1 ≈ h2, Note 2 ≈ h3, …) et poids
 * légèrement inférieur pour rester lisiblement distincts. Le rendu doit
 * matcher visuellement le style appliqué dans l'éditeur. */
.editor-contextmenu__preview--note1 {
    font-size: 1.2rem;
    font-weight: 700;
    color: var(--c-client);
}
.editor-contextmenu__preview--note2 {
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--c-client);
}
.editor-contextmenu__preview--note3 {
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--c-client);
}
.editor-contextmenu__preview--note4 {
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--c-client);
}
.editor-contextmenu__preview--note5 {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--c-client);
    font-style: italic;
}

/* ════════════════════════════════════════════════════════════════════════
 *  CLIENT LIST — page de sélection de client (super_admin sans tenant)
 *  Listing simple : logo (ou dot couleur) + nom + flèche à droite.
 *  Chaque ligne est un bouton form qui POST /clients/switch.
 * ════════════════════════════════════════════════════════════════════════ */
.client-list {
    list-style: none;
    margin: 0; padding: 0;
    display: flex; flex-direction: column;
    gap: var(--sp-1);
}
.client-list li form { margin: 0; }
.client-list__item {
    width: 100%;
    display: flex; align-items: center;
    gap: var(--sp-4);
    padding: var(--sp-3) var(--sp-4);
    background: var(--c-bg-elevated);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-md);
    cursor: pointer;
    text-align: left;
    font: inherit; color: inherit;
    transition: background var(--tr-base), border-color var(--tr-base),
                transform var(--tr-fast);
}
.client-list__item:hover {
    background: var(--c-bg-subtle);
    border-color: var(--c-client);
}
.client-list__item:focus-visible {
    outline: 2px solid var(--c-client);
    outline-offset: 2px;
}
.client-list__logo {
    width: 40px; height: 40px;
    display: flex; align-items: center; justify-content: center;
    flex-shrink: 0;
}
.client-list__logo img {
    max-width: 40px; max-height: 32px;
    object-fit: contain;
}
.client-list__name {
    flex: 1;
    font-size: var(--fs-md);
    font-weight: 600;
}
.client-list__arrow {
    color: var(--c-text-muted);
    transition: color var(--tr-base), transform var(--tr-base);
    flex-shrink: 0;
}
.client-list__item:hover .client-list__arrow {
    color: var(--c-client);
    transform: translateX(3px);
}

/* ════════════════════════════════════════════════════════════════════════
 *  ADMIN — pages CRUD (clients, utilisateurs, super-admins)
 * ════════════════════════════════════════════════════════════════════════ */

/* Grille de cards adaptative (clients, utilisateurs…). */
.admin-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: var(--sp-4);
}

.admin-card {
    background: var(--c-bg-elevated);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-lg);
    padding: var(--sp-5);
    display: flex; flex-direction: column;
    gap: var(--sp-4);
}
.admin-card--archived {
    opacity: 0.6;
}
.admin-card__head {
    display: flex; align-items: center;
    gap: var(--sp-3);
}
.admin-card__id { flex: 1; min-width: 0; }
.admin-card__name {
    margin: 0;
    font-size: var(--fs-lg);
    font-weight: 650;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.admin-card__slug {
    margin: 0;
    font-size: var(--fs-xs);
    color: var(--c-text-faded);
    font-family: var(--font-mono);
}
.admin-card__logo {
    max-height: 36px;
    max-width: 72px;
    object-fit: contain;
}
.admin-card__meta {
    display: flex; gap: var(--sp-5);
    margin: 0;
}
.admin-card__meta > div {
    display: flex; flex-direction: column;
}
.admin-card__meta dt {
    font-size: var(--fs-xs);
    color: var(--c-text-muted);
}
.admin-card__meta dd {
    margin: 0;
    font-size: var(--fs-lg);
    font-weight: 650;
}
.admin-card__actions {
    display: flex; gap: var(--sp-4);
    flex-wrap: wrap;
    align-items: center;
    padding-top: var(--sp-3);
    border-top: 1px solid var(--c-border);
}
.admin-card__inline-form { display: inline-block; }

/* Pastille de couleur (réutilisée dans le sélecteur de client header). */
.client-dot {
    width: 14px; height: 14px;
    border-radius: 50%;
    display: inline-block;
    flex-shrink: 0;
    border: 1px solid rgba(0,0,0,.08);
}

/* Boutons d'action discrets (variantes de .link--button). */
.link--muted  { color: var(--c-text-faded); }
.link--danger { color: var(--c-error-fg); }
.link--danger:hover { color: var(--c-accent); }

/* ── Color picker custom (input hex + swatch cliquable + popover HSV) ── */
.color-picker {
    position: relative;
    display: inline-flex; flex-direction: column;
    gap: var(--sp-3);
}
/* Le swatch est positionné DANS l'input à gauche (carré, absolument).
   L'input réserve la place via padding-left. */
.color-picker__field {
    position: relative;
    display: inline-block;
    width: 160px;
}
.color-picker__swatch {
    position: absolute;
    top: 50%;
    /* Marge gauche égale à la marge verticale (input height − swatch ÷ 2),
       sinon le swatch « colle » au bord gauche. */
    left: 7px;
    transform: translateY(-50%);
    width: 24px; height: 24px;
    border-radius: var(--radius-sm);
    /* Bordure noire en opacité réduite : visible sur swatch clair,
       discrète sur swatch foncé. */
    border: 1px solid rgba(0, 0, 0, 0.2);
    cursor: pointer;
    padding: 0;
    transition: transform var(--tr-fast), border-color var(--tr-base);
    z-index: 1;
}
.color-picker__swatch:hover  { border-color: rgba(0, 0, 0, 0.45); }
.color-picker__swatch:focus-visible {
    outline: 2px solid var(--c-text);
    outline-offset: 2px;
}
.color-picker__input {
    width: 100%;
    padding-left: 40px;                /* place pour le swatch (24 + 6 + ~10) */
    font-family: var(--font-mono);
    text-transform: uppercase;
}

/* Popover du picker : flotte sous le swatch, masquable via [hidden]. */
.color-picker__popover {
    position: absolute;
    top: calc(100% + var(--sp-2));
    left: 0;
    z-index: 50;
    width: 220px;
    padding: var(--sp-3);
    background: var(--c-bg-elevated);
    border: 1px solid var(--c-border-strong);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    display: flex; flex-direction: column;
    gap: var(--sp-3);
    user-select: none;
}
/* Le `display: flex` ci-dessus écrase l'attribut HTML [hidden] qui
   pose display:none avec une spécificité plus faible. On force le hide
   quand l'attribut est présent. */
.color-picker__popover[hidden] { display: none; }
/* Carré saturation/value */
.color-picker__sv {
    position: relative;
    height: 160px;
    border-radius: var(--radius-sm);
    cursor: crosshair;
    /* background appliqué par JS (gradient noir + gradient teinte) */
    background: linear-gradient(to top, #000, transparent),
                linear-gradient(to right, #fff, #ff0000);
}
.color-picker__sv-thumb {
    position: absolute;
    width: 14px; height: 14px;
    border-radius: 50%;
    border: 2px solid #fff;
    box-shadow: 0 0 0 1px rgba(0,0,0,.4);
    transform: translate(-50%, -50%);
    pointer-events: none;
    background: #ff0000;
}
/* Slider de teinte */
.color-picker__hue {
    position: relative;
    height: 14px;
    border-radius: 999px;
    cursor: ew-resize;
    background: linear-gradient(to right,
        #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%,
        #0000ff 67%, #ff00ff 83%, #ff0000 100%);
}
.color-picker__hue-thumb {
    position: absolute;
    top: 50%;
    width: 14px; height: 14px;
    border-radius: 50%;
    border: 2px solid #fff;
    box-shadow: 0 0 0 1px rgba(0,0,0,.4);
    transform: translate(-50%, -50%);
    pointer-events: none;
    background: transparent;
}

/* Preview image dans les formulaires (logo, etc.). `align-self: start`
   pour ne pas se faire stretcher horizontalement par le flex column du
   `.field` parent (sinon object-fit:contain centrerait l'image dans
   une boîte étalée à 100%). */
.form__preview {
    display: block;
    align-self: flex-start;
    max-height: 6rem;
    width: auto;
    object-fit: contain;
    margin: 0 0 var(--sp-2);
}

/* ── Flash toast : fixé en haut, fade in/out sur 1.5s ────────────────── */
.flash-toast {
    position: fixed;
    top: calc(var(--header-height) + var(--sp-3));
    left: 50%;
    transform: translateX(-50%);
    z-index: 100;
    max-width: 90vw;
    padding: var(--sp-3) var(--sp-5);
    border-radius: 999px;
    box-shadow: var(--shadow-lg);
    font-size: var(--fs-sm);
    font-weight: 500;
    pointer-events: none;
    animation: flash-toast 2800ms ease forwards;
}
.flash-toast--success {
    background: var(--c-success-bg);
    color: var(--c-success-fg);
    border: 1px solid rgba(30,111,58,.25);
}
.flash-toast--error {
    background: var(--c-error-bg);
    color: var(--c-error-fg);
    border: 1px solid rgba(138,26,20,.25);
}
@keyframes flash-toast {
    0%   { opacity: 0; transform: translate(-50%, -10px); }
    12%  { opacity: 1; transform: translate(-50%, 0); }
    78%  { opacity: 1; transform: translate(-50%, 0); }
    100% { opacity: 0; transform: translate(-50%, -10px); visibility: hidden; }
}

/* Actions de form (boutons en ligne) */
.form-actions {
    display: flex;
    gap: var(--sp-3);
    align-items: center;
}

/* Erreur sous un champ */
.field__error {
    font-size: var(--fs-xs);
    color: var(--c-error-fg);
}

/* ════════════════════════════════════════════════════════════════════════
 *  DATA TABLE — tableau de listing admin (utilisateurs, etc.)
 * ════════════════════════════════════════════════════════════════════════ */
/* border-collapse: separate (au lieu de collapse) + overflow: visible :
   nécessaire pour que les tooltips des .icon-action de la 1re ligne
   ne se fassent pas clipper par le bord du tableau. Les coins arrondis
   sont appliqués cell-par-cell pour compenser. */
.data-table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    background: var(--c-bg-elevated);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-lg);
    font-size: var(--fs-sm);
}
.data-table th,
.data-table td {
    padding: var(--sp-3) var(--sp-4);
    text-align: left;
    vertical-align: middle;
    border-bottom: 1px solid var(--c-border);
}
.data-table thead th {
    background: var(--c-bg-subtle);
    font-weight: 600;
    color: var(--c-text-muted);
    font-size: var(--fs-xs);
    text-transform: lowercase;
}
/* Coins arrondis : sur les cellules d'angle (en-tête haut, dernière
   ligne bas) pour que le bg colorée respecte le radius du tableau. */
.data-table thead tr:first-child th:first-child {
    border-top-left-radius: calc(var(--radius-lg) - 1px);
}
.data-table thead tr:first-child th:last-child {
    border-top-right-radius: calc(var(--radius-lg) - 1px);
}
.data-table tbody tr:last-child td { border-bottom: 0; }
.data-table tbody tr:last-child td:first-child {
    border-bottom-left-radius: calc(var(--radius-lg) - 1px);
}
.data-table tbody tr:last-child td:last-child {
    border-bottom-right-radius: calc(var(--radius-lg) - 1px);
}
.data-table tbody tr:hover { background: var(--c-bg-subtle); }

.data-table__col--avatar   { width: 48px; padding-right: 0; }
/* Logo client / asset image en cellule d'avatar (carrée, max 38×38). */
.data-table__logo {
    display: block;
    max-width: 38px; max-height: 38px;
    object-fit: contain;
    margin: -6px 0;                    /* compense pour ne pas pousser la ligne */
}
/* Texte principal (nom) + texte secondaire (slug, etc.) dans la même cellule. */
.data-table__primary   { font-weight: 600; }
.data-table__secondary {
    font-size: var(--fs-xs);
    color: var(--c-text-faded);
    font-family: var(--font-mono);
}
/* Variante large du client-dot (utilisé en avatar de table). */
.client-dot--lg { width: 24px; height: 24px; }
/* Avatar visuellement agrandi via transform: scale (n'affecte pas le
   layout, donc la hauteur de ligne ne change pas et il reste centré
   verticalement comme l'avatar nominal). */
.data-table__col--avatar .avatar {
    transform: scale(1.36);
    transform-origin: center;
}
.data-table__col--mono     { font-family: var(--font-mono); font-size: 0.8125rem; }
/* Sélecteur double (.data-table td.X) pour battre `.data-table td`
   (specificité 0,1,1) qui imposait text-align: left. */
.data-table td.data-table__col--actions,
.data-table th.data-table__col--actions {
    text-align: right; white-space: nowrap;
}
.data-table__col--actions form { display: inline-block; }
.data-table__col--actions .link--button + .link--button,
.data-table__col--actions form + form,
.data-table__col--actions .link--button + form,
.data-table__col--actions form + .link--button {
    margin-left: var(--sp-3);
}
.data-table__col--actions .icon-action + .icon-action,
.data-table__col--actions form + .icon-action,
.data-table__col--actions .icon-action + form,
.data-table__col--actions form + form { margin-left: 2px; }

/* Bouton icône carré (pour les colonnes Actions très chargées).
   Tooltip custom couleur client via ::before+attr(aria-label) — pas de
   `title` natif (qui doublonnerait). Accessibilité OK via aria-label. */
.icon-action {
    position: relative;
    display: inline-flex;
    align-items: center; justify-content: center;
    width: 30px; height: 30px;
    border-radius: var(--radius-sm);
    border: 0;
    background: transparent;
    color: var(--c-text-muted);
    cursor: pointer;
    padding: 0;
    transition: background var(--tr-base), color var(--tr-base);
    vertical-align: middle;
    text-decoration: none;
}
.icon-action:hover {
    background: var(--c-bg-subtle);
    color: var(--c-text);
    text-decoration: none;
}
.icon-action svg { width: 16px; height: 16px; display: block; }
.icon-action .icon-mask { width: 16px; height: 16px; }
.icon-action--danger:hover {
    background: var(--c-error-bg);
    color: var(--c-error-fg);
}

/* ── Carte « utilisateurs » (admin/users/index) ───────────────────────
   Wrapper rond blanc + ombre douce dans l'esprit des cartes du dashboard.
   À l'intérieur : header titre + bouton, puis la data-table (sans border
   ni radius propre — la carte porte ces styles). */
.users-card {
    background: var(--c-bg-elevated);
    border: 1px solid var(--c-text);            /* filet noir cohérent avec .card--bordered */
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    /* PAS d'overflow:hidden ici — sinon le dropdown du gear sur la
       dernière ligne se ferait clipper. Les coins arrondis sont préservés
       en posant border-radius sur les cellules d'angle de la table (cf.
       règles ci-dessous), pas via un overflow du conteneur. */
    /* Air sous la dernière ligne pour que le filet du bas soit bien
       séparé du bord arrondi de la card. */
    padding: 0 var(--sp-2) var(--sp-5);
}
.users-card__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-4);
    padding: var(--sp-6) var(--sp-6) var(--sp-5);
}
.users-card__title {
    margin: 0;
    font-size: var(--fs-2xl);
    font-weight: 800;
    color: var(--c-text);
}
/* À l'intérieur d'une users-card, la data-table perd sa propre carte
   (la card englobante porte déjà le bg/border-radius/shadow). Les
   marges + filets sont retravaillés en interne pour matcher l'esprit
   du dashboard (séparateurs insetés, pas bord à bord). */
.users-card .data-table {
    background: transparent;
    border: 0;
}
.users-card .data-table thead th {
    background: transparent;
    color: var(--c-text-faded);
    cursor: pointer;
    user-select: none;
    transition: color var(--tr-base);
}
.users-card .data-table thead th:hover { color: var(--c-text-muted); }
.users-card .data-table thead th.is-sorted { color: var(--c-text); }

/* Padding cellules tbody : plus d'air vertical (sp-5) ET horizontal
   (sp-6) pour aérer le bloc et matcher les marges du header de la card. */
.users-card .data-table tbody td {
    padding: var(--sp-5) var(--sp-6);
}
/* Padding cellules thead : plus compact (sp-2 vertical) — la titière
   reste discrète, c'est seulement un repère de tri. */
.users-card .data-table thead th {
    padding: var(--sp-2) var(--sp-6);
}
/* Pas de filet sous la titière : la thead ne pose pas de border-bottom,
   seuls les filets entre lignes du tbody marquent la séparation. */
.users-card .data-table thead th { border-bottom: 0; }

/* Filets entre les lignes : INSETÉS (pas bord à bord). On retire le
   border-bottom natif des td et on dessine un trait via ::after sur
   chaque cellule. La 1re colonne décale son trait de sp-5 à gauche,
   la dernière colonne le décale de sp-5 à droite → résultat : ligne
   continue rentrée des 2 côtés.
   La DERNIÈRE ligne garde aussi son filet (pas display:none) pour
   matcher l'esprit « marche » sous chaque utilisateur ; un padding-
   bottom sur la card laisse de l'air entre ce dernier filet et le bord. */
.users-card .data-table tbody td { border-bottom: 0; position: relative; }
.users-card .data-table tbody td::after {
    content: '';
    position: absolute;
    left: 0; right: 0; bottom: 0;
    height: 1px;
    background: var(--c-border);
    pointer-events: none;
}
.users-card .data-table tbody td:first-child::after { left:  var(--sp-6); }
.users-card .data-table tbody td:last-child::after  { right: var(--sp-6); }

/* Hover : PAS de changement de fond — seul le filet du bas passe au noir
   pour marquer la ligne survolée. Approche minimaliste cohérente avec
   l'esprit du dashboard. */
.users-card .data-table tbody tr:hover { background: transparent; }
.users-card .data-table tbody tr:hover > td::after { background: var(--c-text); }

/* Avatar dans la cellule dédiée : très légèrement plus gros que dans
   les autres data-tables. Le transform ne pousse pas la ligne. Les
   initiales (pour les comptes sans photo) sont réduites pour ne pas
   paraître écrasées dans le cercle agrandi. */
.users-card .data-table__col--avatar .avatar {
    transform: scale(1.55);
    font-size: 0.625rem;            /* ~10px, plus discret que --fs-xs */
}

/* Caret de tri à côté du titre de colonne. Au repos = ▼ gris atténué.
   Quand la colonne est triée (is-sorted-asc / -desc), il devient noir
   et bascule en ▲ (asc) ou reste ▼ (desc), pour signaler la direction. */
.data-table__sort-caret {
    display: inline-block;
    margin-left: 4px;
    width: 0; height: 0;
    border-left: 4px solid transparent;
    border-right: 4px solid transparent;
    border-top: 5px solid var(--c-text-faded);
    vertical-align: middle;
    transform: translateY(-1px);
    transition: border-top-color var(--tr-base), transform var(--tr-base);
}
.data-table thead th.is-sorted-desc .data-table__sort-caret {
    border-top-color: var(--c-text);
}
.data-table thead th.is-sorted-asc  .data-table__sort-caret {
    border-top-color: var(--c-text);
    transform: translateY(-1px) rotate(180deg);
}

/* Ligne « invitation en attente » : grisée pour distinguer des comptes
   actifs ; le statut est remplacé par le délai d'expiration en couleur
   client (cohérent avec les autres accents UI du dashboard). */
.data-table__row--pending td:not(.data-table__col--actions):not(.data-table__invite-expires) {
    color: var(--c-text-faded);
    font-style: normal;
}
.data-table__row--pending:hover {
    background: transparent;
}
/* Cellules courantes d'une ligne d'invitation (sauf l'action et le
   message d'expiration) : grisées + non italique. */
.users-card .data-table__row--pending td:not(.data-table__col--actions):not(.data-table__invite-expires) {
    font-style: normal;
    color: var(--c-text-faded);
}
/* Cellule du message « expire dans X » : couleur client, poids normal,
   pas d'italique. Posée APRÈS la règle ci-dessus → gagne le cascade
   à spécificité équivalente, et bat le legacy mustard via 3 classes + élément. */
.users-card .data-table__row--pending td.data-table__invite-expires {
    color: var(--c-client);
    font-weight: 400;
    font-style: normal;
}

/* Menu actions (gear icon → dropdown). Le summary est carré, hover doux.
   Le panel est aligné à droite (dropdown__panel--right) avec largeur min
   plus grande pour accueillir les libellés longs. */
.user-actions {
    display: inline-block;
    position: relative;
}
.user-actions__trigger {
    display: inline-flex;
    align-items: center; justify-content: center;
    width: 30px; height: 30px;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--c-text-muted);
    cursor: pointer;
    transition: background var(--tr-base), color var(--tr-base);
    list-style: none;
    position: relative;
}
/* z-index supérieur au menu UNIQUEMENT sur le trigger du dropdown OUVERT,
   pour que la roue reste « épinglée » par-dessus son propre panel.
   Si on l'appliquait à tous les triggers, les roues des AUTRES lignes
   (closed) passeraient aussi au-dessus du menu actif → pollution visuelle. */
.user-actions[open] > .user-actions__trigger {
    z-index: 25;
}
.user-actions__trigger::-webkit-details-marker { display: none; }
.user-actions__trigger:hover {
    background: var(--c-bg-subtle);
    color: var(--c-text);
}
.user-actions__trigger .icon-mask { width: 16px; height: 16px; }
.user-actions__menu {
    min-width: 260px;
    /* Décalé de 3px haut/droite par rapport au trigger pour que la
       roue soit visuellement plus centrée dans le coin du menu (et que
       la pointe de la roue ne soit pas pile sur le bord noir).
       `top: -3px` + `right: -3px` → menu un poil plus haut & plus à droite. */
    top: -3px;
    right: -3px;
    border-color: var(--c-text);
    /* Padding interne. Le padding-right est plus grand (= largeur de
       la roue + une marge) pour que le texte d'un item ne puisse JAMAIS
       passer sous la roue. */
    padding: 0 calc(30px + var(--sp-1)) 0 var(--sp-2);
}
/* Force chaque <form> (impersonate, deactivate, delete) à occuper sa
   propre ligne pleine largeur. La règle générique
   `.data-table__col--actions form { display: inline-block }` plus haut
   dans la feuille gagne sinon en spécificité (0,1,1 vs 0,1,0 pour
   .dropdown__list), ce qui rend les boutons étroits et empile
   désactiver/supprimer sur la même ligne. */
.user-actions__menu form,
.user-actions__menu .dropdown__list {
    display: flex;
    flex-direction: column;
    width: 100%;
}
/* Chaque option occupe sa ligne, plus resserrée que dans .dropdown__item
   générique : padding réduit + font-size légèrement plus petite. Filet
   gris clair entre items, last-child sans filet (bord du menu).
   `font-style: normal` annule l'italique hérité de la ligne d'invitation
   en attente (legacy mustard rule). `margin: 0` + `box-sizing: border-box`
   évitent le décalage 1px que certains UA imposent aux <button>. */
.user-actions__menu .dropdown__item {
    justify-content: flex-start;
    text-align: left;
    border-radius: 0;
    padding: var(--sp-2) var(--sp-3);
    font-size: var(--fs-xs);
    border-bottom: 1px solid var(--c-border);
    margin: 0;
    box-sizing: border-box;
    font-style: normal;
}
/* Les <form class="dropdown__list"> wrappent les items submit — on doit
   donc cibler aussi le dernier item via le parent direct du menu. */
.user-actions__menu > :last-child > .dropdown__item,
.user-actions__menu > .dropdown__item:last-child {
    border-bottom: 0;
}
/* Coins arrondis sur le PREMIER et DERNIER item pour suivre la
   bordure du menu (sinon le hover/focus dépasse le radius du panel).
   On cible aussi bien un item direct enfant du menu qu'un item
   imbriqué dans un <form class="dropdown__list">. */
.user-actions__menu > :first-child > .dropdown__item,
.user-actions__menu > .dropdown__item:first-child {
    border-top-left-radius:  calc(var(--radius-lg) - 1px);
    border-top-right-radius: calc(var(--radius-lg) - 1px);
}
.user-actions__menu > :last-child > .dropdown__item,
.user-actions__menu > .dropdown__item:last-child {
    border-bottom-left-radius:  calc(var(--radius-lg) - 1px);
    border-bottom-right-radius: calc(var(--radius-lg) - 1px);
}

/* Tooltip flottante (au-dessus du bouton). Texte = aria-label. Couleur
   client pour matcher l'identité, avec une petite flèche pointant vers
   le bouton (::after). z-index élevé pour passer au-dessus des autres
   rangées de la table. */
.icon-action::before,
.icon-action::after {
    position: absolute;
    left: 50%;
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--tr-base);
    z-index: 30;
}
.icon-action::before {
    content: attr(aria-label);
    bottom: calc(100% + 5px);                /* 5px = pile au-dessus de la flèche */
    transform: translateX(-50%);
    background: var(--c-client);
    color: var(--c-text-on-dark);
    font-size: 0.6875rem;
    font-weight: 600;
    /* line-height + box-sizing explicites pour que la hauteur soit
       identique quel que soit le contenu (les accents 'é', 'à' etc.
       décalaient légèrement le rendu vs « modifier » sans accent). */
    line-height: 1.4;
    box-sizing: content-box;
    padding: 4px 8px;
    border-radius: var(--radius-sm);
    white-space: nowrap;
}
.icon-action::after {
    content: "";
    /* top: -5px → la flèche est dans le gap de 5px entre le bas du
       tooltip et le haut de l'icône. Sa pointe (5px sous son top) tombe
       pile sur le top de l'icône. Avec bottom:100% le triangle finissait
       caché DERRIÈRE le tooltip (même couleur → invisible). */
    top: -5px;
    transform: translateX(-50%);
    border: 5px solid transparent;
    border-top-color: var(--c-client);       /* triangle pointant vers le bas */
}
.icon-action:hover::before,
.icon-action:hover::after,
.icon-action:focus-visible::before,
.icon-action:focus-visible::after {
    opacity: 1;
}
/* Ligne globale en opacité 0.55 par défaut (autres tables admin). */
.data-table__row--archived { opacity: 0.55; }
/* Variante users-card : on ne baisse PAS l'opacité de la ligne entière
   (sinon le menu gear devient transparent et c'est bizarre au hover) :
   on grise le texte + le picto, et on isole l'opacité réduite sur le
   seul avatar pour signaler l'état désactivé. */
.users-card .data-table__row--archived { opacity: 1; }
.users-card .data-table__row--archived > td { color: var(--c-text-faded); }
.users-card .data-table__row--archived .user-actions__trigger {
    color: var(--c-text-faded);
}
.users-card .data-table__row--archived .data-table__col--avatar .avatar {
    opacity: 0.45;
}
/* Ligne d'invitation en attente : fond blanc classique, texte en jaune
   moutarde + italique pour signaler l'état « pending ». L'italique
   est annulé sur l'avatar (initiales) et le badge de rôle qui doivent
   rester droits, plus lisibles. */
.data-table__row--pending,
.data-table__row--pending td {
    color: #b87800;
    font-style: italic;
}
.data-table__row--pending .avatar,
.data-table__row--pending .role-badge { font-style: normal; }

/* Badge de rôle (pastille couleur héritée de tokens.css). */
.role-badge {
    display: inline-block;
    padding: 2px var(--sp-2);
    border-radius: 999px;
    font-size: var(--fs-xs);
    font-weight: 600;
}
.role-badge--admin    { background: var(--role-admin-bg);    color: var(--role-admin-fg); }
.role-badge--editor   { background: var(--role-editor-bg);   color: var(--role-editor-fg); }
.role-badge--reviewer { background: var(--role-reviewer-bg); color: var(--role-reviewer-fg); }
.role-badge--viewer   { background: var(--role-viewer-bg);   color: var(--role-viewer-fg); }

/* Pastille de statut (rond coloré). */
.status-dot {
    display: inline-block;
    width: 8px; height: 8px;
    border-radius: 50%;
    margin-right: 4px;
    vertical-align: middle;
}
.status-dot--ok  { background: var(--c-success-fg); }
.status-dot--off { background: var(--c-text-faded); }

/* ════════════════════════════════════════════════════════════════════════
 *  AVATAR CROP — modal de recadrage rond avec image qui déborde
 *  visible en transparence (best-practice UX avatar).
 *  Variables --crop-stage et --crop-diam injectées par JS.
 * ════════════════════════════════════════════════════════════════════════ */
.cropper {
    position: fixed; inset: 0;
    z-index: 1000;
    display: flex; align-items: center; justify-content: center;
    --crop-stage: 320px;
    --crop-diam:  120px;
}
.cropper[hidden] { display: none; }
.cropper__backdrop {
    position: absolute; inset: 0;
    background: rgba(255, 255, 255, 0.85);   /* whitewash uniformisé à 85% */
}
.cropper__card {
    position: relative;
    background: var(--c-bg-elevated);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    width: min(90vw, calc(var(--crop-stage) + var(--sp-6) * 2 + var(--sp-2)));
    padding: var(--sp-5) var(--sp-6) var(--sp-6);
    display: flex; flex-direction: column;
    gap: var(--sp-4);
}
.cropper__head { text-align: center; }
.cropper__title { margin: 0; font-size: var(--fs-lg); font-weight: 650; }

/* Stage = carré contenant l'image en entier. L'image peut déborder
   au-delà du cercle de crop : visible en transparence (sombre), pour
   que l'utilisateur voie ce qui est en dehors et puisse centrer. */
.cropper__stage {
    position: relative;
    width:  var(--crop-stage);
    height: var(--crop-stage);
    margin: 0 auto;
    background: var(--c-bg-subtle);
    overflow: hidden;
    border-radius: var(--radius-md);
    cursor: grab;
    user-select: none;
}
.cropper__stage:active { cursor: grabbing; }
.cropper__img {
    position: absolute;
    top: 50%; left: 50%;
    max-width: none;
    pointer-events: none;
    /* transform appliqué en JS (translate -50% -50% + drag + scale) */
}
/* Masque CLAIR sur toute la stage AVEC un trou circulaire au centre :
   l'image apparaît nette DANS le cercle et estompée AUTOUR. */
.cropper__mask {
    position: absolute; inset: 0;
    background: rgba(255, 255, 255, 0.85);   /* whitewash uniformisé à 85% */
    pointer-events: none;
    -webkit-mask: radial-gradient(circle calc(var(--crop-diam) / 2) at center,
                                  transparent 99%, #000 100%);
            mask: radial-gradient(circle calc(var(--crop-diam) / 2) at center,
                                  transparent 99%, #000 100%);
}
/* Cercle noir qui matérialise la zone de crop (visible sur fond clair). */
.cropper__circle {
    position: absolute;
    top: 50%; left: 50%;
    width:  var(--crop-diam);
    height: var(--crop-diam);
    border-radius: 50%;
    border: 1px solid var(--c-text);
    transform: translate(-50%, -50%);
    pointer-events: none;
}

.cropper__controls { padding: 0 var(--sp-2); }
.cropper__zoom { display: block; }
.cropper__zoom input[type="range"] {
    width: 100%;
    accent-color: var(--c-text);
}

.cropper__actions {
    display: flex; gap: var(--sp-3);
    justify-content: flex-end;
}

/* ════════════════════════════════════════════════════════════════════════
 *  PASSWORD STRENGTH METER — barre 4 niveaux (rouge → vert)
 * ════════════════════════════════════════════════════════════════════════ */
.pwd-meter {
    display: flex; align-items: center;
    gap: var(--sp-2);
    margin-top: 4px;
    font-size: var(--fs-xs);
    color: var(--c-text-muted);
}
.pwd-meter__track {
    flex: 1;
    height: 4px;
    background: var(--c-bg-subtle);
    border-radius: 999px;
    overflow: hidden;
}
.pwd-meter__fill {
    height: 100%;
    width: 0%;
    border-radius: inherit;
    background: var(--c-text-faded);
    transition: width var(--tr-base), background var(--tr-base);
}
.pwd-meter__label {
    font-weight: 600;
    min-width: 70px;
    text-align: right;
}
/* Vide → meter invisible (mais conserve l'espace pour pas faire bouger
   le layout au premier caractère). */
.pwd-meter--empty .pwd-meter__fill { background: transparent; }
.pwd-meter--empty .pwd-meter__label { color: transparent; }

.pwd-meter--too-short .pwd-meter__fill { background: #c43838; }
.pwd-meter--too-short .pwd-meter__label { color: #c43838; }
.pwd-meter--poor      .pwd-meter__fill { background: #e02828; }
.pwd-meter--poor      .pwd-meter__label { color: #e02828; }
.pwd-meter--weak      .pwd-meter__fill { background: #f08a1e; }
.pwd-meter--weak      .pwd-meter__label { color: #b56a14; }
.pwd-meter--good      .pwd-meter__fill { background: #e0b800; }
.pwd-meter--good      .pwd-meter__label { color: #876c00; }
.pwd-meter--excellent .pwd-meter__fill { background: #1ea356; }
.pwd-meter--excellent .pwd-meter__label { color: #1ea356; }

/* ── Misc utilities ──────────────────────────────────────────────────── */
.muted        { color: var(--c-text-muted); }
.text-center  { text-align: center; }
.text-sm      { font-size: var(--fs-sm); }
.text-xs      { font-size: var(--fs-xs); }

/* ── Sommaire (TOC) ─────────────────────────────────────────────────────
   Liste hiérarchique des headings du doc, montée dans l'aside gauche
   panel "outline". Indent visuel par niveau. Highlight de l'entrée
   correspondant au heading visible en haut du viewport. */
.editor-outline {
    padding: var(--sp-3);
    overflow-y: auto;
    height: 100%;
    box-sizing: border-box;
}
.editor-outline[hidden] { display: none; }

.editor-outline__empty {
    margin: 0;
    padding: var(--sp-4);
    color: var(--c-text-muted);
    font-size: var(--fs-sm);
    text-align: center;
}

.editor-outline__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 1px;
}

.editor-outline__entry {
    display: flex;
    /* Pas d'indentation par niveau : la hiérarchie est portée par le
       préfixe numérique (1, 1.1, 1.1.2, …). Tous les items alignés à
       gauche → vue plus lisible et compacte dans la sidebar. */
}

.editor-outline__link {
    flex: 1;
    min-width: 0;
    background: transparent;
    border: 0;
    padding: 8px 8px;
    font-family: inherit;
    font-size: var(--fs-xs);         /* 13px — confortable à lire */
    line-height: 1.35;
    color: var(--c-text-muted);
    text-align: left;
    cursor: pointer;
    border-radius: var(--radius-sm);
    border-left: 2px solid transparent;
    display: flex;
    align-items: baseline;
    gap: 4px;
    overflow: hidden;
    transition: background var(--tr-base), color var(--tr-base), border-color var(--tr-base);
}
/* Préfixe hiérarchique (ex: 1.1, 2.3.1) à gauche du titre. Largeur min
   en PIXELS (pas en ch) pour que tous les textes — y compris celui du
   chapter_title (en font-size plus grande) — s'alignent sur la même
   tabulation. Tabular-nums pour que les chiffres aient la même largeur. */
.editor-outline__num {
    flex-shrink: 0;
    min-width: 52px;
    color: var(--c-text-muted);
    font-weight: 600;
}
.editor-outline__text {
    flex: 1;
    min-width: 0;
    /* Texte tronqué sur 2 lignes max (titre potentiellement long). */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.editor-outline__link:hover {
    color: var(--c-text);
    background: color-mix(in srgb, var(--c-text) 4%, transparent);
}
/* Chapter title : police plus marquée (c'est le titre principal). */
.editor-outline__entry--chapter .editor-outline__link {
    font-weight: 650;
    color: var(--c-text);
    font-size: var(--fs-sm);
}
/* Heading h1 (level 1) légèrement plus marqué que h2+. */
.editor-outline__entry--level-1:not(.editor-outline__entry--chapter) .editor-outline__link {
    font-weight: 600;
    color: var(--c-text);
}

/* Entrée courante (heading visible en haut du viewport). Filet client
   à gauche + fond légèrement teinté. */
.editor-outline__entry.is-current .editor-outline__link {
    color: var(--c-text);
    border-left-color: var(--c-client);
    background: color-mix(in srgb, var(--c-client) 8%, transparent);
}

/* ── Recherche / remplacer ──────────────────────────────────────────────
   Popover flottante callée sous le bouton toolbar. Highlight des matches
   dans l'éditeur via .pm-search-match (decoration inline). */
/* ── Mode FLOTTANT par défaut (fallback mobile sans sidebar gauche) ── */
.editor-search-panel {
    position: fixed;
    z-index: 150;
    width: 380px;
    max-width: calc(100vw - 16px);
    box-sizing: border-box;
    container-type: inline-size;        /* enable container queries inside */
    background: var(--c-bg-elevated);
    border: 0;
    border-radius: var(--radius-md);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
    padding: var(--sp-4);               /* + d'air interne autour du contenu */
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
}
/* Mode flottant (mobile/standalone) : bordure noire + shadow renforcée
   pour donner un aspect modale overlay distinct. Margins gérées par
   le JS (left/top/right = 12px). */
.editor-search-panel:not(.editor-search-panel--embedded) {
    border: 1px solid var(--c-text);
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.18);
}
/* ── Mode EMBARQUÉ (desktop / tablette) ──────────────────────────────
   Le panel se pose en `position: absolute` par-dessus la sidebar gauche —
   il ne participe PAS au grid `.editor-shell` (sinon il modifierait le
   template des colonnes/rangs et décalerait tout le layout). Sa position
   et sa largeur suivent la sidebar via top/left/width calés sur le
   containing block du shell (le shell doit être position: relative).
   Bordure noire + shadow pour ressortir du fond. */
.editor-shell { position: relative; }        /* anchor pour le panel absolu */
.editor-search-panel--embedded {
    /* Occupe le MÊME slot de grid que la sidebar gauche (col 1, row 1),
     * ancré en haut avec sticky top calé sous le header + gap.
     * Overlay grâce au z-index (la sidebar passe dessous). */
    grid-column: 1;
    grid-row: 1;
    align-self: start;
    width: auto;
    max-width: none;
    position: sticky;
    top: calc(var(--header-height) + var(--sp-4));
    z-index: 20;
    border: 1px solid var(--c-text);
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.18);
}
/* --editor-side-width : posé en var sur .editor-shell selon les media
 * queries du grid (300px sous 1849, max(320px,20%) au-delà). Le panel
 * hérite via var() → largeur cohérente sans JS. */
.editor-shell { --editor-side-width: 300px; }
@media (min-width: 1850px) {
    .editor-shell { --editor-side-width: max(320px, 20%); }
}
.editor-search-panel__row {
    display: flex;
    align-items: center;
    gap: var(--sp-1);
}
/* Surcharge nécessaire : .editor-search-panel__row { display:flex }
   écraserait le `display:none` implicite de l'attribut HTML `hidden` —
   la row replace resterait visible alors que l'onglet rechercher est actif. */
.editor-search-panel__row[hidden] { display: none; }
.editor-search-panel__row--options {
    column-gap: var(--sp-3);
    row-gap: var(--sp-2);
    align-items: center;
    flex-wrap: wrap;     /* compteur passe sous les options si manque de place */
}
/* Onglets rechercher / remplacer — strip gris bord-à-bord + flex
   équipartage + trait actif en couleur client via ::after. */
.editor-search-panel__tabs {
    display: flex;
    align-items: stretch;
    gap: 2px;
    background: var(--c-bg-subtle);
    padding: 0 var(--sp-2);
    /* Étend bord-à-bord du panel (compense le padding sp-4) +
       reprend les coins arrondis. */
    margin: calc(-1 * var(--sp-4)) calc(-1 * var(--sp-4)) 0;
    border-top-left-radius:  var(--radius-md);
    border-top-right-radius: var(--radius-md);
}
.editor-search-panel__tab {
    flex: 1 1 0;
    min-width: 0;
    height: 60px;
    padding: 0 var(--sp-1);
    background: transparent;
    border: 0;
    font-family: inherit;
    font-size: var(--fs-sm);              /* aligné sur les labels du lexique */
    font-weight: 600;
    line-height: 1.2;
    color: var(--c-text-muted);
    cursor: pointer;
    position: relative;
    text-align: center;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    transition: color var(--tr-base);
}
.editor-search-panel__tab:hover    { color: var(--c-text); }
.editor-search-panel__tab.is-active { color: var(--c-text); }
.editor-search-panel__tab.is-active::after {
    content: "";
    position: absolute;
    left: var(--sp-2); right: var(--sp-2);
    bottom: 0;
    height: 3px;
    background: var(--c-client);
}
.editor-search-panel__close {
    width: 32px;
    flex-shrink: 0;
    align-self: center;
    display: inline-flex;
    align-items: center; justify-content: center;
    background: transparent;
    border: 0;
    border-radius: var(--radius-sm);
    color: var(--c-text-muted);
    cursor: pointer;
    font-size: 18px;
    line-height: 1;
    padding: 0;
    height: 28px;
    transition: background var(--tr-base), color var(--tr-base);
}
.editor-search-panel__close:hover {
    background: color-mix(in srgb, var(--c-text) 8%, transparent);
    color: var(--c-text);
}

.editor-search-panel__input {
    flex: 1;
    min-width: 0;
    padding: 8px var(--sp-2);            /* + d'air vertical pour lisibilité */
    font-size: var(--fs-xs);
    font-family: inherit;
    border: 1px solid var(--c-text);
    border-radius: var(--radius-sm);
    background: var(--c-bg-elevated);
    color: var(--c-text);
    box-sizing: border-box;
}
.editor-search-panel__input:focus {
    outline: none;
    box-shadow: 0 0 0 1px var(--c-text);
}

/* Compteur des résultats : aligné à droite dans la row options, vertical
   avec les checkboxes. Le margin-left: auto pousse à droite quel que
   soit le nombre d'options. */
.editor-search-panel__count {
    margin-left: auto;
    font-size: var(--fs-xs);
    color: var(--c-text-muted);
    white-space: nowrap;
    text-align: right;
}
.editor-search-panel__count.is-empty {
    color: var(--c-error-fg);
}
/* Quand le panel est trop étroit (sidebar gauche compacte) : le compteur
   passe sous les options, aligné à gauche — évite que les checkboxes
   passent elles-mêmes sur 2 lignes. */
@container (max-width: 340px) {
    .editor-search-panel__count {
        margin-left: 0;
        text-align: left;
        flex-basis: 100%;
    }
}

/* Flèches haut/bas pour précédent/suivant. */
.editor-search-panel__icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px; height: 32px;        /* match avec input + boutons aérés */
    background: transparent;
    border: 1px solid var(--c-border);
    border-radius: var(--radius-sm);
    color: var(--c-text);
    cursor: pointer;
    padding: 0;
    flex-shrink: 0;
    transition: background var(--tr-base), border-color var(--tr-base);
}
.editor-search-panel__icon-btn:hover {
    background: color-mix(in srgb, var(--c-text) 5%, transparent);
    border-color: var(--c-text);
}
.editor-search-panel__icon-btn svg { display: block; }

.editor-search-panel__option {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 0.6875rem;            /* ~11px — plus compact */
    color: var(--c-text-muted);
    cursor: pointer;
    user-select: none;
}
.editor-search-panel__option input { margin: 0; accent-color: var(--c-text); }
.editor-search-panel__option:hover { color: var(--c-text); }

.editor-search-panel__btn {
    padding: 8px var(--sp-2);             /* + d'air vertical pour lisibilité */
    font-size: 0.6875rem;                 /* ~11px — texte plus compact */
    font-family: inherit;
    background: transparent;
    border: 1px solid var(--c-text);
    border-radius: var(--radius-sm);
    color: var(--c-text);
    cursor: pointer;
    font-weight: 600;
    white-space: nowrap;
    flex-shrink: 0;
    transition: background var(--tr-base), color var(--tr-base);
}
.editor-search-panel__btn:hover {
    background: var(--c-client);
    color: var(--c-text-on-dark);
    border-color: var(--c-client);
}

/* Highlight des matches dans l'éditeur (decoration inline). */
.pm-search-match {
    background-color: #fff3a8;        /* jaune doux (comme commentaire) */
    border-radius: 1px;
}
.pm-search-match--current {
    background-color: #ffa040;        /* orange saturé pour le match courant */
    color: #000;
}

/* Texte visible uniquement par les lecteurs d'écran (pattern standard). */
.sr-only {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0,0,0,0);
    white-space: nowrap;
    border: 0;
}

/* ── Commentaires (éditeur) ─────────────────────────────────────────────
   Trois zones :
     - .pm-comment           : la mark visible dans le doc (surlignage jaune
                               style Word, cliquable pour scroller vers le thread)
     - .editor-comments      : la sidebar (filtres + liste de threads + replies)
     - .editor-comment-popover : la popover de saisie ouverte au clic toolbar
*/
.pm-comment {
    /* Mauve clair — se distingue nettement du surligneur jaune (mark
       `highlight`) et de tous les autres fonds éditeur. */
    background-color: #ede4f7;
    border-bottom: 1px dotted #8360b5;
    cursor: pointer;
    border-radius: 1px;
}
.pm-comment:hover {
    background-color: #dccef0;            /* palier intermédiaire au survol */
}
/* Mark active = thread sélectionné en sidebar. Fond plus saturé +
   trait inférieur plein pour matérialiser la sélection dans l'éditeur.
   La classe `.is-active` est posée par une PM Decoration.inline. PM
   rend la décoration COMME UN ENFANT du span.pm-comment (pas comme un
   wrapper extérieur) :
       <span class="pm-comment"><span class="is-active …">…</span></span>
   On stylise donc le span.pm-comment via `:has()` quand il contient
   un descendant qui porte la classe. `:has()` est largement supporté
   (Chrome 105+, Firefox 121+, Safari 15.4+). */
.pm-comment:has(.is-active) {
    background-color: #b998e3;
    border-bottom: 1px solid #603ba1;
}
/* Thread résolu : la mark reste dans le doc (pour pouvoir rouvrir) mais
   sa décoration est atténuée. La classe `.is-resolved` est posée par
   une PM Decoration (cf. comments/index.js, buildDecorations) — pas
   par mutation DOM directe, sinon le MutationObserver de PM partait
   en flush récursif et freezait la page. */
/* Mark résolue : décoration toujours visible mais atténuée. On joue
   sur le BACKGROUND (rgba) et le border-color, jamais sur `opacity` :
   `opacity` rend aussi le TEXTE pâle, ce qui n'est pas voulu. Le texte
   doit rester parfaitement lisible — seule la décoration s'efface.
   Même logique d'imbrication PM que .is-active ci-dessus : la classe
   est sur le span enfant, on stylise le span.pm-comment parent via
   :has() — l'override doit donc être posé sur le pm-comment lui-même
   pour neutraliser le background plein du `.pm-comment` de base. */
.pm-comment:has(.is-resolved),
.pm-comment:has(.is-resolved):hover {
    background-color: rgba(255, 243, 168, 0.35);
    border-bottom-color: rgba(193, 148, 0, 0.45);
}
/* Combo actif + résolu : on remonte un peu l'alpha pour bien voir
   où on est, sans masquer le texte. buildDecorations émet UNE seule
   décoration avec `class: 'is-active is-resolved'` quand les deux
   s'appliquent → un seul span enfant porte les deux classes. */
.pm-comment:has(.is-active.is-resolved) {
    background-color: rgba(255, 216, 77, 0.55);
    border-bottom-color: rgba(135, 108, 0, 0.6);
}

/* ── Sidebar : filtres + liste ──────────────────────────────────────── */
.editor-comments {
    display: flex;
    flex-direction: column;
    height: 100%;
    padding: var(--sp-3);
    gap: var(--sp-3);
}
/* Surcharge nécessaire : .editor-comments est posée sur le panel par JS, et
   son `display: flex` bat le `display: none` implicite de l'attribut HTML
   `hidden`. Sans ça, le panel reste visible même quand le tab switcher le
   marque hidden → les commentaires apparaissent dans le « mauvais » onglet. */
.editor-comments[hidden] { display: none; }
.editor-comments__filters {
    display: flex;
    gap: var(--sp-1);
    flex-shrink: 0;
}
.editor-comments__filter {
    flex: 1 1 0;
    min-width: 0;
    padding: var(--sp-1) var(--sp-2);
    background: transparent;
    border: 1px solid var(--c-border);
    border-radius: var(--radius-sm);
    color: var(--c-text-muted);
    font-size: var(--fs-xs);
    font-family: inherit;
    cursor: pointer;
    text-align: center;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    transition: background var(--tr-base), color var(--tr-base), border-color var(--tr-base);
}
.editor-comments__filter:hover {
    background: color-mix(in srgb, var(--c-text) 5%, transparent);
    color: var(--c-text);
}
.editor-comments__filter.is-active {
    background: var(--c-text);
    color: var(--c-text-on-dark);
    border-color: var(--c-text);
}

.editor-comments__list {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
    overflow-y: auto;
    min-height: 0;
    /* Padding latéral 2px : laisse de l'air pour le box-shadow du thread
       actif (sinon l'overflow:auto clippe aussi en X et coupe le halo
       gauche/droite). Compensé par un -2px de marge pour garder l'aspect
       edge-to-edge. */
    padding: 2px;
    margin: 0 -2px;
}
.editor-comments__empty {
    color: color-mix(in srgb, var(--c-text-muted) 65%, transparent);
    font-size: var(--fs-xs);
    font-style: italic;
    text-align: center;
    padding: var(--sp-4);
    margin: 0;
}

/* ── Thread (commentaire racine + ses replies) ─────────────────────── */
.editor-comments__thread {
    background: var(--c-bg-elevated);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-md);
    padding: var(--sp-3);
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
    cursor: pointer;
    transition: border-color var(--tr-base), box-shadow var(--tr-base);
}
.editor-comments__thread:hover {
    border-color: var(--c-border-strong);
}
/* Thread actif (cliqué OU revenu via clic sur mark dans l'éditeur) :
   même charte que la sidebar footnotes — bordure couleur client + halo
   client en opacité réduite pour signaler l'état actif sans agresser. */
.editor-comments__thread.is-active {
    border-color: var(--c-client);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--c-client) 20%, transparent);
}
.editor-comments__thread.is-flash {
    border-color: var(--c-client);
}
.editor-comments__thread.is-resolved {
    opacity: 0.6;
}
.editor-comments__thread.is-resolved .editor-comments__entry-text {
    text-decoration: line-through;
}
/* Position relative pour ancrer le container topright en absolute. */
.editor-comments__thread { position: relative; }

/* ── Coin haut-droit : pill « agence » + bouton supprimer ──────────────
   Container flex absolute pour que la pill se cale naturellement à
   gauche du bouton supprimer (avec un petit gap), et reste bien placée
   même si le bouton n'existe pas. */
.editor-comments__topright {
    position: absolute;
    top: var(--sp-1);
    right: var(--sp-1);
    display: flex;
    align-items: center;
    gap: 6px;
    z-index: 1;
}

/* Pill « agence » — fond couleur client + texte blanc, pour identifier
   immédiatement les commentaires destinés à l'agence. */
.editor-comments__agency-pill {
    display: inline-block;
    padding: 2px 8px;
    font-size: 0.625rem;
    font-weight: 600;
    background: var(--c-client);
    color: var(--c-text-on-dark);
    border: 1px solid var(--c-client);
    border-radius: 999px;
    user-select: none;
    line-height: 1.4;
    white-space: nowrap;
}

/* Bouton supprimer — TOUJOURS visible (pas de hover-only), un peu plus
   gros (28×28). Hover : fond rouge clair. */
.editor-comments__delete {
    width: 28px; height: 28px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: 0;
    border-radius: var(--radius-sm);
    color: var(--c-text-muted);
    cursor: pointer;
    transition: background var(--tr-base), color var(--tr-base);
    padding: 0;
    flex-shrink: 0;
}
.editor-comments__delete:hover {
    background: var(--c-error-bg);
    color: var(--c-error-fg);
}
.editor-comments__delete .icon-mask {
    width: 16px; height: 16px;
}

/* Réserve une zone à droite du header pour le contenu topright
   (sinon il chevauche le nom de l'auteur). Largeur ≈ 32px (delete) +
   pill (variable) + gaps. On prend une marge généreuse. */
.editor-comments__entry--root .editor-comments__entry-header {
    padding-right: 36px;
}
.editor-comments__thread.is-agency .editor-comments__entry--root .editor-comments__entry-header {
    padding-right: 96px;
}

/* ── Entry (parent OU reply) ───────────────────────────────────────── */
.editor-comments__entry {
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
}
.editor-comments__entry--reply {
    padding-left: var(--sp-4);
    border-left: 2px solid var(--c-border);
    margin-left: var(--sp-2);
}
.editor-comments__entry-header {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
}
.editor-comments__entry-meta {
    display: flex;
    flex-direction: column;
    line-height: 1.1;
    min-width: 0;
}
.editor-comments__entry-meta strong {
    font-size: var(--fs-xs);
    font-weight: 600;
    color: var(--c-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.editor-comments__entry-meta time {
    font-size: 0.6875rem;
    color: var(--c-text-muted);
}
.editor-comments__avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px; height: 24px;
    border-radius: 50%;
    background: var(--c-bg-subtle);
    color: var(--c-text);
    font-size: 0.6875rem;
    font-weight: 650;
    overflow: hidden;
    flex-shrink: 0;
}
.editor-comments__avatar img {
    width: 100%; height: 100%;
    object-fit: cover;
}
.editor-comments__entry-text {
    margin: 0;
    font-size: var(--fs-sm);
    line-height: 1.4;
    color: var(--c-text);
    white-space: pre-wrap;
    word-break: break-word;
}

/* ── Toggles + action supprimer en pied de thread ─────────────────────
   Row flex : toggles à GAUCHE, bouton supprimer poussé à DROITE via
   margin-left auto sur `--danger`. Le rendu suit la charte footnotes
   (bouton texte muted qui devient rouge au hover). */
.editor-comments__toggles {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-2);        /* légèrement resserré (était --sp-3 = 12px) */
    margin-top: var(--sp-1);
    padding-top: var(--sp-2);
}
/* Ios-toggles dans le pied de commentaire : version réduite (compact)
   pour resserrer l'action row. La classe .ios-toggle générique reste
   inchangée ailleurs. Ratios internes préservés (knob = track height − 4). */
.editor-comments__toggles .ios-toggle {
    width: 24px; height: 14px;
}
.editor-comments__toggles .ios-toggle__knob {
    width: 10px; height: 10px;
}
.editor-comments__toggles .ios-toggle__input:checked
    + .ios-toggle__track .ios-toggle__knob {
    /* track(24) − knob(10) − 2×offset(2) = 10 */
    transform: translateX(10px);
}
/* Bouton action « supprimer » — style texte-lien, poussé à droite. */
.editor-comments__action {
    appearance: none;
    background: transparent;
    border: 0;
    color: var(--c-text-muted);
    font: inherit;
    font-size: 0.6875rem;    /* légèrement plus petit que --fs-xs (13px) */
    cursor: pointer;
    padding: 3px 6px;
    border-radius: var(--radius-sm);
    transition: background var(--tr-base), color var(--tr-base);
}
.editor-comments__action:hover {
    background: color-mix(in srgb, var(--c-text) 6%, transparent);
    color: var(--c-text);
}
.editor-comments__action--danger {
    color: #c4302b;
    margin-left: auto;
}
.editor-comments__action--danger:hover {
    background: rgba(196, 48, 43, 0.08);
    color: #c4302b;
}
.editor-comments__toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 0.6875rem;
    color: var(--c-text-muted);
    cursor: pointer;
    user-select: none;
}
.editor-comments__toggle:hover { color: var(--c-text); }

/* ── Toggle iOS-style générique (track ovale + knob blanc qui slide) ──
   Calqué sur .v1-toggle du dashboard, en plus compact, sans texte dans
   le knob. Réutilisable partout via la classe .ios-toggle sur un <label>.
   Markup attendu :
     <label class="ios-toggle">
       <input type="checkbox" class="ios-toggle__input">
       <span class="ios-toggle__track">
         <span class="ios-toggle__knob"></span>
       </span>
     </label>
   Coche → track passe en --c-client, knob slide à droite. */
.ios-toggle {
    position: relative;
    display: inline-block;
    width: 28px; height: 16px;
    flex-shrink: 0;
}
.ios-toggle__input {
    position: absolute; opacity: 0;
    width: 100%; height: 100%;
    margin: 0; cursor: pointer;
    z-index: 1;
}
.ios-toggle__track {
    display: block;
    width: 100%; height: 100%;
    border-radius: 999px;
    background: var(--c-text-faded);
    transition: background var(--tr-base);
    position: relative;
}
.ios-toggle__knob {
    position: absolute;
    top: 2px; left: 2px;
    width: 12px; height: 12px;
    border-radius: 50%;
    background: var(--c-bg-elevated);
    transition: transform var(--tr-base);
    box-shadow: 0 1px 2px rgba(0,0,0,0.15);
}
.ios-toggle__input:checked + .ios-toggle__track {
    background: var(--c-client);
}
.ios-toggle__input:checked + .ios-toggle__track .ios-toggle__knob {
    /* Slide : track(28) − knob(12) − 2×offset(2) = 12 */
    transform: translateX(12px);
}
.ios-toggle__input:disabled + .ios-toggle__track {
    cursor: not-allowed; opacity: 0.6;
}

/* ── Formulaire de réponse ─────────────────────────────────────────── */
/* Compact : textarea à hauteur auto-grow (JS) + petit bouton flèche calé
   à droite. Entrée envoie, Shift+Entrée saute une ligne.
   Visible UNIQUEMENT quand le thread est actif (UX Word : un seul thread
   à la fois affiche son input). Toujours présent dans le DOM pour ne pas
   perdre l'état utilisateur entre re-renders. */
.editor-comments__reply {
    display: none;
    gap: var(--sp-1);
    align-items: flex-end;
    margin-top: var(--sp-1);
}
.editor-comments__thread.is-active .editor-comments__reply {
    display: flex;
}
.editor-comments__reply-input {
    flex: 1;
    min-height: 32px;
    max-height: 160px;            /* limite l'auto-grow avant scroll interne */
    padding: var(--sp-1) var(--sp-2);
    font-size: var(--fs-xs);
    line-height: 1.4;
    font-family: inherit;
    border: 1px solid var(--c-text);
    border-radius: var(--radius-sm);
    background: var(--c-bg-elevated);
    resize: none;                 /* pas de poignée de redimensionnement */
    color: var(--c-text);
    overflow-y: auto;
    box-sizing: border-box;
}
.editor-comments__reply-input:focus {
    outline: none;
    /* Bordure déjà noire au repos — on renforce d'un halo léger au focus. */
    box-shadow: 0 0 0 1px var(--c-text);
}
.editor-comments__reply-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px; height: 32px;
    flex-shrink: 0;
    background: transparent;
    border: 1px solid var(--c-text);
    border-radius: var(--radius-sm);
    color: var(--c-text);
    cursor: pointer;
    transition: background var(--tr-base), color var(--tr-base), border-color var(--tr-base);
    padding: 0;
}
.editor-comments__reply-btn:hover {
    background: var(--c-text);
    color: var(--c-text-on-dark);
    border-color: var(--c-text);
}
.editor-comments__reply-btn .icon-mask {
    width: 16px; height: 16px;
}

/* ── Modale de saisie (overlay blanc semi-transparent + card centrée) ── */
/* ── Panneau d'application du lexique — overlay absolu par-dessus la
 * sidebar gauche (même pattern que .editor-search-panel--embedded).
 * Affiche la liste des corrections à proposer + boutons accept/ignore.
 * Décorations posées dans le doc PM : contextuels + suggestions + flash. */
.editor-lexicon-apply-panel--embedded {
    /* Sticky par-dessus la sidebar gauche — même pattern que le search-panel
     * embedded : slot grid col 1 / row 1 + align-self start + sticky top. */
    grid-column: 1;
    grid-row: 1;
    align-self: start;
    width: auto;
    max-width: none;
    position: sticky;
    top: calc(var(--header-height) + var(--sp-4));
    z-index: 20;
    background: var(--c-bg-elevated);
    border: 1px solid var(--c-text);
    border-radius: var(--radius-md);
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.18);
    display: flex;
    flex-direction: column;
    max-height: calc(100vh - var(--header-height) - 40px);
    overflow: hidden;
}
.editor-lexicon-apply-panel__header {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    padding: var(--sp-3) var(--sp-4);
    border-bottom: 1px solid var(--c-border);
}
.editor-lexicon-apply-panel__title {
    margin: 0;
    flex: 1;
    font-size: var(--fs-md);
    font-weight: 650;
    color: var(--c-text);
}
.editor-lexicon-apply-panel__count {
    font-size: var(--fs-sm);
    color: var(--c-text-muted);
    font-weight: 600;
}
.editor-lexicon-apply-panel__close {
    background: transparent;
    border: 0;
    color: var(--c-text);
    font-size: 1.25rem;
    line-height: 1;
    padding: 0 4px;
    cursor: pointer;
}
/* Bandeau d'onglets sous le header — même charte que .editor-search-panel__tabs :
 * strip gris bord-à-bord + flex équipartage + trait actif en couleur client. */
.editor-lexicon-apply-panel__tabs {
    display: flex;
    align-items: stretch;
    gap: 2px;
    background: var(--c-bg-subtle);
    padding: 0 var(--sp-2);
    border-bottom: 1px solid var(--c-border);
}
.editor-lexicon-apply-panel__tab {
    flex: 1 1 0;
    min-width: 0;
    height: 44px;
    padding: 0 var(--sp-2);
    background: transparent;
    border: 0;
    font-family: inherit;
    font-size: var(--fs-sm);
    font-weight: 600;
    line-height: 1.2;
    color: var(--c-text-muted);
    cursor: pointer;
    position: relative;
    /* Layout : icône/label centrés verticalement + horizontalement. */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    white-space: nowrap;
    overflow: hidden;
    transition: color var(--tr-base);
    user-select: none;
    -webkit-user-select: none;
}
.editor-lexicon-apply-panel__tab:hover    { color: var(--c-text); }
.editor-lexicon-apply-panel__tab.is-active { color: var(--c-text); }
.editor-lexicon-apply-panel__tab.is-active::after {
    content: "";
    position: absolute;
    left: var(--sp-2); right: var(--sp-2);
    bottom: 0;
    height: 3px;
    background: var(--c-client);
}
.editor-lexicon-apply-panel__tab .icon-mask {
    width: 16px; height: 16px;
    display: inline-block;
    vertical-align: middle;
}

.editor-lexicon-apply-panel__body {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding: var(--sp-2);
}
.editor-lexicon-apply-panel__list {
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.editor-lexicon-apply-panel__empty {
    margin: 0;
    padding: var(--sp-4) var(--sp-3);
    color: var(--c-text-muted);
    text-align: center;
    font-size: var(--fs-sm);
}
.editor-lexicon-apply-panel__row {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: var(--sp-2);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: background var(--tr-fast), border-color var(--tr-fast);
}
.editor-lexicon-apply-panel__row:hover {
    background: var(--c-bg-subtle);
    border-color: var(--c-border);
}
.editor-lexicon-apply-panel__row.is-active {
    background: var(--c-bg-subtle);
    border-color: var(--c-text);
}
.editor-lexicon-apply-panel__row.is-info-only {
    opacity: 0.75;
}
/* Bandeau haut : preview change (flex 1) + actions à droite. */
.editor-lexicon-apply-panel__top {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
}
.editor-lexicon-apply-panel__top .editor-lexicon-apply-panel__change {
    flex: 1;
    min-width: 0;
}
/* Actions accept/refuse : cachées au repos, révélées uniquement au hover
 * (ou focus dans) de la row. Row active = idem : actions toujours visibles
 * quand la row est sélectionnée. */
.editor-lexicon-apply-panel__actions {
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--tr-fast);
}
.editor-lexicon-apply-panel__row:hover .editor-lexicon-apply-panel__actions,
.editor-lexicon-apply-panel__row:focus-within .editor-lexicon-apply-panel__actions,
.editor-lexicon-apply-panel__row.is-active .editor-lexicon-apply-panel__actions {
    opacity: 1;
    pointer-events: auto;
}
.editor-lexicon-apply-panel__info {
    color: var(--c-warning-fg, #6b4f00);
    font-size: 0.7rem;
    font-weight: 600;
    font-style: italic;
    margin-left: 4px;
}

/* Bloc « avant → après » : preview compact avec styling appliqué à
 * l'after (italique/gras) et strikethrough sur le before quand il
 * s'agit d'une correction de casse. */
.editor-lexicon-apply-panel__change {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 6px;
    font-size: var(--fs-sm);
    line-height: 1.3;
}
.editor-lexicon-apply-panel__before {
    color: var(--c-text-muted);
    text-decoration: line-through;
    text-decoration-color: var(--c-text-faded);
}
.editor-lexicon-apply-panel__arrow {
    color: var(--c-text-muted);
    font-weight: 600;
}
.editor-lexicon-apply-panel__after {
    color: var(--c-text);
    background: color-mix(in srgb, var(--c-client) 18%, transparent);
    padding: 0 3px;
    border-radius: 2px;
    /* Pas de font-weight par défaut : la graisse ne doit refléter QUE
     * la correction demandée (italique / gras). Sinon le user croirait
     * qu'un simple changement de casse va aussi mettre en gras. */
}
.editor-lexicon-apply-panel__after.is-italic { font-style: italic; }
.editor-lexicon-apply-panel__after.is-bold   { font-weight: 700; }

/* Contexte court sur une ligne, tronqué. */
.editor-lexicon-apply-panel__ctx {
    font-size: 0.7rem;
    color: var(--c-text-muted);
    line-height: 1.3;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.editor-lexicon-apply-panel__ctx-target {
    color: var(--c-text);
    background: color-mix(in srgb, var(--c-client) 12%, transparent);
    padding: 0 2px;
    border-radius: 2px;
}

.editor-lexicon-apply-panel__row-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-2);
}
.editor-lexicon-apply-panel__badges {
    display: inline-flex;
    gap: 2px;
    flex-shrink: 0;
}
.editor-lexicon-apply-panel__badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 20px;
    height: 20px;
    padding: 0 4px;
    background: var(--c-text);
    color: var(--c-text-on-dark);
    border-radius: 999px;
    font-size: 0.65rem;
    font-weight: 700;
}
.editor-lexicon-apply-panel__badge--info {
    background: var(--c-warning-bg, #fef5d8);
    color: var(--c-warning-fg, #6b4f00);
    padding: 0 8px;
    font-weight: 600;
}
.editor-lexicon-apply-panel__actions {
    display: inline-flex;
    gap: 4px;
    flex-shrink: 0;
}
.editor-lexicon-apply-panel__accept,
.editor-lexicon-apply-panel__ignore {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px; height: 26px;
    padding: 0;
    background: transparent;
    border: 1px solid var(--c-text);
    border-radius: 999px;
    color: var(--c-text);
    cursor: pointer;
    opacity: 0.75;
    transition: background var(--tr-fast), color var(--tr-fast),
                border-color var(--tr-fast), opacity var(--tr-fast);
}
.editor-lexicon-apply-panel__accept .icon-mask,
.editor-lexicon-apply-panel__ignore .icon-mask {
    width: 16px; height: 16px;
}
.editor-lexicon-apply-panel__accept:hover:not(:disabled) {
    background: var(--c-client);
    color: var(--c-text-on-dark);
    border-color: var(--c-client);
    opacity: 1;
}
.editor-lexicon-apply-panel__ignore:hover {
    background: var(--c-text);
    color: var(--c-text-on-dark);
    border-color: var(--c-text);
    opacity: 1;
}
.editor-lexicon-apply-panel__accept:disabled {
    opacity: 0.3;
    cursor: not-allowed;
}
.editor-lexicon-apply-panel__footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-2);
    padding: var(--sp-3) var(--sp-4);
    border-top: 1px solid var(--c-border);
}
.editor-lexicon-apply-panel__footer-count {
    font-size: 0.75rem;
    font-weight: 500;
    color: var(--c-text-muted);
}
/* Bouton « tout accepter » — plus compact que le .btn primary standard. */
.editor-lexicon-apply-panel__accept-all {
    padding: 6px 12px;
    font-size: var(--fs-sm);
    line-height: 1.2;
}
/* Onglet sans résultat : très faible opacité + non cliquable. */
.editor-lexicon-apply-panel__tab.is-empty {
    opacity: 0.25;
    cursor: not-allowed;
    pointer-events: none;
}

/* Barre de progression affichée pendant l'opération « tout accepter ».
 * Remplace le bouton primaire dans le footer le temps de traiter les
 * chunks (30 corrections par rAF) → l'UI reste responsive. */
.editor-lexicon-apply-panel__progress {
    display: flex;
    flex-direction: column;
    gap: 6px;
    width: 100%;
}
.editor-lexicon-apply-panel__progress-label {
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--c-text-muted);
    text-align: right;
}
.editor-lexicon-apply-panel__progress-bar {
    width: 100%;
    height: 6px;
    background: var(--c-bg-subtle);
    border-radius: 999px;
    overflow: hidden;
}
.editor-lexicon-apply-panel__progress-fill {
    width: 0%;
    height: 100%;
    background: var(--c-client);
    transition: width 120ms linear;
}

/* Décorations dans le doc — posées par le plugin lexicon */
.pm-lexicon-context {
    background: color-mix(in srgb, var(--c-client) 15%, transparent);
    border-radius: 2px;
}
.pm-lexicon-suggest {
    text-decoration: underline wavy var(--c-warning-fg, #b87800);
    text-decoration-thickness: 1px;             /* wave plus fine */
    text-underline-offset: 4px;                 /* décollée pour ne pas être clippée */
    text-decoration-skip-ink: none;             /* évite les "trous" sur les descendants */
}
/* Match ACTIF (clic sur row du panneau) — highlight persistant clignotant
 * dans le doc éditeur. Reste visible tant que la row est active. */
.pm-lexicon-focus {
    background: color-mix(in srgb, var(--c-client) 30%, transparent);
    border-radius: 2px;
    animation: pm-lexicon-blink 1.4s ease-in-out infinite;
}
@keyframes pm-lexicon-blink {
    0%, 100% { background: color-mix(in srgb, var(--c-client) 25%, transparent); }
    50%      { background: color-mix(in srgb, var(--c-client) 65%, transparent); }
}

/* Mode « review » activé quand le panneau lexique est ouvert :
 *   - tous les blocs de contenu de l'éditeur sont estompés
 *   - SAUF le block actif (paragraphe OU list_item si dans une liste)
 * Le dim est posé sur les textblocks et list_item spécifiquement (pas sur
 * les conteneurs ul/ol/blockquote) → pas de cascade opacity qui neutralise
 * l'override du block actif. L'exclusion `:not(active *)` évite de dim les
 * paragraphes internes à un list_item actif. */
.ProseMirror.pm-lexicon-review :is(p, h1, h2, h3, h4, h5, h6, li, blockquote, figure, td, th):not(.pm-lexicon-active-block):not(.pm-lexicon-active-block *) {
    opacity: 0.35;
    transition: opacity var(--tr-base);
}
.ProseMirror.pm-lexicon-review .pm-lexicon-active-block {
    opacity: 1;
}
/* Sidebar gauche cachée pendant le review (le panneau lexique la couvre
 * déjà, mais on l'inertise pour éviter tout focus/tab). :has() remonte
 * l'état depuis le panneau embedded jusqu'au shell parent. */
.editor-shell:has(.editor-lexicon-apply-panel--embedded) .editor-side--left {
    opacity: 0;
    pointer-events: none;
}
/* Toolbar : tous les boutons désactivés SAUF « lexique » qui reste actif
 * pour permettre de fermer le panneau. En review, aucune autre modif
 * n'est autorisée — l'utilisateur doit accepter/refuser depuis le panneau. */
.editor-shell:has(.editor-lexicon-apply-panel--embedded)
    .editor-toolbar__btn:not([data-cmd="lexicon"]),
.editor-shell:has(.editor-lexicon-apply-panel--embedded)
    .editor-toolbar__split,
.editor-shell:has(.editor-lexicon-apply-panel--embedded)
    .editor-toolbar__variant {
    opacity: 0.3;
    pointer-events: none;
}
/* Overlays flottants (image, table, hover popovers…) — masqués pendant
 * le review pour éviter tout clic parasite. */
.editor-shell:has(.editor-lexicon-apply-panel--embedded) .editor-image-toolbar,
.editor-shell:has(.editor-lexicon-apply-panel--embedded) .editor-table-toolbar,
.editor-shell:has(.editor-lexicon-apply-panel--embedded) .pm-table-hover-overlay,
.editor-shell:has(.editor-lexicon-apply-panel--embedded) .editor-contextmenu {
    display: none !important;
}

/* ── Menu contextuel « ajouter au lexique » (clic droit dans l'éditeur) ─
 * Petit menu one-shot ancré au curseur, une seule entrée. Fermeture au
 * mousedown extérieur / Escape (côté JS). */
.editor-lexicon-menu {
    position: fixed;
    z-index: 300;
    min-width: 180px;
    background: var(--c-bg-elevated);
    border: 1px solid var(--c-text);
    border-radius: var(--radius-md);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
    padding: 4px;
    display: flex;
    flex-direction: column;
}
.editor-lexicon-menu__item {
    display: flex;
    align-items: center;
    padding: var(--sp-2) var(--sp-3);
    background: transparent;
    border: 0;
    border-radius: var(--radius-sm);
    color: var(--c-text);
    font-family: inherit;
    font-size: var(--fs-sm);
    font-weight: 500;
    text-align: left;
    cursor: pointer;
    transition: background var(--tr-fast), color var(--tr-fast);
}
.editor-lexicon-menu__item:hover,
.editor-lexicon-menu__item:focus-visible {
    background: var(--c-text);
    color: var(--c-text-on-dark);
    outline: none;
}

/* ── Popover « ajouter au lexique » (modale centrée) ────────────────
 * Même pattern que les autres popovers de l'éditeur : overlay blanc
 * translucide + card centrée. */
.editor-lexicon-add-overlay {
    position: fixed;
    inset: 0;
    z-index: 320;
    background: rgba(255, 255, 255, 0.85);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--sp-4);
}
.editor-lexicon-add {
    width: 100%;
    max-width: 640px;
    background: var(--c-bg-elevated);
    border: 1px solid var(--c-text);
    border-radius: var(--radius-md);
    padding: var(--sp-4);
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.18);
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
}
.editor-lexicon-add__title {
    margin: 0;
    font-size: var(--fs-lg);
    font-weight: 700;
    color: var(--c-text);
}
.editor-lexicon-add__preview {
    margin: 0;
    padding: var(--sp-2) var(--sp-3);
    background: var(--c-bg-subtle);
    border-radius: var(--radius-sm);
    color: var(--c-text);
    font-size: var(--fs-sm);
    font-style: italic;
    word-break: break-word;
}
.editor-lexicon-add__section-label {
    display: block;
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--c-text-muted);
}
.editor-lexicon-add__targets,
.editor-lexicon-add__flags {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-2);
}
.editor-lexicon-add__target,
.editor-lexicon-add__flag {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    padding: 6px 12px;
    background: transparent;
    border: 1px solid var(--c-text);
    border-radius: 999px;
    color: var(--c-text);
    font-family: inherit;
    font-size: var(--fs-sm);
    font-weight: 600;
    cursor: pointer;
    opacity: 0.5;
    transition: background var(--tr-fast), color var(--tr-fast),
                opacity var(--tr-fast);
    user-select: none;
    -webkit-user-select: none;
}
.editor-lexicon-add__target:hover,
.editor-lexicon-add__flag:hover { opacity: 1; }
.editor-lexicon-add__target.is-active,
.editor-lexicon-add__flag.is-active {
    background: var(--c-client);
    color: var(--c-text-on-dark);
    border-color: var(--c-client);
    opacity: 1;
}
.editor-lexicon-add__flag .icon-mask {
    width: 16px; height: 16px;
    flex-shrink: 0;
}
.editor-lexicon-add__actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--sp-2);
    margin-top: var(--sp-2);
}
.editor-lexicon-add__error {
    margin: 0;
    color: var(--c-error-fg, #8a1a14);
    font-size: var(--fs-sm);
}

.editor-comment-popover-overlay {
    position: fixed;
    inset: 0;
    z-index: 100;
    background: rgba(255, 255, 255, 0.85);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--sp-4);
}
.editor-comment-popover {
    width: 100%;
    max-width: 420px;
    background: var(--c-bg-elevated);
    border: 1px solid var(--c-text);
    border-radius: var(--radius-md);
    padding: var(--sp-4);
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.18);
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
}
.editor-comment-popover__title {
    margin: 0;
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--c-text);
}
.editor-comment-popover__textarea {
    width: 100%;
    padding: var(--sp-2);
    font-size: var(--fs-sm);
    font-family: inherit;
    border: 1px solid var(--c-border);
    border-radius: var(--radius-sm);
    background: var(--c-bg-subtle);
    resize: vertical;
    color: var(--c-text);
    box-sizing: border-box;
}
.editor-comment-popover__textarea:focus {
    outline: none;
    border-color: var(--c-text);
    background: var(--c-bg-elevated);
}
.editor-comment-popover__agency {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    font-size: var(--fs-xs);
    color: var(--c-text-muted);
    cursor: pointer;
}
.editor-comment-popover__agency input {
    margin: 0;
}
.editor-comment-popover__actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--sp-2);
}

/* ═══════════════════════════════════════════════════════════════════════
 * Notes de bas de page — sidebar + popover
 *
 *   - .editor-footnotes            : panel liste (aside droite, onglet)
 *   - .editor-footnotes__item      : une entry (numéro + auteur + texte)
 *   - .editor-footnote-popover     : dialog de saisie / édition
 * ═════════════════════════════════════════════════════════════════════ */
.editor-footnotes {
    display: flex;
    flex-direction: column;
    padding: var(--sp-3);
    gap: var(--sp-3);
    height: 100%;
    overflow-y: auto;
    box-sizing: border-box;
}
/* Le panel porte `.editor-footnotes` + `[hidden]` quand un autre onglet
   est actif. `[hidden]` a la même spécificité (0,1,0) que
   `.editor-footnotes` — sans cette règle explicite, le `display: flex`
   déclaré plus tard bat la valeur `display: none` du user-agent, et
   les notes restent visibles quel que soit l'onglet sélectionné. */
.editor-footnotes[hidden] {
    display: none;
}
.editor-footnotes__empty {
    color: color-mix(in srgb, var(--c-text-muted) 65%, transparent);
    font-size: var(--fs-xs);
    text-align: center;
    padding: var(--sp-6) var(--sp-3);
    font-style: italic;
}
.editor-footnotes__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
}
.editor-footnotes__item {
    background: var(--c-bg-elevated);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-md);
    padding: var(--sp-2) var(--sp-3);
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
    cursor: pointer;
    transition: border-color var(--tr-base), box-shadow var(--tr-base);
}
.editor-footnotes__item:hover {
    border-color: color-mix(in srgb, var(--c-text) 30%, transparent);
}
.editor-footnotes__item.is-active {
    border-color: var(--c-client);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--c-client) 20%, transparent);
}
/* Entry en attente de suppression (mark `deletion` du track-changes sur
   le marker) : opacité réduite + barré sur le texte/auteur pour signaler
   « ce n'est pas définitif tant qu'un chef de projet n'a pas accepté ». */
.editor-footnotes__item.is-pending-delete {
    opacity: 0.55;
}
.editor-footnotes__item.is-pending-delete .editor-footnotes__badge,
.editor-footnotes__item.is-pending-delete .editor-footnotes__author,
.editor-footnotes__item.is-pending-delete .editor-footnotes__text {
    text-decoration: line-through;
    text-decoration-color: color-mix(in srgb, #c4302b 60%, transparent);
    text-decoration-thickness: 1.5px;
}
/* Entrée orpheline (marker supprimé du doc mais entry store restante). */
.editor-footnotes__item.is-orphan {
    opacity: 0.55;
    border-style: dashed;
}
.editor-footnotes__item-header {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
}
.editor-footnotes__badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 22px;
    height: 22px;
    padding: 0 6px;
    background: var(--c-client);
    color: #fff;
    font-size: var(--fs-xs);
    font-weight: 650;
    border-radius: 999px;
    flex-shrink: 0;
}
.editor-footnotes__item.is-orphan .editor-footnotes__badge {
    background: var(--c-text-muted);
}
/* Auteur au-dessus, date en dessous — même interlignage que la sidebar
   commentaires (line-height 1.2 sur la colonne meta pour rapprocher les
   deux lignes sans les coller). */
.editor-footnotes__meta {
    display: flex;
    flex-direction: column;
    min-width: 0;
    flex: 1;
    font-size: var(--fs-xs);
    line-height: 1.2;
}
.editor-footnotes__author {
    font-weight: 600;
    color: var(--c-text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.editor-footnotes__date {
    color: var(--c-text-muted);
    font-size: 0.7rem;
}
.editor-footnotes__text {
    margin: 0;
    color: var(--c-text);
    font-size: var(--fs-sm);
    line-height: 1.4;
    white-space: pre-wrap;
    word-break: break-word;
}
.editor-footnotes__actions {
    display: flex;
    gap: var(--sp-1);
    justify-content: flex-end;
}
.editor-footnotes__action {
    appearance: none;
    background: transparent;
    border: 0;
    color: var(--c-text-muted);
    font: inherit;
    font-size: 0.6875rem;    /* aligné sur les actions commentaires */
    cursor: pointer;
    padding: 3px 6px;
    border-radius: var(--radius-sm);
    transition: background var(--tr-base), color var(--tr-base);
}
.editor-footnotes__action:hover {
    background: color-mix(in srgb, var(--c-text) 6%, transparent);
    color: var(--c-text);
}
.editor-footnotes__action--danger {
    color: #c4302b;
}
.editor-footnotes__action--danger:hover {
    background: rgba(196, 48, 43, 0.08);
    color: #c4302b;
}


/* ── Modale de saisie / édition (overlay + card centrée) ─────────────── */
.editor-footnote-popover-overlay {
    position: fixed;
    inset: 0;
    z-index: 300;
    background: rgba(255, 255, 255, 0.85);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--sp-4);
}
.editor-footnote-popover {
    background: var(--c-bg-elevated);
    border: 1px solid var(--c-text);
    border-radius: var(--radius-md);
    box-shadow: 0 12px 32px rgba(0,0,0,0.18);
    padding: var(--sp-4);
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
    width: 100%;
    max-width: 480px;
}
.editor-footnote-popover__title {
    margin: 0 0 var(--sp-1);
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--c-text);
}
.editor-footnote-popover__textarea {
    appearance: none;
    background: var(--c-bg);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-sm);
    padding: var(--sp-2);
    font: inherit;
    font-size: var(--fs-sm);
    color: var(--c-text);
    resize: vertical;
    min-height: 72px;
    outline: none;
    transition: border-color var(--tr-base);
}
.editor-footnote-popover__textarea:focus {
    border-color: var(--c-client);
}
/* Mini-toolbar 4 boutons — même charte visuelle que la toolbar éditeur
   principale (.editor-toolbar). Étirée sur toute la largeur de la zone
   d'édition (bord à bord avec le contenteditable dessous). */
.editor-footnote-popover__toolbar {
    display: flex;
    gap: var(--sp-1);
    padding: var(--sp-2) var(--sp-3);
    background: var(--c-bg-subtle);
    border: 1px solid var(--c-border);
    border-bottom: 0;
    border-radius: var(--radius-sm) var(--radius-sm) 0 0;
    width: 100%;
    box-sizing: border-box;
}
/* Boutons calqués sur `.editor-toolbar__btn` : 32×32 carré, transparent
   au repos, hover blanc + bordure noire 8 %, actif client teinté. */
.editor-footnote-popover__mark-btn {
    appearance: none;
    display: inline-flex; align-items: center; justify-content: center;
    width: 32px; height: 32px;
    background: transparent;
    border: 0;
    border-radius: var(--radius-sm);
    color: var(--c-text);
    cursor: pointer;
    font: inherit;
    font-size: var(--fs-sm);
    font-weight: 600;
    line-height: 1;
    padding: 0;
    transition: background var(--tr-base), box-shadow var(--tr-base), color var(--tr-base);
}
.editor-footnote-popover__mark-btn:hover {
    background: color-mix(in srgb, var(--c-text) 5%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--c-text) 8%, transparent);
}
.editor-footnote-popover__mark-btn.is-active {
    background: color-mix(in srgb, var(--c-client) 5%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--c-client) 60%, transparent);
    color: var(--c-client);
}
.editor-footnote-popover__mark-btn.is-active:hover {
    background: color-mix(in srgb, var(--c-client) 10%, transparent);
}
.editor-footnote-popover__mark-btn .icon-mask {
    width: 18px; height: 18px;
}
/* Fallback lettre stylée quand pas d'icône : la lettre suit le style
   correspondant (B en gras, I en italique, etc.). */
.editor-footnote-popover__mark-btn--bold           { font-weight: 650; }
.editor-footnote-popover__mark-btn--italic         { font-style: italic; }
.editor-footnote-popover__mark-btn--underline      { text-decoration: underline; }
.editor-footnote-popover__mark-btn--strikeThrough  { text-decoration: line-through; }
.editor-footnote-popover__mark-btn--superscript    { font-size: 0.7rem; }
.editor-footnote-popover__mark-btn--subscript      { font-size: 0.7rem; }

/* Zone contenteditable — soudée à la toolbar au-dessus : coins arrondis
   uniquement en bas, pas de gap flex. La toolbar gère `border-bottom: 0`
   pour éviter le double filet à la jonction. Le `margin-top` négatif
   annule le `gap: var(--sp-2)` du container flex parent, pour souder
   visuellement le toolbar et l'éditeur en un seul champ. */
.editor-footnote-popover__editor {
    background: var(--c-bg-elevated);          /* blanc/carte au lieu du fond page */
    border: 1px solid var(--c-border);
    border-radius: 0 0 var(--radius-sm) var(--radius-sm);
    padding: var(--sp-2) var(--sp-3);
    margin-top: calc(-1 * var(--sp-2));
    font-size: var(--fs-sm);
    color: var(--c-text);
    min-height: 96px;
    max-height: 280px;
    overflow-y: auto;
    outline: none;
    transition: border-color var(--tr-base);
    line-height: 1.5;
    /* Autorise le wrap + préserve les <br> ajoutés par contenteditable. */
    white-space: pre-wrap;
    word-break: break-word;
}
/* Volontairement pas de style :focus sur l'éditeur — la bordure reste
   figée en gris (--c-border), pour rester discrète dans la modale.
   L'état actif est signalé par la toolbar au-dessus (boutons is-active). */
/* Placeholder simulé : quand vide, affiche `data-placeholder` en muted.
   Vide = pas de child ou uniquement <br>. */
.editor-footnote-popover__editor:empty::before,
.editor-footnote-popover__editor:has(> br:only-child)::before {
    content: attr(data-placeholder);
    color: var(--c-text-muted);
    pointer-events: none;
}

/* ═══════════════════════════════════════════════════════════════════════
 *  Sidebar « suivi de modifications » (onglet corrections)
 *  Liste des modifications en attente (insertion/deletion/format_change)
 *  avec accept/reject par entry (permission-gated).
 * ═════════════════════════════════════════════════════════════════════ */
.editor-track {
    display: flex;
    flex-direction: column;
    padding: var(--sp-3);
    gap: var(--sp-3);
    height: 100%;
    overflow-y: auto;
    box-sizing: border-box;
}
.editor-track[hidden] { display: none; }
.editor-track__header {
    display: flex;
    gap: var(--sp-2);
    justify-content: flex-end;
}
.editor-track__header[hidden] { display: none; }
.editor-track__bulk {
    appearance: none;
    background: transparent;
    border: 1px solid var(--c-border);
    border-radius: var(--radius-sm);
    padding: 4px 10px;
    font: inherit;
    font-size: var(--fs-xs);
    color: var(--c-text);
    cursor: pointer;
    transition: background var(--tr-base), border-color var(--tr-base);
}
.editor-track__bulk:hover {
    background: color-mix(in srgb, var(--c-text) 5%, transparent);
    border-color: color-mix(in srgb, var(--c-text) 25%, transparent);
}
.editor-track__bulk--accept:hover { border-color: color-mix(in srgb, #1f883d 55%, transparent); }
.editor-track__bulk--reject:hover { border-color: color-mix(in srgb, #c4302b 55%, transparent); color: #c4302b; }
/* Empty state — aligné pixel-perfect sur .editor-footnotes__empty pour
   avoir la même typo (italique, xs, gris muted à 65%) dans les 3
   sidebars (commentaires, footnotes, suivi de modifications). */
.editor-track__empty {
    color: color-mix(in srgb, var(--c-text-muted) 65%, transparent);
    font-size: var(--fs-xs);
    text-align: center;
    padding: var(--sp-6) var(--sp-3);
    font-style: italic;
}
.editor-track__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
}
.editor-track__item {
    background: var(--c-bg-elevated);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-md);
    padding: var(--sp-2) var(--sp-3);
    display: flex;
    flex-direction: column;
    gap: var(--sp-1);
    cursor: pointer;
    transition: border-color var(--tr-base);
}
.editor-track__item:hover { border-color: color-mix(in srgb, var(--c-text) 30%, transparent); }
/* Item actif = celui dont la plage contient le curseur PM de l'éditeur.
   Juste la bordure couleur client — même pattern que les commentaires
   actifs et les appels de notes actifs, pas de fond en surbrillance. */
.editor-track__item.is-active {
    border-color: var(--c-client);
}
.editor-track__item-header {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
}
.editor-track__badge {
    display: inline-flex;
    align-items: center;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: 0.6875rem;
    font-weight: 650;
    text-transform: none;
    flex-shrink: 0;
}
.editor-track__badge--insertion {
    background: color-mix(in srgb, #1f883d 15%, transparent);
    color: #1a6b30;
}
.editor-track__badge--deletion {
    background: color-mix(in srgb, #c4302b 15%, transparent);
    color: #a02520;
}
.editor-track__badge--format_change {
    background: color-mix(in srgb, #8b5cf6 15%, transparent);
    color: #6b3bcc;
}
.editor-track__meta {
    display: flex;
    flex-direction: column;
    min-width: 0;
    flex: 1;
    font-size: var(--fs-xs);
    line-height: 1.2;
}
.editor-track__author {
    font-weight: 600;
    color: var(--c-text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.editor-track__date {
    color: var(--c-text-muted);
    font-size: 0.7rem;
}
.editor-track__excerpt {
    margin: 0;
    color: var(--c-text);
    font-size: var(--fs-sm);
    line-height: 1.4;
    word-break: break-word;
}
.editor-track__excerpt--empty::before {
    content: '—';
    color: var(--c-text-muted);
}
/* Caption « note de bas de page » / « variable » — petite légende
   grise au-dessus du corps de l'excerpt quand la modification porte
   sur un atom (footnote_ref, variable) plutôt que sur du texte pur. */
.editor-track__excerpt-caption {
    display: block;
    font-size: 0.6875rem;
    color: color-mix(in srgb, var(--c-text-muted) 75%, transparent);
    text-transform: none;
    font-style: italic;
    margin-bottom: 2px;
}
.editor-track__excerpt-body {
    display: block;
    color: var(--c-text);
}
/* Texte utilisateur qui accompagne l'atom dans la même mark (typique :
   « abc[note]def » où abc/def entourent la note). Séparé visuellement
   du body de l'atom pour bien indiquer qu'il s'agit de la frappe user
   et pas du contenu résolu de l'atom. */
.editor-track__excerpt-surrounding {
    display: block;
    color: var(--c-text);
    margin-top: 2px;
}
.editor-track__actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--sp-1);
}
.editor-track__action {
    appearance: none;
    background: transparent;
    border: 0;
    color: var(--c-text-muted);
    font: inherit;
    font-size: 0.6875rem;
    cursor: pointer;
    padding: 3px 6px;
    border-radius: var(--radius-sm);
    transition: background var(--tr-base), color var(--tr-base);
}
.editor-track__action--accept:hover {
    background: color-mix(in srgb, #1f883d 10%, transparent);
    color: #1a6b30;
}
.editor-track__action--reject:hover {
    background: rgba(196, 48, 43, 0.08);
    color: #c4302b;
}
.editor-track__no-permission {
    font-size: 0.6875rem;
    font-style: italic;
    color: color-mix(in srgb, var(--c-text-muted) 65%, transparent);
}

/* ═══════════════════════════════════════════════════════════════════════
 *  Sidebar « versions » — liste des versions VISIBLES d'un chapitre
 *  (working + historiques, jamais les phantoms), avec bump depuis le
 *  header et restore par entry historique. Aligné sur .editor-track
 *  pour cohérence visuelle des trois sidebars droites.
 * ═════════════════════════════════════════════════════════════════════ */
/* ═══════════════════════════════════════════════════════════════════════
 *  Modal « historique des versions ».
 *
 *  Overlay plein écran (fond noir 55%) + boîte centrée (max 640px), avec
 *  deux vues internes swappées dans `.versions-modal__body` :
 *    • liste des versions (V(N) + auteur + date + boutons)
 *    • preview d'une version historique (rendu PM read-only + « retour »)
 *
 *  Le modal est monté dans <body>, indépendant du layout de l'éditeur.
 *  Reload uniquement au restore (pour resync Yjs). Le bump se fait via la
 *  pill header en Ajax et le modal écoute `versions:bumped` pour refetch.
 * ═════════════════════════════════════════════════════════════════════ */
.versions-modal {
    position: fixed;
    inset: 0;
    z-index: 300;
    /* Overlay clair — uniformisé avec `.editor-confirm-modal`. Le contraste
       vient du contenu (card blanche sur fond légèrement voilé), pas d'un
       fond opaque foncé. */
    background: rgba(255, 255, 255, 0.85);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--sp-4);
}
.versions-modal__card {
    width: 100%;
    max-width: 640px;
    max-height: 80vh;
    background: var(--c-bg-elevated);
    /* Bordure noire (couleur texte) pour détacher franchement la card
       de l'overlay clair. Convention partagée avec .editor-confirm-modal
       pour toutes les modales de l'app. */
    border: 1px solid var(--c-text);
    border-radius: var(--radius-lg);
    /* Ombre standard légère — aligné sur les autres overlays de l'app
       (`0 8px 24px rgba(0,0,0,0.12)` = pattern répété pour dropdowns,
       popovers, floating toolbars). Uniformité UX. */
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
    display: flex;
    flex-direction: column;
    overflow: hidden;                             /* le body scrolle en interne */
    transition: max-width var(--tr-base);
}
/* Preview : on élargit à la taille du rail éditeur (`.ProseMirror` :
   max-width 1100px) + un peu de padding, pour que le rendu du contenu
   corresponde visuellement à ce que l'utilisateur voit dans l'éditeur.
   Hauteur montée aussi pour ne pas cropper de long chapitres. */
.versions-modal__card.is-previewing {
    max-width: 1180px;
    max-height: 90vh;
}

.versions-modal__header {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-4) var(--sp-5);
    border-bottom: 1px solid var(--c-border);
}
.versions-modal__title {
    margin: 0;
    font-size: var(--fs-lg);
    font-weight: 650;
    color: var(--c-text);
    flex: 1;
}
.versions-modal__close {
    appearance: none;
    background: transparent;
    border: 0;
    color: var(--c-text-muted);
    padding: var(--sp-1);
    cursor: pointer;
    border-radius: var(--radius-sm);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background var(--tr-base), color var(--tr-base);
}
.versions-modal__close:hover {
    background: var(--c-bg-subtle);
    color: var(--c-text);
}

.versions-modal__body {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: var(--sp-4) var(--sp-5);
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
}

.versions-modal__loading,
.versions-modal__empty {
    color: color-mix(in srgb, var(--c-text-muted) 75%, transparent);
    font-size: var(--fs-sm);
    text-align: center;
    padding: var(--sp-6) var(--sp-3);
    font-style: italic;
}

.versions-modal__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
}
.versions-modal__item {
    background: transparent;
    border: 1px solid var(--c-border);
    border-radius: var(--radius-md);
    padding: var(--sp-3);
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
    transition: border-color var(--tr-base);
}
.versions-modal__item:hover {
    border-color: color-mix(in srgb, var(--c-text) 30%, transparent);
}
/* Item courant : bordure client seule, pas de fond ni de halo. Cohérent
   avec le pattern « active » des autres composants (track, commentaires…). */
.versions-modal__item.is-current {
    border-color: var(--c-client);
}

.versions-modal__item-header {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
}
.versions-modal__label {
    font-weight: 650;
    font-size: var(--fs-sm);
    color: var(--c-text);
}
.versions-modal__badge {
    display: inline-flex;
    align-items: center;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: 0.6875rem;
    font-weight: 650;
    flex-shrink: 0;
}
.versions-modal__badge--current {
    background: var(--c-client);
    color: #fff;
}
.versions-modal__spacer { flex: 1; }
.versions-modal__date {
    color: var(--c-text-muted);
    font-size: 0.7rem;
    white-space: nowrap;
}
.versions-modal__meta {
    display: flex;
    min-width: 0;
    font-size: var(--fs-xs);
    line-height: 1.2;
}
.versions-modal__author {
    color: var(--c-text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.versions-modal__actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--sp-2);
}
.versions-modal__action {
    appearance: none;
    background: transparent;
    border: 1px solid var(--c-border);
    color: var(--c-text);
    font: inherit;
    font-size: var(--fs-xs);
    cursor: pointer;
    padding: 4px 10px;
    border-radius: var(--radius-sm);
    transition: background var(--tr-base),
                border-color var(--tr-base),
                color var(--tr-base);
}
.versions-modal__action:hover:not(:disabled) {
    background: var(--c-client);
    border-color: var(--c-client);
    color: var(--c-text-on-dark);
}
.versions-modal__action:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* ── Sous-vue PREVIEW ─────────────────────────────────────────────── */
.versions-modal__preview-header {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding-bottom: var(--sp-3);
    border-bottom: 1px solid var(--c-border);
}
.versions-modal__back {
    appearance: none;
    background: transparent;
    border: 0;
    color: var(--c-text-muted);
    font: inherit;
    font-size: var(--fs-xs);
    cursor: pointer;
    padding: 4px 8px;
    border-radius: var(--radius-sm);
    display: inline-flex;
    align-items: center;
    gap: var(--sp-1);
    transition: background var(--tr-base), color var(--tr-base);
}
.versions-modal__back:hover {
    background: var(--c-bg-subtle);
    color: var(--c-text);
}
.versions-modal__preview-title {
    font-weight: 650;
    font-size: var(--fs-sm);
    color: var(--c-text);
}
.versions-modal__preview-body {
    flex: 1;
    min-height: 0;                                /* pour laisser le scroll parent */
}
/* Rendu ProseMirror read-only : on hérite des styles existants
   `.ProseMirror-root` + `editor-prose.css` déjà chargés dans la page,
   pour parité visuelle avec l'éditeur. On ajoute juste un petit padding
   pour respirer, et un cursor pas-de-texte pour signaler le read-only. */
.versions-modal__preview-view {
    padding: var(--sp-3) 0;
}
.versions-modal__preview-view .ProseMirror {
    outline: none;
    cursor: default;
}
/* Reset du liseré pointillé sur `.editor-chapter-title` — utile en édition
   pour signaler l'atom non-éditable, aucun sens en preview read-only. Même
   règle appliquée dans l'export HTML (cf. HtmlExporter::exportOverridesCss). */
.versions-modal__preview-view .editor-chapter-title,
.versions-modal__preview-view .editor-chapter-title.is-active,
.versions-modal__preview-view .editor-chapter-title.is-hovered {
    border: none !important;
    padding: 0 !important;
    margin-left: 0 !important;
    margin-right: 0 !important;
    cursor: text;
    user-select: text;
    background: transparent;
}

/* ═══════════════════════════════════════════════════════════════════════
 *  Popover flottant au survol d'une mark ou d'un block tracké.
 *  Positionné en fixed via JS (top/left en px). Contenu : badge label +
 *  auteur/date + boutons accept/reject inline (gated par canManageTrack).
 * ═════════════════════════════════════════════════════════════════════ */
.editor-track-hover {
    position: fixed;
    z-index: 400;
    min-width: 200px;
    max-width: 320px;
    background: var(--c-bg-elevated, #fff);
    border: 1px solid var(--c-border, #d0d0d0);
    border-radius: var(--radius-md, 6px);
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
    padding: var(--sp-2, 8px) var(--sp-3, 12px);
    display: flex;
    flex-direction: column;
    gap: var(--sp-1, 4px);
    font-size: 0.75rem;
    pointer-events: auto;
}
.editor-track-hover[hidden] { display: none; }

.editor-track-hover__head {
    display: flex;
    align-items: center;
    gap: var(--sp-1, 4px);
}
.editor-track-hover__badge {
    display: inline-block;
    font-size: 0.6875rem;
    padding: 1px 6px;
    border-radius: 3px;
    text-transform: none;
}
.editor-track-hover__badge--insertion {
    background: color-mix(in srgb, #1f883d 15%, transparent);
    color: #1a6b30;
}
.editor-track-hover__badge--deletion {
    background: color-mix(in srgb, #c4302b 15%, transparent);
    color: #a02520;
}
.editor-track-hover__badge--format_change {
    background: color-mix(in srgb, #6f42c1 15%, transparent);
    color: #5a35a0;
}

.editor-track-hover__meta {
    display: flex;
    align-items: baseline;
    gap: var(--sp-2, 8px);
    color: var(--c-text-muted);
    font-size: 0.6875rem;
}
.editor-track-hover__author { color: var(--c-text); }
.editor-track-hover__date   { font-style: italic; }

.editor-track-hover__actions {
    display: flex;
    justify-content: flex-start;
    gap: var(--sp-1, 4px);
    margin-top: 2px;
}
.editor-track-hover__btn {
    appearance: none;
    background: transparent;
    border: 1px solid var(--c-border, #d0d0d0);
    color: var(--c-text-muted);
    font: inherit;
    font-size: 0.6875rem;
    cursor: pointer;
    padding: 3px 8px;
    border-radius: var(--radius-sm, 3px);
    transition: background var(--tr-base, 0.2s), color var(--tr-base, 0.2s),
                border-color var(--tr-base, 0.2s);
}
.editor-track-hover__btn--accept:hover {
    background: color-mix(in srgb, #1f883d 12%, transparent);
    color: #1a6b30;
    border-color: color-mix(in srgb, #1f883d 40%, transparent);
}
.editor-track-hover__btn--reject:hover {
    background: color-mix(in srgb, #c4302b 12%, transparent);
    color: #a02520;
    border-color: color-mix(in srgb, #c4302b 40%, transparent);
}

/* ═══════════════════════════════════════════════════════════════════════
 *  Popover « insérer une variable »
 *  Ancré sous le bouton toolbar. Liste des variables projet disponibles
 *  avec label + valeur courante. Click sur une entry → insert node PM.
 * ═════════════════════════════════════════════════════════════════════ */
.editor-variable-popover {
    position: absolute;
    z-index: 300;
    background: var(--c-bg-elevated);
    border: 1px solid var(--c-text);
    border-radius: var(--radius-md);
    box-shadow: 0 12px 32px rgba(0,0,0,0.18);
    padding: var(--sp-2);
    display: flex;
    flex-direction: column;
    gap: var(--sp-1);
    min-width: 280px;
    max-width: 380px;
}
.editor-variable-popover__title {
    margin: 0 0 var(--sp-1);
    padding: 0 var(--sp-2);
    font-size: var(--fs-xs);
    font-weight: 650;
    text-transform: none;
    color: color-mix(in srgb, var(--c-text-muted) 65%, transparent);
}
.editor-variable-popover__empty {
    margin: 0;
    padding: var(--sp-3) var(--sp-2);
    font-size: var(--fs-sm);
    font-style: italic;
    color: var(--c-text-muted);
    text-align: center;
}
.editor-variable-popover__list {
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.editor-variable-popover__item {
    appearance: none;
    background: transparent;
    border: 0;
    border-radius: var(--radius-sm);
    padding: var(--sp-2) var(--sp-2);
    cursor: pointer;
    text-align: left;
    font: inherit;
    color: var(--c-text);
    display: flex;
    flex-direction: column;
    gap: 2px;
    transition: background var(--tr-base);
}
.editor-variable-popover__item:hover {
    background: color-mix(in srgb, var(--c-client) 8%, transparent);
}
.editor-variable-popover__item-label {
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--c-text);
}
.editor-variable-popover__item-value {
    font-size: var(--fs-xs);
    color: var(--c-text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.editor-footnote-popover__actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--sp-2);
}
