        /* ============================================================
           IDENTITÉS VISUELLES

           Une identité n'est qu'un JEU DE VARIABLES. Tout le reste de
           cette feuille passe par elles depuis l'origine, si bien
           qu'ajouter une identité ne demande pas de retoucher une seule
           règle — c'est ce qui rend l'exercice sûr sur une interface de
           cette taille.

           « Ardoise » est le défaut et vit sur :root sans attribut :
           une installation qui n'a jamais rien choisi ne dépend donc
           d'aucun réglage, et un fichier d'identité effacé ne laisse pas
           l'application sans couleurs.

           Le serveur ne fabrique aucune couleur : il ne renvoie qu'un
           identifiant pris dans une liste fermée (core/apparence.js).
           Une couleur qui viendrait du client entrerait dans le
           document.
           ============================================================ */
        :root {
            --bg: #0f1218;
            --panel: #171c26;
            --panel2: #1e2531;
            --line: #2a3342;
            --txt: #e6ebf2;
            --muted: #8b97a8;
            --accent: #4c9aff;
            --accent2: #2d6fd6;
            --green: #37c07a;
            --red: #ff6b6b;
            --amber: #ffb454;
            --violet: #a78bfa;
            --chip: #232b38;
        }

        /* ---------- Portoro ----------
           La nuit chaude, le laiton et l'acajou de l'écran de connexion,
           transposés à une interface de travail : les fonds remontent en
           luminosité et les contrastes se resserrent, parce qu'on ne
           reste pas dix heures devant un écran de connexion. */
        /* La première version de Portoro se lisait mal, et le ratio de
           luminance ne le disait pas : il était MEILLEUR que celui
           d'Ardoise sur chaque paire. Toute la palette occupait la
           teinte de son fond — l'ambre à 3° de lui, le laiton à 2°. L'œil
           sépare deux surfaces par la luminance ET par la teinte, et
           quand la seconde tombe à zéro il ne reste qu'un camaïeu.

           Le fond est donc neutralisé — 6 % de saturation au lieu de
           20 % — et les couleurs de sens resaturées : le vert de 28 à
           63 %, le rouge de 61 à 100 %. */
        :root[data-palette="portoro"] {
            --bg: #121110;
            --panel: #1c1a18;
            --panel2: #262320;
            --line: #4c463e;
            --txt: #f6f2ea;
            --muted: #bdb3a4;
            --accent: #f0cf8b;
            --accent2: #b08a3c;
            --green: #43d597;
            --red: #ff7a6a;
            --amber: #ffc25c;
            --violet: #cba8f5;
            --chip: #2c2926;
        }

        /* Le laiton est CLAIR. Sur un bouton ou une pastille, il faut
           donc de l'encre sombre : du blanc sur #b08a3c ne donne que
           3,2:1, sous le seuil de lisibilité. C'est la seule règle
           qu'une palette doive corriger — le reste suit les variables. */
        :root[data-palette="portoro"] button.primary,
        :root[data-palette="portoro"] .badge {
            color: #1a1409;
        }

        /* Le menu sélectionné fait exception, et le laiton s'assombrit
           pour lui. Le blanc est demandé : c'est le repère qui dit « je
           suis ici », et il doit se lire pareil dans les deux identités
           — l'ardoise le pose déjà. Le fond descend donc à #8f6a26, où
           le blanc repasse à 5:1 ; c'est le même laiton, en plus
           profond. */
        :root[data-palette="portoro"] .navitem.active {
            background: #8f6a26;
            color: #fff;
        }

        /* ============================================================
           BARRES DE DÉFILEMENT

           Le navigateur les dessine à sa façon tant qu'on ne lui dit
           rien : sur un tableau de deux cent mille lignes, on obtenait
           une barre gris clair du système, posée au milieu d'un écran
           sombre — le seul élément de l'interface à ne pas suivre
           l'identité choisie.

           Elles la suivent maintenant, et SANS RIEN DÉCLARER DE PROPRE :
           le pouce prend `--accent2`, la couleur des boutons d'action,
           qui vaut le bleu sous Ardoise et le laiton sous Portoro. Une
           palette ajoutée demain les emporte avec elle.

           ⚠ DEUX MÉCANISMES, ET IL FAUT LES DEUX. `scrollbar-color` est
           la propriété normalisée — Firefox, et Chrome depuis la 121 ;
           `::-webkit-scrollbar` couvre les Chrome, Edge et Safari
           antérieurs, qui l'ignorent. Là où les deux sont comprises, la
           première l'emporte : elles doivent donc dire la même chose,
           faute de quoi l'apparence dépendrait de la version du
           navigateur. `scrollbar-color` s'hérite, une seule déclaration
           sur la racine atteint toutes les zones qui défilent.

           La glissière reste TRANSPARENTE : elle laisse voir le fond du
           tableau ou de la page, quel qu'il soit, et une barre qui ne
           sert pas se fait alors oublier. */
        :root {
            scrollbar-width: thin;
            scrollbar-color: var(--accent2) transparent;
        }

        ::-webkit-scrollbar {
            width: 12px;
            height: 12px;
        }

        ::-webkit-scrollbar-track,
        ::-webkit-scrollbar-corner {
            background: transparent;
        }

        /* La bordure transparente amincit le pouce sans rétrécir la zone
           qu'on vise à la souris : on attrape douze pixels, on en voit
           six. */
        ::-webkit-scrollbar-thumb {
            background: var(--accent2);
            background-clip: content-box;
            border: 3px solid transparent;
            border-radius: 8px;
        }

        ::-webkit-scrollbar-thumb:hover {
            background: var(--accent);
            background-clip: content-box;
        }

        /* ============================================================
           TYPOGRAPHIES

           Réglage INDÉPENDANT de la palette : une palette peut plaire
           pendant qu'une police fatigue, et les lier obligeait à
           renoncer aux deux pour n'en corriger qu'une.

           « Système » n'a pas de bloc : c'est le défaut, celui qui ne
           télécharge rien et reste le plus lisible en petit corps. Une
           installation qui n'a jamais rien choisi ne dépend donc
           d'aucun réglage.

           Aucun de ces blocs ne touche à une dimension : les métriques
           verticales des trois fontes sont forcées à l'identique (voir
           leurs @font-face), si bien que changer de police ne déplace
           rien. Deux tests l'imposent.
           ============================================================ */
        :root[data-typo="inter"] body {
            font-family: 'Inter', ui-sans-serif, -apple-system, 'Segoe UI', Roboto, sans-serif;
        }

        :root[data-typo="plex"] body {
            font-family: 'IBM Plex Sans', ui-sans-serif, -apple-system, 'Segoe UI', Roboto, sans-serif;
        }

        :root[data-typo="atkinson"] body {
            font-family: 'Atkinson Hyperlegible', ui-sans-serif, -apple-system, sans-serif;
        }

        :root[data-typo="lexend"] body {
            font-family: 'Lexend', ui-sans-serif, -apple-system, sans-serif;
        }

        :root[data-typo="opendyslexic"] body {
            font-family: 'OpenDyslexic', ui-sans-serif, -apple-system, sans-serif;
        }

        /* Chasse tabulaire sur les fontes téléchargées. Sans elle, une
           colonne de montants cesse d'être une colonne : leurs chiffres
           n'ont pas tous la même largeur, et une balance se lit sur
           l'alignement des unités avant de se lire sur les nombres.
           « Système » en est exempté — il n'a jamais posé le problème,
           et le changer modifierait l'existant sans raison. */
        :root[data-typo="inter"] .num,
        :root[data-typo="inter"] td,
        :root[data-typo="plex"] .num,
        :root[data-typo="plex"] td,
        :root[data-typo="atkinson"] .num,
        :root[data-typo="atkinson"] td,
        :root[data-typo="lexend"] .num,
        :root[data-typo="lexend"] td,
        :root[data-typo="opendyslexic"] .num,
        :root[data-typo="opendyslexic"] td {
            font-variant-numeric: tabular-nums lining-nums;
        }

        * {
            box-sizing: border-box
        }

        html,
        body {
            margin: 0;
            height: 100%;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
            background: var(--bg);
            color: var(--txt);
            font-size: 13px
        }

        #app {
            display: flex;
            /* `dvh` avant tout : sur mobile, la barre d'adresse du
               navigateur mange une part de `100vh`, et le pied de la
               barre latérale — déconnexion, réglages — passait dessous,
               hors d'atteinte. Le repli sert les navigateurs qui
               l'ignorent. */
            height: 100vh;
            height: 100dvh;
            overflow: hidden
        }

        /* Au premier rendu, on ne sait pas encore si une session est
           ouverte : la réponse demande un aller-retour au serveur. La
           coquille de l'application s'affichait pendant ce temps, puis
           l'écran de connexion la remplaçait — on croyait voir passer
           une page à laquelle on n'avait pas droit.

           Aucune donnée n'y figurait : le menu, le pied et la vue sont
           vides tant que le serveur n'a rien renvoyé. Mais un logiciel
           qui a l'air de laisser filer quelque chose se paie comme s'il
           l'avait fait.

           La règle passe devant « #app » par sa spécificité, sans
           `!important` : le sélecteur porte une classe de plus. */
        body.demarrage #app {
            display: none;
        }

        /* ============================================================
           PETITS ÉCRANS — la barre latérale se rétracte

           Au-delà du seuil, rien ne change : la barre est une colonne du
           tableau, comme depuis toujours. En dessous, elle devient un
           tiroir posé PAR-DESSUS le contenu — et non une colonne qu'on
           rétrécit. Sur 390 px de large, 220 px de menu, c'est plus de la
           moitié de l'écran ; les réduire à 60 px aurait donné une
           colonne d'icônes que personne ne sait lire.

           Le seuil est à 860 px et non à 600 : une tablette en portrait
           souffre du même problème qu'un téléphone, en moins grave.
           ============================================================ */
        .nav-bascule {
            display: none;
            background: none;
            border: 1px solid var(--line);
            color: var(--txt);
            font-size: 16px;
            line-height: 1;
            padding: 5px 9px;
            border-radius: 8px;
            cursor: pointer;
            flex: 0 0 auto
        }

        /* Jamais affiché hors du seuil, et pas seulement quand
           l'attribut `hidden` est posé : rester en largeur pleine après
           une rotation d'écran laissait sinon un bloc vide dans la
           colonne, le tiroir étant redevenu une colonne ordinaire. */
        #voileNav {
            display: none
        }

        @media (max-width: 860px) {
            .nav-bascule {
                display: block
            }

            #side {
                position: fixed;
                top: 0;
                bottom: 0;
                left: 0;
                z-index: 1200;
                box-shadow: 14px 0 40px rgba(0, 0, 0, .5);
                transform: translateX(-100%);
                /* `visibility` accompagne la translation : sans elle, le
                   tiroir resterait touchable au bord gauche de l'écran,
                   là où le pouce traîne. */
                visibility: hidden;
                transition: transform .24s cubic-bezier(.22, .8, .3, 1), visibility .24s
            }

            body.nav-ouverte #side {
                transform: none;
                visibility: visible
            }

            body.nav-ouverte #voileNav {
                display: block;
                position: fixed;
                inset: 0;
                z-index: 1150;
                background: rgba(0, 0, 0, .5)
            }

            /* La barre du haut tient sur deux rangs plutôt que de
               déborder : le titre et le menu d'abord, les commandes
               ensuite. Sans quoi l'exercice sortait de l'écran. */
            #top {
                flex-wrap: wrap;
                gap: 8px;
                padding: 8px 12px
            }

            #top h1 {
                font-size: 14px
            }

            #content {
                padding: 12px
            }

            #feuillets {
                padding: 7px 12px 0
            }
        }

        @media (prefers-reduced-motion: reduce) {
            #side {
                transition: none
            }
        }

        /* Sidebar */
        #side {
            width: 220px;
            flex: 0 0 220px;
            background: var(--panel);
            border-right: 1px solid var(--line);
            display: flex;
            flex-direction: column
        }

        #side .brand {
            padding: 16px 16px 10px;
            font-weight: 700;
            font-size: 15px;
            letter-spacing: .3px
        }

        /* Colonne flex pour que la messagerie puisse être poussée en bas
           (voir la règle plus bas). Les menus restent empilés comme avant. */
        #nav {
            padding: 6px;
            overflow: auto;
            flex: 1;
            display: flex;
            flex-direction: column
        }

        .navitem {
            display: flex;
            align-items: center;
            gap: 10px;
            padding: 9px 12px;
            border-radius: 8px;
            cursor: pointer;
            color: var(--muted);
            font-weight: 500;
            user-select: none;
            /* Sans cela, la colonne flex écraserait les menus quand la
               liste dépasse la hauteur disponible. */
            flex: 0 0 auto
        }

        .navitem:hover {
            background: var(--panel2);
            color: var(--txt)
        }

        .navitem.active {
            background: var(--accent2);
            color: #fff
        }

        .navitem .ic {
            width: 18px;
            text-align: center
        }

        #side .foot {
            padding: 10px 14px;
            border-top: 1px solid var(--line);
            color: var(--muted);
            font-size: 11px
        }

        /* Quitter à gauche, régler à droite. Les deux commandes aux
           extrémités : ce sont les seules de la barre latérale qui ne
           mènent pas à une vue, et les éloigner l'une de l'autre évite
           de cliquer sur la mauvaise. */
        #side .foot-actions {
            display: flex;
            align-items: center;
            justify-content: space-between
        }

        /* Main */
        #main {
            flex: 1;
            display: flex;
            flex-direction: column;
            min-width: 0
        }

        #top {
            display: flex;
            align-items: center;
            gap: 12px;
            padding: 10px 18px;
            border-bottom: 1px solid var(--line);
            background: var(--panel)
        }

        #top h1 {
            font-size: 15px;
            margin: 0;
            font-weight: 600
        }

        #top .spacer {
            flex: 1
        }

        select,
        input,
        button {
            font-family: inherit;
            font-size: 12px
        }

        /* `password` figure ici depuis qu'un bouton permet d'afficher la
           saisie : sans lui, basculer le champ en `text` lui aurait fait
           gagner d'un coup bordure, fond et arrondis — un saut visuel au
           moment précis où l'on cherche à relire ce qu'on a tapé. */
        select,
        input[type=text],
        input[type=password],
        input[type=date],
        input[type=number] {
            background: var(--panel2);
            border: 1px solid var(--line);
            color: var(--txt);
            padding: 7px 9px;
            border-radius: 7px;
            outline: none
        }

        /* ---------- Colonne de révision ----------
           Large à dessein : c'est là qu'on écrit, et un champ étroit
           pousse à noter « OK » là où une phrase serait utile. */
        .rev-col {
            min-width: 260px;
            width: 26%;
        }

        /* rows="1" au départ : une ligne sans commentaire doit avoir
           EXACTEMENT la hauteur d'une ligne de balance (tbody td en fait
           6px 10px). Sans ce calage, chaque ligne de la vue Révision se
           voyait deux fois plus haute que dans la Balance, commentée ou
           non. La textarea ne grandit qu'avec du texte qui déborde
           réellement — c'est le rôle de min-height, jamais de rows. */
        .rev-saisie {
            display: block;
            width: 100%;
            min-height: 26px;
            background: var(--panel2);
            border: 1px solid var(--line);
            color: var(--txt);
            border-radius: 6px;
            margin: -6px -10px;
            padding: 5px 7px;
            font-family: inherit;
            font-size: 11px;
            line-height: 1.4;
            resize: none;
            /* La hauteur est pilotée par le script : une barre de
               défilement interne ferait clignoter le champ à chaque
               frappe et masquerait les lignes du bas. */
            overflow-y: hidden;
            /* Le tableau impose nowrap ; une observation doit pouvoir
               tenir sur plusieurs lignes. */
            white-space: pre-wrap;
        }

        .rev-col {
            padding: 6px 10px !important;
        }

        .rev-saisie:focus {
            border-color: var(--accent);
            outline: none;
        }

        .rev-saisie[readonly] {
            opacity: .6;
            cursor: default;
        }

        /* Accusé de réception discret : la sauvegarde a lieu à la sortie
           du champ, sans bouton — il faut bien un signe qu'elle a eu
           lieu. */
        .rev-saisie.rev-enregistre {
            border-color: #2c5a3d;
            background: #16241b;
        }

        /* ---------- Champ de mot de passe avec bouton d'affichage ---------- */
        .mdp-champ {
            position: relative;
            display: block;
        }

        .mdp-champ input {
            width: 100%;
            /* Place réservée au bouton, pour que le texte ne passe pas
               dessous sur un mot de passe long. */
            padding-right: 34px;
        }

        .mdp-oeil {
            position: absolute;
            right: 1px;
            top: 1px;
            bottom: 1px;
            width: 30px;
            background: none;
            border: 0;
            border-radius: 0 6px 6px 0;
            color: var(--txt);
            opacity: .5;
            cursor: pointer;
            font-size: 13px;
            line-height: 1;
            padding: 0;
        }

        .mdp-oeil:hover {
            opacity: 1;
            background: none;
        }

        select:focus,
        input:focus {
            border-color: var(--accent)
        }

        .ms-box {
            background: var(--panel2);
            border: 1px solid var(--line);
            color: var(--txt);
            padding: 7px 9px;
            border-radius: 7px;
            cursor: pointer;
            user-select: none;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
            display: flex;
            justify-content: space-between;
            align-items: center;
            transition: border-color .15s, box-shadow .15s;
        }

        .ms-box:hover {
            border-color: var(--accent);
            box-shadow: 0 0 0 1px rgba(76, 154, 255, .15);
        }

        .ms-box .ms-arrow {
            font-size: 8px;
            margin-left: 6px;
            opacity: .5;
            transition: transform .2s;
        }

        .ms-dd {
            display: none;
            position: absolute;
            top: calc(100% + 2px);
            left: 0;
            min-width: 100%;
            width: max-content;
            background: var(--panel);
            border: 1px solid var(--line);
            border-radius: 6px;
            box-shadow: 0 8px 24px rgba(0, 0, 0, .45), 0 0 0 1px rgba(255, 255, 255, .04);
            z-index: 100;
            max-height: 280px;
            overflow-y: auto;
            padding: 2px 0;
        }

        .ms-opt {
            padding: 4px 10px;
            margin: 0;
            cursor: pointer;
            font-size: 12px;
            color: var(--muted);
            transition: background .1s, color .1s;
            user-select: none;
        }

        .ms-opt:hover {
            background: var(--panel2);
            color: var(--txt);
        }

        .ms-opt.on {
            color: var(--accent);
            font-weight: 500;
        }

        .ms-opt.ms-sep {
            border-bottom: 1px solid var(--line);
            margin-bottom: 1px;
            padding-bottom: 5px;
        }

        button {
            background: var(--panel2);
            border: 1px solid var(--line);
            color: var(--txt);
            padding: 7px 12px;
            border-radius: 7px;
            cursor: pointer;
            display: inline-flex;
            align-items: center;
            gap: 6px
        }

        button:hover {
            background: var(--chip)
        }

        button.primary {
            background: var(--accent2);
            border-color: var(--accent2);
            color: #fff
        }

        button.primary:hover {
            background: var(--accent)
        }

        button.danger {
            color: var(--red);
            border-color: #40262b
        }

        /* ------------------------------------------------------------
           FEUILLETS — les onglets d'une même vue

           Dessin de classeur : l'onglet actif est le seul à porter le
           fond du contenu, et il MORD d'un pixel sur le filet du bas
           (margin-bottom: -1px) — c'est ce chevauchement, et lui seul,
           qui donne l'impression d'une languette solidaire de la page.

           Toutes les couleurs viennent des variables : la barre suit
           l'identité visuelle du cabinet sans une seule règle par
           palette, et sa géométrie ne change donc jamais.
           ------------------------------------------------------------ */
        /* Sans cette règle, l'attribut « hidden » resterait sans effet :
           une classe d'auteur l'emporte sur la feuille du navigateur. */
        .feuillets[hidden] {
            display: none
        }

        .feuillets {
            display: flex;
            align-items: flex-end;
            gap: 3px;
            padding: 9px 18px 0;
            background: var(--panel);
            border-bottom: 1px solid var(--line);
            /* La rangée défile quand les onglets ne tiennent plus. */
            overflow-x: auto;
            /* ⚠ EXPLICITE, ET NÉCESSAIRE. Un axe rendu non visible force
               l'autre à « auto » : `overflow-x: auto` seul suffisait donc
               à rendre l'axe vertical défilant, et un pixel d'arrondi y
               faisait paraître une barre dans une bande qui n'a jamais
               eu qu'une seule rangée. */
            overflow-y: hidden;
            /* Le rail est MASQUÉ, pas supprimé : la rangée continue de
               défiler à la molette et au clavier. Dans une bande de
               quarante pixels de haut, une barre en prenait douze — et
               elle paraissait même quand rien ne dépassait. Les onglets
               coupés disent déjà qu'il y a une suite. */
            scrollbar-width: none;
            flex: 0 0 auto
        }

        .feuillets::-webkit-scrollbar {
            display: none;
        }

        .feuillet {
            display: flex;
            align-items: center;
            margin-bottom: -1px;
            border: 1px solid transparent;
            border-bottom: none;
            border-radius: 9px 9px 0 0;
            max-width: 300px;
            flex: 0 0 auto;
            transition: background .12s, border-color .12s
        }

        .feuillet:hover {
            background: var(--panel2)
        }

        /* L'onglet actif : fond du contenu, filet sur trois côtés, et
           un trait d'accent au sommet — le repère se lit sans compter
           sur la seule nuance de fond, qui s'efface sur un écran mat. */
        .feuillet.on {
            background: var(--bg);
            border-color: var(--line);
            box-shadow: inset 0 2px 0 0 var(--accent)
        }

        .feuillet .fl-nom {
            background: none;
            border: none;
            color: var(--muted);
            font-size: 12px;
            font-weight: 500;
            padding: 7px 6px 7px 13px;
            max-width: 260px;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
            cursor: pointer
        }

        .feuillet.on .fl-nom {
            color: var(--txt)
        }

        /* La croix ne s'allume qu'au survol de son onglet : une rangée
           de croix visibles en permanence invite à fermer, alors que le
           geste courant est de changer d'onglet. */
        .feuillet .fl-x {
            background: none;
            border: none;
            color: var(--muted);
            font-size: 10px;
            line-height: 1;
            padding: 5px 8px 5px 4px;
            border-radius: 5px;
            opacity: 0;
            cursor: pointer
        }

        .feuillet:hover .fl-x,
        .feuillet.on .fl-x {
            opacity: .65
        }

        .feuillet .fl-x:hover {
            opacity: 1;
            color: var(--red);
            background: var(--panel2)
        }

        .feuillet .fl-nom:focus-visible,
        .feuillet .fl-x:focus-visible,
        .fl-plus:focus-visible {
            outline: 2px solid var(--accent);
            outline-offset: -2px;
            border-radius: 5px
        }

        /* Collé à droite : passé une douzaine d'onglets la barre défile,
           et le bouton qui en ouvre un de plus ne doit pas partir avec
           elle. Le fond est indispensable — sans lui, les onglets
           passeraient dessous en défilant. */
        .fl-plus {
            position: sticky;
            right: 0;
            background: var(--panel);
            border: none;
            color: var(--muted);
            font-size: 16px;
            line-height: 1;
            padding: 5px 10px;
            margin: 0 0 4px 4px;
            border-radius: 6px;
            cursor: pointer;
            flex: 0 0 auto
        }

        .fl-plus:hover {
            background: var(--panel2);
            color: var(--txt)
        }

        /* ------------------------------------------------------------
           GRAND LIVRE — la saisie du compte

           Elle a eu deux ENCADRÉS — fond, bordure, titre gras — et ils
           poussaient le tableau, la seule chose qu'on soit venu lire,
           sous la ligne de flottaison. Un champ de saisie n'a pas besoin
           d'être encadré pour se voir : ni les filtres des écritures et
           de la balance, ni le sélecteur d'exercice de la barre du haut
           n'en ont jamais eu.

           Ces champs sont donc de simples `.filters .f` — même intitulé
           maigre en capitales, mêmes hauteurs — avec, en propre, la
           largeur FIXE et le libellé du compte dessous.
           ------------------------------------------------------------ */
        /* ⚠ UNE GRILLE, ET NON UNE PILE, POUR UNE SEULE RAISON :
           l'intitulé, le numéro saisi et le libellé du compte doivent
           commencer sur la même verticale, alors que les flèches
           encadrent le champ.

           Empilés, l'intitulé et le libellé auraient commencé au bord du
           groupe et le numéro une largeur de bouton plus loin. Les
           décaler d'autant à la main ne tient pas : cette largeur change
           avec la police choisie dans les paramètres, et l'alignement
           serait juste avec l'une, faux avec la suivante.

           La grille place les trois DANS LA MÊME COLONNE — celle du
           champ, entre les deux flèches — quelle que soit leur largeur.
           Il ne reste alors que le retrait du texte à l'intérieur du
           champ, bordure plus rembourrage, dix pixels, qu'on rend aux
           deux autres. */
        .filters .f.gl-f {
            display: grid;
            grid-template-columns: auto minmax(0, 1fr) auto;
            align-items: center;
            gap: 4px;
            /* Largeur figée, IDENTIQUE pour les deux champs, et taillée
               pour que le libellé du compte tienne dessous : les
               intitulés du plan comptable font une quarantaine de
               signes, qui entrent ici à douze pixels.

               Figée, et c'est le point : sans cela le libellé — long et
               sur une seule ligne — étirerait la colonne à sa mesure, et
               la rangée sauterait d'un compte au suivant. */
            flex: 0 0 auto;
            width: 300px;
            max-width: 100%;
        }

        .gl-f>label {
            grid-area: 1 / 2 / 2 / 4;
        }

        .gl-f>input {
            grid-area: 2 / 2 / 3 / 3;
        }

        .gl-f>.gl-nom {
            grid-area: 3 / 2 / 4 / 4;
        }

        .gl-f>button:first-of-type {
            grid-area: 2 / 1 / 3 / 2;
        }

        .gl-f>button:nth-of-type(2) {
            grid-area: 2 / 3 / 3 / 4;
        }

        .gl-f>label,
        .gl-f>.gl-nom {
            padding-left: 10px;
        }

        /* Le libellé du compte trouvé. Sa hauteur est RÉSERVÉE même vide,
           sans quoi la rangée monte et descend au fil des recherches. Un
           intitulé plus long que le champ est coupé net et se lit en
           entier au survol (attribut `title`) : l'élargir encore pour un
           cas rare rendrait la place qu'on vient de gagner. */
        .gl-nom {
            font-size: 12px;
            color: var(--accent);
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
            min-height: 15px;
            /* Sans quoi la case de la grille s'élargit à la mesure du
               texte au lieu de le couper, et la colonne se déforme. */
            min-width: 0;
        }

        /* ------------------------------------------------------------
           La note de révision, rappelée à droite des champs

           Elle est écrite depuis la balance, et c'est là qu'on la
           consultait — c'est-à-dire nulle part au moment où l'on en a
           besoin, qui est celui où l'on épluche le compte. Ici elle est
           en LECTURE SEULE : la révision reste le lieu où l'on écrit,
           et deux endroits pour saisir la même note finiraient par ne
           plus dire la même chose.
           ------------------------------------------------------------ */
        .gl-revision {
            border-left: 3px solid var(--amber);
            background: color-mix(in srgb, var(--amber) 9%, transparent);
            border-radius: 0 8px 8px 0;
            padding: 7px 11px;
            font-size: 12px;
            line-height: 1.5;
            white-space: pre-wrap;
            /* Elle occupe la place laissée à droite des champs, sans
               jamais la réclamer : `1 1 220px` la fait passer à la ligne
               plutôt que d'écraser la saisie sur un écran étroit. Et sa
               hauteur est BORNÉE — une note de trente lignes repousserait
               le tableau, exactement le défaut qu'on vient de corriger. */
            flex: 1 1 220px;
            max-width: 620px;
            align-self: stretch;
            max-height: 92px;
            overflow-y: auto;
        }

        .gl-revision[hidden] {
            display: none;
        }

        .gl-revision .gl-revision-tete {
            font-size: 10px;
            text-transform: uppercase;
            letter-spacing: .4px;
            color: var(--muted);
            margin-bottom: 3px;
        }

        /* Colonne des commandes de ligne : étroite, sans intitulé, et
           absente du papier — elle ne porte aucune donnée. */
        td.pin-col,
        th.pin-col {
            width: 34px;
            padding-left: 0;
            padding-right: 4px;
            text-align: right
        }

        /* La ligne qu'on vient rejoindre. Elle s'annonce, puis s'efface :
           un surlignage qui reste finit par désigner n'importe quoi. */
        tr.gl-cible > td {
            /* Repli avant color-mix : sur un navigateur qui l'ignore, la
               ligne doit rester désignée, fût-ce d'un bleu approximatif. */
            background: rgba(76, 154, 255, .22);
            background: color-mix(in srgb, var(--accent) 22%, transparent);
            box-shadow: inset 0 1px 0 var(--accent), inset 0 -1px 0 var(--accent);
            transition: background 1.2s ease-out, box-shadow 1.2s ease-out
        }

        @media (prefers-reduced-motion: reduce) {
            tr.gl-cible > td {
                transition: none
            }
        }

        #content {
            flex: 1;
            overflow: auto;
            padding: 18px
        }

        .view {
            display: none
        }

        .view.active {
            display: block
        }

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

        .card {
            background: var(--panel);
            border: 1px solid var(--line);
            border-radius: 12px;
            padding: 16px;
            margin-bottom: 16px
        }

        /* ------------------------------------------------------------
           `.vue-nue` — l'enveloppe d'une vue qui n'a PAS de cadre

           Les vues principales — écritures, journaux, plan comptable,
           balance, révision, grand livre — étaient posées dans une
           `.card` : un fond, une bordure et seize pixels de marge tout
           autour de l'écran, qui n'entouraient rien d'autre que la vue
           elle-même. Un cadre sépare deux choses ; celui-là n'en
           séparait aucune, il ne faisait que rétrécir le tableau qu'on
           était venu lire.

           `.card` reste, et sert encore, là où elle sépare vraiment des
           blocs entre eux : les comptes annuels, les axes d'analyse,
           l'administration, les écrans de connexion.

           L'enveloppe ne porte aucun style à elle — c'est le propos.
           Elle existe pour que les titres gardent le leur : sans quoi un
           `h2` retrouverait la taille et les marges par défaut du
           navigateur, deux fois trop grandes. */
        .card h2,
        .vue-nue h2 {
            margin: 0 0 12px;
            font-size: 14px
        }

        .grid {
            display: grid;
            gap: 12px
        }

        /* KPI */
        .kpis {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
            gap: 12px
        }

        .kpi {
            background: var(--panel);
            border: 1px solid var(--line);
            border-radius: 12px;
            padding: 14px
        }

        .kpi .lbl {
            color: var(--muted);
            font-size: 11px;
            text-transform: uppercase;
            letter-spacing: .4px
        }

        .kpi .val {
            font-size: 22px;
            font-weight: 700;
            margin-top: 6px
        }

        .kpi .sub {
            font-size: 11px;
            color: var(--muted);
            margin-top: 2px
        }

        /* KPI numérique : montant calé à droite, même typographie que les montants
     des tableaux (dont le solde cumulé) : 12px, graisse normale, chiffres tabulaires */
        .kpi.num .val,
        .kpi.num .sub {
            text-align: right;
            font-variant-numeric: tabular-nums
        }

        .kpi.num .val {
            font-size: 12px;
            font-weight: 400
        }

        /* Table */
        .tablewrap {
            overflow: auto;
            border: 1px solid var(--line);
            border-radius: 10px
        }

        /* Le cadre en moins, l'enveloppe en plus : le tableau court d'un
           bord à l'autre comme la liste des journaux, mais garde ce qui
           fait la différence entre une enveloppe et rien du tout — la
           hauteur bornée et l'en-tête collé en haut pendant qu'on
           défile.

           Un cadre sert quand le tableau flotte au milieu d'autre chose.
           Sur une vue qui n'EST que ce tableau, il ne fait que redoubler
           le bord de la page, à quelques pixels de lui. */
        .tablewrap.sans-cadre {
            border: 0;
            border-radius: 0
        }

        /* ------------------------------------------------------------
           UNE VUE QUI TIENT DANS L'ÉCRAN

           Cinq vues ne sont qu'un long tableau surmonté de ses critères.
           L'en-tête — filtres, comptes saisis, synthèse — GARDE SA PLACE,
           et le tableau seul défile, jusqu'au bas de la fenêtre.

           ⚠ AUCUNE HAUTEUR N'EST CALCULÉE ICI, et c'est tout l'intérêt.
           Borner le tableau par un calcul oblige à connaître tout ce qui
           l'entoure — filtres qui passent à la ligne, note de révision,
           pagination, rangée d'onglets — et se trompe forcément d'un
           rien. Cet écart-là reparaît en seconde barre de défilement sur
           toute la page, par-dessus celle du tableau. Une colonne
           flexible laisse le navigateur faire l'arithmétique, et il ne
           se trompe pas d'un pixel.

           `#content` garde `overflow: auto` : sur une fenêtre trop basse
           pour le minimum du tableau, la page défile plutôt que de
           cacher l'en-tête. Défilement à contrecœur vaut mieux que
           contenu hors d'atteinte.

           La classe est posée sur `#content` par `go()`, d'après
           VUES_PLEINES. Les autres vues empilent plusieurs blocs et
           gardent le défilement de page. */
        #content.vue-pleine {
            display: flex;
            flex-direction: column;
        }

        /* Rien ne se laisse comprimer par défaut : sans cela, l'en-tête
           se tasserait au lieu de tenir sa place, et c'est justement ce
           qu'on lui demande. Le tableau rouvre la règle pour lui seul,
           plus bas. */
        #content.vue-pleine>*,
        #content.vue-pleine .vue-nue>*,
        #content.vue-pleine .zone-tableau>* {
            flex-shrink: 0;
        }

        /* L'enveloppe de la vue, puis — grand livre — le conteneur
           intermédiaire qui porte la pagination et le tableau. Le
           `min-height: 0` est indispensable : sans lui, un élément de
           colonne flexible ne descend jamais sous la taille de son
           contenu, et le tableau déborderait de l'écran au lieu de
           défiler. */
        #content.vue-pleine>.vue-nue,
        #content.vue-pleine .zone-tableau {
            display: flex;
            flex-direction: column;
            flex: 1 1 auto;
            min-height: 0;
        }

        #content.vue-pleine .tablewrap.pleine-hauteur {
            flex: 1 1 auto;
            min-height: 160px;
            max-height: none;
        }

        table {
            border-collapse: collapse;
            width: 100%;
            font-size: 12px;
            white-space: nowrap
        }

        thead th {
            position: sticky;
            top: 0;
            background: var(--panel2);
            text-align: left;
            padding: 8px 10px;
            border-bottom: 1px solid var(--line);
            font-weight: 600;
            cursor: pointer;
            z-index: 2
        }

        thead th.no-sort {
            cursor: default
        }

        tbody td {
            padding: 6px 10px;
            border-bottom: 1px solid var(--line)
        }

        tbody tr:hover {
            background: var(--panel2)
        }

        td.num,
        th.num {
            text-align: right;
            font-variant-numeric: tabular-nums
        }

        /* --- Fenêtre modale « écriture complète » --- */
        .modal-back {
            position: fixed;
            inset: 0;
            background: rgba(0, 0, 0, .62);
            z-index: 1000;
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 20px
        }

        .modal-box {
            background: var(--panel);
            border: 1px solid var(--line);
            border-radius: 12px;
            max-width: min(1400px, 96vw);
            width: 100%;
            max-height: 86vh;
            display: flex;
            flex-direction: column;
            box-shadow: 0 18px 50px rgba(0, 0, 0, .55)
        }

        .modal-head {
            display: flex;
            align-items: center;
            justify-content: space-between;
            gap: 10px;
            padding: 14px 16px;
            border-bottom: 1px solid var(--line);
            flex: 0 0 auto
        }

        .modal-head .ttl {
            font-weight: 700;
            font-size: 15px
        }

        .modal-close {
            background: var(--panel2);
            border: 1px solid var(--line);
            color: var(--txt);
            border-radius: 7px;
            padding: 6px 12px;
            cursor: pointer;
            font-size: 13px
        }

        .modal-close:hover {
            border-color: var(--accent)
        }

        .modal-body {
            padding: 14px 16px;
            flex: 1 1 auto;
            min-height: 0;
            display: flex;
            flex-direction: column
        }

        /* le tableau défile à l'intérieur : l'ascenseur horizontal reste toujours visible,
     même pour une écriture d'à-nouveau de plusieurs centaines de lignes */
        .modal-body .tablewrap {
            flex: 1 1 auto;
            min-height: 0;
            overflow: auto
        }

        /* dans la fenêtre, les libellés longs passent à la ligne (au lieu d'élargir le tableau) ;
     les montants, dates et comptes restent sur une seule ligne */
        #ecrModal table {
            white-space: normal
        }

        #ecrModal td.num,
        #ecrModal th.num,
        #ecrModal td:first-child {
            white-space: nowrap
        }

        #ecrModal tfoot td {
            position: sticky;
            bottom: 0;
            background: var(--panel2);
            border-top: 1px solid var(--line)
        }

        /* --- Bouton Paramétrage (roue dentée) --- */
        .gear {
            background: transparent;
            border: none;
            color: var(--muted);
            font-size: 17px;
            cursor: pointer;
            padding: 4px 6px;
            border-radius: 7px;
            line-height: 1;
            flex: 0 0 auto
        }

        .gear:hover {
            color: var(--txt);
            background: var(--panel2)
        }

        .set-row {
            display: flex;
            align-items: center;
            gap: 12px;
            padding: 9px 4px;
            border-bottom: 1px solid var(--line)
        }

        .set-row:last-child {
            border-bottom: none
        }

        .set-row .ic {
            width: 20px;
            text-align: center
        }

        .set-row .ttl {
            flex: 1
        }

        .set-row.locked {
            opacity: .55
        }

        /* --- Interrupteur style iOS --- */
        .aswitch {
            position: relative;
            display: inline-block;
            width: 40px;
            height: 22px;
            flex: 0 0 auto
        }

        .aswitch input {
            opacity: 0;
            width: 0;
            height: 0;
            position: absolute
        }

        .aswitch .asl {
            position: absolute;
            inset: 0;
            background: var(--panel2);
            border: 1px solid var(--line);
            border-radius: 22px;
            cursor: pointer;
            transition: background .18s, border-color .18s
        }

        .aswitch .asl:before {
            content: "";
            position: absolute;
            height: 16px;
            width: 16px;
            left: 2px;
            top: 2px;
            background: #fff;
            border-radius: 50%;
            transition: transform .18s;
            box-shadow: 0 1px 3px rgba(0, 0, 0, .45)
        }

        .aswitch input:checked+.asl {
            background: #34c759;
            border-color: #34c759
        }

        .aswitch input:checked+.asl:before {
            transform: translateX(18px)
        }

        .aswitch input:focus-visible+.asl {
            outline: 2px solid var(--accent);
            outline-offset: 2px
        }

        /* --- Cadre de synthèse du compte (grand livre) --- */
        .gl-synth {
            margin-left: auto;
            min-width: 330px;
            max-width: 430px;
            background: var(--panel);
            border: 1px solid var(--line);
            border-radius: 10px;
            padding: 12px 16px
        }

        .gl-synth .gs-title {
            color: var(--muted);
            font-size: 11px;
            text-transform: uppercase;
            letter-spacing: .5px;
            margin-bottom: 8px
        }

        .gl-synth .gs-row {
            display: flex;
            justify-content: space-between;
            gap: 18px;
            padding: 4px 0;
            font-size: 12px
        }

        .gl-synth .gs-row .amt {
            font-variant-numeric: tabular-nums;
            text-align: right;
            white-space: nowrap
        }

        .gl-synth .gs-sub {
            border-top: 1px solid var(--line);
            margin-top: 4px;
            padding-top: 6px
        }

        .gl-synth .gs-total {
            font-weight: 700
        }

        .gl-synth .gs-total .amt {
            border-bottom: 3px double var(--txt);
            padding-bottom: 2px
        }

        tr.ecr-hl td {
            background: rgba(76, 154, 255, .10)
        }

        td.ecr-link {
            cursor: zoom-in
        }

        .clickable {
            color: var(--accent);
            cursor: pointer
        }

        .clickable:hover {
            text-decoration: underline
        }

        /* Réglage qui ne s'applique pas à soi-même : lisible, mais
           visiblement hors d'atteinte. Ni couleur d'accentuation ni
           soulignement au survol — rien qui laisse croire qu'on peut
           cliquer. */
        .verrouille {
            color: var(--txt);
            opacity: .42;
            cursor: not-allowed;
        }

        /* Même traitement pour les commandes désactivées, que les
           navigateurs grisent trop discrètement pour qu'on le remarque
           avant d'avoir essayé. */
        select:disabled,
        button:disabled,
        input[type="checkbox"]:disabled+.asl {
            opacity: .42;
            cursor: not-allowed;
        }

        /* Un bouton désactivé ne doit pas réagir au survol : le
           changement de fond suggérerait qu'il attend un clic. */
        button:disabled:hover {
            background: inherit;
            border-color: inherit;
        }

        .aswitch:has(input:disabled) {
            cursor: not-allowed;
        }

        .pos {
            color: var(--green)
        }

        .neg {
            color: var(--red)
        }

        .chip {
            display: inline-block;
            background: var(--chip);
            border-radius: 20px;
            padding: 2px 9px;
            font-size: 11px
        }

        .chip.let {
            background: #22402f;
            color: #7fe0a8
        }

        .chip.unlet {
            background: #3a2c22;
            color: #ffcf9e
        }

        .chip.an {
            background: #2b2a45;
            color: #c3b8ff;
            font-weight: 700
        }

        .badge {
            display: inline-block;
            padding: 1px 7px;
            border-radius: 6px;
            font-size: 11px;
            background: var(--chip)
        }

        /* Nom du journal, à droite de son code dans Écritures et le grand
           livre. Volontairement discret : c'est un rappel, pas une donnée
           qu'on lit ligne à ligne — le code reste ce qui identifie.

           La largeur est bornée et le dépassement coupé : un libellé
           bavard (« Opérations diverses de clôture ») élargirait sinon la
           colonne sur toute la hauteur du tableau. Le nom entier reste
           dans l'attribut title. */
        .jl {
            display: inline-block;
            max-width: 96px;
            vertical-align: middle;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
            font-size: 9px;
            color: var(--muted)
        }

        /* Filters bar */
        .filters {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
            align-items: end;
            margin-bottom: 14px
        }

        /* Rangée de sélection du compte (grand livre) : hauteur unique pour
     les champs, les flèches et les boutons, afin qu'ils s'alignent au pixel.
     Les deux champs y figurent — le général et l'auxiliaire se font face,
     un pixel d'écart entre eux se verrait. */
        #glCn,
        #glCnAux,
        #glPrev,
        #glNext,
        #glPrevAux,
        #glNextAux,
        #glGo,
        #glRaz,
        #glTcd {
            height: 34px;
            box-sizing: border-box;
            display: inline-flex;
            align-items: center;
            justify-content: center;
        }

        #glCn,
        #glCnAux {
            justify-content: flex-start
        }

        /* Mêmes chevrons, même calibre que ceux du sélecteur d'exercice
           de la barre du haut : c'est le même geste. */
        #glPrev,
        #glNext,
        #glPrevAux,
        #glNextAux {
            padding: 0 8px;
            font-size: 11px;
            line-height: 1
        }

        #glGo,
        #glRaz,
        #glTcd {
            padding: 0 14px
        }

        .filters .f,
        #glViewBar .f {
            display: flex;
            flex-direction: column;
            gap: 4px
        }

        .filters .f label,
        #glViewBar .f label {
            font-size: 10px;
            color: var(--muted);
            text-transform: uppercase;
            letter-spacing: .4px
        }

        .filters .f input,
        .filters .f select,
        #glViewBar .f select {
            min-width: 120px
        }

        .seg {
            display: inline-flex;
            border: 1px solid var(--line);
            border-radius: 8px;
            overflow: hidden
        }

        .seg button {
            border: 0;
            border-radius: 0;
            background: var(--panel2);
            padding: 7px 12px
        }

        .seg button.on {
            background: var(--accent2);
            color: #fff
        }

        /* Dropzone */
        #drop {
            border: 2px dashed var(--line);
            border-radius: 14px;
            padding: 40px;
            text-align: center;
            color: var(--muted);
            transition: .15s;
            background: var(--panel)
        }

        #drop.over {
            border-color: var(--accent);
            background: #17233a;
            color: var(--txt)
        }

        #drop .big {
            font-size: 40px;
            margin-bottom: 8px
        }

        /* ------------------------------------------------------------
           RH — journaux de paie

           La zone de dépôt reprend le dessin de celle de l'import
           comptable ; elle ne peut pas la RÉUTILISER telle quelle, #drop
           étant un identifiant et les deux écrans pouvant coexister.
           ------------------------------------------------------------ */
        .rh-drop {
            border: 2px dashed var(--line);
            border-radius: 14px;
            padding: 32px;
            text-align: center;
            color: var(--muted);
            transition: .15s;
            background: var(--panel)
        }

        .rh-drop.on {
            border-color: var(--accent);
            color: var(--txt)
        }

        .rh-drop .big {
            font-size: 34px;
            margin-bottom: 8px
        }

        /* Barres du tableau croisé. La jauge est en pourcentage de la
           plus grande valeur affichée : c'est une proportion, pas un
           chiffre — le montant lu reste celui du serveur, à droite. */
        .rh-barres {
            margin: 4px 0 16px;
            display: flex;
            flex-direction: column;
            gap: 4px
        }

        .rh-barre {
            display: flex;
            align-items: center;
            gap: 10px;
            font-size: 12px
        }

        .rh-barre-nom {
            flex: 0 0 260px;
            color: var(--muted);
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap
        }

        .rh-barre-piste {
            flex: 1;
            height: 14px;
            background: var(--panel2);
            border-radius: 4px;
            overflow: hidden
        }

        .rh-barre-jauge {
            display: block;
            height: 100%;
            background: var(--accent);
            border-radius: 4px
        }

        .rh-barre-jauge.neg {
            background: var(--red)
        }

        .rh-barre-val {
            flex: 0 0 130px;
            text-align: right;
            font-variant-numeric: tabular-nums
        }

        .filelist {
            margin-top: 16px
        }

        .filerow {
            display: flex;
            align-items: center;
            gap: 12px;
            padding: 10px 12px;
            border: 1px solid var(--line);
            border-radius: 10px;
            margin-bottom: 8px;
            background: var(--panel)
        }

        .filerow .nm {
            font-weight: 600
        }

        .filerow .meta {
            color: var(--muted);
            font-size: 11px
        }

        .filerow .spacer {
            flex: 1
        }

        .pager {
            display: flex;
            gap: 8px;
            align-items: center;
            justify-content: flex-end;
            padding: 10px 2px;
            color: var(--muted)
        }

        .pill {
            font-size: 11px;
            padding: 2px 8px;
            border-radius: 20px;
            background: var(--chip)
        }

        .warn {
            background: #3a2c22;
            border: 1px solid #5c3f22;
            color: #ffcf9e;
            padding: 10px 14px;
            border-radius: 10px;
            margin-bottom: 12px
        }

        .warn-crit {
            background: rgba(255, 69, 58, .12);
            border: 1px solid rgba(255, 69, 58, .55);
            color: #ff6b60;
            padding: 8px 12px;
            border-radius: 8px;
            font-size: 12px;
            font-weight: 600
        }

        .ok {
            background: #1c3325;
            border: 1px solid #2c5a3d;
            color: #8fe6b3;
            padding: 10px 14px;
            border-radius: 10px;
            margin-bottom: 12px
        }

        .toolbar {
            display: flex;
            gap: 8px;
            align-items: center;
            margin-bottom: 12px;
            flex-wrap: wrap
        }

        .sub-tabs {
            display: flex;
            gap: 6px;
            margin-bottom: 14px;
            flex-wrap: wrap
        }

        .sub-tabs button {
            background: var(--panel2)
        }

        .sub-tabs button.on {
            background: var(--accent2);
            border-color: var(--accent2);
            color: #fff
        }

        .two-col {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 16px
        }

        @media(max-width:900px) {
            .two-col {
                grid-template-columns: 1fr
            }
        }

        .fin-table td:first-child {
            white-space: normal
        }

        .fin-table .grp {
            font-weight: 700;
            background: var(--panel2)
        }

        .fin-table .tot {
            font-weight: 700;
            border-top: 2px solid var(--line)
        }

        /* ---------- Bilan détaillé ----------
           Repris de la présentation d'un bilan imprimé : les groupes
           portent le nom du poste en gras, les lignes de détail sont
           décalées sous eux. Le trait de séparation est posé AU-DESSUS
           du groupe et non sous chaque ligne — un filet par ligne
           quadrillerait la page et ferait perdre les regroupements, qui
           sont justement ce qu'on lit en premier dans un bilan. */
        .bil-table td,
        .bil-table th {
            border-bottom: 0
        }

        .bil-table .bil-grp>td {
            font-weight: 700;
            border-top: 1px solid var(--line);
            padding-top: 10px
        }

        /* Le premier groupe touche l'en-tête : son filet ferait doublon
           avec celui du thead. */
        .bil-table tbody tr:first-child>td {
            border-top: 0
        }

        .bil-table .bil-det>td:first-child {
            padding-left: 22px;
            color: var(--muted)
        }

        .bil-table .tot>td {
            border-top: 2px solid var(--line);
            padding-top: 10px
        }

        /* Les à-nouveaux dans le grand livre en modale. Ils ne sont pas
           des mouvements de l'exercice : les laisser identiques aux
           autres lignes fait lire un compte comme s'il avait bougé. */
        #modal tr.gl-an>td {
            background: #1c1b2e
        }

        /* Le résultat sous le compte de résultat. Il remplace la carte
           d'indicateur qui doublait l'information : la ligne se lit dans
           la continuité des deux totaux, au lieu de rouvrir un bloc. */
        .bil-resultat {
            display: flex;
            justify-content: space-between;
            align-items: baseline;
            gap: 12px;
            margin-top: 14px;
            padding: 12px 14px;
            border: 1px solid var(--line);
            border-radius: 8px;
            background: var(--panel2);
            font-size: 13px;
        }

        .bil-resultat strong {
            font-size: 17px;
            font-weight: 700;
        }

        /* Les quatre colonnes de l'actif contre deux au passif : sans
           largeur imposée, les libellés de l'actif se retrouvaient à
           l'étroit et ceux du passif au large, et les deux tableaux ne
           se lisaient plus à la même hauteur. */
        .bilan .bil-table th.num,
        .bilan .bil-table td.num {
            width: 22%
        }

        /* ---------- Choix de l'identité visuelle ----------
           Des pastilles de couleur plutôt qu'une liste déroulante :
           « Ardoise » et « Portoro » ne disent rien à qui ne les a pas
           vues, et on choisit une apparence en la regardant. */
        .ident-choix {
            display: flex;
            flex-wrap: wrap;
            gap: 12px;
        }

        .ident-carte {
            flex: 1 1 240px;
            display: block;
            text-align: left;
            padding: 14px;
            border: 1px solid var(--line);
            border-radius: 12px;
            background: var(--panel2);
            cursor: pointer;
            transition: border-color .2s, transform .15s cubic-bezier(.16, 1, .3, 1);
        }

        .ident-carte:hover {
            border-color: var(--accent);
            transform: translateY(-1px);
        }

        /* L'identité active se signale par la BORDURE et par un point
           après son nom, pas par la seule couleur : sur un écran de
           choix de couleurs, distinguer par la couleur est exactement
           ce qui ne peut pas marcher. */
        .ident-carte.active {
            border-color: var(--accent);
            box-shadow: inset 0 0 0 1px var(--accent);
        }

        .ident-teintes {
            display: flex;
            gap: 5px;
            margin-bottom: 10px;
        }

        .ident-teintes span {
            width: 34px;
            height: 22px;
            border-radius: 5px;
            border: 1px solid rgba(255, 255, 255, .12);
        }

        /* L'échantillon est écrit DANS la police qu'il propose : une
           liste de noms ne dit rien à qui ne les connaît pas, et
           « Jost » ne se juge pas sur le mot « Jost ». Le libellé
           reprend une ligne de balance parce que c'est là que la
           police travaillera. */
        .ident-apercu {
            display: block;
            font-size: 15px;
            color: var(--txt);
            margin-bottom: 8px;
            font-variant-numeric: tabular-nums lining-nums;
        }

        .ident-apercu[data-typo-ex="inter"] { font-family: 'Inter', sans-serif; }
        .ident-apercu[data-typo-ex="plex"] { font-family: 'IBM Plex Sans', sans-serif; }
        .ident-apercu[data-typo-ex="atkinson"] { font-family: 'Atkinson Hyperlegible', sans-serif; }
        .ident-apercu[data-typo-ex="lexend"] { font-family: 'Lexend', sans-serif; }
        .ident-apercu[data-typo-ex="opendyslexic"] { font-family: 'OpenDyslexic', sans-serif; }

        /* Le bandeau de confirmation. Il ne paraît que s'il y a
           quelque chose à confirmer : revenir sur ses pas en recliquant
           le réglage d'origine le fait disparaître de lui-même. */
        .ident-barre {
            display: flex;
            align-items: center;
            gap: 10px;
            padding: 12px 14px;
            margin-bottom: 8px;
            border: 1px solid var(--accent);
            border-radius: 10px;
            background: var(--panel2);
            font-size: 12px;
        }

        .ident-nom {
            display: block;
            font-weight: 600;
            font-size: 13px;
            color: var(--txt);
        }

        .ident-desc {
            display: block;
            font-size: 11px;
            color: var(--muted);
            margin-top: 3px;
            white-space: normal;
        }

        .hint {
            font-size: 11px;
            color: var(--muted);
            margin-top: 6px;
            line-height: 1.5
        }

        a {
            color: var(--accent)
        }

        .spin {
            display: inline-block;
            width: 14px;
            height: 14px;
            border: 2px solid var(--line);
            border-top-color: var(--accent);
            border-radius: 50%;
            animation: sp .7s linear infinite;
            vertical-align: middle
        }

        @keyframes sp {
            to {
                transform: rotate(360deg)
            }
        }

        #overlay {
            position: fixed;
            inset: 0;
            background: rgba(6, 9, 14, .7);
            display: none;
            align-items: center;
            justify-content: center;
            z-index: 50
        }

        #overlay .box {
            background: var(--panel);
            border: 1px solid var(--line);
            border-radius: 14px;
            padding: 24px 30px;
            text-align: center
        }

        .empty {
            text-align: center;
            color: var(--muted);
            padding: 60px 20px
        }

        .empty .big {
            font-size: 46px;
            margin-bottom: 10px
        }

        /* L'encart pour mettre en évidence l'exercice actif */
        .ex-highlight {
            display: inline-block;
            background: var(--accent2);
            color: #fff;
            font-size: 15px;
            padding: 6px 14px;
            border-radius: 8px;
            font-weight: 700;
            box-shadow: 0 4px 10px rgba(45, 111, 214, 0.4);
        }

        /* L'en-tête d'impression n'existe que sur papier */
        .print-only {
            display: none
        }

        /* ========== STYLES POUR L'IMPRESSION (PDF) ========== */
        /* Marges de page à zéro : les en-têtes/pieds du navigateur (date, titre,
     chemin du fichier, n° de page) vivent dans ces marges — sans marge,
     ils disparaissent. Les marges du document sont rendues par le padding. */
        @page {
            margin: 0;
        }

        @media print {
            body {
                padding: 12mm 11mm !important;
            }

            .print-only {
                display: block !important;
            }

            /* La note de révision ne s'imprime pas : le grand livre a
               son propre en-tête, conçu pour dire ce qu'on regarde, et
               un encadré ambre au milieu d'une page en noir et blanc ne
               ressemblerait à rien. La note se lit à l'écran, ou dans la
               révision — qui, elle, s'imprime avec la balance. */
            #glSynth,
            #glName,
            #glViewBar,
            #glRevision,
            .no-print {
                display: none !important;
            }

            /* remplacés par l'en-tête d'impression */

            /* --- En-tête professionnel du grand livre --- */
            #glPrint {
                color: #000;
                margin-bottom: 14px;
            }

            #glPrint .gp-band {
                display: flex;
                justify-content: space-between;
                align-items: flex-start;
                gap: 20px;
                border-bottom: 3px double #000;
                padding-bottom: 10px;
            }

            #glPrint .gp-title {
                font-size: 20px;
                font-weight: 700;
                letter-spacing: 1px;
            }

            #glPrint .gp-compte {
                font-size: 14px;
                font-weight: 700;
                margin-top: 4px;
            }

            #glPrint .gp-meta {
                font-size: 10px;
                margin-top: 4px;
                line-height: 1.6;
            }

            #glPrint .gp-edit {
                font-size: 10px;
                text-align: right;
                white-space: nowrap;
            }

            #glPrint table.gp-synth {
                border-collapse: collapse;
                font-size: 11px;
                margin-left: auto;
            }

            #glPrint table.gp-synth td {
                border: none !important;
                padding: 2px 0 2px 24px;
                text-align: right;
            }

            #glPrint table.gp-synth td:first-child {
                text-align: left;
                padding-left: 0;
            }

            #glPrint table.gp-synth tr.gp-tot td {
                font-weight: 700;
                border-top: 1px solid #000 !important;
            }

            #glPrint table.gp-synth tr.gp-tot td:last-child {
                border-bottom: 3px double #000 !important;
            }

            /* --- En-tête d'impression de la balance ---
               Sobre à dessein : le tableau doit rester la matière de la
               page. Corps de texte normal, pas de titre démesuré, un
               simple filet pour séparer. */
            #balPrint {
                color: #000;
                margin-bottom: 10px;
            }

            #balPrint .bp-band {
                display: flex;
                justify-content: space-between;
                align-items: baseline;
                gap: 16px;
                border-bottom: 1.5px solid #000;
                padding-bottom: 3px;
            }

            #balPrint .bp-title {
                font-size: 13px;
                font-weight: 700;
                letter-spacing: .6px;
            }

            #balPrint .bp-ex {
                font-size: 11px;
                font-weight: 600;
                white-space: nowrap;
            }

            #balPrint .bp-meta {
                font-size: 10px;
                margin-top: 3px;
                line-height: 1.45;
            }

            #balPrint .bp-alerte {
                font-size: 10px;
                font-weight: 700;
                margin-top: 2px;
            }

            #balPrint .bp-edit {
                font-size: 9px;
                margin-top: 2px;
            }

            /* Repris dans le bandeau ci-dessus, sous une forme adaptée
               au papier. */
            #balSummary,
            #balNote {
                display: none !important;
            }

            /* La barre de feuillets est de la navigation : sur papier
               elle n'est qu'un rang de mots orphelins au-dessus du
               tableau. C'est l'en-tête d'impression qui dit ce qu'on
               regarde. */
            #side,
            #top,
            #feuillets,
            .pin-col,
            .filters,
            .pager,
            .toolbar,
            .sub-tabs,
            #drop,
            .filelist,
            #btnExport,
            #btnPrint {
                display: none !important;
            }

            body,
            html,
            #app,
            #main,
            #content {
                height: auto !important;
                overflow: visible !important;
                display: block !important;
                background: #fff;
                color: #000;
                font-size: 11px;
            }

            /* body est exclu à dessein : ses 12 mm / 11 mm SONT les
               marges de page. Le remplissage de 18 px de #content, lui,
               s'y ajoutait et rognait la largeur utile pour rien. Une
               fois retiré, bandeau de titre et tableau partagent
               exactement le même alignement, de bord à bord. */
            html,
            #app,
            #main,
            #content {
                padding: 0 !important;
                margin: 0 !important;
            }

            .card {
                border: none !important;
                margin: 0 !important;
                padding: 0 !important;
                box-shadow: none !important;
                background: transparent !important;
            }

            .tablewrap {
                max-height: none !important;
                overflow: visible !important;
                border: none !important;
            }

            /* --- Largeur des tableaux imprimés ---
               À l'écran, `white-space: nowrap` empêche qu'un montant se
               coupe en deux lignes, et la barre de défilement absorbe
               ce qui dépasse. Sur papier il n'y a pas de défilement :
               le tableau débordait donc à droite, au-delà de la zone
               imprimable, et la page semblait décalée vers la gauche —
               marge à gauche, aucune à droite.

               On rend donc leur souplesse aux colonnes de texte, qui
               peuvent se replier, tout en la refusant aux montants et
               aux dates, qu'une coupure rendrait illisibles. Le tableau
               s'aligne alors exactement sur le bandeau de titre. */
            .tablewrap table {
                width: 100% !important;
                max-width: 100% !important;
                font-size: 10px;
            }

            .tablewrap th,
            .tablewrap td {
                white-space: normal !important;
                overflow-wrap: anywhere;
                padding-left: 4px !important;
                padding-right: 4px !important;
            }

            /* Un montant, une date ou un code ne se coupent jamais. */
            .tablewrap th.num,
            .tablewrap td.num,
            .tablewrap td .chip,
            .tablewrap td .badge {
                white-space: nowrap !important;
            }

            /* Sur papier il n'y a pas de survol pour lire l'attribut
               title : le nom du journal s'affiche en entier plutôt que
               coupé au bout de 96 pixels, où il ne servirait à rien. */
            .tablewrap td .jl {
                max-width: none !important;
                overflow: visible !important;
                white-space: normal !important;
                font-size: 8px;
            }

            thead th {
                position: static !important;
                background: #f0f0f0 !important;
                color: #000 !important;
                border-bottom: 2px solid #000 !important;
            }

            tbody td {
                border-bottom: 1px solid #ddd !important;
            }

            .badge,
            .chip {
                background: transparent !important;
                color: #000 !important;
                border: 1px solid #000;
            }

            .pos {
                color: #000 !important;
            }

            .neg {
                color: #000 !important;
                font-weight: bold;
            }

            .kpi {
                border: 1px solid #ccc !important;
            }

            table {
                width: 100%;
                page-break-inside: auto;
            }

            tr {
                page-break-inside: avoid;
                page-break-after: auto;
            }

            thead {
                display: table-header-group;
            }

            .ex-highlight {
                background: transparent !important;
                color: #000 !important;
                border: 2px solid #000;
                box-shadow: none !important;
            }
        }

        .pvtUi {
            color: #333
        }

        table.pvtTable {
            font-size: 8pt;
            text-align: left;
            border-collapse: collapse
        }

        table.pvtTable tbody tr th,
        table.pvtTable thead tr th {
            background-color: #e6EEEE;
            border: 1px solid #CDCDCD;
            font-size: 8pt;
            padding: 5px
        }

        table.pvtTable .pvtColLabel {
            text-align: center
        }

        table.pvtTable .pvtTotalLabel {
            text-align: right
        }

        table.pvtTable tbody tr td {
            color: #3D3D3D;
            padding: 5px;
            background-color: #FFF;
            border: 1px solid #CDCDCD;
            vertical-align: top;
            text-align: right
        }

        .pvtGrandTotal,
        .pvtTotal {
            font-weight: 700
        }

        .pvtVals {
            text-align: center;
            white-space: nowrap
        }

        .pvtColOrder,
        .pvtRowOrder {
            cursor: pointer;
            width: 15px;
            margin-left: 5px;
            display: inline-block
        }

        .pvtAggregator {
            margin-bottom: 5px
        }

        .pvtAxisContainer,
        .pvtVals {
            border: 1px solid gray;
            background: #EEE;
            padding: 5px;
            min-width: 20px;
            min-height: 20px;
            user-select: none;
            -webkit-user-select: none;
            -moz-user-select: none;
            -khtml-user-select: none;
            -ms-user-select: none
        }

        .pvtAxisContainer li {
            padding: 8px 6px;
            list-style-type: none;
            cursor: move
        }

        .pvtAxisContainer li.pvtPlaceholder {
            -webkit-border-radius: 5px;
            padding: 3px 15px;
            -moz-border-radius: 5px;
            border-radius: 5px;
            border: 1px dashed #aaa
        }

        .pvtAxisContainer li span.pvtAttr {
            -webkit-text-size-adjust: 100%;
            background: #F3F3F3;
            border: 1px solid #DEDEDE;
            padding: 2px 5px;
            white-space: nowrap;
            -webkit-border-radius: 5px;
            -moz-border-radius: 5px;
            border-radius: 5px
        }

        .pvtTriangle {
            cursor: pointer;
            color: grey
        }

        .pvtHorizList li {
            display: inline
        }

        .pvtVertList {
            vertical-align: top
        }

        .pvtFilteredAttribute {
            font-style: italic
        }

        .pvtFilterBox {
            z-index: 100;
            width: 300px;
            border: 1px solid gray;
            background-color: #fff;
            position: absolute;
            text-align: center
        }

        .pvtFilterBox h4 {
            margin: 15px
        }

        .pvtFilterBox p {
            margin: 10px auto
        }

        .pvtFilterBox label {
            font-weight: 400
        }

        .pvtFilterBox input[type=checkbox] {
            margin-right: 10px;
            margin-left: 10px
        }

        .pvtFilterBox input[type=text] {
            width: 230px
        }

        .pvtFilterBox .count {
            color: gray;
            font-weight: 400;
            margin-left: 3px
        }

        .pvtCheckContainer {
            text-align: left;
            font-size: 14px;
            white-space: nowrap;
            overflow-y: scroll;
            width: 100%;
            max-height: 250px;
            border-top: 1px solid #d3d3d3;
            border-bottom: 1px solid #d3d3d3
        }

        .pvtCheckContainer p {
            margin: 5px
        }

        .pvtRendererArea {
            padding: 5px
        }

        /* ============================================================
   TCD (PivotTable.js) — habillage aux couleurs de l'application.
   100 % cosmétique : aucune classe n'est renommée, aucun élément
   déplacé. Tout est porté par #tcdPivot / #tcdPivotWrap, dont la
   spécificité (id) l'emporte sur la feuille de style de la lib.
   ============================================================ */

        /* --- Conteneur --- */
        #tcdPivotWrap {
            background: var(--panel);
            border: 1px solid var(--line);
            border-radius: 10px;
            color: var(--txt);
            padding: 12px;
            overflow: auto;
            max-height: calc(100vh - 300px);
            scrollbar-color: var(--line) transparent;
        }

        /* --- Grille de l'interface (zones de dépôt) --- */
        #tcdPivot table.pvtUi {
            color: var(--txt);
            border-collapse: separate;
            border-spacing: 8px;
            width: 100%;
        }

        #tcdPivot .pvtUiCell {
            background: transparent
        }

        #tcdPivot .pvtAxisContainer,
        #tcdPivot td.pvtVals {
            background: var(--bg);
            border: 1px dashed var(--line);
            border-radius: 8px;
            padding: 8px;
        }

        #tcdPivot .pvtAxisContainer li {
            padding: 4px 3px
        }

        /* --- Étiquettes de champs (draggables) --- */
        #tcdPivot .pvtAxisContainer li span.pvtAttr {
            background: var(--chip);
            border: 1px solid var(--line);
            color: var(--txt);
            border-radius: 6px;
            padding: 4px 8px;
            font-size: 12px;
        }

        #tcdPivot .pvtAxisContainer li span.pvtAttr:hover {
            border-color: var(--accent)
        }

        #tcdPivot .pvtAxisContainer li.pvtPlaceholder {
            border: 1px dashed var(--accent);
            border-radius: 6px;
            background: rgba(76, 154, 255, .08);
        }

        /* Un champ filtré est signalé en ambre (repère visuel utile) */
        #tcdPivot .pvtFilteredAttribute {
            font-style: italic;
            color: var(--amber)
        }

        #tcdPivot .pvtTriangle {
            color: var(--muted)
        }

        #tcdPivot .pvtTriangle:hover {
            color: var(--accent)
        }

        #tcdPivot .pvtRowOrder,
        #tcdPivot .pvtColOrder {
            color: var(--muted)
        }

        #tcdPivot .pvtRowOrder:hover,
        #tcdPivot .pvtColOrder:hover {
            color: var(--accent)
        }

        /* --- Listes déroulantes (rendu / agrégation) --- */
        #tcdPivot select.pvtRenderer,
        #tcdPivot select.pvtAggregator,
        #tcdPivot select.pvtAttrDropdown,
        #tcdPivot .pvtVals select,
        #tcdPivotWrap select {
            background: var(--panel2);
            border: 1px solid var(--line);
            color: var(--txt);
            padding: 6px 8px;
            border-radius: 7px;
            font-family: inherit;
            font-size: 12px;
        }

        #tcdPivot select:focus {
            border-color: var(--accent);
            outline: none
        }

        #tcdPivot .pvtRendererArea {
            padding: 4px 0 0
        }

        /* --- Tableau croisé --- */
        #tcdPivot table.pvtTable {
            font-size: 12px;
            border-collapse: collapse
        }

        /* Neutralise les règles globales de l'app qui fuyaient ici
   (en-têtes collants superposés sur les tableaux à plusieurs
   niveaux, et curseur « cliquable » trompeur). */
        #tcdPivot table.pvtTable thead tr th,
        #tcdPivot table.pvtTable tbody tr th {
            position: static;
            cursor: default;
            background: var(--panel2);
            color: var(--txt);
            border: 1px solid var(--line);
            font-size: 12px;
            font-weight: 600;
            padding: 6px 10px;
            text-align: center;
        }

        #tcdPivot table.pvtTable tbody tr td {
            background: var(--panel);
            color: var(--txt);
            border: 1px solid var(--line);
            padding: 6px 10px;
            text-align: right;
            vertical-align: middle;
            font-variant-numeric: tabular-nums;
        }

        #tcdPivot table.pvtTable tbody tr:hover td {
            background: var(--panel2)
        }

        #tcdPivot table.pvtTable th.pvtRowLabel {
            text-align: left;
            font-weight: 500
        }

        #tcdPivot table.pvtTable th.pvtTotalLabel {
            text-align: right;
            color: var(--muted);
            text-transform: uppercase;
            letter-spacing: .4px;
            font-size: 11px
        }

        #tcdPivot table.pvtTable .pvtTotal,
        #tcdPivot table.pvtTable .pvtGrandTotal {
            font-weight: 700;
            background: var(--panel2);
        }

        #tcdPivot table.pvtTable .pvtGrandTotal {
            border-top: 2px solid var(--line)
        }

        /* --- Fenêtre de filtre d'un champ --- */
        #tcdPivot .pvtFilterBox {
            background: var(--panel);
            border: 1px solid var(--line);
            border-radius: 10px;
            color: var(--txt);
            box-shadow: 0 12px 30px rgba(0, 0, 0, .5);
        }

        #tcdPivot .pvtFilterBox h4 {
            color: var(--txt)
        }

        #tcdPivot .pvtFilterBox .count {
            color: var(--muted)
        }

        #tcdPivot .pvtFilterBox input[type=text],
        #tcdPivot .pvtFilterBox input.pvtSearch {
            background: var(--panel2);
            border: 1px solid var(--line);
            color: var(--txt);
            padding: 6px 8px;
            border-radius: 7px;
            font-family: inherit;
        }

        #tcdPivot .pvtFilterBox input[type=text]:focus {
            border-color: var(--accent);
            outline: none
        }

        #tcdPivot .pvtFilterBox button {
            background: var(--panel2);
            border: 1px solid var(--line);
            color: var(--txt);
            padding: 6px 10px;
            border-radius: 7px;
            font-family: inherit;
            font-size: 12px;
            cursor: pointer;
        }

        #tcdPivot .pvtFilterBox button:hover {
            background: var(--chip);
            border-color: var(--accent)
        }

        #tcdPivot .pvtCheckContainer {
            border-top: 1px solid var(--line);
            border-bottom: 1px solid var(--line);
            font-size: 12px;
        }

        #tcdPivot .pvtCheckContainer p {
            margin: 0;
            padding: 4px 6px;
            border-radius: 6px
        }

        #tcdPivot .pvtCheckContainer p:hover {
            background: var(--panel2)
        }

        #tcdPivot .pvtCheckContainer p.selected {
            background: var(--panel2)
        }

        #tcdPivot .pvtFilterBox label {
            color: var(--txt);
            font-weight: 400
        }

        /* --- Impression : on repasse le TCD en clair pour le papier --- */
        @media print {
            #tcdPivotWrap {
                background: #fff !important;
                border: none !important;
                max-height: none !important;
                overflow: visible !important;
                padding: 0 !important;
            }

            #tcdPivot table.pvtTable thead tr th,
            #tcdPivot table.pvtTable tbody tr th {
                background: #f0f0f0 !important;
                color: #000 !important;
                border: 1px solid #999 !important;
            }

            #tcdPivot table.pvtTable tbody tr td {
                background: #fff !important;
                color: #000 !important;
                border: 1px solid #ccc !important;
            }

            #tcdPivot table.pvtTable .pvtTotal,
            #tcdPivot table.pvtTable .pvtGrandTotal {
                background: #f0f0f0 !important;
                color: #000 !important
            }

            /* les zones de glisser-déposer n'ont pas de sens sur papier —
               la zone des axes est nommée séparément parce qu'elle ne
               porte pas « pvtUnused » (voir grouperAxesTCD) */
            #tcdPivot .pvtAxisContainer.pvtUnused,
            #tcdPivot .pvt-zone-axes,
            #tcdPivot .pvtRendererArea select,
            #tcdPivot .pvtTriangle {
                display: none !important
            }

            #tcdPivot .pvtAxisContainer,
            #tcdPivot td.pvtVals {
                background: transparent !important;
                border: none !important;
            }

            #tcdPivot .pvtAxisContainer li span.pvtAttr {
                background: transparent !important;
                color: #000 !important;
                border: 1px solid #000 !important;
            }
        }

