/* FontAwesome Free 7.3.0 auto-hébergé (CSS + webfonts dans assets/), plus de
   dépendance au kit CDN kit.fontawesome.com (cf. audit best-practices Lighthouse
   + durcissement CSP). L'@import est résolu par AssetMapper (CssAssetUrlCompiler)
   qui réécrit à la fois le chemin de cet import et les url() internes du fichier
   importé (assets/styles/fontawesome.css -> ../webfonts/fa-*.woff2). */
@import "fontawesome-MzuvRx7.css";

/* Police Inter auto-hébergée (variable, latin) - RGPD + sans render-blocking */
@font-face {
    font-family: 'Inter';
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url("../fonts/inter-Gz7AlRl.woff2") format('woff2');
}

/* ---------- Replis à métriques ajustées pour Inter.

   Mesuré en ligne le 2026-08-16 sur la production : le swap d'Inter déplaçait
   le contenu de /faq (CLS 0,051) et /documentation (0,046), et Lighthouse en
   attribuait la plus grosse part - 0,033 et 0,028 - à « Web font loaded ».
   Le `preload` de base.html.twig ne suffit pas et ne pouvait pas suffire : il
   avance l'arrivée du fichier (~2,2 s sous throttling mobile), il ne réconcilie
   pas les métriques d'Inter avec celles du repli système. Tant qu'elles
   diffèrent, la bascule repeint le texte à une autre taille et tout ce qui suit
   descend.

   Chaque repli reçoit donc les overrides qui lui donnent l'encombrement exact
   d'Inter. Le texte occupe la même place avant et après le swap, et le
   décalage disparaît sans rien changer au rendu final.

   VALEURS CALCULÉES, JAMAIS SAISIES À LA MAIN : `@capsizecss/unpack` lu sur
   `assets/fonts/inter.woff2` (unitsPerEm 2048, ascent 1984, descent -494,
   lineGap 0, xWidthAvg 978 à la graisse 400), puis `createFontStack` de
   `@capsizecss/core`. **À recalculer si le fichier de police change** - un
   sous-ensemble regénéré suffit à déplacer `xWidthAvg`, donc `size-adjust`.
   `testInterFallbackMetricsMatchTheShippedFont` tient l'empreinte du fichier
   et casse pour le signaler.

   DEUX FACES PAR REPLI, ET C'EST LE POINT LE PLUS FACILE À PERDRE. Un repli
   sans `font-weight` ne porte qu'une graisse 400 ; tout ce que le premier rendu
   affiche en 600 (`.btn`), 700 (les titres) ou 800 (`.mkt-brand`) tombait donc
   dessus et recevait un GRAS SYNTHÉTIQUE. Or `size-adjust` est calculé sur le
   Regular : il ne compense pas la largeur ajoutée par cette synthèse, et le
   texte gras restait 3 % trop étroit (4,2 % en 800) face au vrai Inter. Le
   titre pouvait donc encore se recasser au swap - exactement le décalage que
   tout ce bloc existe pour supprimer. Chaque repli déclare donc une face
   `100 500` calée sur Inter 400 et une face `600 900` calée sur Inter 700,
   pointant la VRAIE graisse installée (`local('Segoe UI Bold')`...).

   Le poids 700 comme cible de la plage 600-900 : c'est celui des titres, de
   loin le plus présent. Le 600 et le 800 gardent un écart résiduel d'environ
   1 %, contre 2 à 4,2 % avant.

   `system-ui` et `-apple-system` restent en fin de pile et non en tête :
   San Francisco n'est pas atteignable par `local()`, donc on ne peut pas lui
   ajuster ses métriques. macOS et iOS prennent le repli « Helvetica Neue »
   ajusté, présent chez eux d'origine. Lui seul n'a PAS de face `600 900` :
   Helvetica Neue est propriétaire Apple, `@capsizecss/metrics` n'en publie que
   le Regular, et on ne peut pas mesurer son Bold sur un poste Windows. Inventer
   ses métriques serait pire que la synthèse qu'on retire ailleurs.

   Les métriques verticales d'Inter (ascent, descent, lineGap) ne bougent pas
   le long de l'axe `wght` : seul `xWidthAvg` change d'une graisse à l'autre.
   D'où des `ascent-override` / `descent-override` qui ne diffèrent entre les
   deux faces que par le `size-adjust` qui les divise.

   MAINTENANCE : ce bloc et la pile `--font` sont dupliqués dans
   `templates/_partials/_critical_public.css.twig`, qui est celui qui compte
   pour le premier rendu des pages publiques. Les deux se modifient
   ensemble. ---------- */
@font-face {
    font-family: "Inter Fallback Segoe UI";
    src: local('Segoe UI'), local('SegoeUI');
    font-weight: 100 500;
    ascent-override: 89.9412%;
    descent-override: 22.3946%;
    size-adjust: 107.7093%;
}
@font-face {
    font-family: "Inter Fallback Segoe UI";
    src: local('Segoe UI Bold'), local('SegoeUI-Bold');
    font-weight: 600 900;
    ascent-override: 93.5113%;
    descent-override: 23.2836%;
    size-adjust: 103.5971%;
}
@font-face {
    font-family: "Inter Fallback Roboto";
    src: local('Roboto Regular'), local('Roboto-Regular');
    font-weight: 100 500;
    ascent-override: 90.2384%;
    descent-override: 22.4686%;
    size-adjust: 107.3546%;
}
@font-face {
    font-family: "Inter Fallback Roboto";
    src: local('Roboto Bold'), local('Roboto-Bold');
    font-weight: 600 900;
    ascent-override: 88.9943%;
    descent-override: 22.1589%;
    size-adjust: 108.8553%;
}
@font-face {
    font-family: "Inter Fallback Helvetica Neue";
    src: local('Helvetica Neue'), local('HelveticaNeue');
    ascent-override: 91.2883%;
    descent-override: 22.7301%;
    line-gap-override: 0%;
    size-adjust: 106.1198%;
}
@font-face {
    font-family: "Inter Fallback Arial";
    src: local('Arial'), local('ArialMT');
    font-weight: 100 500;
    ascent-override: 90.4365%;
    descent-override: 22.518%;
    line-gap-override: 0%;
    size-adjust: 107.1194%;
}
@font-face {
    font-family: "Inter Fallback Arial";
    src: local('Arial Bold'), local('Arial-BoldMT');
    font-weight: 600 900;
    ascent-override: 94.4723%;
    descent-override: 23.5229%;
    line-gap-override: 0%;
    size-adjust: 102.5432%;
}

/* ============================================================
   fx.diamontis.fr - Design system
   Inspiré de la charte Diamontis (intranet) : indigo #3937a3,
   cartes, sidebar, page-header, badges. Autonome (sans Bootstrap).
   ============================================================ */

