.node {
    position: absolute;
    background: var(--node-default-bg);
    border: 2px solid var(--node-default-border);
    border-radius: 6px;
    padding: 6px 6px 28px 6px;
    width: 110px;
    text-align: center;
    cursor: pointer;
    transition: transform 0.1s, box-shadow 0.1s, opacity 0.2s;
    box-shadow: 0 1px 3px rgba(0,0,0,0.12);
    user-select: none;
    /* PAS de overflow:hidden — sinon l'icône d'anomalie (en ::before
       avec left/top -8px) serait masquée. La silhouette en ::after est
       contenue par sa propre boîte (inset:0 + background-size). */
  }

  /* Silhouette de personne en filigrane (arrière-plan) — uniquement pour
     les nœuds qui représentent une PERSONNE (ministre, VP, direction…).
     Ajoutée via la classe .person-bg posée en JS. L'opacité est très
     basse pour ne pas nuire à la lisibilité du texte par-dessus.
     L'image est un SVG inline (data-URL) — léger (~200 octets), résolution-
     indépendant, et auto-contenu (pas de fichier externe).
     IMPORTANT : on utilise ::after (et non ::before) parce que ::before
     est réservé à l'icône d'anomalie qui doit dépasser du cadre. */
  .node.person-bg::after {
    content: "";
    position: absolute;
    /* Pseudo-élément aligné sur l'INTÉRIEUR du cadre (laisse 2px aux
       bordures), donc la silhouette reste à l'intérieur même sans
       overflow:hidden. */
    top: 2px;
    left: 2px;
    right: 2px;
    bottom: 2px;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><circle cx='50' cy='35' r='18' fill='%230a3d62'/><path d='M 18 95 Q 18 60 50 60 Q 82 60 82 95 Z' fill='%230a3d62'/></svg>");
    background-repeat: no-repeat;
    background-position: center 55%;
    background-size: 75% 75%;
    opacity: 0.085;       /* TRÈS pâle — ne nuit pas à la lecture */
    pointer-events: none;
    z-index: 0;
    border-radius: 4px;   /* suit l'arrondi intérieur */
  }
  .node.minister.person-bg::after {
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><circle cx='50' cy='35' r='18' fill='%23ffffff'/><path d='M 18 95 Q 18 60 50 60 Q 82 60 82 95 Z' fill='%23ffffff'/></svg>");
    opacity: 0.10;
  }

  /* S'assurer que le contenu textuel passe au-dessus de la silhouette. */
  .node > .title,
  .node > .name,
  .node > .count {
    position: relative;
    z-index: 1;
  }

  .node:hover {
    transform: scale(1.04);
    box-shadow: 0 4px 12px rgba(0,0,0,0.25);
    z-index: 50;
  }

  .node .title {
    font-weight: 600;
    font-size: 10px;
    line-height: 1.25;
    color: var(--node-default-title);
    word-wrap: break-word;
    hyphens: auto;
  }

  .node .name {
    font-size: 9px;
    margin-top: 4px;
    color: var(--node-default-name);
    line-height: 1.3;
    word-wrap: break-word;
  }

  .node .count {
    font-size: 8px;
    margin-top: 3px;
    color: var(--text-muted);
    font-style: italic;
  }

  .node .info-btn {
    position: absolute;
    bottom: 2px;
    left: 4px;
    background: rgba(255,255,255,0.85);
    border: 1px solid #aaa;
    border-radius: 50%;
    width: 16px;
    height: 16px;
    font-size: 10px;
    line-height: 1;
    color: #333;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: bold;
    padding: 0;
    z-index: 2;
  }
  .node .info-btn:hover {
    background: var(--text-accent);
    color: white;
    border-color: var(--text-accent);
  }

  .node .copy-btn {
    position: absolute;
    bottom: 2px;
    left: 24px; /* à droite du bouton info (qui est à left:4px, width:16px) */
    background: rgba(255,255,255,0.85);
    border: 1px solid #aaa;
    border-radius: 50%;
    width: 16px;
    height: 16px;
    font-size: 10px;
    line-height: 1;
    color: #333;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: bold;
    padding: 0;
    z-index: 2;
  }
  .node .copy-btn:hover {
    background: var(--text-accent);
    color: white;
    border-color: var(--text-accent);
  }
  .node .copy-btn.copy-ok {
    background: #2e7d32 !important;
    color: white !important;
    border-color: #1b5e20 !important;
  }
  .node .copy-btn.copy-fail {
    background: #c62828 !important;
    color: white !important;
    border-color: #8e0000 !important;
  }

  /* Codes couleur par niveau de certitude */
  .node.verified { background: var(--node-verified-bg); border-color: var(--node-verified-border); }
  .node.verified .title { color: var(--node-verified-title); }

  .node.partial { background: var(--node-partial-bg); border-color: var(--node-partial-border); }
  .node.partial .title { color: var(--node-partial-title); }

  .node.approximate { background: var(--node-approximate-bg); border-color: var(--node-approximate-border); }
  .node.approximate .title { color: var(--node-approximate-title); }

  .node.structural { background: var(--node-structural-bg); border-color: var(--node-structural-border); }
  .node.structural .title { color: var(--node-structural-title); }

  /* Types spéciaux */
  .node.minister { background: var(--node-minister-bg); color: white; border-color: var(--node-minister-border); width: 130px; }
  .node.minister .title { color: var(--node-minister-title); font-size: 11px; }
  .node.minister .name { color: var(--node-minister-name); }

  .node.vp { background: var(--node-vp-bg); border-color: var(--node-vp-border); }
  .node.vp .title { color: var(--node-vp-title); }

  .node.pdg { background: var(--node-pdg-bg); border-color: var(--node-pdg-border); }

  .node.direction { background: var(--node-direction-bg); border-color: var(--node-direction-border); width: 100px; }
  .node.direction .title { color: var(--node-direction-title); font-size: 9px; }

  .node.installation { background: var(--node-installation-bg); border-color: var(--node-installation-border); width: 100px; }
  .node.installation .title { color: var(--node-installation-title); font-size: 9px; }

  .node.collapsed::after {
    content: "▸";
    position: absolute;
    right: 4px;
    bottom: 2px;
    font-size: 11px;
    color: var(--text-muted);
    font-weight: bold;
  }

  .node.expanded::after {
    content: "▾";
    position: absolute;
    right: 4px;
    bottom: 2px;
    font-size: 11px;
    color: var(--text-muted);
    font-weight: bold;
  }

  /* Highlight de recherche */
  .node.search-match {
    box-shadow: 0 0 0 3px var(--search-match-border), 0 0 18px 4px var(--search-match-shadow), 0 1px 3px rgba(0,0,0,0.2);
    z-index: 40;
  }

  body.search-active .node:not(.search-match):not(.search-ancestor) {
    opacity: var(--search-dim-opacity);
  }
  body.search-active .node.search-ancestor {
    opacity: 0.65;
  }
  body.search-active #links path { opacity: 0.18; }

  /* SVG des liens */
  #links {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
  }

  #links path {
    stroke: var(--link-stroke);
    stroke-width: var(--link-stroke-width, 2);
    fill: none;
    opacity: var(--link-stroke-opacity);
    /* Halo blanc subtil en mode jour pour contraster le tracé sombre
       sur le drapeau bleu marine + texture rocheuse du back.png.
       Désactivé en dark mode (couleur claire sur fond sombre déjà OK). */
    filter: drop-shadow(0 0 1.5px rgba(255,255,255,0.85));
  }
  body.dark #links path {
    filter: none;
  }

  /* Pop-up de détails */
  #modal-overlay {
    display: none;
    position: fixed;
    top: 0; left: 0; right: 0; bottom: 0;
    background: var(--bg-modal-overlay);
    z-index: 2000;
    justify-content: center;
    align-items: center;
  }

  #modal-overlay.visible { display: flex; }

  #modal {
    background: var(--bg-modal);
    color: var(--text-default);
    border-radius: 10px;
    max-width: 600px;
    width: 90%;
    max-height: 85vh;
    overflow-y: auto;
    padding: 24px 28px;
    box-shadow: 0 20px 60px rgba(0,0,0,0.4);
    position: relative;
  }

  #modal-close {
    position: sticky;
    top: 0;
    float: right;
    margin: -10px -10px 0 0;
    background: transparent;
    border: none;
    font-size: 24px;
    cursor: pointer;
    color: var(--text-muted);
    line-height: 1;
    padding: 4px 8px;
    border-radius: 6px;
    transition: background 0.15s, color 0.15s;
    z-index: 3;
  }

  #modal-close:hover {
    background: rgba(0,0,0,0.08);
    color: var(--text-default);
  }
  body.dark #modal-close:hover {
    background: rgba(255,255,255,0.12);
    color: var(--text-default);
  }

  #modal h2 {
    color: var(--text-accent);
    font-size: 18px;
    margin-bottom: 4px;
    padding-right: 30px;
  }

  .node.anomaly::before {
    content: "⚠";
    position: absolute;
    left: -8px;
    top: -8px;
    background: #d32f2f;
    color: white;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    font-size: 12px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: bold;
    z-index: 5;
    box-shadow: 0 1px 3px rgba(0,0,0,0.3);
  }

  /* ============================================================
     MODE "FLUX D'ARGENT" — activé par le bouton 💼 dans le panneau
     Coûts. Quand body.money-flow-active :
       1. Tous les nœuds .person-bg sont masqués (les personnes
          disparaissent — ministre, VP, directeurs, etc.).
          Seules les ENTITÉS restent (organismes, organisations,
          cabinets, établissements, installations, regroupements).
       2. La racine "Électeurs du Québec" (#node[data-id="root"])
          devient un GIF animé du portefeuille avec billets verts.
       3. Les branches SVG s'animent en vert, comme de l'eau verte
          qui coule du portefeuille vers chaque enfant. La vitesse
          est proportionnelle au coût agrégé du sous-arbre cible
          (--flow-speed est calculé en JS et injecté inline sur
          chaque <path>).
     ============================================================ */

  /* 1. Cacher les personnes en mode flux d'argent.
        EXCEPTION : on n'utilise pas display:none parce que ça
        tord le layout des branches ; on utilise visibility:hidden
        pour conserver les positions et que les flèches descendent
        toujours sur l'arbre des entités correctement. Le pointer-
        events:none évite les interactions avec une boîte invisible. */
  body.money-flow-active .node.person-bg {
    visibility: hidden !important;
    pointer-events: none !important;
  }

  /* 2. Racine "Électeurs du Québec" → portefeuille animé.
        On garde la boîte mais on remplace son contenu visuel par
        le GIF. Le texte d'origine est masqué avec opacity:0 (et
        pointer-events:none sur les enfants pour éviter les clics
        accidentels sur les boutons ℹ et ⧉). */
  body.money-flow-active .node[data-id="root"] {
    background: transparent !important;
    border: none !important;
    box-shadow: none !important;
    /* Cacher le filigrane personne (ne devrait pas être présent sur
       root, mais sécurité supplémentaire) */
  }
  body.money-flow-active .node[data-id="root"] > * {
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.25s;
  }
  body.money-flow-active .node[data-id="root"]::before {
    content: "";
    position: absolute;
    inset: 0;
    background-image: url("../assets/wallet.gif");
    background-repeat: no-repeat;
    background-position: center;
    background-size: contain;
    /* Léger rebond pour souligner que c'est lui la « source de
       l'argent ». Animation courte, ne distrait pas. */
    animation: money-flow-wallet-bob 2.4s ease-in-out infinite;
    pointer-events: auto;
    z-index: 2;
  }
  /* Légère lueur verte autour du portefeuille pour insister sur
     « c'est l'origine du flot ». */
  body.money-flow-active .node[data-id="root"]::after {
    content: "";
    position: absolute;
    inset: -8px;
    border-radius: 50%;
    background: radial-gradient(closest-side, rgba(67,160,71,0.28), rgba(67,160,71,0) 70%);
    pointer-events: none;
    animation: money-flow-wallet-glow 2.4s ease-in-out infinite;
    z-index: 1;
  }
  @keyframes money-flow-wallet-bob {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(-3px); }
  }
  @keyframes money-flow-wallet-glow {
    0%, 100% { opacity: 0.55; transform: scale(0.96); }
    50%      { opacity: 0.95; transform: scale(1.04); }
  }

  /* 3. Animation des branches : "eau verte qui coule".
        Technique : un stroke-dasharray (motif tirets) qu'on fait
        glisser via stroke-dashoffset. Plus la vitesse est élevée,
        plus le décalage est rapide. La couleur passe à un vert
        argent. La variable --flow-speed est posée en JS sur
        chaque <path> et donne la durée de l'animation (en sec).
        Plus la valeur de --flow-speed est BASSE, plus l'animation
        est RAPIDE (moins de secondes par cycle). */
  body.money-flow-active #links path {
    stroke: #43a047;
    stroke-width: 2.4;
    stroke-linecap: round;
    stroke-dasharray: 8 6;
    opacity: 0.95;
    /* Filtre lueur verte — donne l'impression de fluide lumineux */
    filter: drop-shadow(0 0 2.5px rgba(76,175,80,0.55));
    /* La durée est lue depuis --flow-speed (posée en JS).
       Valeur par défaut : 3s (vitesse moyenne), au cas où le JS
       n'aurait pas encore tourné. */
    animation: money-flow-stream var(--flow-speed, 3s) linear infinite;
    transition: stroke 0.25s, stroke-width 0.25s;
  }
  /* Les paths qui aboutissent à une personne masquée SANS descendance
     entité visible sont marqués `data-mf-hidden="1"` par le JS et
     doivent disparaître complètement (ils ne mènent nulle part). */
  body.money-flow-active #links path[data-mf-hidden="1"] {
    display: none;
  }
  body.money-flow-active.dark #links path,
  body.dark.money-flow-active #links path {
    stroke: #66bb6a;
    filter: drop-shadow(0 0 3px rgba(129,199,132,0.7));
  }
  /* Décalage du motif → l'eau « coule » du parent vers l'enfant.
     Note : le path est dessiné du parent (haut) vers l'enfant
     (bas), donc un dashoffset qui DIMINUE fait avancer le motif
     dans le sens parent → enfant. */
  @keyframes money-flow-stream {
    from { stroke-dashoffset: 0; }
    to   { stroke-dashoffset: -28; }
  }

  /* Désactiver l'animation pour les utilisateurs qui ont demandé
     prefers-reduced-motion : la barre reste verte mais ne coule
     plus. Bonne pratique d'accessibilité. */
  @media (prefers-reduced-motion: reduce) {
    body.money-flow-active #links path { animation: none; }
    body.money-flow-active .node[data-id="root"]::before,
    body.money-flow-active .node[data-id="root"]::after { animation: none; }
  }

  /* Intro modal */