/* ---------- Compte rendu par fichier + éclatement des tiers ---------- */
.filewrap {
    border: 1px solid var(--line);
    border-radius: 10px;
    margin-bottom: 8px;
    background: var(--panel);
}
.filewrap .filerow {
    border: 0;
    margin: 0;
    background: transparent;
}
.filedet {
    border-top: 1px solid var(--line);
    padding: 0 12px 10px;
}
.filedet > summary {
    cursor: pointer;
    padding: 8px 0;
    font-size: 12px;
    color: var(--accent);
}
.filedet-body {
    font-size: 12px;
}
.rep-grid {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 3px 14px;
    font-size: 12px;
    margin: 4px 0 10px;
}
.rep-grid .k {
    color: var(--muted);
}
.rep-h {
    font-weight: 600;
    font-size: 12px;
    margin: 10px 0 2px;
}
.rep-prev {
    font-family: ui-monospace, Menlo, Consolas, monospace;
    font-size: 11px;
    line-height: 1.7;
}

/* ---------- Balance : sous-lignes auxiliaires, dépliage, filtres ---------- */
.filters .f.f-check {
    justify-content: flex-end;
}
.filters .f.f-check label {
    display: flex;
    gap: 6px;
    align-items: center;
    padding: 8px 0;
    white-space: nowrap;
    cursor: pointer;
}
.filters .f.f-check input[type=checkbox] {
    min-width: 0;
    width: 14px;
    height: 14px;
    margin: 0;
}
tbody tr.bal-sub td {
    background: rgba(255, 255, 255, .028);
    font-size: 11px;
    padding-top: 4px;
    padding-bottom: 4px;
}
.bal-exp { cursor: pointer; }
.exp-c {
    display: inline-block;
    width: 10px;
    font-size: 10px;
}

