    /* ══ La largeur de la barre ═══════════════════════════════════════
       CES TROIS RÈGLES VIVAIENT DANS renderDashboardLayout(), c'est-à-dire dans
       un <style> RÉÉMIS À CHAQUE RENDU à l'intérieur du innerHTML de la page.
       Insérer une feuille de style invalide le style calculé du DOCUMENT ENTIER
       — un recalcul complet, à chaque navigation, pour trois règles qui ne
       changent presque jamais. En thème sombre c'est pire encore : la feuille
       `dirigi-dark-theme` porte ~1800 sélecteurs `[style*="…"]`, et chacun se
       remesure contre chaque élément à chaque recalcul.
       Maintenant la largeur est un ATTRIBUT sur <html> (`data-sidebar`), posé par
       renderDashboardLayout(). Changer un attribut ne coûte un recalcul que
       lorsque la valeur change vraiment, et la valeur ne change qu'au repli.
       Les valeurs : agence 216 / repliée 72 ; directeur de projet 240 / 64.
       La requête mobile doit rester APRÈS et porter `:root[data-sidebar]`, sinon
       elle perd en spécificité (0,1,0) contre `:root[data-sidebar="72"]` (0,2,0)
       et le rail continuerait de réserver sa gouttière sur téléphone.
       Tout ce qui s'ancre sur la barre lit --cf-sidebar-w, jamais un nombre. */
    :root { --cf-sidebar-w: 216px; }
    :root[data-sidebar="72"]  { --cf-sidebar-w: 72px; }
    :root[data-sidebar="240"] { --cf-sidebar-w: 240px; }
    :root[data-sidebar="64"]  { --cf-sidebar-w: 64px; }
    @media (min-width: 768px) {
      .mobile-nav { display: none !important; }
      .main-content { margin-left: var(--cf-sidebar-w); }
    }
    @media (max-width: 767px) {
      :root, :root[data-sidebar] { --cf-sidebar-w: 0px; }
      .sidebar-main { display: none !important; }
      .main-content { margin-left: 0 !important; padding-bottom: 80px; }
    }

    /* ══ Barre agence · le système d'états ════════════════════════════
       Figma UI-DC 709:165 « États — barre agence, version premium », posé sur
       l'écran 707:165 « Découverte · variante D ». La planche tient en une règle :

           LE SURVOL CHANGE LE FOND, LA SÉLECTION CHANGE LE PLAN
           — elle seule a droit au relief et à l'accent.

       « Un survol qui colorerait l'item ferait croire à une sélection : c'est
       l'erreur à ne pas commettre en reprenant ces jetons. » C'est pourtant ce
       que faisait la barre, et par accident — les rangées portaient
       `cf-btn-ghost`, dont le :hover peint un lavis teal en !important, qui
       battait le #f3f4f6 posé en style inline juste à côté.

       ELLES NE PORTENT DONC PLUS NI cf-btn NI cf-btn-ghost, et ce n'est pas
       qu'une question de spécificité : `.cf-btn` ajoute aussi un scale(.98) au
       clic (la planche veut un changement de FOND, pas un mouvement) et un halo
       de 3 px au focus (elle veut un anneau net de 2 px, en OUTSIDE). Ne pas les
       remettre.

       Animation : 120 ms sur le fond, 160 ms sur l'ombre et le bâton d'accent.
       ════════════════════════════════════════════════════════════ */
    /* LE MOUVEMENT DE LA BARRE, EN UN SEUL ENDROIT.
       Une courbe, deux durées, et rien d'autre à retenir :
         • --cf-nav-ease : cubic-bezier(.16,1,.3,1), une SORTIE exponentielle.
           Elle part vite — la moitié du trajet est franchie dans le premier
           sixième du temps — puis se pose longuement. C'est ce départ-là qu'on
           lit comme « snappy » : la symétrique (.4,0,.2,1) mettait deux images
           à quitter la rangée, et sur 500 ms ce préambule est tout ce qu'on
           remarque. Sa longue queue est ce qui la sépare de l'ancienne
           (.2,.7,.3,1), franche au départ ET à l'arrivée, qui claquait à
           l'atterrissage. La DURÉE ne bouge pas — c'est la courbe, pas le
           chrono, qui rendait le geste mou (7 septembre 2026).
         • --cf-nav-tint : 180 ms, tout ce qui change de COULEUR sur place (fond,
           libellé, filet, compteur). La planche dit 120 ; 120 ms sur un fond qui
           s'assombrit de trois points, c'est un clignotement.
         • --cf-nav-travel : 500 ms, tout ce qui PARCOURT une distance — c'est-à-
           dire la tuile et son bâton, et RIEN d'autre. Une course a une longueur,
           un fondu n'en a pas : les deux ne peuvent pas partager un chiffre. Le
           chevron du groupe a essayé cette durée et l'a rendue en traînant — il
           tourne sur place, il ne traverse rien, donc il est repassé sur --tint.
           320 puis 360 ms restaient trop vifs sur les 220 px qui séparent Accueil
           de la ligne de profil (26 août 2026).
       Ralenti le 26 août 2026, reserré le 7 septembre : la durée de ce jour-là
       était la bonne, la courbe ne l'était pas. */
    .sidebar-main {
      --cf-nav-ease: cubic-bezier(.16, 1, .3, 1);
      --cf-nav-tint: .18s;
      --cf-nav-travel: .5s;
    }

    /* A · item de nav — six états */
    .cf-nav {
      position: relative; box-sizing: border-box;
      display: flex; align-items: center; gap: 6px;
      width: 100%; height: 36px; padding: 0 10px 0 8px;
      border: 1px solid transparent; border-radius: 8px;
      background: transparent; cursor: pointer; text-align: left;
      font-family: inherit; -webkit-user-select: none; user-select: none;
      transition: background-color var(--cf-nav-tint) var(--cf-nav-ease),
                  border-color var(--cf-nav-tint) var(--cf-nav-ease),
                  box-shadow var(--cf-nav-tint) var(--cf-nav-ease);
    }
    /* LE SURVOL EST L'ESQUISSE DE LA TUILE, PLUS UN APLAT.
       Les gris successifs (#F0F2F4 de la planche, puis un #E5ECEF tiré vers le
       cyan de la marque) partageaient le même défaut, et ce n'était pas leur
       couleur : c'était leur NATURE. Depuis que la sélection est un objet qui
       traverse la barre, un survol qui peint un bloc coloré parle une autre
       langue que l'animation qu'il précède — on voyait un aplat, puis une tuile.
       Maintenant le survol dessine la MÊME forme que la tuile — même blanc, même
       rayon, même filet — mais SANS le relief. Survoler montre où la tuile se
       poserait ; la sélection l'y pose et la fait monter d'un plan. Un seul
       vocabulaire, deux hauteurs.
       La règle de la planche tient toujours : « le survol change le fond, la
       sélection change le plan — elle seule a droit au relief et à l'accent ». Le
       survol n'a ni ombre ni bâton, donc il ne monte pas d'un plan ; un filet de
       1 px est un contour, pas une élévation. Il réutilise la bordure transparente
       que la rangée porte déjà : rien ne se décale.
       LA RANGÉE SÉLECTIONNÉE EST EXCLUE, et pas par prudence : sa tuile est un
       élément séparé posé SOUS les rangées, donc un fond blanc peint par la rangée
       passerait par-dessus avec un filet plus pâle que le sien. Son propre survol
       est déjà géré par le :has() plus bas, qui durcit le filet et creuse l'ombre.
       Revu le 26 août 2026 (« le hover ne va pas du tout avec l'animation »). */
    .cf-nav:not([aria-current="page"]):hover {
      background-color: #fff;
      border-color: #eaeff1;
    }
    .cf-nav:not([aria-current="page"]):active {
      background-color: #f3f7f8;
      border-color: #dfe6e9;
    }
    /* Anneau de 2 px EN DEHORS : un box-shadow diffus se lirait comme le relief
       de l'état sélectionné, et ce sont deux choses différentes. */
    .cf-nav:focus-visible { outline: 2px solid #006a85; outline-offset: 0; }

    /* ÉTAT SÉLECTIONNÉ : UNE SEULE TUILE POUR TOUTE LA BARRE, ET ELLE VOYAGE.
       La tuile blanche n'est pas peinte par la rangée — c'est un élément unique
       posé en absolu dans l'<aside>, qui GLISSE jusqu'à la page qu'on vient de
       choisir au lieu d'apparaître déjà arrivée. Peinte par la rangée, elle ne
       pouvait pas : chaque rangée a la sienne, l'une s'allume pendant que
       l'autre s'éteint, et deux fondus croisés ne font pas un déplacement.
       Le bâton d'accent est SON ENFANT et non un frere : ils bougent donc d'un
       seul bloc, et aucune des deux animations ne peut prendre du retard sur
       l'autre. C'est la seule façon de garantir qu'ils arrivent ensemble.
       Position, taille et transitions sont posées par cfSlideNavTile(). */
    .cf-nav-tile {
      position: absolute; left: 0; top: 0; width: 0; height: 0;
      box-sizing: border-box;
      background: #fff; border: 1px solid #e4e9ed; border-radius: 8px;
      box-shadow: 0 1px 1px rgba(0,0,0,.05), 0 6px 8px rgba(0,0,0,.06);
      opacity: 0; pointer-events: none; z-index: 0;
    }
    /* Le survol durcit le filet et creuse l'ombre — le fond, lui, ne bouge plus :
       la tuile est déjà en haut. La tuile n'est pas dans la rangée, donc le
       survol l'atteint par :has() plutôt que par descendance. Si un navigateur
       ne le connaît pas, on perd le durcissement et rien d'autre. */
    .sidebar-main:has(.cf-nav[aria-current="page"]:hover) .cf-nav-tile,
    .sidebar-main:has(.cf-nav[aria-current="page"]:active) .cf-nav-tile {
      border-color: #dde3e8;
      box-shadow: 0 2px 2px rgba(0,0,0,.06), 0 10px 11px rgba(0,0,0,.08);
    }
    /* Le contenu de la rangée passe AU-DESSUS de la tuile qui circule dessous. */
    .cf-nav > * { position: relative; z-index: 1; }

    /* Le bâton d'accent, enfant de la tuile : il se centre tout seul dessus.
       Il garde l'entrée de la planche — un passage de 0 à 16 de HAUTEUR, jamais
       une opacité, « c'est ce qui lui donne son côté mécanique » — mais
       seulement la PREMIÈRE fois : ensuite il se déplace, il ne renaît pas. */
    .cf-nav-accent {
      position: absolute; left: -1px; top: 50%; transform: translateY(-50%);
      width: 3px; height: 0; border-radius: 2px; background: #006a85;
      pointer-events: none;
    }

    /* Le glyphe suit l'état : graphite #7C888E au repos, #5A666B sous le doigt,
       teal #006A85 à la sélection. Trois fichiers exportés de Figma, échangés en
       background-image via des variables — un <img> ne sait pas changer de source
       en CSS, et ces glyphes sont bicolores (trait + papier), donc ni un masque ni
       un filtre ne savent les repeindre. Le papier passe au blanc à l'état
       sélectionné : c'est la couleur de la tuile sur laquelle il se pose. */
    .cf-nav-slot { position: relative; flex-shrink: 0; width: 26px; height: 26px; }
    .cf-nav-ico  { position: absolute; background: var(--ir) center/100% 100% no-repeat;
                   transition: opacity var(--cf-nav-tint) var(--cf-nav-ease); }
    .cf-nav:hover .cf-nav-ico,
    .cf-nav:active .cf-nav-ico,
    .cf-nav:focus-visible .cf-nav-ico { background-image: var(--is); }
    .cf-nav[aria-current="page"] .cf-nav-ico,
    .cf-nav[aria-current="page"]:hover .cf-nav-ico { background-image: var(--ia, var(--is)); }

    /* L'échelle d'encre est descendue d'un cran le 24 août 2026 (« moins gris ») :
       ce qui était la teinte de survol de la planche est devenu la teinte de REPOS,
       et le survol a pris un cran de plus. Les états ne sont pas écrasés pour
       autant — c'est toute l'échelle qui glisse, pas le repos qui rattrape le
       survol. */
    .cf-nav-label {
      flex: 1; min-width: 0; font-size: 12px; font-weight: 400; color: #3b474d;
      white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
      transition: color var(--cf-nav-tint) var(--cf-nav-ease);
    }
    .cf-nav:hover .cf-nav-label,
    .cf-nav:active .cf-nav-label,
    .cf-nav:focus-visible .cf-nav-label { color: #0f1e23; }
    .cf-nav[aria-current="page"] .cf-nav-label { color: #0f1e23; font-weight: 500; }

    /* Le compteur suit l'item : son filet se durcit au survol, il passe en teal
       pâle à la sélection. */
    .cf-nav-badge {
      flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center;
      padding: 2px 7px; border: 1px solid #d9dee3; border-radius: 9px; background: #fff;
      font-size: 10px; font-weight: 500; line-height: 1.2; color: #5a666b;
      transition: background-color var(--cf-nav-tint) var(--cf-nav-ease),
                  border-color var(--cf-nav-tint) var(--cf-nav-ease),
                  color var(--cf-nav-tint) var(--cf-nav-ease);
    }
    .cf-nav:hover .cf-nav-badge, .cf-nav:active .cf-nav-badge { border-color: #c9d1d8; }
    .cf-nav[aria-current="page"] .cf-nav-badge { background: #e3f0f3; border-color: #e3f0f3; color: #006a85; }
    .cf-nav[aria-current="page"]:hover .cf-nav-badge { background: #d7e9ee; border-color: #d7e9ee; color: #005a71; }

    /* B · rangée de groupe — quatre états. ELLE NE SE SÉLECTIONNE JAMAIS, elle
       ouvre : ni tuile blanche ni bâton d'accent, seuls le fond et le chevron
       changent. Son libellé est déjà en Medium foncé au repos — sinon ouvrir un
       groupe se lirait comme sélectionner une page. */
    .cf-nav--group .cf-nav-label { flex: 0 0 auto; color: #0f1e23; font-weight: 500; }
    .cf-nav-push { flex: 1; min-width: 0; }
    /* Un chiffre qui appelle une action garde sa pastille, un simple dénombrement
       reste en texte (599:189). */
    .cf-nav-count { flex-shrink: 0; margin-left: 8px; font-size: 11px; color: #6e797e; }
    .cf-nav-pill {
      flex-shrink: 0; margin-left: 8px; display: inline-flex; align-items: center;
      justify-content: center; min-width: 16px; height: 16px; padding: 0 4px;
      box-sizing: border-box; border-radius: 8px; background: #006a85;
      font-size: 9px; font-weight: 500; line-height: 1; color: #fff;
    }
    /* LES CAPITALES GRISES (.cf-nav--kicker) SONT PARTIES le 1er septembre 2026.
       Elles disaient « ceci n'est pas une page » et servaient à distinguer les
       trois flux — Découverte, Mes outils, Messages — des quatre pages. Depuis
       que Découverte est remontée en rangée de nav (maquette 8956:5882), il ne
       reste que deux groupes, tous deux au pied de la barre, et le CHEVRON dit
       déjà qu'ils s'ouvrent. Deux typographies pour six lignes coûtaient plus
       en bruit qu'elles ne rapportaient en grammaire. Les deux groupes portent
       maintenant le libellé de la maquette : Medium 12 px sur #0f1e23, celui de
       `.cf-nav--group .cf-nav-label` ci-dessus. */

    /* LA RANGÉE FANTÔME — « + Nouveau projet », sous Projets (Figma 8956:5932).
       Le bouton de création plein a été essayé deux fois et refusé deux fois,
       teal puis graphite, pour la même raison : un APLAT de 36 px crie plus fort
       que tout ce que la barre contient. La maquette du 1er septembre 2026 le
       reprend une troisième fois, mais en NÉGATIF — filet pointillé, aucun fond,
       encre grise. Elle ne prend pas le relief de la tuile de sélection (elle n'a
       pas de fond), elle ne prend pas le teal (il reste à la sélection et aux
       nouveautés) : elle se lit comme un emplacement à remplir, ce qu'est
       exactement un projet qui n'existe pas encore. Elle est POSÉE DANS la nav,
       juste sous Projets, donc le geste reste collé à sa destination.
       Elle n'est jamais `aria-current` : elle n'a pas de page. */
    .cf-nav-ghost {
      box-sizing: border-box; display: flex; align-items: center; gap: 9px;
      width: 100%; height: 34px; padding: 0 10px 0 13px;
      border: 1px dashed #b9c6cc; border-radius: 8px;
      background: transparent; cursor: pointer; text-align: left;
      font-family: inherit; color: #7d8b91; -webkit-user-select: none; user-select: none;
      transition: background-color var(--cf-nav-tint) var(--cf-nav-ease),
                  border-color var(--cf-nav-tint) var(--cf-nav-ease),
                  color var(--cf-nav-tint) var(--cf-nav-ease);
    }
    .cf-nav-ghost:hover  { border-color: #006a85; color: #006a85; background: #f2fafc; }
    .cf-nav-ghost:active { border-color: #00596f; color: #00596f; background: #e6f3f6; }
    .cf-nav-ghost:focus-visible { outline: 2px solid #006a85; outline-offset: 0; }
    .cf-nav-ghost-plus  { flex-shrink: 0; font-size: 14px; font-weight: 600; line-height: 1; }
    .cf-nav-ghost-label { flex-shrink: 0; font-size: 12px; line-height: 1; }

    .cf-nav-chevron {
      flex-shrink: 0; width: 10px; height: 5px; margin-left: 8px;
      background: var(--ir) center/100% 100% no-repeat;
      transition: transform var(--cf-nav-tint) var(--cf-nav-ease);
    }
    .cf-nav:hover .cf-nav-chevron, .cf-nav:active .cf-nav-chevron { background-image: var(--is); }
    .cf-nav--open .cf-nav-chevron { transform: rotate(180deg); }


    /* D · ce qui ne bouge pas — le logo suit les mêmes règles de fond que l'item de
       nav. La ligne de profil, elle, bascule sur la tuile blanche de l'état
       sélectionné : c'est une `.cf-nav` à part entière, marquée aria-current quand
       Paramètres est ouvert. */
    /* Même esquisse pour le bloc logo. Le filet passe par un box-shadow en anneau
       et non par une bordure : ce bloc n'en a pas, et lui en donner une décalerait
       le lockup d'un pixel à chaque survol. */
    /* LE « + » SUR LA PASTILLE DE PROJETS A ÉTÉ RETIRÉ le 1er septembre 2026.
       Il portait le geste « nouveau projet » sans coûter de ligne, mais la
       maquette 8956:5882 rend au geste sa propre rangée — la rangée fantôme
       ci-dessus, qui ne coûte que 34 px et n''aplatit rien. Deux portes vers
       startNewCall() dans la même barre, c''était une de trop : le morph de la
       pastille au survol effaçait le glyphe de Projets, donc survoler la rangée
       pour aller aux projets faisait disparaître l''icône des projets.
       Les styles .cf-nav-create* sont partis avec lui. */

    .cf-nav-logo { border-radius: 8px; transition: background-color var(--cf-nav-tint) var(--cf-nav-ease), box-shadow var(--cf-nav-tint) var(--cf-nav-ease); }
    .cf-nav-logo:hover  { background-color: #fff; box-shadow: 0 0 0 1px #eaeff1; }
    .cf-nav-logo:active { background-color: #f3f7f8; box-shadow: 0 0 0 1px #dfe6e9; }

    /* Le fond de la barre est #FAFBFC dans la planche. `body.light-theme aside`
       la repeint en rgba(248,250,252,.95) en !important — un vestige de la
       feuille light-theme qui vise TOUT <aside> et qui gagnait déjà contre le
       blanc posé en inline. On le reprend ici, une fois, au bon jeton. */
    body.light-theme aside.sidebar-main { background-color: #fafbfc !important; }