:root {
    --primary: #3937a3;
    --primary-rgb: 57, 55, 163;
    --primary-600: #322f93;
    --primary-700: #2a2880;
    --primary-50: #eeeefb;
    --accent: #15b8a6;

    --bg: #f5f6fa;
    --surface: #ffffff;
    --surface-2: #f8f9fc;
    --border: #e6e8ef;
    --text: #1c1f2b;
    --muted: #525a68;

    --success: #16a34a;
    --warning: #d97706;
    --danger: #dc2626;
    --info: #2563eb;

    --radius: 10px;
    --radius-lg: 16px;
    --shadow-sm: 0 1px 2px rgba(16, 24, 40, .06), 0 1px 3px rgba(16, 24, 40, .04);
    --shadow: 0 4px 16px rgba(16, 24, 40, .08);
    --shadow-lg: 0 18px 48px rgba(16, 24, 40, .14);
    --sidebar-w: 256px;

    /* Hauteur commune des contrôles alignés sur une même ligne (cf. .filter-bar).
       Vaut la hauteur naturelle d'un `.form-control` : padding .6rem en haut et
       en bas, texte à .95rem, plus les deux bordures. */
    --control-height: 2.6rem;

    /* Les quatre « Inter Fallback » précèdent les familles système : ce sont
       les mêmes polices, avec les métriques d'Inter. Cf. le bloc @font-face
       plus haut. */
    --font: "Inter", "Inter Fallback Segoe UI", "Inter Fallback Roboto", "Inter Fallback Helvetica Neue", "Inter Fallback Arial", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

* { box-sizing: border-box; }

html, body { height: 100%; }

body {
    margin: 0;
    font-family: var(--font);
    color: var(--text);
    background: var(--bg);
    font-size: 15px;
    line-height: 1.55;
    -webkit-font-smoothing: antialiased;
}

a { color: var(--primary); text-decoration: none; }
a:hover { opacity: .85; }

/* Un lien AU MILIEU D'UN TEXTE ne peut pas se signaler par la seule couleur.
   Entre le violet de marque (#3937a3) et le gris du texte courant, le contraste
   n'est que de 1,35:1 quand WCAG 1.4.1 en exige 3:1 : un daltonien ne voit
   simplement pas qu'il y a un lien. Le soulignement est le second signal,
   indépendant de la couleur.
   Ciblé sur les liens SANS CLASSE, c'est-à-dire ceux du corps de texte : les
   boutons, les cartes cliquables et la navigation portent tous une classe et
   gardent leur apparence. Constat Lighthouse `link-in-text-block` du 2026-08-04,
   relevé sur l'accueil ET les mentions légales - corrigé pour la famille
   entière, pas pour les trois liens cités. */
p > a:not([class]) { text-decoration: underline; text-underline-offset: .15em; }

h1, h2, h3, h4 { line-height: 1.2; margin: 0 0 .5rem; font-weight: 700; letter-spacing: -.01em; }

/* `hidden` doit CACHER, quoi qu'en dise la classe portee par l'element.
   Le `display: none` de l'attribut vient de la feuille du NAVIGATEUR : la
   moindre regle d'auteur posant un `display` (`.empty-state { display: flex }`,
   `.modal-overlay { display: flex }`) gagne et rend l'element visible alors que
   le HTML le declare cache. Le bug s'etait deja paye trois fois, corrige a
   chaque fois au cas par cas - jusqu'a l'ecran /admin/stockage, qui affichait
   « Aucun resultat » AU-DESSUS de la liste de fichiers.
   `!important` est ici a sa place : c'est une regle de derniere instance sur un
   attribut d'etat, pas une surcharge de style. */
[hidden] { display: none !important; }

/* ---------- Buttons ---------- */
.btn {
    display: inline-flex; align-items: center; gap: .5rem;
    padding: .55rem 1rem; border-radius: var(--radius);
    border: 1px solid transparent; font-weight: 600; font-size: .9rem;
    cursor: pointer; transition: all .15s ease; text-decoration: none; white-space: nowrap;
}
.btn:hover { text-decoration: none; transform: translateY(-1px); }
.btn-primary { background: var(--primary); color: #fff; box-shadow: var(--shadow-sm); }
.btn-primary:hover { background: var(--primary-600); color: #fff; }
.btn-outline { background: var(--surface); color: var(--primary); border-color: var(--border); }
.btn-outline:hover { border-color: var(--primary); background: var(--primary-50); }
.btn-ghost { background: transparent; color: var(--muted); }
.btn-ghost:hover { background: var(--surface-2); color: var(--text); }
.btn-lg { padding: .8rem 1.5rem; font-size: 1rem; }
.btn-block { width: 100%; justify-content: center; }
.btn-sm { padding: .35rem .7rem; font-size: .8rem; border-radius: 8px; }
.btn-danger-soft { background: #fdecec; color: var(--danger); border-color: #f8cccc; }
.btn-danger-soft:hover { background: var(--danger); color: #fff; }
/* Action lourde de conséquences mais RÉVERSIBLE (clôturer un client, archiver) :
   neutre au repos - elle n'a pas à crier en permanence au milieu d'actions
   ordinaires - et rouge au survol, c'est-à-dire au moment précis où le geste va
   être fait. À distinguer de `.btn-danger-soft`, qui signale d'emblée une
   destruction. */
.btn-ghost-danger { background: transparent; color: var(--muted); }
.btn-ghost-danger:hover { background: #fdecec; color: var(--danger); }

/* ---------- Forms ---------- */
.form-group { margin-bottom: 1.1rem; }
.form-label { display: block; font-weight: 600; font-size: .85rem; margin-bottom: .4rem; }
.form-control, .form-select {
    width: 100%; padding: .6rem .8rem; font-size: .95rem; font-family: inherit;
    color: var(--text); background: var(--surface);
    border: 1px solid var(--border); border-radius: var(--radius); transition: border-color .15s, box-shadow .15s;
}
.form-control:focus, .form-select:focus {
    outline: none; border-color: var(--primary);
    box-shadow: 0 0 0 3px rgba(var(--primary-rgb), .18);
}
.form-hint { font-size: .78rem; color: var(--muted); margin-top: .35rem; }

/* Champ compact, à hauteur de `.btn-sm` : pour les formulaires POSÉS DANS une
   cellule de tableau, où un champ pleine taille désaligne la ligne. Écrit le
   jour où /admin/fonctions réclamait un `.input-sm` qui n'a jamais existé. */
.form-control-sm { padding: .35rem .6rem; font-size: .82rem; border-radius: 8px; }

/* Barre de filtre : des champs et un bouton d'application sur une même ligne
   (période d'un export, d'un rapport, d'une campagne).

   Le bouton doit prendre la HAUTEUR DU CHAMP et non la sienne. `.btn` est
   calibré pour une barre d'ACTIONS - padding plus serré, police plus petite -
   et posé à côté d'un `.form-control` il paraît systématiquement plus court.
   `align-items: flex-end` alignait bien les bas, ce qui reportait tout l'écart
   en haut : deux contrôles côte à côte de hauteurs différentes.

   Une hauteur commune plutôt qu'un accord de paddings : les deux éléments n'ont
   ni la même police ni le même `line-height`, et faire coïncider leurs paddings
   ne tient que jusqu'à la prochaine retouche de l'un des deux. `box-sizing:
   border-box` est global, la hauteur est donc directement comparable. */
.filter-bar { display: flex; gap: .75rem; align-items: flex-end; flex-wrap: wrap; }
.filter-bar .form-control,
.filter-bar .form-select,
.filter-bar .btn { height: var(--control-height); }

/* Utilitaire d'accessibilité : masqué visuellement mais lu par les lecteurs
   d'écran (contrairement à display:none). Sert notamment à la région
   aria-live du bouton de révélation du mot de passe (D5). */
.visually-hidden {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* ---------- Champ mot de passe avec bouton de révélation (oeil) - D5 ---------- */
.password-field { position: relative; }
.password-field .form-control { padding-right: 2.75rem; }
.password-toggle-btn {
    position: absolute; top: 50%; right: .35rem; transform: translateY(-50%);
    display: flex; align-items: center; justify-content: center;
    width: 2.1rem; height: 2.1rem; padding: 0;
    background: transparent; border: none; border-radius: var(--radius);
    color: var(--muted); cursor: pointer; transition: color .15s, background-color .15s;
}
.password-toggle-btn:hover { color: var(--text); background: var(--surface-2); }
.password-toggle-btn:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; color: var(--text); }

/* Champs date/heure natifs : look cohérent avec .form-control même sans la classe,
   et indicateur de calendrier aux couleurs de la charte. */
.app-content :where(
    input[type="date"], input[type="datetime-local"],
    input[type="month"], input[type="time"], input[type="week"]
) {
    width: 100%; padding: .6rem .8rem; font-size: .95rem; font-family: inherit;
    color: var(--text); background: var(--surface);
    border: 1px solid var(--border); border-radius: var(--radius);
    transition: border-color .15s, box-shadow .15s;
}
.app-content input[type="date"]:focus,
.app-content input[type="datetime-local"]:focus,
.app-content input[type="month"]:focus,
.app-content input[type="time"]:focus,
.app-content input[type="week"]:focus {
    outline: none; border-color: var(--primary);
    box-shadow: 0 0 0 3px rgba(var(--primary-rgb), .18);
}
.app-content input[type="date"]::-webkit-calendar-picker-indicator,
.app-content input[type="datetime-local"]::-webkit-calendar-picker-indicator,
.app-content input[type="month"]::-webkit-calendar-picker-indicator,
.app-content input[type="time"]::-webkit-calendar-picker-indicator,
.app-content input[type="week"]::-webkit-calendar-picker-indicator {
    cursor: pointer; opacity: .55; transition: opacity .15s;
    /* Teinte l'icône calendrier en --primary (#3937a3). */
    filter: invert(20%) sepia(64%) saturate(2200%) hue-rotate(232deg) brightness(85%) contrast(95%);
}
.app-content input[type="date"]::-webkit-calendar-picker-indicator:hover,
.app-content input[type="datetime-local"]::-webkit-calendar-picker-indicator:hover,
.app-content input[type="month"]::-webkit-calendar-picker-indicator:hover,
.app-content input[type="time"]::-webkit-calendar-picker-indicator:hover,
.app-content input[type="week"]::-webkit-calendar-picker-indicator:hover { opacity: 1; }

/* ---------- Zone de dépôt de fichiers (import, GED) ---------- */
.dropzone {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: .6rem; padding: 2.5rem 1.5rem; text-align: center; cursor: pointer;
    border: 2px dashed var(--border); border-radius: var(--radius-lg);
    background: var(--surface-2); transition: border-color .15s, background .15s;
}
.dropzone:hover { border-color: var(--primary); }
.dropzone.is-dragover { border-color: var(--primary); background: var(--primary-50); }
.dropzone .dz-icon { font-size: 2rem; color: var(--primary); }
.dropzone .dz-title { font-weight: 600; }
.dropzone .dz-hint { font-size: .85rem; color: var(--muted); }
.dropzone.dz-compact { padding: 1.6rem 1.25rem; }

/* Champs de fichier : bouton de sélection stylé (au lieu de l'apparence native).
   Les dropzones cachent leur input via style="display:none" inline (non affecté). */
input[type="file"] { cursor: pointer; max-width: 100%; }
input[type="file"]::file-selector-button {
    margin-right: .8rem; padding: .45rem .9rem; border: 0; border-radius: 7px;
    background: var(--primary); color: #fff; font-weight: 600; font-size: .85rem;
    cursor: pointer; transition: background .15s;
}
input[type="file"]::file-selector-button:hover { background: var(--primary-600); }

/* ---------- Éditeur de lignes de facture (émission compacte) ---------- */
.invoice-lines thead th { padding: .5rem .55rem; }
.invoice-lines tbody td { padding: .35rem .5rem; }
.invoice-lines .form-control, .invoice-lines .form-select { padding: .42rem .55rem; font-size: .88rem; }
.invoice-lines .line-total { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* Bouton « ajouter une ligne » : ligne d'ajout pleine largeur (style add-row). */
.line-add {
    display: flex; align-items: center; justify-content: center; gap: .5rem; width: 100%;
    padding: .6rem; margin: .6rem 0 0; border: 1px dashed var(--border); border-radius: var(--radius);
    background: var(--surface-2); color: var(--muted); font-weight: 600; font-size: .88rem;
    cursor: pointer; transition: border-color .15s, color .15s, background .15s;
}
.line-add:hover { border-color: var(--primary); color: var(--primary); background: var(--primary-50); }

.emit-foot { display: flex; justify-content: space-between; align-items: flex-start; gap: 1.5rem; flex-wrap: wrap; margin: 1.1rem 0; }
.emit-totals { min-width: 240px; margin-left: auto; }
.emit-totals > div { display: flex; justify-content: space-between; gap: 2rem; padding: .2rem 0; }
.emit-total-ttc { font-size: 1.1rem; border-top: 1px solid var(--border); margin-top: .3rem; padding-top: .4rem; }

/* Gestionnaire d'étiquettes GED (modal CRUD couleur) */
/* Spécificité .modal.modal-* : .modal (défini plus bas) fixe max-width:440px et
   écraserait .modal-lg/.modal-xl à spécificité égale - on la relève ici. */
.modal.modal-lg { max-width: 560px; }
.modal.modal-xl { max-width: min(1080px, 94vw); width: 100%; }
.tag-manager { display: flex; flex-direction: column; gap: .5rem; margin-top: 1rem; max-height: 52vh; overflow-y: auto; }
.tag-row { display: flex; align-items: center; gap: .5rem; }
.tag-row form { margin: 0; }
.tag-row .tag-edit { display: flex; align-items: center; gap: .5rem; flex: 1; }
.tag-color {
    width: 2.4rem; height: 2.4rem; flex-shrink: 0; padding: 2px; cursor: pointer;
    border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface);
}
.tag-add { border-top: 1px solid var(--border); padding-top: .85rem; margin-top: .35rem; }
.tag-empty { color: var(--muted); font-size: .88rem; padding: .5rem 0; }

/* Barre d'outils GED (recherche + filtres) */
.ged-toolbar { display: flex; gap: .6rem; flex-wrap: wrap; align-items: center; }
.ged-search { position: relative; flex: 1; min-width: 220px; }
.ged-search i { position: absolute; left: .8rem; top: 50%; transform: translateY(-50%); color: var(--muted); font-size: .85rem; }
.ged-search input { padding-left: 2.1rem; }

/* ---------- Cards ---------- */
.card {
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
}
.card-pad { padding: 1.25rem 1.5rem; }
.card-header { padding: 1rem 1.5rem; border-bottom: 1px solid var(--border); font-weight: 600; }
.card-hover { transition: box-shadow .2s, transform .2s; }
.card-hover:hover { box-shadow: var(--shadow); transform: translateY(-2px); }

/* Stat cards */
.stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 1rem; }
.stat-card { display: flex; align-items: center; gap: 1rem; padding: 1.25rem; }
.stat-figure {
    width: 3rem; height: 3rem; flex-shrink: 0; border-radius: var(--radius);
    display: flex; align-items: center; justify-content: center;
    background: var(--primary-50); color: var(--primary); font-size: 1.25rem;
}
.stat-value { font-size: 1.6rem; font-weight: 800; line-height: 1; }
.stat-title { font-size: .72rem; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); margin-top: 10px; }
.stat-subvalue { font-size: .78rem; color: var(--muted); margin-top: .15rem; }

/* ---------- Admin : statistiques produit (barres CSS, sans librairie JS) ---------- */
.section-title { font-size: 1rem; font-weight: 700; margin: 1.75rem 0 .9rem; color: var(--text); }
.bar-list { display: flex; flex-direction: column; gap: .8rem; }
.bar-row { display: grid; grid-template-columns: 8rem 1fr 3.5rem; align-items: center; gap: .75rem; }
.bar-row .bar-label { font-size: .85rem; font-weight: 600; color: var(--text); }
.bar-row .bar-track { background: var(--surface-2); border-radius: 999px; height: .6rem; overflow: hidden; }
.bar-row .bar-fill { display: block; height: 100%; background: var(--primary); border-radius: 999px; transition: width .3s ease; }
.bar-row .bar-count { text-align: right; font-size: .82rem; font-variant-numeric: tabular-nums; color: var(--muted); }
/* Distributions stats (par plan / périodicité) : libellé 20 %, barre 60 %, valeur 20 %.
   `padding-right` sur la valeur : sans lui le texte « 12 (34,5 %) » colle au bord
   droit de la carte (.card-pad n'a pas de marge interne supplémentaire). */
.bar-list--wide .bar-row { grid-template-columns: 20% 60% 20%; }
.bar-list--wide .bar-row .bar-count { padding-right: .75rem; white-space: nowrap; }

/* Occupation de l'offre (/app/abonnement) : la valeur (« 12,3 Go / 75 Go ») doit
   tenir sur UNE seule ligne -> colonne de droite dimensionnée sur son contenu.
   Modificateur dédié plutôt qu'un élargissement de `.bar-row` : `.bar-count`
   sert aussi à la GED, au tableau de bord et à /admin/systeme, où la valeur est
   un pourcentage court que la colonne de 3,5 rem cadre très bien. */
.bar-row--usage { grid-template-columns: 8rem minmax(0, 1fr) minmax(3.5rem, max-content); }
.bar-row--usage .bar-count { white-space: nowrap; }
@media (max-width: 640px) {
    .bar-row--usage { grid-template-columns: 1fr; gap: .35rem; }
    .bar-row--usage .bar-count { text-align: left; }
}
.dynamics-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 1rem; }
.dynamics-card { display: flex; flex-direction: column; gap: .35rem; padding: 1.1rem 1.25rem; }
.dynamics-card .dynamics-value { font-size: 1.4rem; font-weight: 800; line-height: 1; }
.dynamics-card .dynamics-label { font-size: .82rem; color: var(--muted); display: flex; align-items: center; gap: .4rem; }

/* ---------- Tables ---------- */
.table-container { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); overflow-x: auto; -webkit-overflow-scrolling: touch; box-shadow: var(--shadow-sm); }
table.table { width: 100%; border-collapse: collapse; }
.table thead th {
    text-align: left; padding: .75rem 1rem; background: var(--surface-2);
    font-size: .72rem; text-transform: uppercase; letter-spacing: .05em; color: var(--muted);
    border-bottom: 1px solid var(--border);
}
.table tbody td { padding: .8rem 1rem; border-bottom: 1px solid var(--border); }
.table tbody tr:last-child td { border-bottom: 0; }
.table tbody tr { transition: background .12s; }
.table tbody tr:hover { background: var(--surface-2); }
.table .num { text-align: right; font-variant-numeric: tabular-nums; }
/* Historique système : données centrées dans les colonnes. */
.sys-history .table th, .sys-history .table td { text-align: center; }
/* Ligne d'état vide d'un tableau (cellule unique en `colspan`) : message centré
   au milieu du tableau plutôt que collé à la première colonne. Classe portée par
   la cellule, donc valable pour `.table` comme pour `.data-table`. */
.table-empty-cell { text-align: center; padding: 2rem 1rem; }

/* Page « Tâches planifiées » (/admin/supervision/taches) : rappel discret de la
   clé technique, historique compact et indice de retard sous le badge d'état. */
.cron-key { display: block; font-size: .72rem; font-family: var(--font-mono, monospace); }
.cron-late-hint { font-size: .75rem; margin-top: .25rem; }
.cron-runs { list-style: none; margin: 0; padding: 0; font-size: .78rem; color: var(--muted); }
.cron-runs li { white-space: nowrap; }
.cron-row--down td:first-child { box-shadow: inset 3px 0 0 var(--danger); }
.cron-row--degraded td:first-child { box-shadow: inset 3px 0 0 var(--warning); }

/* ---------- Badges ---------- */
.badge {
    display: inline-flex; align-items: center; gap: .3rem; padding: .2rem .6rem;
    border-radius: 999px; font-size: .73rem; font-weight: 600; line-height: 1.4;
    background: var(--surface-2); color: var(--muted); border: 1px solid var(--border);
}
.badge-success { background: #eafaf0; color: #15803d; border-color: #c9f0d8; }
.badge-warning { background: #fef5e7; color: var(--warning); border-color: #fbe3bd; }
.badge-danger  { background: #fdecec; color: var(--danger);  border-color: #f8cccc; }
.badge-info    { background: #eaf1fe; color: var(--info);    border-color: #cfe0fc; }
.badge-primary { background: var(--primary-50); color: var(--primary); border-color: #d9d8f5; }
.badge-neutral { background: var(--surface-2); color: var(--muted); border-color: var(--border); }

/* Badge ACTIONNABLE : le badge lui-même est le bouton (relancer une
   consultation d'annuaire depuis la liste des clients, sans ouvrir la fiche).
   `<button>` et non `<a>` : c'est un POST qui écrit.
   La petite flèche circulaire signale l'action - un badge cliquable qui
   ressemble en tout point à un badge inerte n'invite personne à cliquer. */
/* `min-height` explicite : la hauteur naturelle du badge vaut 23,3 px
   (line-height 1.4 x .73rem + .2rem x 2 + 2 px de bordure), sous les 24 px
   du critère WCAG 2.5.8 - et l'exception « cible en ligne dans une phrase »
   ne vaut pas dans une cellule de tableau. */
.badge-action { cursor: pointer; font-family: inherit; min-height: 24px; transition: filter .15s, box-shadow .15s; }
.badge-action:hover { filter: brightness(.97); box-shadow: var(--shadow-sm); }
.badge-action:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.badge-action .badge-action-icon { font-size: .68rem; opacity: .75; }

/* ---------- Liste déroulante avec recherche ----------
   Champ de filtre posé AU-DESSUS d'un `<select>` natif (contrôleur Stimulus
   searchable-select), pour les listes qui grossissent avec l'usage : carnet
   clients, catalogue produits. Le select reste natif - donc utilisable sans
   JavaScript, et servi par le sélecteur du système sur mobile.
   `.searchable-select-idle` : le contrôleur masque le champ quand la liste est
   assez courte pour se parcourir à l'œil. */
.searchable-select { display: flex; flex-direction: column; gap: .4rem; }
.searchable-select-field { position: relative; display: flex; align-items: center; }
.searchable-select-field > i {
    position: absolute; left: .75rem; color: var(--muted); font-size: .82rem; pointer-events: none;
}
/* 6,5 rem et non 4,5 : « 128 résultat(s) » fait ~87 px à .72rem, il mordait
   sur la zone de saisie dès qu'on tapait un nom un peu long. */
.searchable-select-field .form-control { padding-left: 2.1rem; padding-right: 6.5rem; }
.searchable-select-count {
    position: absolute; right: .75rem; font-size: .72rem; pointer-events: none; white-space: nowrap;
}
.searchable-select-idle .searchable-select-field { display: none; }

/* ---------- Timeline (cycle de vie facture) ---------- */
.timeline { list-style: none; margin: 0; padding: 0; }
.timeline-item { position: relative; padding: 0 0 1.4rem 1.85rem; }
.timeline-item:last-child { padding-bottom: 0; }
.timeline-item::before {
    content: ''; position: absolute; left: .5rem; top: 1.35rem; bottom: -.15rem;
    width: 2px; background: var(--border);
}
.timeline-item:last-child::before { display: none; }
.timeline-dot {
    position: absolute; left: 0; top: .15rem; width: 1.1rem; height: 1.1rem; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    font-size: .52rem; color: #fff; box-shadow: 0 0 0 3px var(--surface);
}
.timeline-label { font-weight: 600; font-size: .9rem; line-height: 1.35; }
.timeline-meta { color: var(--muted); font-size: .78rem; margin-top: .15rem; display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
.timeline-meta .by-you { color: var(--primary); font-weight: 600; }

/* ---------- Alerts / flash ---------- */
.alert { padding: .8rem 1rem; border-radius: var(--radius); margin-bottom: 1rem; border: 1px solid var(--border); font-size: .9rem; background: var(--surface); }
.alert-success { background: #eafaf0; color: #14622f; border-color: #c9f0d8; }
.alert-warning { background: #fef5e7; color: #7a4a06; border-color: #fbe3bd; }
.alert-error, .alert-danger { background: #fdecec; color: #8a1c1c; border-color: #f8cccc; }
.alert-info { background: #eaf1fe; color: #14367a; border-color: #cfe0fc; }

/* ---------- Bannière d'annonce globale (sous la navbar) ---------- */
.announcement {
    display: flex; align-items: flex-start; gap: .7rem;
    padding: .8rem 1rem; margin-bottom: 1rem;
    border: 1px solid var(--border); border-left-width: 4px;
    border-radius: var(--radius); background: var(--surface); font-size: .9rem;
}
.announcement-icon { margin-top: .15rem; flex: 0 0 auto; }
.announcement-body { flex: 1 1 auto; min-width: 0; }
.announcement-body p { margin: 0; }
.announcement-body p + p { margin-top: .4rem; }
.announcement-body ul { margin: .3rem 0 0; padding-left: 1.2rem; }
.announcement-body a { text-decoration: underline; }
.announcement-body code {
    font-size: .82em; background: rgba(0, 0, 0, .06);
    padding: .05rem .3rem; border-radius: 4px;
}
.announcement-close {
    flex: 0 0 auto; background: none; border: 0; cursor: pointer;
    color: inherit; opacity: .55; padding: .1rem .3rem; font-size: 1rem; line-height: 1;
}
.announcement-close:hover { opacity: 1; }
.announcement--info    { background: #eaf1fe; color: #14367a; border-color: #cfe0fc; border-left-color: var(--info); }
.announcement--warning { background: #fef5e7; color: #7a4a06; border-color: #fbe3bd; border-left-color: var(--warning); }
.announcement--success { background: #eafaf0; color: #14622f; border-color: #c9f0d8; border-left-color: var(--success); }
.announcement--danger  { background: #fdecec; color: #8a1c1c; border-color: #f8cccc; border-left-color: var(--danger); }

/* ============================================================
   App layout (sidebar)
   ============================================================ */
.app-layout { display: flex; min-height: 100vh; }

.app-sidebar {
    width: var(--sidebar-w); flex-shrink: 0; background: #1b1c39; color: #c9cae2;
    display: flex; flex-direction: column; position: sticky; top: 0; height: 100vh;
}
.app-brand { display: flex; align-items: center; gap: .65rem; padding: 1.15rem 1.25rem; color: #fff; font-weight: 800; font-size: 1.05rem; }
.app-brand .logo { width: 30px; height: 30px; border-radius: 8px; background: var(--primary); display: flex; align-items: center; justify-content: center; color: #fff; font-weight: 800; }
.app-nav { padding: .5rem .75rem; display: flex; flex-direction: column; gap: .15rem; flex: 1; overflow-y: auto; }
.app-nav .nav-label { font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; color: #6f719c; padding: 1rem .75rem .35rem; }
/* Barre de défilement de la nav thématisée (sidebar sombre) au lieu de la native. */
.app-nav { scrollbar-width: thin; scrollbar-color: rgba(255, 255, 255, .18) transparent; }
.app-nav::-webkit-scrollbar { width: 8px; }
.app-nav::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, .18); border-radius: 8px; }
.app-nav::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, .3); }
.app-nav::-webkit-scrollbar-track { background: transparent; }
.app-nav a {
    display: flex; align-items: center; gap: .7rem; padding: .4rem .5rem; border-radius: var(--radius);
    color: #c9cae2; font-weight: 500; font-size: .92rem; text-decoration: none; transition: background .12s, color .12s;
}
.app-nav a i { width: 1.1rem; text-align: center; opacity: .85; }
.app-nav a:hover { background: rgba(255, 255, 255, .06); color: #fff; }
.app-nav a.active { background: var(--primary); color: #fff; }
.app-nav-bottom { padding: .5rem .75rem; display: flex; flex-direction: column; gap: .15rem; }
.app-nav-bottom a {
    display: flex; align-items: center; gap: .7rem; padding: .6rem .75rem; border-radius: var(--radius);
    color: #c9cae2; font-weight: 500; font-size: .92rem; text-decoration: none; transition: background .12s, color .12s;
}
.app-nav-bottom a i { width: 1.1rem; text-align: center; opacity: .85; }
.app-nav-bottom a:hover { background: rgba(255, 255, 255, .06); color: #fff; }
.app-nav-bottom a.active { background: var(--primary); color: #fff; }

/* ---------- Paramètres : sous-sidebar dédiée ---------- */
.settings-shell { display: flex; gap: 1.5rem; align-items: flex-start; }
.settings-subnav {
    flex-shrink: 0; width: 288px; position: sticky; top: calc(62px + 1.5rem);
    background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: .6rem; box-shadow: var(--shadow-sm);
}
.settings-subnav-title { font-size: .72rem; text-transform: uppercase; letter-spacing: .08em; color: var(--muted); font-weight: 700; padding: .6rem .7rem .5rem; }
.settings-subnav a {
    display: flex; align-items: center; gap: .65rem; padding: .6rem .7rem; border-radius: var(--radius);
    color: var(--text); font-weight: 500; font-size: .9rem; text-decoration: none; transition: background .12s, color .12s;
}
.settings-subnav a i { width: 1.1rem; text-align: center; color: var(--muted); }
.settings-subnav a:hover { background: var(--surface-2); }
.settings-subnav a.active { background: var(--primary-50); color: var(--primary); }
.settings-subnav a.active i { color: var(--primary); }
.settings-content { flex: 1; min-width: 0; }
/* Cards des Paramètres : toujours pleine largeur de la zone de contenu (pas de blanc). */
.settings-content .card { width: 100%; }
/* Sous-nav Paramètres : bouton d'action (ajout entreprise) + séparateur avant suppression. */
.settings-subnav-action { padding: .5rem .35rem 0; }
.settings-subnav-action .btn { width: 100%; justify-content: flex-start; }
.settings-subnav-sep { border: 0; border-top: 1px solid var(--border); margin: .7rem .35rem .4rem; }
/* Badge « À raccorder » cliquable dans la liste des entreprises. */
.company-connect-badge { text-decoration: none; cursor: pointer; }
.company-connect-badge:hover { filter: brightness(.96); text-decoration: none; }

@media (max-width: 860px) {
    .settings-shell { flex-direction: column; align-items: stretch; }
    .settings-subnav { width: 100%; position: static; }
    .settings-content { width: 100%; }
}

/* ---------- Grilles réutilisables ----------
   Elles remplacent les `style="grid-template-columns:..."` posés en attribut :
   un attribut `style` l'emporte sur toute media query, une colonne figée le
   restait donc jusqu'à 320 px de large (un code postal dans 48 px).

   Deux invariants à ne pas perdre :
   - chaque colonne s'écrit `minmax(0, 1fr)` et jamais `1fr` nu : la largeur
     minimale d'une piste de grille vaut `auto`, donc un contenu long (IBAN,
     nom de fichier) élargit la colonne au lieu de se contracter ;
   - tout se replie en une seule colonne sur mobile.

   L'écart se règle par `--grid-gap`, via les modificateurs `.grid-gap-sm`
   (.75rem) / `.grid-gap-md` (1rem) / `.grid-gap-lg` (1.5rem). L'alignement
   vertical est `start` par défaut ; `.grid-stretch` rétablit des colonnes de
   même hauteur (cartes côte à côte). */

/* Utilitaire nu : les colonnes viennent d'ailleurs (motifs `auto-fit`). */
.grid { display: grid; }

.grid-2,
.grid-3,
.grid-ratio-2-1,
.grid-ratio-1-2,
.grid-ratio-1-2-1 {
    display: grid;
    gap: var(--grid-gap, 1rem);
    align-items: start;
}

/* Colonnes égales. */
.grid-2 { --grid-gap: 1.5rem; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }

/* Colonnes inégales : colonne principale à gauche (2-1), à droite (1-2) ou au
   centre (1-2-1, le triplet code postal / ville / pays). */
.grid-ratio-2-1   { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
.grid-ratio-1-2   { grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); }
.grid-ratio-1-2-1 { grid-template-columns: minmax(0, 1fr) minmax(0, 2fr) minmax(0, 1fr); }

/* Modificateurs, déclarés après les grilles pour l'emporter à spécificité égale. */
.grid-gap-sm { --grid-gap: .75rem; }
.grid-gap-md { --grid-gap: 1rem; }
.grid-gap-lg { --grid-gap: 1.5rem; }
.grid-stretch { align-items: stretch; }

@media (max-width: 860px) { .grid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 720px) {
    .grid-2,
    .grid-ratio-2-1,
    .grid-ratio-1-2,
    .grid-ratio-1-2-1 { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 560px) { .grid-3 { grid-template-columns: minmax(0, 1fr); } }

/* En-têtes de page : les actions passent à la ligne sur mobile plutôt que déborder. */
@media (max-width: 640px) {
    .page-header { flex-wrap: wrap; }
    .page-header .page-actions { margin-left: 0; width: 100%; }
    .seats-badge { margin-left: 0; }
}

/* Sélecteur d'entreprise (topbar).
   - Variante simple (mono-entreprise) : nom long tronqué pour ne pas écraser le reste.
   - Variante menu (<details>) : NE PAS tronquer ni masquer le dépassement, sinon le
     panneau déroulant (position:absolute) est clippé par overflow:hidden et le menu
     « ne s'ouvre pas ». Le bouton prend la largeur de son contenu (+ marge) en desktop. */
.app-topbar .company-switch:not(.company-switch-menu) {
    min-width: 0; max-width: 50%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.company-switch-menu { max-width: none; overflow: visible; }
.company-switch-menu summary { min-width: 0; white-space: nowrap; }
/* Sur écran étroit, on retronque le nom dans le bouton pour préserver la topbar. */
@media (max-width: 640px) {
    .company-switch-menu summary { max-width: 55vw; }
    .company-switch-menu summary > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
}

/* ---------- Wizard d'onboarding (pleine largeur) ---------- */
.wizard-head { margin-bottom: 1.75rem; }
.wizard-head .page-title { font-size: 1.6rem; margin: 0; }
.wizard-head .page-subtitle { color: var(--muted); margin: .25rem 0 1.5rem; }

.wizard-lang { display: flex; justify-content: flex-end; margin-bottom: .75rem; }
.wizard-steps { display: flex; list-style: none; padding: 0; margin: 0; }
.wizard-step { flex: 1; display: flex; flex-direction: column; align-items: center; gap: .45rem; position: relative; text-align: center; }
.wizard-step::before {
    content: ''; position: absolute; top: 1rem; left: -50%; width: 100%; height: 2px; background: var(--border); z-index: 0;
}
.wizard-step:first-child::before { display: none; }
.wizard-step.done::before, .wizard-step.current::before { background: var(--primary); }
.wizard-step-dot {
    position: relative; z-index: 1; width: 2rem; height: 2rem; border-radius: 50%;
    display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: .85rem;
    background: var(--surface-2); color: var(--muted); border: 2px solid var(--border);
}
.wizard-step.current .wizard-step-dot { background: var(--primary); color: #fff; border-color: var(--primary); }
.wizard-step.done .wizard-step-dot { background: #15b8a6; color: #fff; border-color: #15b8a6; }
.wizard-step-label { font-size: .82rem; color: var(--muted); }
.wizard-step.current .wizard-step-label { color: var(--text); font-weight: 600; }
@media (max-width: 560px) { .wizard-steps { gap: .25rem; } .wizard-step-label { font-size: .72rem; } }

/* Ruban d'étapes du parcours d'import assisté (/app/factures/importer) : centré
   dans la page, avec une respiration au-dessus de l'étape courante.
   Le `padding-top` est porté par le conteneur et non par le seul `.current` :
   `.wizard-step::before` (le trait de liaison) est positionné à `top:1rem` dans
   le repère du <li>, décaler une seule étape désaligne le trait des pastilles
   voisines. Modificateur dédié parce que `.wizard-steps` sert aussi à
   l'onboarding et à l'inscription, dont la mise en page ne bouge pas. */
.wizard-steps--import { max-width: 640px; margin: 0 auto 1.75rem; padding-top: .85rem; }
.wizard-steps--import .wizard-step { justify-content: center; text-align: center; }

.wizard-body { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: 1.5rem; align-items: start; }
.wizard-aside .card { background: var(--surface); }
.wizard-aside h3 { font-size: 1rem; margin: 0 0 .5rem; }
.wizard-aside p { color: var(--muted); font-size: .88rem; margin: 0 0 .75rem; }
.wizard-aside ul { margin: 0; padding-left: 1.1rem; color: var(--muted); font-size: .88rem; line-height: 1.7; }
@media (max-width: 980px) {
    .wizard-body { grid-template-columns: 1fr; }
}

/* Flash violet d'un champ recopié depuis une suggestion annuaire (onboarding) :
   clignote 2 fois pour matérialiser la recopie SIREN / raison sociale. */
@keyframes field-flash-violet {
    0%, 100% { background-color: var(--surface); box-shadow: 0 0 0 0 rgba(var(--primary-rgb), 0); }
    25%, 75% { background-color: var(--primary-50); box-shadow: 0 0 0 3px rgba(var(--primary-rgb), .35); }
}
.field-flash { animation: field-flash-violet .8s ease-in-out 2; }
@media (prefers-reduced-motion: reduce) { .field-flash { animation: none; } }

/* Retours de la recherche annuaire (onboarding). La consultation recharge la
   page : sans témoin ni message d'absence de résultat, l'écran reste identique
   et l'utilisateur ne sait pas si la recherche a tourné. */
.directory-status {
    display: flex; align-items: center; gap: .5rem;
    margin: 0 0 1rem; font-size: .88rem; color: var(--muted);
}
/* `hidden` doit l'emporter sur le `display:flex` ci-dessus. */

.directory-count {
    display: flex; align-items: center; gap: .5rem;
    margin: 0; padding: .6rem .75rem .2rem;
    font-size: .82rem; font-weight: 600; color: var(--muted);
}

.directory-empty { background: var(--surface-2); padding: .9rem 1rem; }
.directory-empty-title {
    display: flex; align-items: center; gap: .5rem;
    margin: 0 0 .25rem; font-size: .92rem; font-weight: 600;
}
.directory-empty .form-hint { margin: 0; }

.app-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.app-topbar {
    height: 62px; background: var(--surface); border-bottom: 1px solid var(--border);
    display: flex; align-items: center; justify-content: space-between; padding: 0 1.5rem; position: sticky; top: 0; z-index: 20;
}
.app-topbar .company-switch { font-weight: 600; display: flex; align-items: center; gap: .5rem; }

/* Sélecteur d'entreprise active (comptes multi-entreprises) */
.company-switch-menu { position: relative; display: block; }
.company-switch-menu summary {
    list-style: none; cursor: pointer; display: flex; align-items: center; gap: .5rem;
    padding: .4rem .7rem; border-radius: 8px; border: 1px solid transparent; user-select: none;
}
.company-switch-menu summary::-webkit-details-marker { display: none; }
.company-switch-menu summary:hover { background: var(--bg); border-color: var(--border); }
.company-switch-menu[open] summary { background: var(--bg); border-color: var(--border); }
.company-switch-caret { font-size: .65rem; color: var(--muted); transition: transform .15s; }
.company-switch-menu[open] .company-switch-caret { transform: rotate(180deg); }
.company-switch-list {
    position: absolute; top: calc(100% + 6px); left: 0; min-width: 280px; z-index: 50;
    background: var(--surface); border: 1px solid var(--border); border-radius: 10px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, .12); padding: .4rem; display: flex; flex-direction: column;
}
.company-switch-label {
    font-size: .7rem; text-transform: uppercase; letter-spacing: .05em; color: var(--muted);
    padding: .35rem .6rem .25rem; font-weight: 600;
}
.company-switch-item {
    display: flex; align-items: center; gap: .6rem; padding: .5rem .6rem; border-radius: 7px;
    color: inherit; text-decoration: none; font-weight: 500; font-size: .92rem;
}
.company-switch-item small { display: block; color: var(--muted); font-weight: 400; font-size: .75rem; }
.company-switch-item:hover { background: var(--bg); }
.company-switch-item.current { background: var(--bg); cursor: default; }
.company-switch-item.current > i { color: #15b8a6; }
.company-switch-add { border-top: 1px solid var(--border); border-radius: 0 0 7px 7px; margin-top: .25rem; color: var(--muted); font-size: .85rem; }
.app-topbar .user { display: flex; align-items: center; gap: .75rem; color: var(--muted); font-size: .9rem; }
.app-topbar .user-link { display: inline-flex; align-items: center; gap: .5rem; color: inherit; text-decoration: none; padding: .25rem .5rem; border-radius: var(--radius, .5rem); transition: background .15s, color .15s; }
.app-topbar .user-link:hover { color: var(--primary); background: var(--primary-50, rgba(57,55,163,.08)); }

/* Aide de la barre du haut : une icône seule, donc une cible de 36 px - en
   dessous, elle devient impraticable au doigt (WCAG 2.5.8 demande 24 px au
   minimum, et le pouce ne vise pas au pixel). */
.app-topbar .topbar-help {
    display: inline-flex; align-items: center; justify-content: center;
    width: 36px; height: 36px; border-radius: var(--radius, .5rem);
    color: var(--muted); text-decoration: none; transition: background .15s, color .15s;
}
.app-topbar .topbar-help:hover,
.app-topbar .topbar-help:focus-visible,
.app-topbar .topbar-help.active { color: var(--primary); background: var(--primary-50, rgba(57,55,163,.08)); }

/* Infobulle en CSS pur : `title` seul attend une seconde et se rend au style du
   système, et un composant JS ne se justifie pas pour un libellé fixe. Le nom
   accessible reste porté par `aria-label` - l'infobulle est un doublon visuel,
   jamais la seule source de l'information.

   Elle apparaît aussi au clavier (`:focus-visible`), sans quoi elle ne dirait
   rien à qui n'utilise pas de souris, et `pointer-events: none` l'empêche de
   s'interposer entre le curseur et sa propre cible. */
.tip { position: relative; }
.tip::after {
    content: attr(data-tip);
    position: absolute; top: calc(100% + .4rem); left: 50%; transform: translateX(-50%);
    padding: .3rem .55rem; border-radius: .4rem;
    background: var(--text, #1f2937); color: #fff;
    font-size: .74rem; font-weight: 600; line-height: 1.3; white-space: nowrap;
    opacity: 0; visibility: hidden; transition: opacity .12s ease;
    pointer-events: none; z-index: 60;
}
.tip:hover::after,
.tip:focus-visible::after { opacity: 1; visibility: visible; }
@media (prefers-reduced-motion: reduce) { .tip::after { transition: none; } }
/* Pleine largeur assumée : les écrans de ce produit sont des tableaux de
   factures et des grilles de suivi, qui gagnent à chaque pixel. Le plafond de
   1560 px qui valait pour du texte d'aide laissait 700 px de vide de part et
   d'autre sur un 2560 px, tableau tronqué au milieu. Les gouttières passent à
   2.5rem pour que le contenu ne colle pas aux bords. */
.app-content { padding: 1.5rem 2.5rem; flex: 1; display: flex; flex-direction: column; width: 100%; max-width: 100%; }

/* --- Débordement horizontal des listes clé/valeur ---------------------
   Onze gabarits posent `display:grid; grid-template-columns: max-content 1fr`
   en style inline (fiches client, admin, PDP, revue d'import). `max-content` ne
   se contracte pas, et le minimum d'une piste `1fr` est le MIN-CONTENT de son
   contenu : une adresse électronique, un IBAN ou un identifiant d'annuaire est
   un mot insécable, dont le min-content vaut la largeur entière.

   Mesuré sur la fiche client à 320 px : 135 px d'intitulé + 16 de gouttière
   + 200 de valeur = 351 px pour 264 px disponibles. Rien ne clippe en amont
   (`.card` et `.app-content` n'ont pas d'`overflow`, et il n'y a pas
   d'`overflow-x: hidden` sur `body`) : c'est TOUTE LA PAGE qui part en
   défilement horizontal.

   `anywhere` et non `break-word` : seul le premier modifie la taille
   min-content, donc la largeur minimale de la piste qui porte la valeur. */
.app-content dd,
.portal-main dd { min-width: 0; overflow-wrap: anywhere; }

/* Un bouton contraint en largeur ne peut pas rester `nowrap` : sa largeur
   minimale reste celle du libellé complet, il déborde donc de toute colonne
   plus étroite. Cas mesuré : la colonne latérale de la fiche facture offre
   128 px utiles à 861 px de fenêtre, pour un libellé qui en réclame 181. */
.btn-block,
.app-content [style*="width:100%"].btn,
.app-content [style*="width: 100%"].btn { white-space: normal; text-align: center; }

/* Longueur de ligne des textes d'accompagnement. */
.form-hint, .page-subtitle, .modal-message { max-width: 78ch; }

/* ---------- Sélecteur de langue (interrupteur "pilule" FR/EN) ----------
   Composant unique : templates/_partials/_locale_switcher.html.twig,
   piloté par assets/controllers/locale_switch_controller.js. */
.lang-toggle { display: inline-flex; margin: 0; flex-shrink: 0; }
.lang-toggle-track {
    position: relative; display: inline-flex; align-items: center;
    width: 60px; height: 30px; padding: 3px; border-radius: 999px;
    background: var(--surface-2); border: 1px solid var(--border);
    cursor: pointer; user-select: none; transition: border-color .15s;
}
.lang-toggle-track:hover { border-color: var(--primary); }
.lang-toggle-track:focus-visible {
    outline: none; border-color: var(--primary);
    box-shadow: 0 0 0 3px rgba(var(--primary-rgb), .22);
}
.lang-toggle-option {
    position: relative; z-index: 1; flex: 1; display: flex; align-items: center; justify-content: center;
    height: 100%; opacity: .45; transition: opacity .2s ease;
}
.lang-toggle-option svg { width: 18px; height: 12px; border-radius: 2px; display: block; box-shadow: 0 0 0 1px rgba(16, 24, 40, .08); }
.lang-toggle-track:not(.is-en) .lang-toggle-option-fr,
.lang-toggle-track.is-en .lang-toggle-option-en { opacity: 1; }
.lang-toggle-thumb {
    position: absolute; top: 3px; left: 3px; width: 24px; height: 24px; border-radius: 50%;
    background: var(--surface); box-shadow: var(--shadow-sm);
    transition: transform .2s ease; pointer-events: none;
}
.lang-toggle-track.is-en .lang-toggle-thumb { transform: translateX(30px); }
/* Sur la vitrine (fond clair, header flouté) : garder un contraste net. */
.mkt-header .lang-toggle-track { background: #fff; }

/* Footer applicatif : barre fine collée en bas de la zone principale (sticky).

   Trois zones en grille `1fr auto 1fr` plutôt qu'un `space-between` : la
   version reste alors VRAIMENT au centre de la barre, et non au centre de
   l'espace laissé par ses deux voisins - qui n'ont pas la même largeur et n'en
   auront jamais, les crédits étant quatre fois plus longs que l'état du
   réseau. */
.app-footer {
    flex-shrink: 0; padding: .5rem 1.5rem;
    border-top: 1px solid var(--border); background: var(--surface);
    color: var(--muted); font-size: .78rem;
    position: sticky; bottom: 0; z-index: 10;
    display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: .75rem;
}
.app-footer-credits { justify-self: start; }
/* Pied de page à mention unique (portail client) : la grille en trois colonnes
   n'a rien à répartir, la mention se centre sur toute la largeur. */
.app-footer--single { display: flex; justify-content: center; text-align: center; }
.app-footer .app-status { justify-self: end; }
.app-version { justify-self: center; color: var(--muted); opacity: .7; font-variant-numeric: tabular-nums; letter-spacing: .02em; user-select: all; }
.app-footer i.fa-heart { color: #e0245e; margin: 0 .1rem; }
.app-footer a { color: var(--muted); font-weight: 600; }
.app-footer a:hover { color: var(--primary); }

.page-header { display: flex; align-items: center; gap: 1rem; margin-bottom: 1.5rem; }

/* Profil : cartes en grille 2 x 2, repliées en 1 colonne sur mobile/tablette étroite. */
.profile-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); gap: 1.5rem; align-items: start; }
@media (max-width: 720px) { .profile-grid { grid-template-columns: 1fr; } }

/* Badge sièges (page Équipe) : sièges consommés / inclus dans l'offre. */
.seats-badge {
    margin-left: auto; display: flex; flex-direction: column; align-items: flex-end;
    padding: .5rem .85rem; border: 1px solid var(--border); border-radius: 10px;
    background: var(--surface); line-height: 1.2; white-space: nowrap;
}
.seats-badge-main { display: inline-flex; align-items: center; gap: .35rem; font-size: .95rem; }
.seats-badge-main i { color: var(--primary); }
.seats-badge small { color: var(--muted); font-size: .7rem; text-transform: uppercase; letter-spacing: .03em; margin-top: .1rem; }
.seats-badge-full { border-color: #fbe3bd; background: #fef5e7; }
.seats-badge-full .seats-badge-main, .seats-badge-full .seats-badge-main i { color: #7a4a06; }
.page-header .page-title { font-size: 1.5rem; margin: 0; }
.page-header .page-subtitle { color: var(--muted); font-size: .9rem; margin: .15rem 0 0; }
/* `align-items: center` n'est pas cosmétique : sans lui la valeur par défaut est
   `stretch`, et une barre d'actions mélange toujours deux natures d'enfants - le
   lien `<a class="btn">` posé directement, et le bouton emballé dans un `<form>`
   (CSRF oblige). Le premier s'étire sur toute la hauteur de la ligne, le second
   non : son `<form>` s'étire à sa place et le bouton garde sa hauteur propre. Le
   même `.btn` paraissait donc PLUS GROS selon qu'il portait un POST ou non. */
/* `flex-wrap` n'est pas un confort : `.btn` porte `white-space: nowrap`, donc sa
   largeur minimale est celle de son libellé entier. Le repli en colonne
   n'existait qu'en dessous de 640 px, si bien qu'entre 641 px et ~860 px une
   barre à trois boutons pleins - toutes les pages de détail - faisait défiler la
   page HORIZONTALEMENT. Un écran s'en était déjà sorti par un `flex-wrap` posé
   en style inline : le symptôme était connu, la cause non. */
.page-header .page-actions { margin-left: auto; display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; }

/* Empty state */
.empty-state { display: flex; flex-direction: column; align-items: center; text-align: center; padding: 3.5rem 1rem; }
.empty-state .icon { width: 4rem; height: 4rem; border-radius: 50%; background: var(--primary-50); color: var(--primary); display: flex; align-items: center; justify-content: center; font-size: 1.5rem; margin-bottom: 1rem; }
.empty-state h3 { margin: 0 0 .35rem; }
.empty-state p { color: var(--muted); max-width: 26rem; margin: 0 0 1.25rem; }

/* ---------- Auth ---------- */
.auth-wrap { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 2rem; background: radial-gradient(1200px 600px at 100% -10%, rgba(var(--primary-rgb), .12), transparent), var(--bg); }
.auth-card { width: 100%; max-width: 400px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: var(--shadow-lg); padding: 2rem; }
.auth-card .brand { display: flex; align-items: center; gap: .6rem; font-weight: 800; font-size: 1.15rem; margin-bottom: 1.5rem; }
.auth-card .brand.brand-stacked { flex-direction: column; justify-content: center; gap: .5rem; text-align: center; }
.auth-card .brand .logo { width: 34px; height: 34px; border-radius: 9px; background: var(--primary); color: #fff; display: flex; align-items: center; justify-content: center; font-weight: 800; }

/* Onglets de la page de connexion unifiée (Espace fournisseur / Espace client). */
.login-tabs-nav { display: flex; gap: .35rem; background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius); padding: .3rem; margin-bottom: 1.25rem; }
.login-tab { flex: 1; border: 0; background: transparent; color: var(--muted, #6b7280); font: inherit; font-size: .9rem; font-weight: 600; padding: .55rem .5rem; border-radius: calc(var(--radius) - 3px); cursor: pointer; display: inline-flex; align-items: center; justify-content: center; gap: .4rem; transition: background .15s ease, color .15s ease, box-shadow .15s ease; }
.login-tab:hover { color: var(--text, #111827); }
.login-tab.is-active { background: var(--surface); color: var(--primary); box-shadow: 0 1px 2px rgba(0, 0, 0, .08); }
.login-tab { white-space: nowrap; }
/* Carte de connexion élargie : les libellés d'onglets tiennent sur une ligne. */
.auth-card--wide { max-width: 480px; }
/* Inscription : le parcours affiche un fil de 5 étapes en tête, à l'étroit dans
   une carte d'authentification classique. On l'élargit nettement sur grand
   écran seulement - en dessous, la largeur reste pilotée par `auth-card`. */
.auth-card--wizard { max-width: 900px; }
.auth-card--wizard .brand { justify-content: center; margin-bottom: 2.25rem; }
/* Transition fluide au changement d'onglet (le panneau affiché apparaît en fondu). */
.login-panel:not([hidden]) { animation: login-panel-in .18s ease both; }
@keyframes login-panel-in {
    from { opacity: 0; transform: translateY(4px); }
    to { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
    .login-panel:not([hidden]) { animation: none; }
}

/* ============================================================
   Parcours d'authentification - durcissement mobile (2026-07-26)
   Aucune media query ne ciblait `.auth-*` / `.login-*` : tout le parcours
   était servi avec ses valeurs de bureau. Corrigés ici, dans l'ordre :
   débordement horizontal des onglets, zoom iOS au focus, cibles tactiles,
   hauteur de fenêtre, densité verticale sur téléphone.
   ============================================================ */

/* Onglets : autoriser le rétrécissement sous la largeur min-content.
   `white-space: nowrap` (l. 670) combiné au `min-width: auto` implicite d'un
   élément flex figeait la barre à ~307 px, alors que la carte n'en offre que
   190 sur un écran de 320 px -> la barre sortait de la carte, puis du
   document, et toute la page partait en défilement horizontal. */
.login-tab { white-space: normal; min-width: 0; min-height: 44px; }
.login-tab > i { flex: 0 0 auto; }

/* État actif : blanc sur `--surface-2` donne 1,03:1, indiscernable en plein
   jour. Le soulignement porte l'information sans dépendre de la couleur -
   or c'est ici la décision structurante de la page (fournisseur vs client). */
.login-tab.is-active { box-shadow: 0 1px 2px rgba(0, 0, 0, .08), inset 0 -2px 0 var(--primary); }

/* Ni `.btn` ni `.login-tab` n'avaient de focus visible. */
.btn:focus-visible,
.login-tab:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

/* Sur écran tactile, `:hover` reste collé après un appui : le bouton gardait
   son décalage de 1 px et sa teinte jusqu'à l'appui suivant. */
@media (hover: none) {
    .btn:hover { transform: none; }
}

/* Barre d'URL iOS : `100vh` mesure la fenêtre SANS elle, la carte est donc
   poussée sous la ligne de flottaison puis « rebondit ». */
.auth-wrap { min-height: 100svh; }

/* Typographie des écrans d'authentification : elle était posée en
   `style="font-size:..."` dans sept gabarits, qu'aucune media query ne peut
   surcharger (spécificité d'un attribut de style). */
.auth-title { font-size: clamp(1.15rem, 4.6vw, 1.3rem); margin: 0 0 .35rem; }
.auth-subtitle { font-size: .9rem; margin: 0 0 1rem; }
.auth-hint { font-size: .88rem; margin: 0 0 1rem; }
.auth-footnote { font-size: .85rem; }

/* Libellé de champ et lien de service sur la même ligne (« Mot de passe » /
   « Mot de passe oublié ? ») : ils passent l'un sous l'autre si la place
   manque, plutôt que de se chevaucher. */
.form-label-row { display: flex; align-items: baseline; justify-content: space-between; gap: .5rem; flex-wrap: wrap; }
.form-label-row .form-label { margin-bottom: 0; }

/* Séparateur « ou » : les deux panneaux le stylaient différemment (gouttière
   .75rem contre .5rem, marges 1,1rem contre 1,5rem), d'où un saut vertical du
   bouton de clé d'accès au changement d'onglet. `margin: 0` neutralise en
   prime la marge par défaut du navigateur sur `<hr>`, qui gonflait un trait
   d'1 px à 17 px. */
.auth-sep { display: flex; align-items: center; gap: .75rem; margin: 1.1rem 0; }
.auth-sep hr { flex: 1; margin: 0; border: none; border-top: 1px solid var(--border); }
.auth-sep span { font-size: .8rem; white-space: nowrap; }

/* Zone d'erreur remplie en JavaScript (clé d'accès) : masquée tant qu'elle est
   vide, sans dépendre du seul `style="display:none"` du gabarit. */
.auth-async-error { margin-bottom: .75rem; }
.auth-async-error:empty { display: none; }

@media (max-width: 480px) {
    /* 2rem de `.auth-wrap` + 2rem de `.auth-card` de chaque côté, soit 64 px :
       un cinquième de la largeur d'un écran de 320 px passait en marges. */
    .auth-wrap { padding: 1rem .75rem; align-items: flex-start; }
    .auth-card { padding: 1.25rem 1rem; border-radius: var(--radius); box-shadow: var(--shadow); }
    .auth-card .brand { margin-bottom: 1rem; }
    /* Marque en ligne plutôt qu'empilée : ~40 px regagnés au-dessus de la
       ligne de flottaison, où se trouve le bouton de connexion. */
    .auth-card .brand.brand-stacked { flex-direction: row; gap: .5rem; }
    .login-tabs-nav { margin-bottom: 1rem; }
    .login-tab { font-size: .85rem; padding: .5rem .4rem; gap: .3rem; }
    .form-group { margin-bottom: .9rem; }
}

/* Les champs passent à 16 px (zoom automatique iOS) et les boutons à leur
   taille tactile dans le bloc « Adaptation mobile » en fin de fichier, qui
   couvre toute l'application. Ne reste ici que le réglage propre aux écrans
   d'authentification : le bouton oeil grandit avec le champ, il faut donc
   relever le retrait du texte, sans quoi le mot de passe saisi passerait
   dessous. */
@media (pointer: coarse) {
    .password-field .form-control { padding-right: 3.1rem; }
}

/* Écrans d'erreur (404/403/500...) - shell app ou page autonome. */
.error-panel { text-align: center; padding: 1.5rem 0; }
.error-code { font-size: clamp(3.5rem, 12vw, 6rem); font-weight: 800; line-height: 1; color: var(--primary); letter-spacing: -.02em; }
.error-title { font-size: 1.4rem; margin: .5rem 0 .35rem; }
.error-text { max-width: 46ch; margin: 0 auto 1.25rem; }
.error-incident { font-size: .85rem; margin: 0 0 1.25rem; }
.error-incident code { background: var(--surface-2); padding: .1rem .45rem; border-radius: 6px; font-weight: 600; }
.error-actions { display: flex; gap: .6rem; justify-content: center; flex-wrap: wrap; }

/* Ligne d'entreprise active (tableau de bord). */
.table tr.row-selected td { background: var(--surface-2); }
.table tr.row-selected td:first-child { box-shadow: inset 3px 0 0 var(--primary); }

/* Onglets pur CSS de la page Statut (radios frères, CSP-safe). */
.status-tab-input { position: absolute; opacity: 0; pointer-events: none; }
.status-tabs-nav { display: flex; gap: .35rem; background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius); padding: .3rem; margin-bottom: 1.25rem; }
.status-tabs-nav label { flex: 1; text-align: center; padding: .55rem; border-radius: calc(var(--radius) - 3px); cursor: pointer; font-weight: 600; font-size: .9rem; color: var(--muted); display: inline-flex; align-items: center; justify-content: center; gap: .4rem; }
#tab-account:checked ~ .status-tabs-nav label[for="tab-account"],
#tab-system:checked ~ .status-tabs-nav label[for="tab-system"] { background: var(--surface); color: var(--primary); box-shadow: 0 1px 2px rgba(0, 0, 0, .08); }
.status-tab-panel { display: none; }
#tab-account:checked ~ .status-panel-account { display: block; }
#tab-system:checked ~ .status-panel-system { display: block; }

/* ---------- Utilities ---------- */
.container { max-width: 1140px; margin: 0 auto; padding: 0 1.25rem; }
/* Conteneur large pour le site vitrine (marketing) : pleine largeur adaptative. */
.container-wide { width: 100%; max-width: 1440px; margin: 0 auto; padding: 0 clamp(1.25rem, 5vw, 4rem); box-sizing: border-box; }
/* Conteneur des pages de CONTENU long (pages piliers) : une mesure de ligne
   lisible. Les trois gabarits `public/pillar_*.html.twig` l'employaient depuis
   leur livraison alors qu'il n'était défini NULLE PART - mesuré le 13/08 sur
   la production : paragraphes à 1 425 px de large, collés au bord gauche. */
.container-narrow { width: 100%; max-width: 52rem; margin: 0 auto; padding: 0 1.25rem; box-sizing: border-box; }
/* Chapô d'une page de contenu. Même histoire : `.lead` n'existait que sous
   `.hero`, donc le chapô des pages piliers se rendait en paragraphe ordinaire. */
.lead { font-size: 1.12rem; color: var(--muted); }
/* Liste de LIENS (« pour aller plus loin ») : un lien de corps de texte fait
   19 px de haut, sous les 24 px exigés pour une cible tactile - Lighthouse le
   relevait sur `/facturation-electronique-2026` (accessibilité 95). Le
   remplissage vertical porte la cible à ~29 px sans toucher au rythme du texte.
   À réserver aux listes dont CHAQUE élément est un lien. */
.link-list li { margin-bottom: .55rem; }
.link-list a { display: inline-block; padding: .3rem 0; }
.muted { color: var(--muted); }
.text-center { text-align: center; }
.flex { display: flex; }
.items-center { align-items: center; }
.between { justify-content: space-between; }
/* Sans `wrap`, une rangee de boutons deborde en largeur des l'ecran etroit :
   `.btn` porte `white-space: nowrap`, sa largeur minimale est celle de son
   libelle entier. C'est ce qui faisait defiler toute la page horizontalement. */
.wrap { flex-wrap: wrap; }
.gap-1 { gap: .25rem; } .gap-2 { gap: .5rem; } .gap-3 { gap: 1rem; } .gap-4 { gap: 1.5rem; }
.mt-1 { margin-top: .5rem; } .mt-2 { margin-top: 1rem; } .mt-3 { margin-top: 1.5rem; } .mt-4 { margin-top: 2.5rem; }
.mb-0 { margin-bottom: 0; } .mb-2 { margin-bottom: 1rem; } .mb-3 { margin-bottom: 1.5rem; }

/* Navigation mobile : la sidebar devient un tiroir off-canvas piloté par #nav-toggle. */
.nav-burger, .nav-overlay { display: none; }
@media (max-width: 860px) {
    .nav-burger {
        display: inline-flex; align-items: center; justify-content: center;
        width: 40px; height: 40px; flex-shrink: 0; margin-right: .5rem;
        border: 1px solid var(--border); border-radius: 8px; background: var(--surface);
        color: var(--text); cursor: pointer;
    }
    .app-sidebar {
        position: fixed; inset: 0 auto 0 0; z-index: 200; height: 100vh;
        transform: translateX(-100%); transition: transform .2s ease;
        box-shadow: 0 0 40px rgba(0, 0, 0, .35);
    }
    #nav-toggle:checked ~ .app-layout .app-sidebar { transform: none; }
    #nav-toggle:checked ~ .app-layout .nav-overlay {
        display: block; position: fixed; inset: 0; z-index: 150; background: rgba(0, 0, 0, .45);
    }
}

/* ============================================================
   Vitrine (marketing)
   ============================================================ */
.mkt-header { position: sticky; top: 0; z-index: 30; background: rgba(255, 255, 255, .85); backdrop-filter: blur(10px); border-bottom: 1px solid var(--border); }
.mkt-nav { display: flex; align-items: center; justify-content: space-between; height: 68px; }
.mkt-brand { display: flex; align-items: center; gap: .6rem; font-weight: 800; font-size: 1.15rem; color: var(--text); }
.mkt-brand .logo { width: 34px; height: 34px; border-radius: 9px; background: var(--primary); color: #fff; display: flex; align-items: center; justify-content: center; font-weight: 800; }
.mkt-nav-links { display: flex; align-items: center; gap: 1.5rem; }
.mkt-nav-links a { color: var(--text); font-weight: 500; }
.mkt-nav-links a:hover { color: var(--primary); text-decoration: none; }
.mkt-nav-links a.btn-primary, .mkt-nav-links a.btn-primary:hover { color: #fff; }
/* Navigation vitrine sur mobile : menu déroulant piloté par #mkt-nav-toggle.
   Le burger porte tabindex/role="button" (cf. contrôleur `mkt-nav`) : il lui
   faut donc un focus visible, qu'un <label> n'a pas par défaut. */
.mkt-burger { display: none; }
.mkt-burger:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
@media (max-width: 768px) {
    .mkt-burger {
        display: inline-flex; align-items: center; justify-content: center;
        width: 44px; height: 44px; margin-left: auto; cursor: pointer;
        border: 1px solid var(--border); border-radius: 8px; background: #fff; color: var(--text);
    }
    .mkt-nav-links {
        display: none; position: absolute; top: 100%; left: 0; right: 0;
        flex-direction: column; align-items: stretch; gap: .25rem;
        background: #fff; border-bottom: 1px solid var(--border); padding: .5rem 1rem 1rem;
        box-shadow: 0 10px 20px rgba(0, 0, 0, .08);
        max-height: calc(100dvh - 68px); overflow-y: auto;
    }
    /* Cibles tactiles : chaque entrée du menu doit faire au moins 44px de haut. */
    .mkt-nav-links > a { display: flex; align-items: center; min-height: 44px; padding: .25rem .25rem; }
    .mkt-nav-links > a.btn { justify-content: center; padding: .7rem 1rem; margin-top: .35rem; }
    #mkt-nav-toggle:checked ~ .mkt-nav-links { display: flex; }
}

.hero { padding: 5rem 0 4rem; background: radial-gradient(900px 500px at 90% -10%, rgba(var(--primary-rgb), .14), transparent); overflow: hidden; }
.hero h1 { font-size: clamp(2.1rem, 4.5vw, 3.4rem); letter-spacing: -.02em; max-width: 14ch; }
.hero p.lead { font-size: 1.2rem; color: var(--muted); max-width: 46ch; margin: 1.25rem 0 2rem; }
.hero .cta-row { display: flex; gap: .75rem; flex-wrap: wrap; }
.hero .eyebrow { display: inline-flex; align-items: center; gap: .5rem; background: var(--primary-50); color: var(--primary); font-weight: 600; font-size: .82rem; padding: .35rem .8rem; border-radius: 999px; margin-bottom: 1.25rem; }

/* Hero à deux colonnes (texte + schéma SVG) - le schéma passe sous le texte
   en dessous de 900px (cf. media query). */
.hero-grid { display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr); gap: 2.5rem; align-items: center; }
.hero-visual { display: flex; justify-content: center; }
.hero-perks { list-style: none; display: flex; flex-wrap: wrap; gap: .5rem 1.5rem; padding: 0; margin: 1.5rem 0 0; }
.hero-perks li { display: flex; align-items: center; gap: .45rem; font-size: .85rem; color: var(--muted); }
.hero-perks li i { color: var(--success); }
@media (max-width: 900px) {
    .hero-grid { grid-template-columns: 1fr; gap: 2.25rem; }
    .hero-visual { order: 2; }
}

.section { padding: 4rem 0; }
.section-head { text-align: center; max-width: 40rem; margin: 0 auto 2.5rem; }
.section-head h2 { font-size: clamp(1.6rem, 3vw, 2.3rem); }
.section-head p { color: var(--muted); font-size: 1.05rem; }

.feature-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.75rem; }
.feature { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 2rem 1.5rem; box-shadow: var(--shadow-sm); transition: transform .2s, box-shadow .2s; text-align: center; }
.feature:hover { transform: translateY(-3px); box-shadow: var(--shadow); }
.feature .fic { width: 3rem; height: 3rem; border-radius: var(--radius); background: var(--primary-50); color: var(--primary); display: flex; align-items: center; justify-content: center; font-size: 1.3rem; margin: 0 auto 1.25rem; }
.feature h3 { font-size: 1.1rem; margin-bottom: .5rem; }
.feature p { color: var(--muted); margin: 0; font-size: .95rem; line-height: 1.55; }

@media (max-width: 1024px) { .feature-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .feature-grid { grid-template-columns: 1fr; } }

.schema-wrap { width: 100%; overflow-x: auto; margin-bottom: 2.5rem; }

/* Quatre étapes : même rythme responsive que .feature-grid (4 / 2 / 1), pour
   eviter l'orphelin d'un auto-fit qui retombe a trois colonnes en tablette. */
.steps { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.5rem; }
@media (max-width: 1024px) { .steps { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .steps { grid-template-columns: 1fr; } }
/* Pastille de numéro centrée dans sa carte, comme l'icône des cartes .feature. */
.step { text-align: center; }
.step .n { width: 2.25rem; height: 2.25rem; border-radius: 50%; background: var(--primary); color: #fff; display: flex; align-items: center; justify-content: center; font-weight: 800; margin: 0 auto .8rem; }

/* Migration depuis une autre Plateforme Agréée : réutilise les cartes .feature
   dans une grille à 3 colonnes qui se replie sans largeur minimale imposée
   (min(...,100%) évite tout débordement horizontal sous 260px utiles). */
.migration-grid {
    display: grid; gap: 1.5rem; align-items: stretch;
    grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
    max-width: 1140px; margin: 0 auto;
}
.migration-note { max-width: 52rem; margin: 1.75rem auto 0; text-align: center; font-size: .9rem; line-height: 1.6; }

.cta-band { background: linear-gradient(135deg, var(--primary), #5654c9); color: #fff; border-radius: var(--radius-lg); padding: 3rem; text-align: center; }
.cta-band h2 { color: #fff; }
.cta-band p { color: rgba(255, 255, 255, .85); max-width: 40rem; margin: .5rem auto 1.75rem; }
.cta-band .btn-primary { background: #fff; color: var(--primary); }
.cta-band .btn-primary:hover { background: #f1f1fb; }

.mkt-footer { border-top: 1px solid var(--border); padding: 2.5rem 0; color: var(--muted); font-size: .9rem; }
.mkt-footer a { color: var(--muted); }
.mkt-footer a:hover { color: var(--primary); }

/* État de service : pastille + libellé, lien vers la page de statut. Deux
   emplacements, même règle - le pied de page vitrine (`mkt-`) et celui de
   l'application (`app-`). Ils disent la même chose, lue dans le même cache ;
   deux jeux de couleurs qui divergeraient au fil des retouches seraient un
   piège pour qui compare les deux pendant un incident. */
.mkt-status,
.app-status { display: inline-flex; align-items: center; gap: .45rem; font-weight: 600; }
.mkt-status-dot,
.app-status-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--muted, #9ca3af); flex: none; }
.mkt-status--operational .mkt-status-dot,
.app-status--operational .app-status-dot { background: var(--success, #16a34a); animation: mktStatusPulse 2s infinite; }
.mkt-status--degraded .mkt-status-dot,
.app-status--degraded .app-status-dot { background: var(--warning, #d97706); }
.mkt-status--down .mkt-status-dot,
.app-status--down .app-status-dot { background: var(--danger, #dc2626); }
.mkt-status--unknown .mkt-status-dot,
.app-status--unknown .app-status-dot { background: var(--muted, #9ca3af); }
/* Dans le pied de page applicatif, l'état n'est ni un titre ni une alerte : il
   suit la couleur du texte alentour, et ne se colore que par sa pastille. */
.app-footer .app-status { color: var(--muted); text-decoration: none; white-space: nowrap; }
.app-footer .app-status:hover { color: var(--primary); }
@keyframes mktStatusPulse {
    0% { box-shadow: 0 0 0 0 rgba(22, 163, 74, .5); }
    70% { box-shadow: 0 0 0 6px rgba(22, 163, 74, 0); }
    100% { box-shadow: 0 0 0 0 rgba(22, 163, 74, 0); }
}
@media (prefers-reduced-motion: reduce) {
    .mkt-status--operational .mkt-status-dot,
    .app-status--operational .app-status-dot { animation: none; }
}

/* Page Statut (Paramètres) : bandeau global + pastilles d'état par catégorie. */
.status-banner { display: flex; align-items: center; gap: .9rem; border-left: 4px solid var(--muted, #9ca3af); }
.status-dot-lg { width: 14px; height: 14px; border-radius: 50%; background: var(--muted, #9ca3af); flex: none; }
.status-banner--operational { border-left-color: var(--success, #16a34a); }
.status-banner--operational .status-dot-lg { background: var(--success, #16a34a); }
.status-banner--degraded { border-left-color: var(--warning, #d97706); }
.status-banner--degraded .status-dot-lg { background: var(--warning, #d97706); }
.status-banner--down { border-left-color: var(--danger, #dc2626); }
.status-banner--down .status-dot-lg { background: var(--danger, #dc2626); }
.status-pill { display: inline-flex; align-items: center; gap: .4rem; font-size: .82rem; font-weight: 600; white-space: nowrap; }
.status-pill .status-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--muted, #9ca3af); flex: none; }
.status-pill--operational { color: var(--success, #16a34a); }
.status-pill--operational .status-dot { background: var(--success, #16a34a); }
.status-pill--degraded { color: var(--warning, #d97706); }
.status-pill--degraded .status-dot { background: var(--warning, #d97706); }
.status-pill--down { color: var(--danger, #dc2626); }
.status-pill--down .status-dot { background: var(--danger, #dc2626); }
.status-pill--unknown { color: var(--muted, #9ca3af); }

/* Grille de cards d'état par sous-service (onglet « État du système »), une
   card par sonde `/health`, regroupées par catégorie fonctionnelle. Le liseré
   de gauche reprend le code couleur du bandeau global. */
.status-category { margin-top: 1.5rem; }
.status-category-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; flex-wrap: wrap; margin-bottom: .65rem; }
.status-category-title { font-size: 1rem; font-weight: 700; margin: 0; color: var(--text); }
.status-category-desc { font-size: .82rem; color: var(--muted); margin: .15rem 0 0; }
.status-cards-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: .75rem; }
.status-card { display: flex; align-items: center; gap: .8rem; padding: .9rem 1rem; border-left: 3px solid var(--border); }
.status-card-icon {
    width: 2.25rem; height: 2.25rem; flex: none; border-radius: var(--radius);
    display: flex; align-items: center; justify-content: center;
    background: var(--surface-2); color: var(--muted); font-size: .95rem;
}
.status-card-body { display: flex; flex-direction: column; gap: .2rem; min-width: 0; }
.status-card-label { font-size: .88rem; font-weight: 600; color: var(--text); }
.status-card--operational { border-left-color: var(--success, #16a34a); }
.status-card--degraded { border-left-color: var(--warning, #d97706); }
.status-card--down { border-left-color: var(--danger, #dc2626); }
.status-card--unknown { border-left-color: var(--muted, #9ca3af); }

/* Onglet « Mon compte » : le nom de l'entreprise bascule l'entreprise active. */
.status-company-link { display: inline-flex; align-items: center; gap: .45rem; color: inherit; text-decoration: none; }
.status-company-link:hover { color: var(--primary); text-decoration: underline; }
.status-company-link i { font-size: .72rem; color: var(--muted); }
.status-company-link:hover i { color: var(--primary); }

/* Interrupteur (toggle) rouge/vert - préférences de notifications. */
.toggle { position: relative; display: inline-block; width: 46px; height: 26px; flex-shrink: 0; }
.toggle input { position: absolute; opacity: 0; width: 0; height: 0; }
.toggle .slider { position: absolute; inset: 0; background: var(--danger, #dc2626); border-radius: 999px; cursor: pointer; transition: background .15s; }
.toggle .slider::before { content: ""; position: absolute; height: 20px; width: 20px; left: 3px; top: 3px; background: #fff; border-radius: 50%; transition: transform .15s; box-shadow: 0 1px 2px rgba(0, 0, 0, .3); }
.toggle input:checked + .slider { background: var(--success, #16a34a); }
.toggle input:checked + .slider::before { transform: translateX(20px); }
.toggle input:focus-visible + .slider { outline: 2px solid var(--primary); outline-offset: 2px; }
.notif-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: .85rem 1rem; border: 1px solid var(--border); border-radius: .5rem; cursor: pointer; }

/* GED : filtres/recherche masquables (details) + bouton stockage discret. */
.ged-filters { margin: 0 0 1rem; }
.ged-filters-summary { display: inline-flex; align-items: center; gap: .5rem; cursor: pointer; font-size: .9rem; font-weight: 600; color: var(--muted); padding: .4rem .2rem; list-style: none; user-select: none; }
.ged-filters-summary::-webkit-details-marker { display: none; }
.ged-filters-summary::marker { content: ""; }
.ged-filters-summary:hover { color: var(--text); }
.ged-filters[open] .ged-filters-summary { color: var(--text); margin-bottom: .6rem; }
.ged-storage-btn.is-warning { color: var(--warning, #d97706); border-color: var(--warning, #d97706); }
.ged-storage-btn.is-critical { color: var(--danger, #dc2626); border-color: var(--danger, #dc2626); }

/* Import PDF : ajout dynamique d'un client (formulaire pré-rempli à confirmer). */
.import-addclient { margin-top: .6rem; border-top: 1px dashed var(--border); padding-top: .6rem; }
.import-addclient-summary { display: inline-flex; align-items: center; gap: .4rem; cursor: pointer; font-size: .82rem; font-weight: 600; color: var(--primary); list-style: none; user-select: none; }
.import-addclient-summary::-webkit-details-marker { display: none; }
.import-addclient-summary::marker { content: ""; }

.legal { max-width: 800px; margin: 0 auto; padding: 3rem 1.25rem; }
.legal h1 { margin-bottom: 1.5rem; }
.legal h2 { font-size: 1.2rem; margin-top: 2rem; }
.legal h3 { font-size: 1rem; margin-top: 1.25rem; }
.legal p { color: #333; line-height: 1.6; }
.legal ul { color: #333; line-height: 1.6; padding-left: 1.25rem; }
.legal li { margin-bottom: .4rem; }
.legal .draft-note { display: block; margin-top: .5rem; font-size: .85rem; font-style: italic; color: #92400e; background: #fffbeb; border-left: 3px solid #f59e0b; padding: .6rem .85rem; border-radius: 4px; }
.legal .todo-note { display: block; margin-top: .5rem; font-size: .85rem; font-style: italic; color: #1e40af; background: #eff6ff; border-left: 3px solid #3b82f6; padding: .6rem .85rem; border-radius: 4px; }

/* ---------- Admin : navigation par onglets (liens) ---------- */
.admin-tabs { display: flex; flex-wrap: wrap; gap: .25rem; border-bottom: 1px solid var(--border); margin-bottom: 1.5rem; }
.admin-tabs a {
    display: inline-flex; align-items: center; gap: .5rem;
    padding: .7rem 1.1rem; margin-bottom: -1px;
    font-weight: 600; font-size: .9rem; color: var(--muted);
    border-bottom: 2px solid transparent; text-decoration: none;
    transition: color .15s, border-color .15s;
}
.admin-tabs a:hover { color: var(--text); }
.admin-tabs a.active { color: var(--primary); border-bottom-color: var(--primary); }

/* ---------- Admin : monitoring des outils ---------- */
.health-summary { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem; margin-bottom: 1.5rem; }
.health-pill { padding: .3rem .7rem; border-radius: 999px; border: 1px solid var(--border); font-size: .82rem; }
.health-pill strong { font-weight: 800; }
.health-docker { margin-left: auto; font-size: .85rem; }
.health-group-title { font-size: 1rem; font-weight: 700; margin: 1.5rem 0 .75rem; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }

.tools-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(330px, 1fr)); gap: 1rem; }
.tool-card { border-left: 4px solid var(--border); }
.tool-card.tool-ok { border-left-color: var(--success); }
.tool-card.tool-degraded { border-left-color: var(--warning); }
.tool-card.tool-down { border-left-color: var(--danger); }
.tool-card.tool-unknown { border-left-color: var(--muted); }

.tool-card-head { display: flex; align-items: center; gap: .75rem; }
.tool-icon { width: 2.4rem; height: 2.4rem; flex-shrink: 0; border-radius: var(--radius); background: var(--primary-50); color: var(--primary); display: flex; align-items: center; justify-content: center; font-size: 1.05rem; }
.tool-title { display: flex; flex-direction: column; gap: .25rem; min-width: 0; }
.tool-title strong { font-size: .98rem; }
.tool-title .badge { align-self: flex-start; }
.tool-latency { margin-left: auto; font-size: .78rem; color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.tool-message { font-size: .85rem; color: var(--text); margin: .8rem 0 .6rem; }

.tool-details { display: grid; grid-template-columns: 1fr; gap: .25rem; margin: 0 0 .6rem; }
.tool-details > div { display: flex; justify-content: space-between; gap: 1rem; font-size: .8rem; border-bottom: 1px dashed var(--border); padding-bottom: .2rem; }
.tool-details dt { color: var(--muted); margin: 0; }
.tool-details dd { margin: 0; text-align: right; font-variant-numeric: tabular-nums; word-break: break-all; }

/* Tâches planifiées : ligne mise en évidence quand la tâche est en retard.
   Dégradé -> orange, hors service (retard massif) -> rouge, pour repérer
   immédiatement la tâche fautive sans lire chaque valeur. */
.tool-details > div.tool-detail-degraded,
.tool-details > div.tool-detail-down {
    margin: 0 -.4rem; padding: .25rem .4rem; border-radius: 6px;
    border-bottom-color: transparent;
}
.tool-details > div.tool-detail-degraded { background: #fef5e7; border-left: 3px solid var(--warning); }
.tool-details > div.tool-detail-degraded dt,
.tool-details > div.tool-detail-degraded dd { color: var(--warning); font-weight: 600; }
.tool-details > div.tool-detail-down { background: #fdecec; border-left: 3px solid var(--danger); }
.tool-details > div.tool-detail-down dt,
.tool-details > div.tool-detail-down dd { color: var(--danger); font-weight: 700; }

.tool-actions { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .25rem; }
.tool-actions form { margin: 0; }

/* ---------- Modale de confirmation (remplace window.confirm) ---------- */
.modal-overlay {
    position: fixed; inset: 0; z-index: 1000; padding: 1rem;
    display: flex; align-items: center; justify-content: center;
    background: rgba(16, 24, 40, .55); opacity: 0; transition: opacity .15s ease;
}
.modal-overlay.is-open { opacity: 1; }
.modal {
    background: var(--surface); border-radius: var(--radius-lg); box-shadow: var(--shadow-lg);
    width: 100%; max-width: 440px; padding: 1.5rem;
    transform: translateY(10px) scale(.98); transition: transform .15s ease;
}
.modal-overlay.is-open .modal { transform: none; }
.modal-head { display: flex; align-items: center; gap: .8rem; margin-bottom: .25rem; }
/* Le titre prend la place restante et la croix est poussée au bout de la ligne.
   Sans cela, `.modal-head` étant un flex de trois enfants, la croix se collait
   au titre - au milieu de l'en-tête sur une modale large, là où tout le monde
   la cherche en haut à droite. `min-width: 0` autorise le titre à se réduire
   plutôt qu'à repousser la croix hors de la boîte. */
.modal-head .modal-title { flex: 1 1 auto; min-width: 0; }
.modal-head .modal-close { margin-left: auto; flex-shrink: 0; }
.modal-icon {
    width: 2.5rem; height: 2.5rem; flex-shrink: 0; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    background: var(--primary-50); color: var(--primary); font-size: 1.1rem;
}
.modal-icon-danger { background: #fdecec; color: var(--danger); }
.modal-title { margin: 0; font-size: 1.1rem; }
.modal-message { color: var(--muted); font-size: .92rem; margin: .5rem 0 0; line-height: 1.5; }
.modal-actions { display: flex; justify-content: flex-end; gap: .5rem; margin-top: 1.5rem; }

/* ---------- Modale de confirmation SANS JavaScript (ancre :target) ----------
   Variante 100% CSS de la modale ci-dessus, sur le modèle des onglets en radios
   plus bas : la fermeture d'une ligne d'annuaire de routage est un acte
   irréversible qui doit rester confirmable même sans JS (CSP stricte, script
   bloqué, navigateur dégradé). L'ouverture est un simple lien `#id`, la
   fermeture un lien `#`. */
.modal-overlay-target { display: none; opacity: 1; }
.modal-overlay-target:target { display: flex; }
.modal-overlay-target:target .modal { transform: none; }

/* ---------- Annuaire de routage (adresses de réception) ---------- */
.directory-scope {
    display: flex; align-items: flex-start; gap: .75rem;
    padding: .8rem 1rem; margin-bottom: .5rem; cursor: pointer;
    border: 1px solid var(--border); border-radius: var(--radius);
    transition: border-color .12s, background .12s;
}
.directory-scope:hover { background: var(--surface-2); }
.directory-scope:has(input:checked) { border-color: var(--primary); background: var(--primary-50); }
.directory-scope > span { display: flex; flex-direction: column; gap: .2rem; }
.directory-scope .muted { font-size: .85rem; line-height: 1.45; }
/* Maille dont l'adresse est déjà ouverte : elle reste LISIBLE (l'utilisateur
   doit comprendre pourquoi elle lui est refusée) mais n'est plus choisissable.
   Le badge « déjà active » porte l'information, la couleur ne fait que
   l'appuyer - un daltonien lit le badge. */
.directory-scope.is-taken { cursor: default; background: var(--surface-2); opacity: .75; }
.directory-scope.is-taken:hover { background: var(--surface-2); }
.directory-scope.is-taken strong { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }

/* Chaque maille à complément a son champ propre : le SIRET et le suffixe
   n'attendent ni le même format, ni la même aide. Seul celui de la maille
   cochée est montré.
   Formulé en `:not(:has(...))` À DESSEIN : un navigateur sans `:has()` ignore
   la règle entière et affiche les deux champs, ce qui reste utilisable. La
   forme directe (masquer par défaut, révéler par `:has()`) les aurait au
   contraire masqués tous les deux, rendant le formulaire inutilisable. */
.directory-form:not(:has(#scope_siren_siret:checked)) .directory-siret { display: none; }
.directory-form:not(:has(#scope_siren_suffixe:checked)) .directory-suffixe { display: none; }
.directory-duplicate { color: var(--danger); }

/* Champ précédé d'une partie non saisissable (ici le SIREN de l'entreprise, qui
   vient de la base et jamais de la saisie). Le préfixe est un `<span>`, pas un
   champ désactivé : il n'y a donc rien à soumettre, ni à neutraliser côté
   serveur. */
.input-group { display: flex; align-items: stretch; }
.input-group-text {
    display: flex; align-items: center;
    padding: .55rem .75rem;
    border: 1px solid var(--border); border-right: 0;
    border-radius: var(--radius) 0 0 var(--radius);
    background: var(--surface-2); color: var(--muted);
    font-size: .95rem; font-variant-numeric: tabular-nums; white-space: nowrap;
}
.input-group > .form-control { min-width: 0; border-radius: 0 var(--radius) var(--radius) 0; }
/* Le préfixe peut dépasser la largeur disponible sur un petit écran : il passe
   alors au-dessus du champ plutôt que de l'écraser. */
@media (max-width: 480px) {
    .input-group { flex-direction: column; }
    .input-group-text {
        border-right: 1px solid var(--border); border-bottom: 0;
        border-radius: var(--radius) var(--radius) 0 0;
    }
    .input-group > .form-control { border-radius: 0 0 var(--radius) var(--radius); }
}

/* ---------- Onglets (CSS pur, radios masqués - aucune dépendance JS) ---------- */
.tabs > .tab-radio,
.settings-shell > .tab-radio {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.tab-nav {
    display: flex; gap: .25rem; flex-wrap: wrap;
    border-bottom: 1px solid var(--border); margin-bottom: 1.5rem;
}
.tab-nav label {
    display: inline-flex; align-items: center; gap: .55rem;
    padding: .7rem 1.1rem; margin-bottom: -1px; cursor: pointer;
    font-weight: 600; font-size: .9rem; color: var(--muted);
    border: 1px solid transparent; border-bottom: 2px solid transparent;
    border-top-left-radius: var(--radius); border-top-right-radius: var(--radius);
    transition: color .15s, border-color .15s, background .15s;
}
.tab-nav label:hover { color: var(--text); background: var(--surface-2); }
.tab-nav label .tab-count {
    font-size: .72rem; font-weight: 700; line-height: 1; padding: .15rem .45rem;
    border-radius: 999px; background: var(--surface-2); color: var(--muted);
    border: 1px solid var(--border);
}
.tab-panel { display: none; }

/* Onglets e-reporting : le radio coché (général sibling ~) pilote l'onglet actif
   et l'affichage du panneau correspondant (data-tab). 100% CSS, aucun JS. */
#er-tab-tx:checked   ~ .tab-nav label[for="er-tab-tx"],
#er-tab-pay:checked  ~ .tab-nav label[for="er-tab-pay"],
#er-tab-intl:checked ~ .tab-nav label[for="er-tab-intl"],
#er-tab-ppf:checked  ~ .tab-nav label[for="er-tab-ppf"] {
    color: var(--primary); border-bottom-color: var(--primary);
}
#er-tab-tx:checked   ~ .tab-nav label[for="er-tab-tx"]   .tab-count,
#er-tab-pay:checked  ~ .tab-nav label[for="er-tab-pay"]  .tab-count,
#er-tab-intl:checked ~ .tab-nav label[for="er-tab-intl"] .tab-count,
#er-tab-ppf:checked  ~ .tab-nav label[for="er-tab-ppf"]  .tab-count {
    background: var(--primary-50); color: var(--primary); border-color: #d9d8f5;
}
#er-tab-tx:checked   ~ .tab-panels .tab-panel[data-tab="tx"],
#er-tab-pay:checked  ~ .tab-panels .tab-panel[data-tab="pay"],
#er-tab-intl:checked ~ .tab-panels .tab-panel[data-tab="intl"],
#er-tab-ppf:checked  ~ .tab-panels .tab-panel[data-tab="ppf"] { display: block; }

/* Accessibilité clavier : anneau de focus visible sur l'onglet ciblé. */
#er-tab-tx:focus-visible   ~ .tab-nav label[for="er-tab-tx"],
#er-tab-pay:focus-visible  ~ .tab-nav label[for="er-tab-pay"],
#er-tab-intl:focus-visible ~ .tab-nav label[for="er-tab-intl"],
#er-tab-ppf:focus-visible  ~ .tab-nav label[for="er-tab-ppf"] {
    outline: 2px solid var(--primary); outline-offset: 2px; border-radius: var(--radius);
}

/* ---------- Configuration effective : onglets par domaine (CSS pur) ----------
   Les onglets sont numérotés et NON nommés d'après ConfigSectionKey : ajouter un
   domaine à l'enum ne demande donc aucune retouche ici, tant qu'il y en a moins
   de 8. Nommer les sélecteurs d'après l'enum aurait fait qu'un domaine ajouté
   plus tard aurait un onglet dont le panneau ne s'ouvre jamais - une page à
   moitié muette, sans rien d'autre pour le signaler. */
#cfg-tab-0:checked ~ .tab-nav label[for="cfg-tab-0"],
#cfg-tab-1:checked ~ .tab-nav label[for="cfg-tab-1"],
#cfg-tab-2:checked ~ .tab-nav label[for="cfg-tab-2"],
#cfg-tab-3:checked ~ .tab-nav label[for="cfg-tab-3"],
#cfg-tab-4:checked ~ .tab-nav label[for="cfg-tab-4"],
#cfg-tab-5:checked ~ .tab-nav label[for="cfg-tab-5"],
#cfg-tab-6:checked ~ .tab-nav label[for="cfg-tab-6"],
#cfg-tab-7:checked ~ .tab-nav label[for="cfg-tab-7"] {
    color: var(--primary); border-bottom-color: var(--primary);
}
#cfg-tab-0:checked ~ .tab-nav label[for="cfg-tab-0"] .tab-count,
#cfg-tab-1:checked ~ .tab-nav label[for="cfg-tab-1"] .tab-count,
#cfg-tab-2:checked ~ .tab-nav label[for="cfg-tab-2"] .tab-count,
#cfg-tab-3:checked ~ .tab-nav label[for="cfg-tab-3"] .tab-count,
#cfg-tab-4:checked ~ .tab-nav label[for="cfg-tab-4"] .tab-count,
#cfg-tab-5:checked ~ .tab-nav label[for="cfg-tab-5"] .tab-count,
#cfg-tab-6:checked ~ .tab-nav label[for="cfg-tab-6"] .tab-count,
#cfg-tab-7:checked ~ .tab-nav label[for="cfg-tab-7"] .tab-count {
    background: var(--primary-50); color: var(--primary); border-color: #d9d8f5;
}
#cfg-tab-0:checked ~ .tab-panels .tab-panel[data-tab="0"],
#cfg-tab-1:checked ~ .tab-panels .tab-panel[data-tab="1"],
#cfg-tab-2:checked ~ .tab-panels .tab-panel[data-tab="2"],
#cfg-tab-3:checked ~ .tab-panels .tab-panel[data-tab="3"],
#cfg-tab-4:checked ~ .tab-panels .tab-panel[data-tab="4"],
#cfg-tab-5:checked ~ .tab-panels .tab-panel[data-tab="5"],
#cfg-tab-6:checked ~ .tab-panels .tab-panel[data-tab="6"],
#cfg-tab-7:checked ~ .tab-panels .tab-panel[data-tab="7"] { display: block; }
#cfg-tab-0:focus-visible ~ .tab-nav label[for="cfg-tab-0"],
#cfg-tab-1:focus-visible ~ .tab-nav label[for="cfg-tab-1"],
#cfg-tab-2:focus-visible ~ .tab-nav label[for="cfg-tab-2"],
#cfg-tab-3:focus-visible ~ .tab-nav label[for="cfg-tab-3"],
#cfg-tab-4:focus-visible ~ .tab-nav label[for="cfg-tab-4"],
#cfg-tab-5:focus-visible ~ .tab-nav label[for="cfg-tab-5"],
#cfg-tab-6:focus-visible ~ .tab-nav label[for="cfg-tab-6"],
#cfg-tab-7:focus-visible ~ .tab-nav label[for="cfg-tab-7"] {
    outline: 2px solid var(--primary); outline-offset: 2px; border-radius: var(--radius);
}

/* ---------- Fiche client admin : onglets latéraux (CSS pur) ----------
   Réutilise l'apparence de la sous-nav Paramètres (.settings-subnav) mais
   pilotée par des radios masqués (.tab-radio, déjà défini plus haut) plutôt
   que par des liens : un seul panneau .tab-panel visible à la fois, aucune
   dépendance JS (CSP stricte à nonce). */
.settings-subnav label {
    display: flex; align-items: center; gap: .65rem; padding: .6rem .7rem; border-radius: var(--radius);
    color: var(--text); font-weight: 500; font-size: .9rem; cursor: pointer; transition: background .12s, color .12s;
}
.settings-subnav label i { width: 1.1rem; text-align: center; color: var(--muted); }
.settings-subnav label:hover { background: var(--surface-2); }
.settings-subnav label .badge { margin-left: auto; }

#sub-tab-overview:checked     ~ .settings-subnav label[for="sub-tab-overview"],
#sub-tab-subscription:checked ~ .settings-subnav label[for="sub-tab-subscription"],
#sub-tab-moderation:checked   ~ .settings-subnav label[for="sub-tab-moderation"],
#sub-tab-companies:checked    ~ .settings-subnav label[for="sub-tab-companies"],
#sub-tab-activity:checked     ~ .settings-subnav label[for="sub-tab-activity"] {
    background: var(--primary-50); color: var(--primary);
}
#sub-tab-overview:checked     ~ .settings-subnav label[for="sub-tab-overview"] i,
#sub-tab-subscription:checked ~ .settings-subnav label[for="sub-tab-subscription"] i,
#sub-tab-moderation:checked   ~ .settings-subnav label[for="sub-tab-moderation"] i,
#sub-tab-companies:checked    ~ .settings-subnav label[for="sub-tab-companies"] i,
#sub-tab-activity:checked     ~ .settings-subnav label[for="sub-tab-activity"] i {
    color: var(--primary);
}
#sub-tab-overview:checked     ~ .settings-content .tab-panel[data-tab="overview"],
#sub-tab-subscription:checked ~ .settings-content .tab-panel[data-tab="subscription"],
#sub-tab-moderation:checked   ~ .settings-content .tab-panel[data-tab="moderation"],
#sub-tab-companies:checked    ~ .settings-content .tab-panel[data-tab="companies"],
#sub-tab-activity:checked     ~ .settings-content .tab-panel[data-tab="activity"] {
    display: block;
}
#sub-tab-overview:focus-visible     ~ .settings-subnav label[for="sub-tab-overview"],
#sub-tab-subscription:focus-visible ~ .settings-subnav label[for="sub-tab-subscription"],
#sub-tab-moderation:focus-visible   ~ .settings-subnav label[for="sub-tab-moderation"],
#sub-tab-companies:focus-visible    ~ .settings-subnav label[for="sub-tab-companies"],
#sub-tab-activity:focus-visible     ~ .settings-subnav label[for="sub-tab-activity"] {
    outline: 2px solid var(--primary); outline-offset: 2px; border-radius: var(--radius);
}

/* --- Grille tarifaire (vitrine) --------------------------------------- */
.hero-compact { padding-bottom: 1rem; }
/* `.mt-3` et `.text-center` étaient REDÉCLARÉS ici, le premier à 1rem : la
   seconde déclaration gagnant, tout le projet croyait écrire 1,5rem et obtenait
   la valeur de `.mt-2`. Les utilitaires vivent une seule fois, avec le reste
   (cf. `.mt-1 … .mt-4`) ; toute exception se porte sur une classe de composant,
   jamais en redéfinissant un utilitaire. */

/* Cartes elargies (1140px au lieu de 1000px) pour limiter les retours a la
   ligne des libelles de fonctionnalites. `min(300px, 100%)` en borne basse :
   la piste ne peut jamais depasser la largeur utile du conteneur, donc pas de
   debordement horizontal sur les petits ecrans (< 340px de viewport). */
.pricing-grid {
    display: grid; gap: 1.5rem; align-items: stretch;
    grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
    max-width: 1140px; margin: 0 auto;
}
/* Offres non self-service (Cabinet + ETI) : 2 cartes centrees sous la ligne de 3.
   Le point de rupture suit l'elargissement : 2 x 280px + 1.5rem de gouttiere
   demandent ~584px utiles, soit ~660px de viewport avec le padding de
   .container-wide - d'ou le repli a une colonne des 680px. */
.pricing-grid--contact {
    max-width: 760px; margin-top: 1.5rem;
    grid-template-columns: repeat(2, minmax(min(280px, 100%), 1fr));
}
@media (max-width: 680px) { .pricing-grid--contact { grid-template-columns: 1fr; } }
.pricing-card {
    position: relative; display: flex; flex-direction: column;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
    padding: 1.75rem 1.75rem;
}
.pricing-card-featured {
    border-color: var(--primary);
    box-shadow: 0 0 0 3px rgba(var(--primary-rgb), .18), var(--shadow);
}
.pricing-badge {
    position: absolute; top: -.75rem; left: 50%; transform: translateX(-50%);
}
.pricing-name { margin: 0 0 .5rem; font-size: 1.15rem; }
.pricing-price { display: flex; align-items: baseline; gap: .4rem; margin-bottom: .25rem; flex-wrap: wrap; }
.pricing-amount { font-size: 2rem; font-weight: 700; color: var(--text); }
/* Offres « contact » : le montant est une phrase (« dès 31 € », « à partir de
   99 € »), pas un nombre - une taille plus mesuree le garde sur une ligne. */
.pricing-grid--contact .pricing-amount { font-size: 1.5rem; }
.pricing-period { font-size: .85rem; color: var(--muted); }
.pricing-tagline { margin: 0 0 1.25rem; min-height: 2.5em; }
.pricing-storage {
    display: flex; align-items: center; gap: .5rem; margin: -.5rem 0 1.25rem;
    font-size: .85rem; font-weight: 600; color: var(--primary);
}
.pricing-storage i { color: var(--accent); }
.pricing-features { list-style: none; padding: 0; margin: 0 0 1.5rem; flex: 1; }
.pricing-features li { display: flex; align-items: flex-start; gap: .55rem; padding: .35rem 0; font-size: .92rem; }
.pricing-features li i { color: var(--success); margin-top: .2rem; }

/* --- Bascule mensuel / annuel (pur CSS) ------------------------------- */
.pricing-toggle-input { position: absolute; opacity: 0; pointer-events: none; }
.pricing-toggle {
    display: inline-flex; gap: .25rem; margin: 0 auto 1.75rem; padding: .25rem;
    background: var(--surface-2); border: 1px solid var(--border);
    border-radius: 999px; justify-content: center;
}
.pricing { display: flex; flex-direction: column; align-items: center; }
.pricing-toggle label {
    display: inline-flex; align-items: center; gap: .4rem; cursor: pointer;
    padding: .4rem 1rem; border-radius: 999px; font-size: .9rem; font-weight: 600;
    color: var(--muted); transition: background .15s, color .15s;
}
#bi-monthly:checked ~ .pricing-toggle label[for="bi-monthly"],
#bi-yearly:checked ~ .pricing-toggle label[for="bi-yearly"] {
    background: var(--surface); color: var(--text); box-shadow: var(--shadow-sm);
}
.pricing-toggle-input:focus-visible ~ .pricing-toggle label[for="bi-monthly"],
.pricing-toggle-input:focus-visible ~ .pricing-toggle label[for="bi-yearly"] {
    outline: 2px solid var(--primary); outline-offset: 2px;
}
.pricing-grid { width: 100%; }
.price-monthly, .price-yearly { display: inline-flex; align-items: baseline; gap: .4rem; }
.price-yearly { display: none; }
#bi-yearly:checked ~ .pricing-grid .price-monthly { display: none; }
#bi-yearly:checked ~ .pricing-grid .price-yearly { display: inline-flex; }
/* Toggle periodicite dans le tunnel d'onboarding (grille .offre-grid). */
#bi-yearly:checked ~ .offre-grid .price-monthly { display: none; }
#bi-yearly:checked ~ .offre-grid .price-yearly { display: inline; }
.offre-grid .price-yearly { display: none; }

/* Cartes de choix de plan (onboarding) : le radio est masque, la selection est
   materialisee par la bordure + la pastille de coche, en suivant le choix en
   direct (:has, sans JS - CSP-safe). */
.offre-card { cursor: pointer; display: block; border: 2px solid var(--border); transition: border-color .15s, box-shadow .15s; }
.offre-card input[name="plan"] { position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none; }
.offre-card-check { color: var(--border); font-size: 1.15rem; transition: color .15s; }
.offre-card:hover { border-color: var(--primary-200, var(--primary)); }
.offre-card:has(input[name="plan"]:checked) { border-color: var(--primary); box-shadow: 0 0 0 1px var(--primary); }
.offre-card:has(input[name="plan"]:checked) .offre-card-check { color: var(--primary); }
.offre-card:has(input[name="plan"]:focus-visible) { outline: 2px solid var(--primary); outline-offset: 2px; }

/* Offre mise en avant dans le wizard, alignée sur la vitrine /tarifs : les deux
   écrans doivent recommander la même chose, sous peine de donner l'impression
   que le conseil change entre la page publique et l'inscription. */
.offre-card-featured { position: relative; border-color: var(--primary-200, var(--primary)); }
.offre-card-badge { position: absolute; top: -.7rem; right: 1rem; }

/* Quota de stockage de l'offre (même information que .pricing-storage) */
.offre-card-storage {
    display: flex; align-items: center; gap: .4rem;
    margin: .4rem 0 0; font-size: .8rem; color: var(--muted);
}

/* Renvoi vers les offres sur devis (Cabinet, ETI), non self-service */
.offre-bigger-needs {
    margin: 1.25rem 0 0; padding: .85rem 1rem;
    background: var(--surface-2); border-radius: var(--radius, 8px);
    font-size: .85rem; color: var(--muted); line-height: 1.6;
}
.offre-bigger-needs strong { color: var(--text); margin-right: .25rem; }
.offre-bigger-needs a { white-space: nowrap; }

/* Features « a venir » (roadmap) sur les grilles tarifaires */
.pricing-features li.feature-coming { color: var(--muted); }

/* ============================================================
   Admin - Stockage objet (/admin/stockage)
   ============================================================ */

/* --- Carte de téléversement --- */
.storage-upload-card { max-width: 720px; margin-bottom: 1.25rem; }
.storage-upload-title {
    display: flex; align-items: center; gap: .6rem;
    font-size: .98rem; margin-bottom: 1rem;
}
.storage-upload-form { display: flex; flex-direction: column; gap: 0; }
.storage-upload-row { display: flex; gap: .75rem; align-items: flex-end; flex-wrap: wrap; }

/* Input file custom : masque le natif, affiche un bouton cohérent avec .btn */
.storage-file-input {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.storage-file-label {
    display: flex; align-items: center; gap: .55rem;
    width: 100%; padding: .55rem .85rem;
    border: 1px solid var(--border); border-radius: var(--radius);
    background: var(--surface); color: var(--muted);
    font-size: .92rem; cursor: pointer;
    transition: border-color .15s, box-shadow .15s, color .15s;
    user-select: none;
}
.storage-file-label:hover {
    border-color: var(--primary); color: var(--text);
}
.storage-file-input:focus-visible + .storage-file-label,
.storage-file-label:focus-within {
    border-color: var(--primary);
    box-shadow: 0 0 0 3px rgba(var(--primary-rgb), .18);
    outline: none;
}
.storage-file-label.has-file {
    border-color: var(--primary); color: var(--text); font-weight: 600;
}
.storage-file-label.has-file .storage-file-icon { color: var(--primary); }
.storage-file-icon {
    width: 1.6rem; height: 1.6rem; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
    border-radius: 6px; background: var(--primary-50);
    color: var(--primary); font-size: .85rem;
    transition: background .15s, color .15s;
}
.storage-file-text { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* --- Barre de recherche + filtres --- */
.storage-toolbar {
    display: flex; gap: .75rem; align-items: center; flex-wrap: wrap;
    margin-bottom: 1rem; padding: .9rem 1.25rem;
}
.storage-search-wrap { position: relative; flex: 1 1 220px; min-width: 0; }
.storage-search-icon {
    position: absolute; left: .8rem; top: 50%; transform: translateY(-50%);
    color: var(--muted); font-size: .82rem; pointer-events: none;
}
.storage-search-input { padding-left: 2.2rem; }

.storage-filter-wrap { display: flex; gap: .5rem; flex-wrap: wrap; }
.storage-filter-icon {
    position: absolute; left: .8rem; top: 50%; transform: translateY(-50%);
    color: var(--muted); font-size: .8rem; pointer-events: none; z-index: 1;
}
.storage-filter-select { padding-left: 2.2rem; min-width: 160px; }

.storage-counter {
    margin-left: auto; font-size: .82rem; color: var(--muted);
    white-space: nowrap; padding: .3rem .6rem;
    background: var(--surface-2); border: 1px solid var(--border);
    border-radius: 999px; font-weight: 600;
}

/* Pagination du tableau des objets. `wrap` par principe : deux boutons et un
   libelle tiennent large, mais `.btn` ne se compresse pas (white-space: nowrap). */
.storage-pagination {
    display: flex; align-items: center; justify-content: center;
    flex-wrap: wrap; gap: .75rem;
    padding: 1rem; border-top: 1px solid var(--border);
}
.storage-pagination .btn:disabled { opacity: .45; cursor: default; transform: none; }

/* --- Tableau des objets --- */
.storage-file-cell { max-width: 420px; }
.storage-file { display: flex; align-items: center; gap: .6rem; min-width: 0; }
.storage-file-thumb {
    flex: 0 0 auto; width: 38px; height: 38px; border-radius: 6px;
    object-fit: cover; background: var(--surface-2); border: 1px solid var(--border);
}
.storage-file-ico {
    flex: 0 0 auto; width: 38px; height: 38px; border-radius: 6px;
    display: inline-flex; align-items: center; justify-content: center;
    font-size: 1rem; border: 1px solid var(--border); background: var(--surface-2);
}
.storage-file-meta { min-width: 0; display: flex; flex-direction: column; line-height: 1.25; }
.storage-file-name {
    font-weight: 600; font-size: .86rem; white-space: nowrap;
    overflow: hidden; text-overflow: ellipsis;
}
.storage-file-dir {
    font-size: .72rem; color: var(--muted); white-space: nowrap;
    overflow: hidden; text-overflow: ellipsis;
}
.storage-company-name { display: block; font-weight: 600; font-size: .88rem; }
.storage-company-id { font-size: .73rem; }

/* --- Couleurs par type métier (icône de fichier + anciens badges) --- */
.storage-type-archive { background: #eaf1fe; color: var(--info);    border-color: #cfe0fc; }
.storage-type-import  { background: #fef5e7; color: var(--warning); border-color: #fbe3bd; }
.storage-type-ged     { background: #eafaf0; color: var(--success); border-color: #c9f0d8; }
.storage-type-autre   { background: var(--surface-2); color: var(--muted); border-color: var(--border); }

/* Responsive : resserre la cellule et masque la colonne ID sur les petits écrans */
@media (max-width: 860px) {
    .storage-file-cell { max-width: 200px; }
    .storage-company-id { display: none; }
    .storage-filter-select { min-width: 120px; }
}
.pricing-features li.feature-coming i { color: var(--muted); }
.feature-soon { font-size: .62rem; vertical-align: middle; margin-left: .15rem; }
.feature-coming { opacity: .85; }

/* ---------- Modale de saisie (motif de refus) ---------- */
.modal-input { max-width: 520px; }
.modal-body { margin: .75rem 0 0; }
.modal-body .form-label { font-size: .85rem; }
.modal-body .form-control { margin-top: .35rem; }

/* ---------- Sélecteur de client destinataire (emission) ---------- */
.emit-client-header {
    display: flex; align-items: center; justify-content: space-between;
    flex-wrap: wrap; gap: .5rem; margin-bottom: .35rem;
}
.emit-client-header .form-label { margin: 0; }
.emit-client-actions { display: flex; gap: .4rem; flex-wrap: wrap; }
.required-star { color: var(--danger); margin-left: .15rem; }
.emit-no-client {
    display: flex; align-items: flex-start; gap: 1rem;
    padding: 1.25rem 1.5rem; margin-bottom: 0;
}
.emit-no-client > i { font-size: 1.4rem; color: var(--warning); margin-top: .15rem; flex-shrink: 0; }
.emit-no-client > div > p { margin: .35rem 0 .75rem; font-size: .92rem; }

/* ---------- Dropzone de dépôt de fichiers ---------- */
.dropzone {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: .75rem; padding: 3.5rem 1.5rem;
    border: 2px dashed var(--border); border-radius: var(--radius-lg);
    background: var(--surface-2); cursor: pointer; text-align: center;
    transition: border-color .15s, background .15s;
}
.dropzone.is-dragover,
.dropzone:hover { border-color: var(--primary); background: var(--primary-50); }
.dz-icon { font-size: 2.5rem; color: var(--primary); }
.dz-title { font-weight: 600; }
.dz-hint { font-size: .85rem; color: var(--muted); }

/* ---------- Import par lot - revue groupée ---------- */
.batch-item { display: grid; grid-template-columns: 1fr auto; gap: 1rem; align-items: start; }
.batch-item-info { min-width: 0; }
.batch-item-actions { display: flex; flex-direction: column; gap: .6rem; min-width: 240px; }
.batch-exclude-row { display: flex; align-items: center; gap: .5rem; padding-top: .25rem; }

/* ---------- Import en lot : modal (backdrop) + progression + rapport ---------- */
body.modal-open { overflow: hidden; }
.import-modal { position: fixed; inset: 0; z-index: 1000; display: flex; align-items: center; justify-content: center; padding: 1rem; }
/* L'attribut hidden doit primer sur display:flex (même spécificité sinon). */
.import-modal-backdrop { position: absolute; inset: 0; background: rgba(15, 23, 42, .55); }
.import-modal-dialog {
    position: relative; z-index: 1; width: 100%; max-width: 520px; max-height: 90vh; overflow-y: auto;
    background: var(--surface); border-radius: 14px; border: 1px solid var(--border);
    box-shadow: 0 24px 60px rgba(0, 0, 0, .25); padding: 1.75rem;
}
.import-modal-close {
    position: absolute; top: .85rem; right: .85rem; width: 34px; height: 34px; border-radius: 8px;
    border: 0; background: transparent; color: var(--muted); cursor: pointer; font-size: 1.1rem;
}
.import-modal-close:hover { background: var(--bg); color: var(--text); }
.import-modal-title { font-size: 1.15rem; margin: 0 0 .5rem; display: flex; align-items: center; gap: .5rem; }
.import-modal-title i { color: var(--primary); }
.import-modal-help { color: var(--muted); font-size: .9rem; margin: 0 0 1rem; }
.import-modal-model { margin-bottom: 1rem; }
.import-modal-form { display: flex; flex-direction: column; gap: 1rem; }
.import-dropzone {
    display: flex; flex-direction: column; align-items: center; gap: .5rem; padding: 1.5rem;
    border: 2px dashed var(--border); border-radius: 10px; cursor: pointer; color: var(--muted); text-align: center;
    transition: border-color .15s, color .15s;
}
.import-dropzone:hover { border-color: var(--primary); color: var(--primary); }
.import-dropzone i { font-size: 1.6rem; }
.import-progress-track { height: 10px; background: var(--bg); border-radius: 999px; overflow: hidden; }
.import-progress-track > span { display: block; height: 100%; width: 0; background: var(--primary); transition: width .3s ease; }
.import-progress-status { margin: .6rem 0 0; font-size: .88rem; color: var(--muted); text-align: center; }
.import-report-summary { display: flex; flex-wrap: wrap; gap: .5rem 1rem; padding: .75rem 1rem; border-radius: 10px; font-size: .9rem; }
.import-report-summary.is-ok { background: #eafaf7; border: 1px solid #bfe9e4; color: #0f6b60; }
.import-report-summary.is-fail { background: #fdecec; border: 1px solid #f5c6c6; color: #a02222; }
.import-report-list { list-style: none; margin: .75rem 0 0; padding: 0; max-height: 220px; overflow-y: auto; font-size: .85rem; display: flex; flex-direction: column; gap: .35rem; }
.import-report-list li { padding: .4rem .6rem; border-radius: 7px; background: var(--bg); }
.import-report-list li.import-report-error { background: #fdecec; color: #a02222; }
.import-report-list li.import-report-warning { background: #fef5e7; color: #7a4a06; }
.import-report-actions { display: flex; justify-content: flex-end; gap: .5rem; margin-top: 1rem; }

/* ---------- Widget de retour/assistance (bulle bas-droite) ---------- */
.feedback-widget { position: fixed; right: 1.5rem; bottom: 1.5rem; z-index: 900; }
.feedback-bubble {
    width: 3.4rem; height: 3.4rem; border-radius: 50%; border: 0; cursor: pointer;
    display: flex; align-items: center; justify-content: center; font-size: 1.3rem;
    background: var(--primary); color: #fff; box-shadow: var(--shadow-lg);
    transition: transform .15s ease, background .15s ease;
}
.feedback-bubble:hover { background: var(--primary-600); transform: translateY(-2px); }
.feedback-bubble:focus-visible { outline: 3px solid var(--primary-50); outline-offset: 2px; }
.feedback-dismiss {
    position: absolute; top: -.35rem; right: -.35rem; width: 1.5rem; height: 1.5rem;
    border-radius: 50%; border: 1px solid var(--border); background: var(--surface);
    color: var(--muted); font-size: .65rem; cursor: pointer; z-index: 1;
    display: flex; align-items: center; justify-content: center; box-shadow: var(--shadow-sm);
}
.feedback-dismiss:hover { color: var(--danger); border-color: #f8cccc; }
.feedback-panel {
    position: absolute; right: 0; bottom: calc(100% + .85rem);
    width: min(340px, calc(100vw - 3rem));
    background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg); padding: 1.1rem;
}
.feedback-panel-head { display: flex; align-items: center; justify-content: space-between; gap: .5rem; margin-bottom: .35rem; }
.feedback-panel-title { font-size: 1rem; margin: 0; display: flex; align-items: center; gap: .5rem; color: var(--text); }
.feedback-panel-title i { color: var(--primary); }
.feedback-panel-close {
    border: 0; background: transparent; color: var(--muted); cursor: pointer;
    width: 1.8rem; height: 1.8rem; border-radius: 6px; font-size: 1rem; flex-shrink: 0;
}
.feedback-panel-close:hover { background: var(--surface-2); color: var(--text); }
.feedback-panel-intro { color: var(--muted); font-size: .85rem; margin: 0 0 .9rem; }
.feedback-submit { width: 100%; justify-content: center; }
@media (max-width: 480px) {
    .feedback-widget { right: 1rem; bottom: 1rem; }
    .feedback-panel { right: -.5rem; }
}

/* ============================================================
   Console d'administration : menu profil (dropdown) + thème admin
   dédié. Contexte indépendant de l'espace utilisateur (cf.
   templates/layout/admin.html.twig). Accent ambre = zone privilégiée.
   ============================================================ */
:root {
    --admin: #3937a3;      /* violet Diamontis (aligné sur la marque) */
    --admin-600: #2d2b81;  /* violet plus sombre (hover/actif) */
    --admin-50: #edeefb;
    --admin-sidebar: #201f3a;  /* nuit violette (cohérent avec l'accent) */
}

/* ---- Menu profil (topbar, dropdown <details>) ---- */
.user-menu { position: relative; display: inline-block; }
.user-menu > summary {
    list-style: none; cursor: pointer; display: inline-flex; align-items: center; gap: .5rem;
    padding: .3rem .6rem; border-radius: 8px; border: 1px solid transparent; user-select: none; color: inherit;
}
.user-menu > summary::-webkit-details-marker { display: none; }
.user-menu > summary:hover, .user-menu[open] > summary { background: var(--bg); border-color: var(--border); }
.user-menu-name { font-weight: 600; max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.user-menu-caret { font-size: .65rem; color: var(--muted); transition: transform .15s; }
.user-menu[open] .user-menu-caret { transform: rotate(180deg); }
.user-menu-list {
    position: absolute; top: calc(100% + 6px); right: 0; min-width: 250px; z-index: 50;
    background: var(--surface); border: 1px solid var(--border); border-radius: 10px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, .12); padding: .4rem; display: flex; flex-direction: column;
}
.user-menu-head { padding: .5rem .6rem; border-bottom: 1px solid var(--border); margin-bottom: .25rem; }
.user-menu-fullname { display: block; font-weight: 600; font-size: .9rem; }
.user-menu-head small { color: var(--muted); font-size: .75rem; word-break: break-all; }
.user-menu-item {
    display: flex; align-items: center; gap: .6rem; padding: .5rem .6rem; border-radius: 7px;
    color: inherit; text-decoration: none; font-weight: 500; font-size: .9rem;
}
.user-menu-item i { width: 1.1rem; text-align: center; color: var(--muted); }
.user-menu-item:hover { background: var(--bg); }
.user-menu-admin, .user-menu-admin i { color: var(--admin-600); }
.user-menu-admin:hover { background: var(--admin-50); }
.user-menu-leave, .user-menu-leave i { color: var(--primary); }
.user-menu-logout { border-top: 1px solid var(--border); margin-top: .25rem; color: var(--danger); }
.user-menu-logout i { color: var(--danger); }

/* ---- Thème console admin (léger décalage de couleur + badge) ---- */
.admin-context .app-sidebar { background: var(--admin-sidebar); }
.admin-context .app-brand .logo { background: var(--admin); }
.admin-context .app-nav a.active,
.admin-context .app-nav-bottom a.active { background: var(--admin); color: #fff; }
.admin-context .app-nav-bottom { border-top: 1px solid rgba(255, 255, 255, .08); }
/* Topbar admin en thème sombre (cohérent avec la sidebar) + séparation par une
   bordure violette claire en bas (plus de barre violette en haut). */
.admin-context .app-topbar { background: var(--admin-sidebar); box-shadow: none; border-bottom: 1px solid #6f6dd0; }
.admin-context .app-topbar .admin-topbar-heading { color: #fff; }
/* Le fond blanc de `.nav-burger` n'était PAS repris ici, seule la couleur
   l'était : icône #d9d8ea sur fond blanc, soit 1,40:1. Le seul moyen d'ouvrir
   la navigation de /admin sur téléphone était donc illisible (WCAG 1.4.11
   exige 3:1). Le fond translucide de la topbar sombre rétablit le contraste. */
.admin-context .app-topbar .nav-burger {
    background: rgba(255, 255, 255, .08);
    border-color: rgba(255, 255, 255, .18);
    color: #e7e6f2;
}
.admin-context .app-topbar .user-menu > summary { color: #e7e6f2; }
.admin-context .app-topbar .user-menu > summary:hover,
.admin-context .app-topbar .user-menu[open] > summary { background: rgba(255, 255, 255, .08); border-color: rgba(255, 255, 255, .16); }
.admin-context .app-topbar .user-menu-caret { color: rgba(255, 255, 255, .6); }

.admin-badge {
    display: inline-flex; align-items: center; gap: .3rem; font-size: .62rem; font-weight: 800;
    letter-spacing: .06em; text-transform: uppercase; padding: .15rem .45rem; border-radius: 999px;
    background: var(--admin); color: #fff; white-space: nowrap;
}
.app-brand .admin-badge { margin-left: .1rem; }
.admin-badge-lg { font-size: .68rem; padding: .25rem .55rem; }
.admin-topbar-title { display: flex; align-items: center; gap: .6rem; min-width: 0; flex: 1; }
.admin-topbar-heading { font-weight: 700; color: var(--text); font-size: .95rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
@media (max-width: 640px) { .admin-topbar-heading { display: none; } }

/* ---- FAQ / aide admin (accordéon) ---- */
.help-item { border-top: 1px solid var(--border); padding: .25rem 0; }
.help-item > summary {
    cursor: pointer; padding: .85rem .25rem; font-weight: 600; font-size: .95rem;
    list-style: none; display: flex; align-items: center; gap: .5rem;
}
.help-item > summary::-webkit-details-marker { display: none; }
/* Chevron des questions de la FAQ. DEUX défauts cumulés corrigés le 2026-08-11,
   chacun suffisant à ne rien afficher :

   1. la famille demandée était « Font Awesome 6 Free » alors que le projet est
      en Font Awesome 7 (cf. les `@font-face` de fontawesome.css). Une famille
      inconnue fait retomber le navigateur sur une police système, où le point
      de code n'a aucun glyphe ;
   2. le glyphe `\f105` (fa-angle-right) n'était pas dans le SOUS-ENSEMBLE :
      `bin/fa-subset.php` collecte les CLASSES `fa-*` employées dans les
      gabarits et ici, et ne voit donc pas une icône appelée par son point de
      code.

   D'où `\f054`, celui de `fa-chevron-right`. Le nom de classe est écrit dans ce
   commentaire À DESSEIN : le script scanne ce fichier en entier, commentaires
   compris, donc cette mention garantit que le glyphe reste dans le
   sous-ensemble même si plus aucun gabarit n'emploie `fa-chevron-right`.
   `tests/Shared/FontAwesomeSubsetTest` vérifie désormais aussi les `content`
   littéraux, ce qui aurait attrapé le second défaut. */
.help-item > summary::before { content: '\f054'; font-family: 'Font Awesome 7 Free'; font-weight: 900; color: var(--muted); transition: transform .15s; }
.help-item[open] > summary::before { transform: rotate(90deg); }
.help-answer { padding: 0 .25rem 1rem 1.35rem; font-size: .9rem; color: var(--text); line-height: 1.6; }
.help-answer code { background: var(--surface-2); border: 1px solid var(--border); border-radius: 4px; padding: .05rem .3rem; font-size: .85em; }

/* ============================================================
   GED - explorateur (arborescence), jauge de stockage, corbeille
   (templates/ged/index.html.twig, templates/ged/trash.html.twig,
   templates/admin/ged.html.twig, templates/admin/ged_trash.html.twig)
   ============================================================ */

/* --- Conteneur général de l'explorateur (fil d'Ariane + dossiers + table) --- */
.ged-explorer { display: flex; flex-direction: column; gap: 1.25rem; }

/* --- Fil d'Ariane --- */
.ged-breadcrumb { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; font-size: .9rem; color: var(--muted); }
.ged-breadcrumb a { color: var(--muted); font-weight: 600; display: inline-flex; align-items: center; gap: .4rem; }
.ged-breadcrumb a:hover { color: var(--primary); }
.ged-breadcrumb [aria-current="page"] { color: var(--text); font-weight: 700; display: inline-flex; align-items: center; gap: .4rem; }
.ged-breadcrumb .crumb-sep { color: var(--border); font-size: .72rem; }

/* --- Grille des dossiers (cartes cliquables) --- */
.ged-folder-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: .75rem; }
.ged-folder-card {
    display: flex; align-items: center; justify-content: space-between; gap: .5rem;
    padding: .7rem .6rem .7rem 1rem; border: 1px solid var(--border); border-radius: var(--radius);
    background: var(--surface); transition: border-color .15s, box-shadow .15s;
}
.ged-folder-card:hover { border-color: var(--primary); box-shadow: var(--shadow-sm); }
.ged-folder-link { display: flex; align-items: center; gap: .6rem; min-width: 0; flex: 1; color: var(--text); font-weight: 600; font-size: .92rem; }
.ged-folder-link i { color: var(--primary); font-size: 1.1rem; flex-shrink: 0; }
.ged-folder-link span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Carte de dossier CLIENT (racine « Clients », vue dérivée) : deux lignes
   empilées - la raison sociale, puis le nombre de pièces et le SIREN.
   `.ged-folder-card` seule ne convient pas : elle range ses enfants EN LIGNE
   avec `space-between`, disposition faite pour le couple « nom + menu
   d'actions » de « Mes dossiers ». Appliquée à deux blocs empilés, elle
   comprimait la raison sociale dans une demi-carte, où elle se cassait sur
   plusieurs lignes faute d'ellipsis (celle de `.ged-folder-link span` ne porte
   pas jusqu'ici).

   Une raison sociale est longue par nature (forme juridique comprise) : d'où
   des colonnes plus larges, la ligne de nom qui prend toute la carte, et
   l'ellipsis avec le nom complet en `title`. */
.ged-client-grid { grid-template-columns: repeat(auto-fill, minmax(min(17rem, 100%), 1fr)); }
.ged-client-card {
    flex-direction: column; align-items: stretch; justify-content: flex-start;
    gap: .3rem; padding: .8rem 1rem;
}
.ged-client-card .ged-client-name {
    display: flex; align-items: baseline; gap: .5rem; min-width: 0;
    color: var(--text); font-weight: 600; font-size: .95rem;
}
.ged-client-card .ged-client-name i { color: var(--primary); flex-shrink: 0; }
.ged-client-card .ged-client-name span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ged-client-card .ged-client-meta { color: var(--muted); font-size: .82rem; }

/* Menu d'actions par dossier (renommer / déplacer / supprimer), même motif
   <details>/<summary> que .user-menu / .company-switch-menu (sans JS). */
.ged-folder-menu { position: relative; flex-shrink: 0; }
.ged-folder-menu > summary {
    list-style: none; cursor: pointer; width: 2rem; height: 2rem; border-radius: 8px;
    display: flex; align-items: center; justify-content: center; color: var(--muted);
}
.ged-folder-menu > summary::-webkit-details-marker { display: none; }
.ged-folder-menu > summary:hover, .ged-folder-menu[open] > summary { background: var(--surface-2); color: var(--text); }
.ged-folder-menu-list {
    position: absolute; top: calc(100% + 4px); right: 0; z-index: 40; min-width: 230px;
    background: var(--surface); border: 1px solid var(--border); border-radius: 10px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, .12); padding: .65rem; display: flex; flex-direction: column; gap: .5rem;
}
.ged-folder-menu-list form { margin: 0; display: flex; gap: .35rem; }
.ged-folder-menu-list .form-control, .ged-folder-menu-list .form-select { padding: .45rem .6rem; font-size: .85rem; }

/* --- Jauge de consommation du quota de stockage (partial _storage_gauge) --- */
.ged-storage-bar { grid-template-columns: 11rem 1fr 3.5rem; }
.ged-storage-gauge.is-warning .bar-fill { background: var(--warning); }
.ged-storage-gauge.is-critical .bar-fill { background: var(--danger); }
.ged-storage-gauge.is-critical { border-color: #f8cccc; background: #fdf7f7; }
.ged-storage-detail { margin: .65rem 0 0; font-size: .85rem; color: var(--muted); }
.ged-storage-trash a { color: var(--muted); font-weight: 600; }
.ged-storage-trash a:hover { color: var(--primary); }
.ged-storage-alert {
    margin: .65rem 0 0; font-size: .85rem; color: var(--danger); font-weight: 600;
    display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
}
.ged-storage-alert a { color: var(--danger); text-decoration: underline; }

/* Bannière de blocage de quota (page abonnement) : le flash reçoit un CTA. */
.ged-storage-alert-banner { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }

/* Carte de stockage sur le tableau de bord (variante compacte de .stat-card).
   Ici .bar-track/.bar-fill sont utilisées SANS le conteneur .bar-row (mini
   barre sous la valeur) - règles autonomes, pas de dépendance à .bar-row. */
.ged-storage-stat-card { text-decoration: none; color: inherit; transition: box-shadow .2s, transform .2s; }
.ged-storage-stat-card:hover { box-shadow: var(--shadow); transform: translateY(-2px); opacity: 1; }
.ged-storage-stat-card .bar-track {
    display: block; background: var(--surface-2); border-radius: 999px; height: .4rem; overflow: hidden;
}
.ged-storage-stat-card .bar-fill { display: block; height: 100%; background: var(--primary); border-radius: 999px; transition: width .3s ease; }
.ged-storage-stat-card.is-warning .bar-fill { background: var(--warning); }
.ged-storage-stat-card.is-critical .bar-fill { background: var(--danger); }

/* --- Barre d'actions groupées (bulk), sous le tableau documents/corbeille --- */
.ged-bulk-bar {
    display: flex; align-items: center; justify-content: space-between; gap: .75rem; flex-wrap: wrap;
    padding: .75rem 1.25rem; border-top: 1px solid var(--border); background: var(--surface-2);
    border-bottom-left-radius: var(--radius-lg); border-bottom-right-radius: var(--radius-lg);
}
.ged-bulk-hint { font-size: .82rem; color: var(--muted); }
/* Pagination de la liste de documents (lot L6). `flex-wrap` obligatoire : les
   boutons portent `white-space: nowrap`, sans quoi c'est la page qui défile. */
.ged-pagination {
    display: flex; align-items: center; justify-content: center; gap: .5rem; flex-wrap: wrap;
    padding: .75rem 1.25rem; font-size: .85rem;
}

@media (max-width: 640px) {
    .ged-storage-bar { grid-template-columns: 1fr; gap: .35rem; }
    .ged-storage-bar .bar-count { text-align: left; }
    .ged-folder-grid { grid-template-columns: 1fr; }
}

/* --- Glisser-déposer (contrôleur Stimulus ged-dnd) + arbre indenté ---
   Amélioration progressive des actions de déplacement <select> + bouton déjà
   existantes (templates/ged/index.html.twig, templates/admin/ged.html.twig) :
   `draggable` n'est posé qu'en JS (connect() du contrôleur), donc ces styles
   n'ont aucun effet visuel sans JavaScript. */
.ged-layout { display: flex; align-items: flex-start; gap: 1.25rem; }

/* Panneau latéral (arbre des dossiers), profondeur pilotée par la variable CSS
   --ged-tree-depth (posée par nœud dans templates/ged/index.html.twig via la
   macro fmt.gedFolderTree, cf. GedFolder::getDepth()). */
.ged-tree-panel { flex: 0 0 15.5rem; width: 15.5rem; position: sticky; top: 1rem; align-self: flex-start; }
.ged-tree-panel-head {
    display: flex; align-items: center; gap: .5rem; margin-bottom: .6rem;
    font-size: .78rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--muted);
}
.ged-tree { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .1rem; max-height: 60vh; overflow-y: auto; }
.ged-tree-link {
    display: flex; align-items: center; gap: .5rem; min-width: 0;
    padding: .4rem .6rem;
    padding-left: calc(.6rem + var(--ged-tree-depth, 0) * 1.05rem);
    border-radius: 8px; color: var(--text); font-size: .85rem; font-weight: 600;
    text-decoration: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    transition: background-color .12s, color .12s;
}
.ged-tree-link i { color: var(--muted); flex-shrink: 0; font-size: .9em; }
.ged-tree-link span { overflow: hidden; text-overflow: ellipsis; }
.ged-tree-link:hover { background: var(--surface-2); }
.ged-tree-link.is-current { background: var(--primary-50); color: var(--primary); }
.ged-tree-link.is-current i { color: var(--primary); }

.ged-explorer { min-width: 0; flex: 1; }

/* Élément en cours de glissé (document ou dossier). */
[data-ged-dnd-target~="draggable"] { cursor: grab; }
[data-ged-dnd-target~="draggable"].is-dragging { opacity: .4; cursor: grabbing; }

/* Cible de dépôt survolée par un glissé valide. */
.ged-folder-card.is-dragover {
    border-color: var(--primary); background: var(--primary-50);
    box-shadow: 0 0 0 2px var(--primary-50), var(--shadow-sm);
}
.ged-tree-link.is-dragover,
.ged-breadcrumb a.is-dragover {
    background: var(--primary-50); color: var(--primary); outline: 2px dashed var(--primary); outline-offset: -2px;
}

@media (max-width: 900px) {
    .ged-layout { flex-direction: column; }
    .ged-tree-panel { width: 100%; flex: 1 1 auto; position: static; }
    .ged-tree { max-height: 14rem; }
}

/* ============================================================
   Schémas SVG animés (vitrine) - CSS pur / SMIL uniquement, aucune
   dépendance JS. Illustratifs (flux métier), sans jamais nommer de
   prestataire ou de brique technique interne. Techniques : flux via
   stroke-dasharray/stroke-dashoffset, respiration et ondes via
   transform:scale (transform-box:fill-box requis sur les éléments SVG),
   flottement via translateY. Toutes tournent en boucle infinie et sont
   coupées via prefers-reduced-motion (cf. media query en fin de bloc).
   ============================================================ */
.schema { width: 100%; height: auto; display: block; }
.schema-hero { max-width: 420px; }
.schema-steps { max-width: 760px; margin: 0 auto; }

/* Flux le long des liaisons (tracé animé). La période de pointillés (6+20=26)
   est EGALE au delta d'offset anime (-26) : le motif retombe exactement sur
   lui-meme a chaque cycle -> boucle sans saut (l'ancien rendu "saccade" venait
   d'une periode qui ne tuilait pas l'offset). will-change limite les
   re-layouts sur le trace SVG. */
.schema-link {
    stroke-dasharray: 6 20;
    animation: schema-flow 1.8s linear infinite;
    will-change: stroke-dashoffset;
}
@keyframes schema-flow {
    to { stroke-dashoffset: -26; }
}
.schema-link--1 { animation-delay: 0s; }
.schema-link--2 { animation-delay: .3s; }
.schema-link--3 { animation-delay: .6s; }
.schema-link--4 { animation-delay: .9s; }

/* Respiration du noeud central (schéma hero). */
.schema-core {
    transform-box: fill-box; transform-origin: center;
    animation: schema-breathe 4s ease-in-out infinite;
}
@keyframes schema-breathe {
    0%, 100% { transform: scale(1); }
    50% { transform: scale(1.035); }
}

/* Ondes concentriques autour du noeud central (schéma hero). */
.schema-ring {
    transform-box: fill-box; transform-origin: center;
    animation: schema-pulse 3s ease-out infinite;
}
@keyframes schema-pulse {
    0% { transform: scale(.82); opacity: .55; }
    100% { transform: scale(1.25); opacity: 0; }
}
.schema-ring--1 { animation-delay: 0s; }
.schema-ring--2 { animation-delay: 1.5s; }

/* Flottement léger des puces (chips) métier. */
.schema-chip {
    transform-box: fill-box; transform-origin: center;
    animation: schema-float 3.6s ease-in-out infinite;
}
@keyframes schema-float {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(-4px); }
}
.schema-chip--1 { animation-delay: 0s; }
.schema-chip--2 { animation-delay: .45s; }
.schema-chip--3 { animation-delay: .9s; }
.schema-chip--4 { animation-delay: 1.35s; }

@media (prefers-reduced-motion: reduce) {
    .schema-link, .schema-core, .schema-ring, .schema-chip { animation: none; }
}

/* Suggestions d'autocomplétion (adresse BAN, réutilisable). */
.ac-suggestions {
    position: absolute;
    left: 0;
    right: 0;
    top: calc(100% - 1.1rem);
    z-index: 30;
    background: var(--surface, #fff);
    border: 1px solid var(--border, #e2e8f0);
    border-radius: var(--radius, 8px);
    box-shadow: var(--shadow, 0 6px 24px rgba(15, 23, 42, .12));
    display: none;
    max-height: 260px;
    overflow: auto;
}
.ac-item {
    padding: .5rem .8rem;
    cursor: pointer;
    font-size: .88rem;
    border-bottom: 1px solid var(--border, #e2e8f0);
}
.ac-item:last-child { border-bottom: 0; }
.ac-item:hover { background: var(--surface-2, #f1f5f9); }
/* Suggestion sur deux lignes (annuaire des entreprises) : le nom, puis le SIREN
   et la commune - deux entreprises homonymes ne se distinguent que par là. */
.ac-item-title { font-weight: 600; font-size: .9rem; }
.ac-item-meta { font-size: .78rem; }

/* Grille des cartes d'infos entreprise : 2 colonnes (2x2), 1 sur mobile. */
.info-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1rem;
}
@media (max-width: 720px) {
    .info-grid { grid-template-columns: 1fr; }
}

/* Liste de définitions clé/valeur (fiche Infos entreprise) - une ligne par info. */
.info-list { margin: 0; }
.info-list > div {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 1rem;
    padding: .5rem 0;
    border-bottom: 1px solid var(--border, #e2e8f0);
}
.info-list > div:last-child { border-bottom: 0; }
.info-list dt { color: var(--muted, #64748b); font-size: .85rem; margin: 0; flex: 0 0 auto; }
.info-list dd {
    margin: 0;
    text-align: right;
    font-weight: 600;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Portail client final - cercle 1, page publique /f/{token} (docs/15 §5.1). */
.portal-wrap { min-height: 100vh; background: var(--bg); }
.portal-header {
    background: var(--surface);
    border-bottom: 3px solid var(--portal-brand, var(--primary));
    box-shadow: var(--shadow-sm);
}
.portal-header-inner {
    max-width: 720px;
    margin: 0 auto;
    padding: 1.25rem 1.25rem;
    display: flex;
    align-items: center;
    gap: .85rem;
    flex-wrap: wrap;
}
.portal-logo { max-height: 42px; max-width: 220px; object-fit: contain; }
.portal-brand-name { font-weight: 800; font-size: 1.15rem; color: var(--portal-brand, var(--primary)); }
.portal-siren { color: var(--muted); font-size: .85rem; margin-left: auto; }
.portal-main { max-width: 720px; margin: 0 auto; padding: 1.75rem 1.25rem 2.5rem; }
.portal-eyebrow { color: var(--muted); font-size: .8rem; text-transform: uppercase; letter-spacing: .05em; font-weight: 700; margin: 0 0 .35rem; }
.portal-amount { font-size: 2.4rem; font-weight: 800; line-height: 1.1; margin: 0 0 .3rem; }
.portal-dates {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1rem;
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px solid var(--border);
}
.portal-actions { display: flex; gap: .75rem; flex-wrap: wrap; margin-top: 1.25rem; }
.portal-actions form { margin: 0; }
.portal-preview-frame { width: 100%; height: 70vh; min-height: 420px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface-2); }
.portal-teaser { text-align: center; font-size: .85rem; margin-top: 1.5rem; }
.portal-footer { text-align: center; padding: 1.5rem 1.25rem 2.5rem; color: var(--muted); font-size: .82rem; }
@media (max-width: 560px) {
    .portal-dates { grid-template-columns: 1fr; }
    .portal-actions { flex-direction: column; }
    .portal-actions .btn { width: 100%; justify-content: center; }
}

/* Hub de l'espace client final (docs/15 §5.3, lot C4). */

/* Espace fournisseur (/espace/{fournisseur}) : variante large de .portal-main/
   .portal-header-inner (720px), réservée aux pages tableau denses - la page
   facture unique du cercle 1 reste étroite (cf. portal/_layout.html.twig). */
.portal-main-wide { max-width: 1100px; }
.portal-header-inner-wide { max-width: 1100px; }

.hub-totals-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1.5rem; }
.hub-totals-value { font-size: 1.5rem; font-weight: 800; line-height: 1.15; }

.hub-filters { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: .85rem; align-items: end; }
.hub-filters-actions { display: flex; gap: .5rem; align-items: center; }

.table-cards-download-label { display: none; }

/* Barre d'export du hub (lot C5) : sélection multiple -> CSV/Excel/ZIP. */
.hub-export-bar { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: .75rem; }
.hub-export-actions { display: flex; gap: .5rem; flex-wrap: wrap; }

@media (max-width: 640px) {
    .hub-totals-grid { grid-template-columns: 1fr; gap: .75rem; }
    .hub-export-bar { flex-direction: column; align-items: stretch; }
    .hub-export-actions .btn { flex: 1; justify-content: center; }
}

/* Cartes empilées sous 768px (spec §5.3) : le tableau du hub porte désormais
   `.table-cards` (§4b), patron unique de l'application. Ne reste ici que ce qui
   lui est propre : l'action « télécharger » au pouce, dont le libellé
   n'apparaît qu'en mode carte (le tableau dense n'a que l'icône). */
@media (max-width: 768px) {
    .table-cards-download-label { display: inline; }
}

/* Bandeau de totaux de la liste des factures /app/factures (lot D1, docs/14
   §6 constat 1) : 4 tuiles (total, dû, échu, prochaine échéance), même patron
   visuel que .hub-totals-grid (portail) mais nom dédié - grille à 4 colonnes
   au lieu de 3. */
.invoice-totals-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1.5rem; }
.invoice-totals-value { font-size: 1.5rem; font-weight: 800; line-height: 1.15; }
.invoice-totals-value--overdue { color: var(--danger); }

@media (max-width: 720px) {
    .invoice-totals-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem; }
}
@media (max-width: 420px) {
    .invoice-totals-grid { grid-template-columns: 1fr; }
}

/* Barre d'actions de lot de la liste des factures /app/factures (lot D2,
   docs/14 §6 constat 2) : sélection multiple -> export CSV / ZIP groupé.
   Masquée par défaut (attribut HTML `hidden`), révélée par le contrôleur
   Stimulus `bulk-select` dès qu'au moins une case est cochée. Même patron
   visuel que .hub-export-bar (portail), nom dédié côté /app. */
.bulk-action-bar { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: .75rem; }
.bulk-action-bar-actions { display: flex; gap: .5rem; flex-wrap: wrap; }

@media (max-width: 640px) {
    .bulk-action-bar { flex-direction: column; align-items: stretch; }
    .bulk-action-bar-actions .btn { flex: 1; justify-content: center; }
}

/* Checklist de conformité pré-réforme (lot UX D4, docs/14 §6 constats 5 et
   10) : remplace l'état vide de la liste des factures d'un compte neuf par un
   parcours d'amorçage en 3 étapes (raccordement PA, adresse d'annuaire,
   premier client). templates/_partials/_conformity_checklist.html.twig,
   partagée avec le tableau de bord à terme. */
.checklist-intro { display: flex; align-items: flex-start; gap: 1rem; border-bottom: 1px solid var(--border); }
.checklist-intro-icon {
    width: 2.75rem; height: 2.75rem; border-radius: 50%; flex-shrink: 0;
    background: var(--primary-50); color: var(--primary);
    display: flex; align-items: center; justify-content: center; font-size: 1.1rem;
}
.checklist-intro h3 { margin: 0 0 .25rem; }
.checklist-intro p { margin: 0; }

.checklist { list-style: none; margin: 0; padding: 0; }
.checklist-item { display: flex; align-items: flex-start; gap: .85rem; padding: 1.1rem 1.5rem; border-bottom: 1px solid var(--border); }
.checklist-item:last-child { border-bottom: 0; }
.checklist-item-status { font-size: 1.25rem; color: var(--warning); flex-shrink: 0; margin-top: .1rem; }
.checklist-item.is-done .checklist-item-status { color: var(--success); }
.checklist-item-icon { font-size: 1rem; color: var(--muted); flex-shrink: 0; margin-top: .2rem; width: 1.2rem; text-align: center; }
.checklist-item-body { flex: 1; min-width: 0; }
.checklist-item-title { font-weight: 600; }
.checklist-item.is-done .checklist-item-title { color: var(--muted); text-decoration: line-through; text-decoration-color: var(--border); }
.checklist-item-desc { margin: .2rem 0 0; color: var(--muted); font-size: .85rem; }
.checklist-item-action { flex-shrink: 0; margin-top: .1rem; }

@media (max-width: 560px) {
    .checklist-item { flex-wrap: wrap; }
    .checklist-item-body { flex-basis: 100%; }
    .checklist-item-action { margin-left: auto; }
}

/* Barre de recherche unifiée + filtre "à payer" de la liste des factures
   /app/factures et /app/factures/emises (lot UX D6, docs/14 §6 constats 7,
   8, 9) : un seul champ de recherche (numéro, tiers, SIREN, montant,
   désignation de ligne) au lieu de filtres empilés, cf.
   InvoiceRepository::search(). */
.invoice-search-bar { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 1rem; }
.invoice-search { display: flex; align-items: center; gap: .6rem; flex: 1; min-width: 220px; }
.invoice-search-input {
    flex: 1;
    min-width: 0;
    padding: .55rem .85rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--text);
    font: inherit;
}
.invoice-search-input:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }
.invoice-search-clear { font-size: .85rem; color: var(--muted); white-space: nowrap; }
.invoice-search-clear:hover { color: var(--text); }

/* Segmented control "Toutes" / "À payer" : deux liens simples (GET), pas de
   JS - l'état actif s'appuie sur `to_pay_active` posé côté contrôleur. */
.filter-toggle {
    display: inline-flex;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
    flex-shrink: 0;
}
.filter-toggle-option {
    padding: .5rem 1rem;
    font-size: .85rem;
    font-weight: 600;
    color: var(--muted);
    background: var(--surface);
    white-space: nowrap;
}
.filter-toggle-option:hover { color: var(--text); background: var(--surface-2); text-decoration: none; }
.filter-toggle-option:not(:last-child) { border-right: 1px solid var(--border); }
.filter-toggle-option.is-active { background: var(--primary); color: #fff; }
.filter-toggle-badge {
    display: inline-block;
    margin-left: .35rem;
    padding: .05rem .4rem;
    border-radius: 999px;
    background: rgba(255, 255, 255, .25);
    font-size: .75rem;
}
.filter-toggle-option:not(.is-active) .filter-toggle-badge { background: var(--primary-50); color: var(--primary); }

@media (max-width: 640px) {
    .invoice-search-bar { flex-direction: column; align-items: stretch; }
    .invoice-search { min-width: 0; }
    .filter-toggle { align-self: stretch; }
    .filter-toggle-option { flex: 1; text-align: center; }
}

/* ---------- Aperçu PDF en modale (partial _partials/_pdf_preview) ---------- */
.pdf-preview-actions { margin-left: auto; display: flex; gap: .4rem; flex-wrap: wrap; }
.pdf-preview-dialog { display: flex; flex-direction: column; max-height: 92vh; }
.pdf-preview-frame {
    width: 100%; flex: 1; min-height: min(70vh, 780px);
    margin-top: .75rem; border: 1px solid var(--border);
    border-radius: var(--radius); background: var(--bg);
}

/* ---------- Revue d'import : recherche et création du destinataire ---------- */
.import-client-tools { display: flex; gap: .4rem; flex-wrap: wrap; margin-top: .6rem; }
.discover-status { margin: .35rem 0 0; font-size: .82rem; }
.discover-status-success { color: var(--success); }
.discover-status-danger { color: var(--danger); }
.discover-status-muted { color: var(--muted); }
[data-batch-card].is-excluded { opacity: .45; }
.import-candidates { display: flex; flex-direction: column; gap: .25rem; margin: .5rem 0; }
.import-candidate { justify-content: flex-start; text-align: left; font-size: .82rem; }

/* ---------- Étape 3 : récapitulatif cliquable + carte d'aperçu ---------- */
.import-confirm-row { cursor: pointer; }
.import-confirm-row.is-muted { opacity: .6; }
.import-confirm-row.is-selected { background: var(--primary-50); }
.import-preview-card { position: sticky; top: 1rem; }
.import-preview-empty { font-size: .88rem; margin: 0; }
.import-preview-detail + .import-preview-detail { border-top: 1px solid var(--border); margin-top: 1rem; padding-top: 1rem; }
.import-preview-filename { font-weight: 600; font-size: .9rem; word-break: break-all; margin: 0 0 .6rem; }
.import-preview-list { display: grid; grid-template-columns: auto 1fr; gap: .2rem .75rem; font-size: .85rem; margin: 0; }
.import-preview-list dt { color: var(--muted); }
.import-preview-list dd { margin: 0; text-align: right; }
.import-preview-flag { margin: .7rem 0 0; }
.import-preview-subtitle { font-weight: 600; font-size: .85rem; margin: .9rem 0 .3rem; }
.import-preview-lines { font-size: .82rem; }
.import-preview-lines th, .import-preview-lines td { padding: .3rem .4rem; }
.import-preview-actions { margin-top: .9rem; }

/* ---------- Suggestion générée (dernier recours LLM) : signalement visuel ----------
   Une valeur proposée par l'analyse automatique ne doit jamais se présenter
   comme une donnée vérifiée : bandeau d'avertissement + marquage champ par
   champ, distincts d'une valeur issue de SIRENE. */
.ai-suggestion-notice { display: flex; gap: .6rem; align-items: flex-start; margin: .75rem 0; }
.ai-flag {
    display: inline-flex; align-items: center; gap: .25rem;
    font-size: .72rem; font-weight: 600; text-transform: none;
    color: var(--warning); white-space: nowrap;
}
.form-control.is-ai-suggested,
.form-select.is-ai-suggested {
    border-color: var(--warning);
    box-shadow: inset 3px 0 0 var(--warning);
}

/* ============================================================
   Adaptation mobile - audit UI/UX du 2026-07-26
   Complète les lots responsive du 2026-07-03 (docs/audit-responsive.md), dont
   plusieurs n'avaient pas produit l'effet attendu - voir §4a : une table en
   `width: 100%` ne déborde jamais, donc son `overflow-x: auto` ne défilait pas.
   Ordre : saisie -> cibles tactiles -> en-têtes -> tableaux -> grilles ->
   navigation -> modales -> densité -> hauteur de fenêtre -> paysage -> divers.
   ============================================================ */

/* --- 1. Zoom automatique de Safari iOS -------------------------------
   Sous 16 px, iOS recadre la page au focus d'un champ et ne dézoome jamais.
   Toutes les tailles du design system sont sous ce seuil (`.form-control`
   vaut .95rem, soit 14,25 px pour un `body` à 15 px). C'est le défaut mobile
   le plus universel de l'application : il frappe chaque écran de saisie. */
@media (max-width: 900px) {
    .form-control, .form-select, .invoice-search-input,
    .invoice-lines .form-control, .invoice-lines .form-select,
    .ged-folder-menu-list .form-control, .ged-folder-menu-list .form-select,
    .storage-filter-select,
    :where(input[type="date"], input[type="datetime-local"],
           input[type="month"], input[type="time"], input[type="week"]),
    textarea.form-control, .app-content textarea {
        font-size: 16px;
    }
}

/* --- 2. Cibles tactiles (WCAG 2.2 AA, 2.5.8) -------------------------
   Conditionné au pointeur grossier : la densité des tableaux sur grand écran
   reste inchangée. */
@media (pointer: coarse) {
    .btn { min-height: 40px; }
    .btn-sm { min-height: 38px; padding: .45rem .8rem; }
    .table td .btn, .table td .btn-sm, .table th .btn {
        min-height: 40px; min-width: 40px; justify-content: center;
    }
    .table td .flex.gap-2 { gap: .65rem; }
    .app-content input[type="checkbox"], .app-content input[type="radio"] { width: 20px; height: 20px; }
    .table td > input[type="checkbox"], .table th > input[type="checkbox"] { width: 22px; height: 22px; }
    .nav-burger { width: 44px; height: 44px; }
    .app-nav a, .app-nav-bottom a, .settings-subnav a, .settings-subnav label,
    .company-switch-item, .user-menu-item { min-height: 44px; }
    .ged-folder-menu > summary { width: 2.75rem; height: 2.75rem; }
    .line-remove, .import-modal-close, .password-toggle-btn,
    .feedback-panel-close, .announcement-close { min-width: 44px; min-height: 44px; }
    /* Badge devenu bouton (relance de consultation d'annuaire) : au doigt, un
       badge de 24 px reste une cible manquée. */
    .badge-action { min-height: 36px; padding-inline: .85rem; }
    .table td .badge-action { min-height: 40px; }
}

/* --- 3. En-têtes de page : les actions ne débordent plus -------------
   `.page-actions` n'avait pas de `flex-wrap`, et `.btn` porte
   `white-space: nowrap` : la largeur minimale d'un bouton est celle de son
   libellé complet. Les quatre boutons en tête de la GED réclamaient ainsi
   720 px dans les 272 px utiles d'un écran de 375. */
@media (max-width: 640px) {
    .page-header { flex-wrap: wrap; align-items: flex-start; }
    .page-header > div { min-width: 0; }
    .page-header .page-title { font-size: 1.25rem; }
    .page-header .page-actions { margin-left: 0; width: 100%; flex-wrap: wrap; }
    .page-header .page-actions > .btn,
    .page-header .page-actions > form > .btn { flex: 1 1 auto; justify-content: center; }
    .seats-badge { margin-left: 0; }
}

/* --- 4a. Tableaux : défilement RÉEL ----------------------------------
   `.table-container { overflow-x: auto }` ne servait à rien tant que
   `table.table { width: 100% }` empêchait le moindre débordement : les
   colonnes se contractaient jusqu'à couper les mots. Une largeur plancher
   rétablit le défilement voulu. */
@media (max-width: 768px) {
    .table-container > table.table:not(.table-cards) {
        min-width: 34rem;
    }
    /* La saisie des lignes de facture réclame plus large que le plancher
       commun. Le sélecteur doit rester AU MOINS aussi spécifique que celui
       ci-dessus, sinon il perd la cascade et la largeur voulue n'est jamais
       appliquée - c'était le cas jusqu'ici, `table.invoice-lines` (0,2,1)
       cédant à `table.table:not(.table-cards)` (0,3,1). */
    .table-container > table.table.invoice-lines:not(.table-cards) { min-width: 44rem; }
    .table td { overflow-wrap: anywhere; }
}

/* --- 4b. Mode carte, patron UNIQUE de l'application -------------------
   L'en-tête disparaît, chaque ligne devient un bloc, chaque cellule affiche son
   libellé via `data-label`. Absorbe les deux classes qui faisaient exactement la
   même chose et qui ont été supprimées : `.invoice-table` (liste des factures et
   relevé) et `.hub-table` (portail). 100 % CSS, aucun JS (CSP stricte).
   Prérequis côté gabarit : un `data-label` sur chaque `<td>`. */
@media (max-width: 768px) {
    .table-cards thead { display: none; }
    .table-cards, .table-cards tbody, .table-cards tr, .table-cards td { display: block; width: 100%; }
    .table-cards tr {
        border: 1px solid var(--border); border-radius: var(--radius);
        margin-bottom: .75rem; padding: .25rem .9rem;
    }
    /* Ligne d'erreur de la liste des factures : elle prolonge la carte
       précédente au lieu d'en ouvrir une nouvelle (spécificité héritée de
       l'ancienne `.invoice-table`, cf. templates/invoices/index.html.twig). */
    .table-cards tr.invoice-error-row { border: 0; margin-top: -.75rem; padding: 0 .9rem .75rem; }
    .table-cards td { border-bottom: 1px dashed var(--border); padding: .6rem 0; text-align: right; }
    .table-cards td:last-child { border-bottom: 0; }
    .table-cards td[data-label]:not([data-label=""])::before {
        content: attr(data-label); float: left; font-weight: 600;
        color: var(--muted); font-size: .82rem;
    }
    /* Un bouton d'action est parfois enveloppé dans un `<form style="display:inline">`
       (attribut HTML, cf. le bouton « Relancer » de templates/invoices/index.html.twig) :
       il l'emporte sur la cascade externe, d'où le !important pour le repasser en
       bloc et laisser `.btn { width: 100% }` s'appliquer réellement. */
    .table-cards td form { display: block !important; }
    .table-cards td .btn { width: 100%; justify-content: center; }
}

/* --- 5. (supprimé) Filet des grilles posées en style inline -----------
   Il ramenait à une colonne, en `!important`, tout attribut `style` contenant
   `1fr 1fr` / `2fr 1fr` / `1fr 2fr` / `repeat(3, 1fr)`. Deux défauts : il ne
   couvrait que `.app-content` (la page publique de collecte, qui étend
   `base.html.twig`, y échappait) et il traitait le symptôme. Les gabarits sont
   passés aux classes `.grid-2` / `.grid-3` / `.grid-ratio-*` déclarées plus
   haut, le filet n'a plus lieu d'être. */

/* --- 6. Tiroir de navigation : clavier, focus, défilement de fond -----
   La case portait `hidden`, donc `display: none` : elle sortait du parcours
   de tabulation et le tiroir devenait inatteignable au clavier comme au
   lecteur d'écran (échec WCAG 2.1.1). Elle est désormais masquée par
   `.visually-hidden` et reste focusable ; le focus doit donc se voir sur le
   déclencheur, qui est un `<label>` distinct.
   `visibility` retire par ailleurs les liens du parcours quand le tiroir est
   fermé, et le fond ne défile plus derrière lui. */
#mkt-nav-toggle:focus-visible + .mkt-burger,
#nav-toggle:focus-visible ~ .app-layout .nav-burger,
#nav-toggle:focus-visible ~ .app-layout .app-main .nav-burger {
    outline: 2px solid var(--primary); outline-offset: 2px;
}
@media (max-width: 860px) {
    .app-sidebar {
        width: min(var(--sidebar-w), 82vw);
        visibility: hidden; transition: transform .2s ease, visibility 0s .2s;
    }
    #nav-toggle:checked ~ .app-layout .app-sidebar {
        visibility: visible; transition: transform .2s ease;
    }
    body:has(#nav-toggle:checked) { overflow: hidden; }
    .app-nav { overscroll-behavior: contain; }
}

/* --- 7. Modales : défilement interne, actions au pouce ---------------
   `.modal` n'avait ni hauteur maximale ni défilement : en paysage, une modale
   plus haute que la fenêtre était rognée en haut ET en bas, ses boutons de
   validation devenant inatteignables. */
.modal { max-height: calc(100dvh - 2rem); overflow-y: auto; overscroll-behavior: contain; }
.modal-head { flex-wrap: wrap; }
@media (max-width: 640px) {
    .modal-overlay { padding: .5rem; }
    .modal { padding: 1.15rem; max-height: calc(100dvh - 1rem); }
    .modal-actions { flex-direction: column-reverse; gap: .5rem; }
    .modal-actions .btn { width: 100%; justify-content: center; }
}
@media (max-width: 768px) {
    .modal.modal-xl.pdf-preview-dialog {
        max-width: 100%; width: 100%; max-height: 100dvh;
        padding: .75rem; border-radius: 0;
    }
    .modal-overlay:has(.pdf-preview-dialog) { padding: 0; }
    .pdf-preview-frame { min-height: 0; margin-top: .5rem; }
    .pdf-preview-actions { margin-left: 0; width: 100%; }
}

/* --- 8. Densité : 96 px de gouttières sur un écran de 320 px ---------- */
@media (max-width: 640px) {
    .app-topbar { padding: 0 .75rem; }
    /* La réserve du bas doit couvrir le pied de page COLLANT, qui fait trois
       lignes à 390 px (crédits sur deux, version et état sur la troisième) :
       79 px mesurés, contre 72 que réservait `4.5rem` - le bas du contenu
       passait dessous. */
    .app-content { padding: 1rem .75rem 5.5rem; }
    .card-pad { padding: 1rem; }
    .card-header { padding: .85rem 1rem; }
    .table thead th { padding: .6rem .7rem; }
    .table tbody td { padding: .65rem .7rem; }
    /* Sous 640 px, les trois zones ne tiennent plus sur une ligne : la grille
       laisse la place à un empilement, plutôt qu'à trois colonnes écrasées où
       les crédits se coupent en quatre lignes.

       Deux lignes et pas trois : ce pied de page est COLLANT, chaque ligne de
       plus est prise sur la hauteur utile, et une barre de 87 px sur un écran
       de 800 en mange 11 %. Les crédits occupent leur ligne, la version et
       l'état se partagent la suivante. */
    .app-footer {
        padding: .5rem .75rem; row-gap: .25rem;
        display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center;
    }
    .app-footer-credits { flex-basis: 100%; text-align: center; }
    .app-footer-credits, .app-footer .app-status, .app-version { justify-self: auto; }
    .user-menu-name { max-width: 6rem; }
    /* Une infobulle au doigt ne s'affiche qu'au moment du tap, juste avant que
       la page change : elle gêne plus qu'elle n'informe. */
    .tip::after { display: none; }
}

/* --- 9. Hauteur réelle de la fenêtre (barre d'URL rétractable) --------
   Les écrans d'authentification gardent `svh` (cf. section Auth) : `dvh` y
   ferait sauter une carte centrée au fil des rétractations de la barre. */
@supports (height: 100dvh) {
    .app-layout { min-height: 100dvh; }
    .app-sidebar { height: 100dvh; }
}

/* --- 10. Paysage bas : les barres collantes mangeaient 26 % de l'écran */
@media (max-height: 480px) and (orientation: landscape) {
    .app-topbar { height: 50px; }
    .app-footer { position: static; }
    .settings-subnav, .import-preview-card, .ged-tree-panel { position: static; }
}

/* --- 11. Sélecteur d'entreprise : jamais coupé hors écran ------------- */
@media (max-width: 640px) {
    .company-switch-list {
        min-width: 0; width: max-content; max-width: calc(100vw - 1.5rem);
        left: 0; right: auto;
    }
    .company-switch-item > span { min-width: 0; overflow: hidden; }
    .company-switch-item > span > small {
        display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
}

/* --- 12. Sous-navigation des Paramètres en barre d'onglets ------------
   Empilée, elle place douze liens (~470 px) AVANT le contenu : la première vue
   de l'équipe, des notifications ou de la sécurité n'affichait plus rien
   d'utile sur un téléphone. En barre horizontale défilante, elle tient sur une
   ligne et reste atteignable d'un seul geste. */
@media (max-width: 860px) {
    .settings-subnav {
        display: flex; flex-direction: row; align-items: stretch;
        gap: .35rem; overflow-x: auto; overscroll-behavior-x: contain;
        padding-bottom: .35rem; margin-bottom: 1rem;
    }
    .settings-subnav > .settings-subnav-title,
    .settings-subnav > hr { display: none; }
    .settings-subnav > a,
    .settings-subnav > form > button,
    .settings-subnav > .btn {
        flex: 0 0 auto; white-space: nowrap; min-height: 44px;
    }
}

/* --- 13. Correctifs ponctuels ---------------------------------------- */
@media (max-width: 980px) { .import-preview-card { position: static; } }
@media (max-width: 720px) {
    /* `.batch-item-actions` exigeait 240 px à lui seul dans 224 px utiles. */
    .batch-item { grid-template-columns: minmax(0, 1fr); gap: .75rem; }
    .batch-item-actions { min-width: 0; }
}
@media (max-width: 640px) {
    .app-content .flex.gap-2, .app-content .flex.gap-3 { flex-wrap: wrap; }
    .tag-row { flex-wrap: wrap; }
    .tag-row .tag-edit { flex-basis: 100%; }
    .ged-toolbar .form-select, .ged-toolbar .btn { width: 100% !important; }
    .ged-search { min-width: 0; flex-basis: 100%; }
    .emit-totals { min-width: 0; width: 100%; margin-left: 0; }
    .wizard-step-label { hyphens: auto; overflow-wrap: anywhere; }
    /* La bulle d'assistance recouvrait le bouton d'enregistrement et la
       dernière ligne d'action de tous les tableaux. */
    .feedback-widget { right: .75rem; bottom: calc(.75rem + env(safe-area-inset-bottom, 0px)); }
    .feedback-bubble { width: 3rem; height: 3rem; font-size: 1.1rem; }
}
/* L'import d'un dossier entier (`webkitdirectory`) est sans effet sur iOS et
   Android : ne pas proposer une action morte. */
@media (max-width: 860px) {
    label[for="pdfs_dir"], label[for="pdfs_dir"] + span { display: none; }
}

/* ============================================================
   Adaptation mobile - vitrine, espace client et administration
   (même audit du 2026-07-26). Séparé du bloc précédent, qui ne
   traite que l'application fournisseur.
   ============================================================ */

/* L'en-tête vitrine est collant sur 68 px, la topbar applicative sur 62 px :
   sans marge de défilement, toute cible d'ancre atterrit dessous. Concernait
   les quatre sections de l'accueil, les quatre de la FAQ et les sept parcours
   de la documentation. */
[id] { scroll-margin-top: 5.5rem; }

/* --- Vitrine ---------------------------------------------------------- */
@media (max-width: 768px) {
    /* Le panneau déroulant ne stylait que son conteneur : les liens faisaient
       23 px de haut, espacés de 4 px. Et sans hauteur maximale, il sortait de
       l'écran en paysage sans pouvoir défiler. */
    .mkt-nav-links { gap: 0; padding: .5rem; overscroll-behavior: contain; }
    .mkt-nav-links > a { min-height: 48px; padding: .25rem .75rem; border-radius: var(--radius); }
    .mkt-nav-links > a:not(.btn):active { background: var(--surface-2); }
    .mkt-nav-links > a.btn { margin: .6rem 0 .35rem; }
    .mkt-nav-links .lang-toggle { margin: .35rem .75rem; }

    /* Hero : à 320 px la `clamp` du titre retombe sur sa borne basse (33,6 px)
       et le `max-width: 14ch`, utile à la mise en page à deux colonnes, n'a
       plus de raison d'être une fois la grille repliée. Le seul bouton de
       conversion de la page arrivait vers 700 px de haut. */
    .hero { padding: 2.25rem 0 2.5rem; }
    .hero h1 { max-width: none; font-size: clamp(1.75rem, 7.5vw, 2.4rem); }
    .hero p.lead { max-width: none; font-size: 1.02rem; margin: .9rem 0 1.5rem; }
    .hero .eyebrow { font-size: .76rem; margin-bottom: .9rem; }
    .hero .cta-row { flex-direction: column; align-items: stretch; }
    .hero .cta-row .btn { justify-content: center; }
    .hero-perks { gap: .4rem 1rem; }

    /* Rythme vertical : six sections à 4 rem faisaient près de 770 px de vide
       sur la seule page d'accueil. */
    .section { padding: 2.5rem 0; }
    .section-head { margin-bottom: 1.75rem; }
    .cta-band { padding: 2rem 1.25rem; border-radius: var(--radius); }
    .cta-band p { margin-bottom: 1.25rem; }

    .mkt-footer { padding: 2rem 0 2.5rem; }
    .mkt-footer a { display: inline-flex; align-items: center; min-height: 40px; padding: 0 .3rem; }
}

/* La pastille du hero passe sur deux lignes sous 400 px : un rayon de 999 px
   y produit une forme bâtarde. */
@media (max-width: 400px) {
    .hero .eyebrow { border-radius: var(--radius); font-size: .74rem; }
}

/* Bascule mensuel/annuel : contrôle central de la page Tarifs, à 35 px. */
.pricing-toggle label { min-height: 44px; padding: .5rem 1.15rem; }
@media (max-width: 480px) {
    .pricing-card { padding: 1.35rem 1.15rem; }
    .pricing-amount { font-size: 1.75rem; }
    /* Hauteur plancher utile en grille, superflue une fois les cartes empilées. */
    .pricing-tagline { min-height: 0; }
}

@media (max-width: 640px) {
    .legal { padding: 1.75rem 1.15rem 3rem; }
}

/* Sélecteur de langue : 60 x 30 px, dont une zone réellement visée de
   18 x 12 px - le plus petit contrôle du site. */
@media (pointer: coarse) {
    .lang-toggle-track { width: 76px; height: 44px; padding: 4px; }
    .lang-toggle-thumb { width: 36px; height: 36px; top: 4px; left: 4px; }
    .lang-toggle-track.is-en .lang-toggle-thumb { transform: translateX(32px); }
    .lang-toggle-option svg { width: 22px; height: 15px; }
}

/* --- Espace client final ---------------------------------------------- */

.portal-amount { font-size: clamp(1.9rem, 8vw, 2.4rem); }

/* Aperçu PDF : `min-height: 420px` imposait un cadre plus haut que l'écran
   entier en paysage, alors que l'action utile (télécharger) est au-dessus. */
@media (max-width: 768px) { .portal-preview-frame { height: 58vh; min-height: 300px; } }
@media (max-height: 520px) { .portal-preview-frame { height: 75vh; min-height: 0; } }

@media (max-width: 768px) {
    /* Cases de sélection restées natives (~13 px) dans un tableau en cartes.
       Le portail n'est pas sous `.app-content` : la règle de cibles tactiles du
       §2 ne l'atteint pas, celle-ci reste donc nécessaire. */
    .table-cards input[type="checkbox"] { width: 1.4rem; height: 1.4rem; accent-color: var(--primary); margin: .15rem 0; }
    .table-cards td:first-child { padding: .7rem 0; }
    .table-cards .btn { min-height: 44px; }
}

@media (max-width: 640px) {
    /* Le total TTC garde toute la largeur, les deux autres se partagent la ligne. */
    .hub-totals-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .hub-totals-grid > div:first-child { grid-column: 1 / -1; }
}

/* Aperçu d'e-mail de l'espace fournisseur : la hauteur était posée en style
   inline, donc hors de portée de toute media query. */
.portal-email-preview {
    width: 100%; height: clamp(320px, 60vh, 640px);
    border: 1px solid var(--border); border-radius: var(--radius); background: #fff;
}

/* --- Coque applicative et administration ------------------------------ */

/* Topbar : hauteur figée à 62 px, sans repli, pour près de 480 px de contenu
   incompressible sur le hub client (marque + sélecteur de société + adresse +
   deux boutons en `nowrap`) -> toute la page défilait horizontalement. */
@media (max-width: 640px) {
    .app-topbar { height: auto; min-height: 62px; padding: .55rem 1rem; flex-wrap: wrap; row-gap: .5rem; }
    .app-topbar .app-brand > span:not(.logo) { display: none; }
    .app-topbar .user > span.muted { display: none; }
    .app-topbar .user { gap: .4rem; }
    .app-topbar .company-switch-menu { order: 3; flex-basis: 100%; }
    .company-switch-menu summary { max-width: 100%; }
    .company-switch-list { width: 100%; }
}

/* La borne basse de 330 px dépassait la largeur utile (327 px à 375 px), et la
   variante à trois colonnes de /admin/systeme était figée en style inline. */
.tools-grid { grid-template-columns: repeat(auto-fill, minmax(min(330px, 100%), 1fr)); }
.tools-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 1024px) { .tools-grid--3 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 700px) { .tools-grid--3 { grid-template-columns: 1fr; } }

/* --- La falaise de 861 px --------------------------------------------
   La sidebar bascule en tiroir sous 860 px. À 861 px elle repasse EN FLUX et
   prend ses 256 px : la zone de contenu tombe de 812 px (fenêtre 860) à
   557 px (fenêtre 861). On perd 255 px de contenu en gagnant 1 px de fenêtre.

   Or toutes les adaptations mobiles sont calées sur la FENÊTRE (768, 640 px) :
   elles ne s'appliquent donc jamais entre 861 et ~1090 px, précisément la
   plage où le contenu est plus étroit qu'à 860 px, où elles s'appliquaient
   toutes. Les tableaux y perdent leur plancher de défilement, le mode cartes
   n'est pas encore actif, et les grilles restent à trois ou quatre colonnes.

   On prolonge donc ces règles tant que la sidebar occupe la place. */
@media (min-width: 861px) and (max-width: 1090px) {
    .table-container > table.table:not(.table-cards) { min-width: 34rem; }
    .table-container > table.table.invoice-lines:not(.table-cards) { min-width: 44rem; }
    .grid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .invoice-totals-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .hub-totals-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 861px) and (max-width: 1020px) {
    .grid-ratio-2-1, .grid-ratio-1-2 { grid-template-columns: minmax(0, 1fr); }
}

/* --- Grilles IMBRIQUÉES : replier sur la colonne, pas sur la fenêtre ---
   Les points de rupture généraux parlent de la fenêtre. Une `.grid-ratio-1-2-1`
   (code postal / ville / pays) placée dans la colonne d'une `.grid-2` ne
   dispose pas de 720 px mais de la moitié : à 1024 px de fenêtre, la piste du
   code postal tombe à 66 px, dont 27,6 de chrome de champ - 39 px de saisie,
   inutilisable. Mesuré sur le formulaire client passé en deux colonnes. */
@media (max-width: 1180px) {
    .grid-2 .grid-2, .grid-2 .grid-3,
    .grid-2 .grid-ratio-2-1, .grid-2 .grid-ratio-1-2, .grid-2 .grid-ratio-1-2-1,
    .grid-ratio-2-1 .grid-2, .grid-ratio-2-1 .grid-3,
    .grid-ratio-1-2 .grid-2, .grid-ratio-1-2 .grid-3 {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* --- Jetons de TEXTE, distincts des jetons de fond --------------------
   `--warning` (#d97706) sur son propre fond de badge (#fef5e7) donne 2,95:1,
   très en dessous des 4,5:1 exigés pour du texte de .73rem - or ces badges
   portent « Non vérifié », « Programmée », « En attente de transmission ».
   Le fichier avait déjà les teintes sombres pour les alertes, il manquait les
   jetons pour les textes colorés. */
:root {
    --success-text: #15803d;
    --warning-text: #92620a;
    --danger-text: #b91c1c;
}
.badge-warning { color: var(--warning-text); }
.badge-danger { color: var(--danger-text); }
.status-pill--operational { color: var(--success-text); }
.status-pill--degraded { color: var(--warning-text); }
.discover-status-success { color: var(--success-text); }
.ai-flag { color: var(--warning-text); }

/* --- Focus visible : filet de sécurité --------------------------------
   `:where()` a une spécificité NULLE : les règles de focus déjà écrites plus
   haut continuent de l'emporter, celle-ci ne couvre que ce qu'elles oublient -
   les `<summary>` désolidarisés de leur marqueur, les cartes-liens, les
   onglets d'administration. */
:where(a, button, summary, [tabindex]):focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
}
/* Sur fond sombre, l'indigo #3937a3 donne 1,77:1 contre #1b1c39 : sous les
   3:1 qu'exige WCAG 1.4.11 pour un indicateur de focus. */
.app-sidebar :where(a, summary, button):focus-visible,
.admin-context .app-topbar :where(a, button, summary):focus-visible {
    outline-color: #fff;
}

/* Au-dessus du point de rupture, le déclencheur du tiroir est en
   `display: none` : sa case ne doit plus être atteignable au clavier, sinon la
   PREMIÈRE tabulation de chaque page tombe sur un contrôle invisible. */
@media (min-width: 861px) { #nav-toggle { display: none; } }
@media (min-width: 769px) { #mkt-nav-toggle { display: none; } }

/* Lien d'évitement : hors écran tant qu'il n'a pas le focus. Sans lui, un
   utilisateur au clavier traverse les 16 liens de la sidebar, le sélecteur
   d'entreprise et le menu profil avant d'atteindre le contenu, sur CHAQUE
   page (WCAG 2.4.1, niveau A). */
.skip-link {
    position: absolute; left: .5rem; top: -3.5rem; z-index: 1000;
    padding: .55rem 1rem; border-radius: var(--radius);
    background: var(--primary); color: #fff; font-weight: 600;
    transition: top .15s ease;
}
.skip-link:focus { top: .5rem; color: #fff; outline: 2px solid #fff; outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .skip-link { transition: none; } }