/* ---------- TCD : adaptation de PivotTable.js au thème sombre ----------
   Les règles d'origine (plus haut) sont en thème clair : cellules blanches
   sur fond sombre. On les surcharge ici plutôt que de les réécrire. */
.pvtUi {
    color: var(--txt);
    background: transparent;
}
.pvtUi select,
.pvtUi input {
    background: var(--panel2);
    color: var(--txt);
    border: 1px solid var(--line);
    border-radius: 6px;
    padding: 3px 6px;
    font-size: 12px;
}
table.pvtTable {
    font-size: 12px;
    color: var(--txt);
}
table.pvtTable tbody tr th,
table.pvtTable thead tr th {
    background-color: var(--panel2);
    border: 1px solid var(--line);
    color: var(--txt);
    font-size: 12px;
    font-weight: 600;
}
table.pvtTable tbody tr td {
    background-color: var(--panel);
    border: 1px solid var(--line);
    color: var(--txt);
    font-variant-numeric: tabular-nums;
}
.pvtGrandTotal,
.pvtTotal {
    background-color: var(--panel2) !important;
    font-weight: 700;
}
.pvtAxisContainer,
.pvtVals,
.pvtRenderers,
.pvtAggregator {
    background: var(--panel2);
    border: 1px solid var(--line);
    color: var(--txt);
}
.pvtAxisContainer li span.pvtAttr {
    background: var(--panel);
    border: 1px solid var(--line);
    color: var(--txt);
    border-radius: 6px;
    padding: 3px 6px;
}
.pvtTriangle { color: var(--muted); }
.pvtFilterBox {
    background: var(--panel);
    color: var(--txt);
    border: 1px solid var(--line);
    border-radius: 8px;
}
.pvtFilterBox h4,
.pvtFilterBox p,
.pvtFilterBox label { color: var(--txt); }
.pvtCheckContainer { color: var(--txt); }
.pvtCheckContainer p { background: var(--panel2); }
.pvtCheckContainer p.selected { background: var(--accent); color: #fff; }
.pvtDragHandle { color: var(--muted); }
.pvtRowOrder,
.pvtColOrder { color: var(--muted); }
/* Le tableau croisé peut être très large : il défile dans son propre cadre
   plutôt que de pousser la page entière. */
#tcdPivot {
    overflow-x: auto;
    max-width: 100%;
}

/* ---------- Écran de connexion ---------- */
/* ============================================================
   POLICES DE L'ÉCRAN DE CONNEXION

   Deux fontes VARIABLES, un fichier chacune : toutes les graisses
   d'une famille tiennent dans le même fichier, ce qui divise le poids
   par trois par rapport à un fichier par graisse. 64 Ko en tout.

   HÉBERGÉES ICI, jamais appelées chez un tiers. Une requête sortante au
   moment précis où quelqu'un s'apprête à ouvrir une session dit à ce
   tiers qu'un poste se connecte au logiciel. Dans un cabinet tenu au
   secret professionnel, cela ne se paie pas en pixels.

   ELLES NE SERVENT QU'À CET ÉCRAN. Une didone sur une balance de
   200 000 lignes serait illisible, et la police de l'application est
   choisie pour la densité, pas pour l'allure. Le seuil est ici : on
   entre par un salon, on travaille dans un bureau.

   `font-display: swap` — le texte s'affiche tout de suite dans la
   police système et se substitue à l'arrivée. Personne n'attend devant
   un champ de connexion vide parce qu'une fonte se charge. */
@font-face {
    font-family: 'Cormorant Garamond';
    src: url('vendor/polices/cormorant-garamond.woff2') format('woff2');
    font-weight: 300 700;
    font-style: normal;
    font-display: swap;
    /* Mêmes métriques que Jost, pour la même raison : la marque de la
       barre latérale ne doit pas déplacer les menus qui la suivent. */
    ascent-override: 98%;
    descent-override: 25%;
    line-gap-override: 0%;
}

@font-face {
    font-family: 'Atkinson Hyperlegible';
    src: url('vendor/polices/atkinson-hyperlegible.woff2') format('woff2');
    font-weight: 400 700;
    font-style: normal;
    font-display: swap;
    /* Mêmes métriques forcées que les autres : changer de police ne
       doit rien déplacer (voir le @font-face de Jost). */
    ascent-override: 98%;
    descent-override: 25%;
    line-gap-override: 0%;
}

@font-face {
    font-family: 'Lexend';
    src: url('vendor/polices/lexend.woff2') format('woff2');
    font-weight: 100 900;
    font-style: normal;
    font-display: swap;
    /* Mêmes métriques forcées que les autres : changer de police ne
       doit rien déplacer (voir le @font-face de Jost). */
    ascent-override: 98%;
    descent-override: 25%;
    line-gap-override: 0%;
}

@font-face {
    font-family: 'OpenDyslexic';
    src: url('vendor/polices/opendyslexic.woff2') format('woff2');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
    /* Mêmes métriques forcées que les autres : changer de police ne
       doit rien déplacer (voir le @font-face de Jost). */
    ascent-override: 98%;
    descent-override: 25%;
    line-gap-override: 0%;
}

@font-face {
    font-family: 'Inter';
    src: url('vendor/polices/inter.woff2') format('woff2');
    font-weight: 100 900;
    font-style: normal;
    font-display: swap;
    /* Mêmes métriques forcées que les autres : changer de police ne
       doit rien déplacer (voir le @font-face de Jost). */
    ascent-override: 98%;
    descent-override: 25%;
    line-gap-override: 0%;
}

@font-face {
    font-family: 'IBM Plex Sans';
    src: url('vendor/polices/ibm-plex-sans.woff2') format('woff2');
    font-weight: 100 700;
    font-style: normal;
    font-display: swap;
    /* Mêmes métriques forcées que les autres : changer de police ne
       doit rien déplacer (voir le @font-face de Jost). */
    ascent-override: 98%;
    descent-override: 25%;
    line-gap-override: 0%;
}

@font-face {
    font-family: 'Jost';
    src: url('vendor/polices/jost.woff2') format('woff2');
    font-weight: 100 900;
    font-style: normal;
    font-display: swap;
    /* MÉTRIQUES FORCÉES. Une fonte déclare son ascendante, sa
       descendante et son interligne, et c'est de là que sort la
       hauteur d'une ligne quand personne ne la fixe. Jost annonce des
       valeurs plus généreuses que les polices système : la barre
       latérale passait de 34 à 37 px par entrée, et l'écart se cumulait
       sur quinze menus.

       Le total 98 + 25 + 0 = 123 % reproduit exactement la hauteur de
       ligne des polices système, mesurée sur cette barre latérale — une
       entrée de 34 px pour 16 px de remplissage, soit 18 px de ligne à
       15 px de corps.

       C'est le bon endroit pour corriger : le réglage porte sur la
       fonte, pas sur les règles qui l'emploient. Les retoucher une par
       une aurait laissé passer tout ce qu'on n'aurait pas pensé à
       regarder. */
    ascent-override: 98%;
    descent-override: 25%;
    line-gap-override: 0%;
}

/* ============================================================
   ÉCRAN DE CONNEXION — salon d'une banque privée

   L'intention n'est pas « faire luxueux » mais REPRODUIRE CE QUI REND
   UNE PIÈCE CHALEUREUSE. Trois choses, et aucune n'est une couleur :

   1. la lumière vient de LAMPES, pas du plafond. Des foyers chauds,
      décentrés, d'intensités différentes — c'est l'éclairage indirect
      qui distingue un salon d'un bureau ;
   2. les murs ont une MATIÈRE. Une boiserie à claire-voie, très
      discrète : on ne la lit pas, on la sent. Un aplat lisse reste un
      écran ; une surface qui accroche un peu de lumière devient un mur ;
   3. les bords sont SOMBRES. La vignette est ce qui fait le plus de
      travail ici : elle referme l'espace autour de celui qui regarde.
      Sans elle, les mêmes couleurs donnent un dégradé, pas une pièce.

   Le laiton reste le seul éclat — sur le panneau, le filet et le
   bouton. Un salon a une lampe de bureau, pas six.
   ============================================================ */
#connexion {
    /* Acajou, cuir bordeaux, laiton, parchemin. Le noir tire sur le
       brun-rouge : un noir neutre aurait éteint les bois, et c'est
       d'eux que vient la chaleur. */
    --cx-nuit: #130d0a;
    --cx-acajou: #7a3b22;
    --cx-bordeaux: #6d2530;
    --cx-lampe: #c8873c;
    --cx-or: #c9a961;
    --cx-or-clair: #f0dda9;
    --cx-encre: #1a1409;

    /* Le retrait du texte à l'intérieur d'un champ : 1 px de bordure
       plus le remplissage. Le libellé s'aligne dessus, si bien que
       « Adresse électronique » et l'adresse qu'on tape dessous partent
       du même point. Sans cela, le libellé démarre au bord du bloc et
       le texte 14 px plus loin — un décalage qu'on ne nomme pas mais
       qui se voit.

       La valeur est ici, en un seul endroit, et le champ comme le
       libellé s'y réfèrent : changer le remplissage sans toucher au
       libellé désalignerait les deux en silence. */
    --cx-retrait: 14px;

    position: fixed;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: var(--cx-nuit);
    z-index: 200;
    overflow: hidden;
    font-family: 'Jost', ui-sans-serif, -apple-system, 'Segoe UI', sans-serif;
}

/* Les champs et les boutons n'héritent PAS de la police de leur
   conteneur : sans cette ligne, le formulaire resterait en police
   système au milieu d'un écran qui ne l'est plus. */
#connexion input,
#connexion button,
#connexion select {
    font-family: inherit;
}

/* --- 1. Les lampes ----------------------------------------------
   Trois foyers portés par un seul élément : trois enfants animés
   séparément coûteraient trois compositions au lieu d'une. Seuls
   `transform` et `opacity` bougent — le reste force un repaint.

   Les périodes sont premières entre elles (17, 23, 29 s) pour que le
   dessin ne se répète jamais tout à fait. Un cycle qu'on finit par
   saisir devient une horloge et se met à compter l'attente. */
#connexion .cx-lampes {
    position: absolute;
    inset: -25%;
    z-index: 0;
    pointer-events: none;
}

#connexion .cx-lampes i {
    position: absolute;
    display: block;
    border-radius: 50%;
    filter: blur(100px);
    will-change: transform;
}

/* La lampe de bureau : le foyer le plus vif, en haut à gauche, là où
   se pose le regard avant de descendre sur le formulaire. */
#connexion .cx-lampes i:nth-child(1) {
    width: 44%; height: 48%; top: 4%; left: 6%;
    background: var(--cx-lampe);
    opacity: .34;
    animation: cx-lampe-a 17s ease-in-out infinite alternate,
               cx-veilleuse 9s ease-in-out infinite;
}

/* Le mur d'acajou, en fond, large et sourd. */
#connexion .cx-lampes i:nth-child(2) {
    width: 52%; height: 56%; top: 30%; left: 46%;
    background: var(--cx-acajou);
    opacity: .3;
    animation: cx-lampe-b 23s ease-in-out infinite alternate;
}

/* Le cuir bordeaux du fauteuil : la note la plus profonde, en bas. */
#connexion .cx-lampes i:nth-child(3) {
    width: 40%; height: 38%; top: 58%; left: 14%;
    background: var(--cx-bordeaux);
    opacity: .32;
    animation: cx-lampe-c 29s ease-in-out infinite alternate;
}

@keyframes cx-lampe-a {
    from { transform: translate3d(0, 0, 0) scale(1); }
    to   { transform: translate3d(11%, 7%, 0) scale(1.14); }
}

@keyframes cx-lampe-b {
    from { transform: translate3d(0, 0, 0) scale(1.08); }
    to   { transform: translate3d(-13%, -8%, 0) scale(.94); }
}

@keyframes cx-lampe-c {
    from { transform: translate3d(0, 0, 0) scale(.96); }
    to   { transform: translate3d(8%, -11%, 0) scale(1.16); }
}

/* La respiration d'une flamme, et rien de plus : quatre centièmes
   d'opacité sur neuf secondes. Un vacillement qu'on remarque devient
   un clignotement, et un clignotement se regarde au lieu de se
   ressentir. */
@keyframes cx-veilleuse {
    0%, 100% { opacity: .34; }
    50%      { opacity: .38; }
}

/* --- 2. Le marbre ------------------------------------------------
   Portoro : le marbre noir veiné d'or. C'est celui des halls de banque
   privée, et le seul dont les veines soient dorées — il tombe donc
   exactement sur le laiton du reste de l'écran, sans qu'on ait à
   choisir entre les deux.

   IL EST CALCULÉ, PAS PHOTOGRAPHIÉ. Le fond n'est qu'un jeu de traits
   fins et droits ; un filtre SVG les déforme par bruit fractal, et des
   traits droits déformés donnent des veines. Rien à charger, aucun
   motif qui se répète, et un veinage qui ne coûte que le premier
   rendu — la couche est immobile, elle n'est tramée qu'une fois.

   Deux familles de traits à des périodes et des largeurs différentes :
   une seule donnerait des veines toutes parallèles, ce qu'aucune pierre
   ne fait. L'élément déborde de 15 % parce que la déformation va
   chercher de la matière au-delà du cadre — sans cette marge, les bords
   se dégarnissent. */
#connexion .cx-marbre {
    position: absolute;
    inset: -15%;
    z-index: 0;
    pointer-events: none;
    opacity: .34;
    background-image:
        repeating-linear-gradient(64deg,
            transparent 0 44px, rgba(240, 221, 169, .55) 44px 45px, transparent 45px 108px),
        repeating-linear-gradient(58deg,
            transparent 0 150px, rgba(240, 221, 169, .3) 150px 153px, transparent 153px 330px),
        repeating-linear-gradient(71deg,
            transparent 0 260px, rgba(255, 245, 225, .18) 260px 262px, transparent 262px 520px);
    filter: url(#cx-veines);
    -webkit-mask-image: radial-gradient(76% 84% at 50% 46%, #000 12%, transparent 88%);
    mask-image: radial-gradient(76% 84% at 50% 46%, #000 12%, transparent 88%);
}

/* Là où le filtre n'est pas rendu, il reste des traits droits — une
   fine résille dorée. Ce n'est pas du marbre, mais ce n'est pas une
   panne non plus, et personne ne verra les deux versions. */

/* --- Les joints de dalles ---------------------------------------
   Ce sont EUX qui font lire « plaques de marbre » plutôt que
   « texture » : une pierre posée au mur est découpée, et l'œil cherche
   la découpe. Ils ne passent pas par le filtre — un joint déformé
   n'est plus un joint. */
#connexion .cx-dalles {
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background-image:
        repeating-linear-gradient(to right,
            rgba(240, 221, 169, .07) 0 1px, transparent 1px 470px),
        repeating-linear-gradient(to bottom,
            rgba(240, 221, 169, .07) 0 1px, transparent 1px 310px),
        repeating-linear-gradient(to right,
            rgba(0, 0, 0, .3) 1px 3px, transparent 3px 470px),
        repeating-linear-gradient(to bottom,
            rgba(0, 0, 0, .3) 1px 3px, transparent 3px 310px);
    -webkit-mask-image: radial-gradient(72% 80% at 50% 46%, #000 15%, transparent 84%);
    mask-image: radial-gradient(72% 80% at 50% 46%, #000 15%, transparent 84%);
}

/* --- 3. La vignette ---------------------------------------------
   C'est la couche qui fait le plus de travail. Elle assombrit les
   bords et referme la pièce autour du panneau — sans elle, les mêmes
   couleurs ne donnent qu'un dégradé plein écran. */
#connexion .cx-vignette {
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background: radial-gradient(78% 88% at 50% 44%,
        transparent 32%, rgba(9, 6, 4, .55) 72%, rgba(6, 4, 3, .88) 100%);
}

/* --- Fond personnalisé -----------------------------------------
   LA PHOTO EST MONTRÉE TELLE QUELLE. Ni voile, ni flou : elle a été
   choisie pour être vue.

   Un voile sombre couvrait tout l'écran, doublé d'un flou de 3 px. Il
   avait sa raison — une photo claire rend les champs de saisie
   illisibles — mais il la payait beaucoup trop cher : il abîmait
   l'image ENTIÈRE pour protéger le seul rectangle où se trouve le
   texte.

   La lisibilité se règle donc là où elle se pose, et nulle part
   ailleurs : le panneau du formulaire porte son propre verre dépoli
   (voir « #connexion .card »), assez opaque pour tenir sur n'importe
   quelle photo. Ce qui est autour reste la photo.

   Le décor s'efface entièrement : une photo est déjà une ambiance, et
   deux superposées se salissent au lieu de s'enrichir.

   ⚠ LA PHOTO N'EST JAMAIS ROGNÉE. « contain » et non « cover » : elle
   est mise à l'échelle jusqu'à tenir ENTIÈRE dans la fenêtre, et ce
   qui reste est du noir — en haut et en bas si elle est plus large que
   l'écran, à gauche et à droite si elle est plus haute.

   « cover » faisait l'inverse : il remplissait la fenêtre et coupait le
   débordement. Sur une photo de paysage en 3:2 posée sur un écran
   presque 2:1, cela retirait un quart de la hauteur — le haut du ciel
   et le bas du décor — et agrandissait le reste de deux fois et demie.
   On ne voyait plus la photo qu'on avait choisie mais une bande
   centrale, grossie. Un fond d'écran est une image qu'on a cadrée : le
   logiciel n'a pas à la recadrer.

   Le noir des bandes est un vrai noir et non le brun-noir du décor :
   contre une photo, une bande teintée se lit comme une erreur
   d'affichage plutôt que comme un cadre. */
#connexion.avec-fond {
    background-size: contain;
    background-position: center;
    background-repeat: no-repeat;
    background-color: #000;
}

#connexion.avec-fond .cx-lampes,
#connexion.avec-fond .cx-marbre,
#connexion.avec-fond .cx-dalles,
#connexion.avec-fond .cx-vignette { display: none; }

/* Le panneau se ferme un peu quand une photo est là : son verre est
   accordé au décor sombre du logiciel, et une photo claire derrière un
   verre à 76 % laisse remonter assez de lumière pour affadir le texte.
   C'est le seul endroit où l'image est atténuée, et c'est celui où l'on
   lit. */
#connexion.avec-fond .card {
    background: rgba(24, 17, 12, .88);
}

/* --- Le panneau -------------------------------------------------
   Verre teinté chaud, et non gris : posé sur des bois, un verre neutre
   se voit comme une vitre rapportée. */
#connexion .card {
    position: relative;
    z-index: 1;
    width: 392px;
    max-width: 92vw;
    padding: 36px 34px;
    background: rgba(28, 20, 15, .76);
    backdrop-filter: blur(22px) saturate(140%);
    -webkit-backdrop-filter: blur(22px) saturate(140%);
    border: 1px solid rgba(201, 169, 97, .16);
    border-radius: 20px;
    box-shadow:
        0 2px 6px rgba(0, 0, 0, .5),
        0 34px 90px rgba(0, 0, 0, .7),
        inset 0 1px 0 rgba(240, 221, 169, .12);
    animation: cx-entre .5s cubic-bezier(.16, 1, .3, 1) both;
}

@keyframes cx-entre {
    from { opacity: 0; transform: translateY(10px) scale(.985); }
    to   { opacity: 1; transform: none; }
}

/* Le filet de laiton qui fait le tour du panneau.

   Il monte vers l'ivoire puis REDESCEND vers l'or au lieu de sauter
   d'une couleur à l'autre : c'est ce retour qui donne la lecture d'une
   lumière glissant sur une tranche dorée, et non d'un néon qui tourne.

   L'angle passe par une propriété typée : sans `@property`, un
   navigateur interpole un angle personnalisé par pas entiers et la
   rotation saccade. Là où elle manque, l'animation ne démarre pas — il
   reste un cadre net. */
@property --cx-angle {
    syntax: '<angle>';
    initial-value: 0deg;
    inherits: false;
}

#connexion .card::before {
    content: '';
    position: absolute;
    inset: -1px;
    border-radius: inherit;
    padding: 1px;
    background: conic-gradient(from var(--cx-angle),
        transparent 0 60%,
        rgba(201, 169, 97, .5) 72%,
        rgba(240, 221, 169, .95) 79%,
        rgba(201, 169, 97, .5) 84%,
        transparent 92% 100%);
    /* Le masque ne garde que le liseré : les deux calques se
       neutralisent partout sauf sur l'épaisseur du padding. */
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
    mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    mask-composite: exclude;
    animation: cx-tourne 8s linear infinite;
    pointer-events: none;
}

@keyframes cx-tourne {
    to { --cx-angle: 360deg; }
}

/* Repli pour les navigateurs sans flou d'arrière-plan : un panneau
   translucide y deviendrait illisible sur une photo claire. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    #connexion .card { background: rgba(28, 20, 15, .97); }
}

#connexion.avec-fond .card {
    box-shadow:
        0 2px 6px rgba(0, 0, 0, .6),
        0 34px 90px rgba(0, 0, 0, .85);
}

/* Le mouvement est un agrément, jamais une information : qui l'a
   désactivé garde exactement la même pièce, immobile. */
@media (prefers-reduced-motion: reduce) {
    #connexion .cx-lampes i,
    #connexion .card,
    #connexion .card::before { animation: none; }
}

/* ---------- Marque ---------- */
#connexion .cx-marque {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 26px;
}

/* Une marque dessinée, pas un émoji : un émoji change de dessin d'un
   système à l'autre, et c'est la première chose que voit celui qui se
   connecte. */
#connexion .cx-logo {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    border-radius: 13px;
    background: linear-gradient(145deg, var(--cx-or-clair), var(--cx-or));
    box-shadow: 0 8px 22px rgba(201, 169, 97, .3);
    color: var(--cx-encre);
}

/* Le nom en Cormorant Garamond, graisse légère et corps ample : une
   didone se juge sur le contraste entre pleins et déliés, et ce
   contraste disparaît en gras et en petit. C'est l'inverse du réflexe
   habituel — pour qu'un nom pèse, on l'allège. */
#connexion .cx-nom {
    font-family: 'Cormorant Garamond', 'Didot', 'Bodoni 72', Georgia, serif;
    font-size: 30px;
    font-weight: 300;
    letter-spacing: .015em;
    line-height: 1.05;
}

/* Les chiffres du nom, en Jost.

   Réduits à 0,78 em : Jost a une hauteur de capitale bien plus grande
   que Cormorant à corps égal, et des chiffres à la même taille
   paraîtraient plus gros que les lettres. C'est un calage optique, pas
   une mesure — l'œil compare des hauteurs perçues, pas des ems.

   `tabular-nums` en plus de `lining-nums` : les chiffres prennent tous
   la même largeur, ce qui empêche le « 1 » de creuser un trou au milieu
   du nom. */
#connexion .cx-chiffres {
    font-family: 'Jost', ui-sans-serif, -apple-system, sans-serif;
    font-weight: 300;
    font-size: .78em;
    letter-spacing: .05em;
    font-variant-numeric: lining-nums tabular-nums;
    vertical-align: .015em;
}

#connexion .cx-sous {
    font-size: 11px;
    font-weight: 300;
    letter-spacing: .04em;
    color: var(--muted);
    margin-top: 5px;
}

/* ---------- Champs ---------- */
#connexion .f {
    margin-bottom: 14px;
}

#connexion .f label {
    display: block;
    padding-left: var(--cx-retrait);
    font-size: 10px;
    font-weight: 500;
    letter-spacing: .13em;
    text-transform: uppercase;
    color: var(--muted);
    margin-bottom: 7px;
}

#connexion .f input {
    width: 100%;
    /* 44 px de haut : la cible tactile minimale. Un écran de connexion
       se remplit aussi au téléphone, souvent debout. */
    min-height: 44px;
    /* Le remplissage se déduit du retrait, bordure comprise : c'est le
       texte saisi qui doit tomber sur le libellé, pas l'inverse. */
    padding: 11px calc(var(--cx-retrait) - 1px);
    font-size: 13px;
    background: rgba(15, 18, 24, .72);
    border-radius: 10px;
}

#connexion .f input:focus {
    border-color: var(--cx-or);
    box-shadow: 0 0 0 3px rgba(201, 169, 97, .24);
}

/* Le bloc du second facteur n'apparaît pas d'un coup : le glissement
   dit d'où il vient et pourquoi le formulaire s'est allongé. */
#connexion .cx-code-bloc {
    animation: cx-apparait .28s cubic-bezier(.16, 1, .3, 1);
}

@keyframes cx-apparait {
    from { opacity: 0; transform: translateY(-6px); }
    to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    #connexion .cx-code-bloc { animation: none; }
}

/* Un code à six chiffres se relit chiffre par chiffre : chasse fixe et
   lettres espacées, comme sur le téléphone qui l'affiche. */
#connexion #cxCode {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 17px;
    letter-spacing: .32em;
    text-align: center;
}

/* ---------- Bouton principal ---------- */
#connexion button.primary {
    width: 100%;
    min-height: 46px;
    margin-top: 6px;
    font-size: 12px;
    font-weight: 500;
    letter-spacing: .12em;
    text-transform: uppercase;
    border: 0;
    border-radius: 10px;
    /* Texte SOMBRE sur l'or : du blanc sur un fond clair descendrait
       sous le seuil de contraste, et c'est le bouton qu'on cherche. */
    color: var(--cx-encre);
    background: linear-gradient(135deg, var(--cx-or-clair), var(--cx-or));
    transition: transform .15s cubic-bezier(.16, 1, .3, 1), box-shadow .2s, filter .2s;
}

#connexion button.primary:hover:not([disabled]) {
    filter: brightness(1.05);
    box-shadow: 0 10px 26px rgba(201, 169, 97, .32);
}

/* L'enfoncement se voit : sans lui, sur un réseau lent, on clique deux
   fois faute de savoir si le premier clic a porté. */
#connexion button.primary:active:not([disabled]) {
    transform: scale(.985);
}

#connexion button.primary[disabled] {
    opacity: .65;
    cursor: progress;
    filter: none;
}

@media (prefers-reduced-motion: reduce) {
    #connexion button.primary { transition: none; }
}

/* ---------- Liens secondaires ---------- */
/* De vrais boutons, et non des liens sans destination : sous cette
   apparence, ils se rejoignent à la tabulation et s'annoncent aux
   lecteurs d'écran. */
#connexion .cx-lien {
    background: none;
    border: 0;
    padding: 4px 2px;
    font-size: 11px;
    color: var(--cx-or);
    cursor: pointer;
    text-decoration: none;
}

#connexion .cx-lien:hover { text-decoration: underline; }

#connexion .cx-pied {
    margin-top: 18px;
    padding-top: 14px;
    border-top: 1px solid rgba(255, 255, 255, .07);
    text-align: center;
}

#connexion input {
    background: var(--panel2);
    color: var(--txt);
    border: 1px solid var(--line);
    border-radius: 8px;
    padding: 9px 10px;
    font-size: 14px;
}
#connexion input:focus {
    outline: none;
    border-color: var(--accent);
}

/* ---------- Évolution des soldes ----------
   Palette des courbes : voir EVO_COULEURS dans app.js. Les huit teintes
   ont été validées contre --panel (#171c26) — bande de clarté, plancher
   de chroma, écart en vision daltonienne et contraste sur le fond. */
.evo-barre {
    display: flex;
    align-items: flex-end;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 14px;
}
.evo-serie {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
    padding: 6px 0;
    border-top: 1px solid var(--line);
}
.evo-serie:first-child { border-top: 0; }
.evo-serie input[type=text],
.evo-serie select {
    background: var(--panel2);
    color: var(--txt);
    border: 1px solid var(--line);
    border-radius: 6px;
    padding: 5px 7px;
    font-size: 12px;
}
.evo-inv {
    display: flex;
    align-items: center;
    gap: 4px;
    font-size: 11px;
    color: var(--muted);
    white-space: nowrap;
}
.evo-pastille {
    width: 10px;
    height: 10px;
    border-radius: 3px;
    flex: 0 0 auto;
    display: inline-block;
}

/* La hauteur suit le contenu : figer le conteneur ferait défiler la
   carte pour quelques pixels d'axe. */
.evo-graphe {
    position: relative;
    width: 100%;
}
.evo-graphe svg { display: block; }

.evo-bulle {
    position: absolute;
    pointer-events: none;
    background: var(--panel2);
    border: 1px solid var(--line);
    border-radius: 8px;
    padding: 8px 10px;
    font-size: 12px;
    box-shadow: 0 6px 20px rgba(0, 0, 0, .45);
    white-space: nowrap;
    z-index: 5;
}
.evo-bulle-t {
    color: var(--txt);
    font-weight: 600;
    margin-bottom: 5px;
}
.evo-bulle-l {
    display: flex;
    align-items: center;
    gap: 7px;
    line-height: 1.7;
}
/* Le texte porte l'encre courante ; la pastille porte l'identité. */
.evo-bulle-n { color: var(--muted); }
.evo-bulle-v {
    color: var(--txt);
    margin-left: auto;
    padding-left: 14px;
    font-variant-numeric: tabular-nums;
}

.evo-legende {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 14px;
}
.evo-lg {
    display: flex;
    align-items: center;
    gap: 7px;
    background: var(--panel2);
    border: 1px solid var(--line);
    border-radius: 999px;
    padding: 5px 12px;
    font-size: 12px;
    color: var(--txt);
    cursor: pointer;
}
.evo-lg:hover { border-color: var(--accent); }
.evo-lg .muted { font-size: 11px; }
.evo-lg.off { opacity: .45; }
.evo-lg.off span:not(.evo-pastille) { text-decoration: line-through; }

@media print {
    .evo-barre, .evo-serie button, .evo-bulle { display: none !important; }
}

/* ---------- Évolution : repli de la sélection et zoom ---------- */

/* grid-template-rows 1fr → 0fr anime vers la hauteur réelle du contenu,
   ce que max-height ne sait pas faire sans valeur arbitraire. */
.evo-repli {
    display: grid;
    grid-template-rows: 1fr;
    transition: grid-template-rows .3s cubic-bezier(.4, 0, .2, 1),
                opacity .22s ease, margin-top .3s cubic-bezier(.4, 0, .2, 1);
    opacity: 1;
}
.evo-repli > div {
    overflow: hidden;
    min-height: 0;
}
.evo-repli.ferme {
    grid-template-rows: 0fr;
    opacity: 0;
    margin-top: -6px;
}

.evo-bouton-repli .evo-chevron {
    display: inline-block;
    transition: transform .3s cubic-bezier(.4, 0, .2, 1);
    font-size: 10px;
    opacity: .7;
}
.evo-bouton-repli.ferme .evo-chevron { transform: rotate(-90deg); }

.evo-zoom {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 10px;
    font-size: 11px;
    min-height: 26px;
}
.evo-zoom-actif { color: var(--txt); }
.evo-zoom button {
    padding: 3px 9px;
    font-size: 11px;
}
/* Repère d'usage : discret, il s'efface dès qu'on a compris. */
.evo-astuce {
    color: var(--muted);
    opacity: .65;
    margin-left: auto;
}

.evo-graphe svg { touch-action: pan-y; }

/* Un mouvement qui gêne doit pouvoir être coupé. */
@media (prefers-reduced-motion: reduce) {
    .evo-repli,
    .evo-bouton-repli .evo-chevron {
        transition: none;
    }
}

@media print {
    .evo-zoom { display: none !important; }
    .evo-repli.ferme { grid-template-rows: 1fr; opacity: 1; }
}

/* ---------- Messagerie ---------- */

/* Épinglée au bas de la barre latérale, juste au-dessus du dossier
   sélectionné : la messagerie n'appartient pas à la progression
   comptable des autres menus, et la séparer d'un blanc le dit mieux
   qu'un trait.

   « margin-top: auto » dans une colonne flex absorbe l'espace libre.
   Quand la liste des menus remplit toute la hauteur, la marge tombe à
   zéro et l'entrée se range simplement à la suite — la dégradation est
   celle qu'on veut. */
#nav .navitem[data-v="messagerie"] { margin-top: auto; }

/* Le menu étant lui-même un flex, la pastille se rangerait contre le
   titre. Poussée à droite, elle se lit d'un coup d'œil sans avoir à
   chercher où finit le mot. */
#nav .msg-pastille { margin-left: auto; }

.msg-cadre { display: flex; gap: 12px; height: calc(100vh - 160px); }
.msg-volet { width: 280px; display: flex; flex-direction: column;
  border: 1px solid var(--line); border-radius: 8px; overflow: hidden; }
.msg-nouveau { padding: 8px; border-bottom: 1px solid var(--line); }
.msg-nouveau select { width: 100%; }
#msgListe { overflow-y: auto; flex: 1; }
.msg-item { padding: 10px 12px; cursor: pointer; position: relative;
  border-bottom: 1px solid var(--line); }
.msg-item:hover { background: var(--panel2); }
.msg-item.on { background: var(--panel2); font-weight: 600; }
.msg-item-nom { font-size: 14px; }
.msg-item-apercu { font-size: 12px; color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.msg-pastille { background: var(--accent); color: #fff; border-radius: 10px;
  padding: 0 6px; font-size: 11px; margin-left: 6px; }
.msg-item .msg-pastille { position: absolute; right: 10px; top: 10px; }
.msg-fil { flex: 1; display: flex; flex-direction: column;
  border: 1px solid var(--line); border-radius: 8px; overflow: hidden; }
.msg-messages { flex: 1; overflow-y: auto; padding: 12px; }
.msg-bulle { max-width: 70%; margin-bottom: 10px; padding: 8px 10px;
  border-radius: 8px; background: var(--panel2); }
.msg-bulle.moi { margin-left: auto; background: var(--accent); color: #fff; }
.msg-corps { font-size: 14px; word-wrap: break-word; }
.msg-pied { font-size: 11px; opacity: .7; margin-top: 4px; }
.msg-retirer { cursor: pointer; text-decoration: underline; }
.msg-saisie { display: flex; gap: 8px; padding: 8px;
  border-top: 1px solid var(--line); }
.msg-saisie textarea { flex: 1; resize: none; font-family: inherit; }

/* ---------- Champs d'axes dans le TCD ----------
   Remontés en tête de la liste des champs disponibles et séparés des
   champs bruts du FEC par un retour à la ligne : ils ne sont pas de
   même nature. « Compte » ou « Journal » viennent du fichier du client,
   un axe vient d'une grille de lecture définie au cabinet. */
/* La zone des axes est une ligne de tableau à part, au-dessus des champs
   bruts. Aucune règle de cadre ici : elle prend celui de
   `#tcdPivot .pvtAxisContainer`, identique à la zone des champs, et les
   axes eux-mêmes gardent l'aspect des autres champs. C'est d'être un
   cadre SÉPARÉ qui les distingue, pas la couleur. */
