/* ---------------------------------------------------------------------------
   L'ESTHÉTIQUE DES ARBRES — une seule, pour les trois.

   `arbre.js` tient le comportement commun aux arbres — le repli, le glissé.
   Ce fichier en est le pendant : il tient leur APPARENCE, et pour la même
   raison. Trois arbres coexistent — les gammes de l'accueil, la structure de
   l'atelier, le rangement du catalogue — et rien ne justifiait qu'ils se
   ressemblent de loin sans se ressembler de près.

   La référence est l'arbre de l'accueil, qui est le plus calme :

     une PASTILLE arrondie par ligne, séparée de la suivante par deux pixels ;
     un CHEVRON tracé en CSS, qui pivote ;
     un COMPTEUR en texte sourd, sans pastille ni cerne ;
     une SÉLECTION marquée par un liseré, pas par un lavis.

   Ce dernier point est le seul qui demande une explication : un voile clair
   posé sur une surface déjà teintée vire au gris, et le texte y perd son
   contraste. Le liseré, lui, tient sur n'importe quel fond.

   Ce fichier ne connaît AUCUN genre d'élément : ni JITÉ, ni profil, ni gamme.
   Il ne décrit que la ligne, son chevron, son intitulé et son compte. Tout ce
   qui distingue un arbre d'un autre — les puces, l'œil, les icônes de dossier,
   les actions au survol — reste dans la feuille de style de son écran.

   Il se charge APRÈS `app.css`, dont il utilise les jetons.
   --------------------------------------------------------------------------- */

/* La LARGEUR d'une colonne d'arbre, déclarée une fois pour les trois écrans.
   Elle valait 22 rem à l'atelier, 22 rem à l'accueil et 15 rem au catalogue :
   la même colonne, montrant la même chose, changeait de largeur d'un écran à
   l'autre — et un rayon profond y débordait alors qu'il tenait ailleurs. */
:root {
  --arbre-etroit: 16rem;
  --arbre-large: 22rem;
}

/* Le conteneur. Le pas d'indentation est déclaré ici, une fois : c'est lui qui
   fait qu'un sous-dossier se lit comme un sous-dossier. */
.arbre {
  --pas: 0.85rem;
  /* L'ABSCISSE du premier trait de branche : le centre de la case d'ouverture
     d'une ligne racine, soit le retrait de la ligne plus la moitié de la case.
     Déclarée ici parce que trois choses doivent tomber dessus au pixel — le
     trait vertical, le tiret horizontal, et la case elle-même. */
  --axe: 0.95rem;
  --trait: var(--panneau-filet);
  display: flex;
  flex-direction: column;
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Un sous-arbre. L'écart entre lignes est porté par la LIGNE et non par une
   `gap` de conteneur : les groupes s'imbriquent, et une `gap` par niveau
   creuserait un fossé de plus en plus large à chaque profondeur. */
.arbre-groupe {
  display: flex;
  flex-direction: column;
}

.arbre-ligne {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.35rem 0.5rem;
  /* La profondeur se lit à l'œil, et se déclare en une seule variable. */
  padding-left: calc(0.5rem + var(--depth, 0) * var(--pas, 0.85rem));
  /* Aucune gouttière entre deux lignes : les traits de branche courent d'une
     ligne à l'autre, et deux pixels de blanc les hachaient. */
  border-radius: 4px;
  line-height: 1.3;
  cursor: pointer;
  user-select: none;
  /* Déclaré, et pas seulement omis : une ligne est parfois un `button`, qui
     arriverait sinon avec le fond et le cadre de tous les autres boutons. */
  background: none;
}

/* LES TRAITS DE BRANCHE.

   La hiérarchie se lit au TRAIT, plus à l'aplat. Un fond par dossier disait le
   niveau par la couleur : trois niveaux imbriqués donnaient trois aplats
   presque identiques, et l'œil devait comparer des gris pour savoir qui
   contenait qui. Un trait vertical par niveau le dit d'un coup, et se lit
   aussi bien à dix lignes qu'à cent.

   Tout est porté par la LIGNE, à partir de sa seule profondeur — pas par les
   groupes : le rangement du catalogue est un arbre PLAT, il n'a pas de groupes
   à border. Une ligne de profondeur n trace les n traits de ses ancêtres, un
   par pas d'indentation, plus le tiret qui la raccroche au dernier. Comme les
   lignes se touchent, les traits se rejoignent de l'une à l'autre et forment
   les branches continues sans que personne n'ait à savoir qui suit qui.

   `background-repeat` fait le travail que ferait sinon un élément par niveau :
   le dégradé répété pose un trait tous les `--pas`, et la largeur de la boîte
   décide combien on en voit. */
.arbre-ligne::before {
  content: '';
  position: absolute;
  left: var(--axe);
  top: 0;
  bottom: 0;
  /* Un pixel de moins que les pas : le motif répété reprenait son trait PILE sur
     le bord de la boîte, et l'arrondi des écrans denses en laissait un filet —
     une branche fantôme sur l'axe de la ligne elle-même, qui traversait sa
     plaque. */
  width: calc(var(--depth, 0) * var(--pas) - 1px);
  pointer-events: none;
  background:
    /* Le tiret qui raccroche la ligne à la branche de son parent. Il s'arrête
       à 5 px de l'axe, soit sur le BORD de la case : le laisser courir jusqu'au
       centre l'aurait fait passer sous elle, et il aurait fallu remplir la case
       d'un aplat opaque pour le masquer — un aplat qui aurait juré dès que la
       ligne prend son fond de survol. */
    linear-gradient(var(--trait), var(--trait)) right 5px center / calc(var(--pas) - 5px) 1px no-repeat,
    /* un trait vertical par niveau au-dessus d'elle */
    repeating-linear-gradient(90deg, var(--trait) 0 1px, transparent 1px var(--pas));
}

/* Le trait qui DESCEND d'un dossier ouvert vers son contenu. Il part du bas de
   la case et rejoint la branche que tracent les lignes du dessous : sans lui,
   la branche commencerait dans le vide, une demi-ligne trop bas. */
.arbre-ligne.is-open::after {
  content: '';
  position: absolute;
  left: calc(var(--axe) + var(--depth, 0) * var(--pas));
  /* Sous la case, pas depuis son centre : sinon il la traverse. */
  top: calc(50% + 5px);
  bottom: 0;
  width: 1px;
  pointer-events: none;
  background: var(--trait);
}

/* Une ligne ne porte plus d'aplat, dossier compris : le trait dit la
   hiérarchie, le survol dit qu'on peut cliquer, et c'est tout ce qu'un fond
   avait à dire. */
.arbre-ligne:hover { background: var(--dossier); }

/* LA LIGNE QUI CRÉE, en pied de liste — voir `creer.js`.

   Elle ne ressemble pas à un bouton : c'est une ligne de la liste, en retrait,
   qui ne se réveille qu'au survol. Un aplat minium en tête de colonne tirait
   l'œil vers la commande la plus rare de la page ; ici la couleur ne vient que
   lorsqu'on la vise déjà.

   Le trait pointillé dit qu'elle n'est pas un élément mais une place à
   remplir : elle appartient à la liste sans y compter. */
.ligne-creer {
  margin-top: 0.35rem;
  border: 1px dashed var(--filet-panneau);
  color: var(--muted);
  font-size: 0.8rem;
  font-style: italic;
}
.ligne-creer:hover,
.ligne-creer:focus-visible {
  color: var(--action);
  border-color: var(--action);
  border-style: solid;
  background: none;
  outline: none;
}
.ligne-creer-plus {
  flex: 0 0 auto;
  width: 1.1rem;
  text-align: center;
  font-size: 1rem;
  font-style: normal;
  line-height: 1;
}
.ligne-creer-mot { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.arbre-ligne.dossier:hover { background: var(--dossier); }

/* LA SÉLECTION : un liseré, rien d'autre.

   L'aplat est parti. C'était le pire des deux marquages : posé sur le fond déjà
   teinté du dossier, il virait au gris et coûtait au texte son contraste, tout
   en repeignant une ligne sur trois d'une colonne qu'on lit de haut en bas. Le
   liseré minium tient sur n'importe quel fond — c'est la règle que ce fichier
   énonçait depuis le début sans l'appliquer jusqu'au bout.

   Ce qui reste est le fond de SURVOL, figé : la ligne désignée est celle sur
   laquelle on a la main, elle le garde après qu'on l'a quittée. Un seul aplat,
   déjà connu de l'œil, au lieu d'une teinte de plus à apprendre. */
.arbre-ligne.choisie {
  background: var(--dossier);
  box-shadow: inset 3px 0 0 var(--accent);
}
.arbre-ligne.choisie:hover { background: var(--dossier); }

/* Une ligne qui n'est pas un dossier mais une VUE — « Tout », « Non rangé »,
   « À classer ». Le tireté dit qu'il n'y a rien à ranger là, ou que ce qui s'y
   trouve attend d'aller ailleurs. */
.arbre-ligne.a-part {
  background: none;
  border: 1px dashed var(--panneau-filet);
  color: var(--panneau-sourd);
}
.arbre-ligne.a-part:hover { background: var(--dossier); color: var(--panneau-texte); }

/* Le glisser-déposer. En dernier : pendant un glissé, savoir où l'on va prime
   sur savoir où l'on était. */
.arbre-ligne[draggable="true"] { cursor: grab; }
.arbre-ligne.drop-target,
.arbre-ligne.dossier.drop-target,
.arbre-ligne.choisie.drop-target {
  background: var(--choix-fort);
  box-shadow: inset 0 0 0 1px var(--accent);
}
/* Lâcher dans le vide du panneau : l'élément remonte à la racine, et le
   panneau entier le dit. */
.arbre.drop-target { background: var(--choix); border-radius: 5px; }

/* LA CASE D'OUVERTURE : un carré, + fermé, − ouvert.

   Tracée en CSS : un émoji change de dessin d'un système à l'autre, et n'a ni
   la même chasse ni le même alignement — deux glyphes de largeurs différentes
   décalaient l'intitulé à chaque ouverture.

   Le carré remplace le chevron parce qu'il porte un SIGNE : + dit ce qui va se
   passer, le chevron ne disait qu'un sens. Et c'est un nœud de la branche —
   les traits butent sur ses bords, sans jamais la traverser.

   La place reste tenue sur les lignes sans branche, pour que tous les intitulés
   s'alignent. */
.arbre-chevron {
  flex: 0 0 auto;
  position: relative;
  width: 0.9rem;
  height: 0.9rem;
  padding: 0;
  border: 0;
  background: none;
  font-size: 0;
  cursor: pointer;
}
.arbre-chevron.vide { cursor: default; }

/* Le carré. Son fond est celui du panneau, et il est OPAQUE : c'est lui qui
   interrompt le trait de branche. */
.arbre-chevron:not(.vide)::before {
  content: '';
  position: absolute;
  inset: 0;
  margin: auto;
  width: 11px;
  height: 11px;
  border: 1px solid var(--trait);
  border-radius: 2px;
}
/* Le signe. Deux filets d'un pixel posés en fond plutôt que deux bordures : à
   cette taille, une bordure d'un pixel se décale d'un demi-pixel selon la
   parité de la boîte, et la barre du − ne tombait pas au milieu du +. */
.arbre-chevron:not(.vide)::after {
  content: '';
  position: absolute;
  inset: 0;
  margin: auto;
  width: 5px;
  height: 5px;
  background:
    linear-gradient(var(--panneau-texte), var(--panneau-texte)) center / 5px 1px no-repeat,
    linear-gradient(var(--panneau-texte), var(--panneau-texte)) center / 1px 5px no-repeat;
}
/* Ouvert : la barre verticale tombe, il ne reste que le −. */
.arbre-chevron.ouvert::after {
  background:
    linear-gradient(var(--panneau-texte), var(--panneau-texte)) center / 5px 1px no-repeat;
}
.arbre-chevron:hover::before { border-color: var(--panneau-sourd); }
.arbre-ligne.choisie .arbre-chevron::before { border-color: var(--accent); }

/* La puce d'une feuille : elle tient la place du chevron, pour que les
   intitulés d'un dossier et de son contenu s'alignent. Un rond par défaut — un
   repère, sans épaisseur ; chaque arbre en décline la forme selon ce qu'il
   range. */
.arbre-puce {
  flex: none;
  display: grid;
  place-items: center;
  width: 0.9rem;
  font-size: 0;
}
.arbre-puce::before {
  content: '';
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--accent);
}

/* LE PICTOGRAMME D'UNE LIGNE D'ARBRE, entre le chevron et le nom.

   EN RETRAIT, jamais à égalité avec l'intitulé : c'est le nom qu'on lit et
   qu'on cherche, le pictogramme ne fait que dire de quelle espèce est la
   ligne. À pleine couleur, la colonne se lirait comme une file d'images
   ponctuées de mots.

   La ligne DÉSIGNÉE le rallume : elle porte déjà l'accent sur son intitulé, et
   un pictogramme resté sourd y ferait un trou. */
/* LA PLAQUE d'une ligne d'arbre — sa forme est celle de l'écriture au tampon,
   déclarée une fois dans `app.css`. Ne reste ici que ce qui appartient à l'arbre :
   la taille du dessin, l'écart au nom, et la couleur en retrait. */
.arbre-ligne > .icone {
  --picto-encre: 0.95rem;
  margin-right: 0.175rem;
  color: var(--panneau-sourd);
}
/* LES ARBRES À PICTOGRAMMES SE LISENT COMME L'EXPLORATEUR DE WINDOWS XP.

   La case d'un enfant tombe SOUS LE PICTOGRAMME de son parent, et le trait de
   branche passe par les cases des sœurs, puis court de la case au pictogramme.
   Les + et les plaques tiennent ainsi une seule verticale d'un niveau à
   l'autre : le + d'un sous-dossier pointe la plaque du dossier qui le range.
   Le pas d'indentation valait une case, et chaque niveau décrochait la
   colonne d'une demi-plaque.

   LE PAS SE DÉDUIT DE LA PLAQUE : case + écart + demi-plaque, moins la
   demi-case que l'axe porte déjà. L'arbre de l'atelier, qui n'a pas de
   pictogramme, garde le sien.

   **ET TOUT TOMBE SUR DES PIXELS ENTIERS.** En rem, le pas valait 24,14 px et
   l'axe 15,2 : chaque trait se posait entre deux pixels, et le navigateur ne
   l'arrondissait pas du même côté selon qu'il le dessinait en motif, en dégradé
   ou en élément — la branche décrochait d'un pixel d'une ligne à l'autre. Le
   retrait (8), la case (15, donc un carré de 11 centré au pixel), l'écart (6)
   et le pas arrondi sont entiers ; la plaque rattrape l'arrondi par sa marge,
   et reste centrée sur la case de ses enfants.

   `--coupe` : ce que le trait laisse de place à la case. Une ligne sans
   branche n'a pas de case — son trait est continu et son tiret part de l'axe. */
.arbre-ligne:has(> .icone) {
  --axe: 15px; /* retrait 8 + demi-case 7 */
  --pas: round(calc(14px + var(--picto-large) / 2), 1px);
  --coupe: 6px;
  gap: 6px;
  padding-left: calc(8px + var(--depth, 0) * var(--pas));
}
.arbre-ligne:has(> .icone) > .arbre-chevron { width: 15px; height: 15px; }
.arbre-ligne:has(> .icone) > .icone {
  /* 13,5 et non 14 : le trait d'un pixel posé sur l'axe a son centre à ,5 —
     la plaque se centre sur lui, pas sur son bord. */
  margin-left: calc(var(--pas) - 13.5px - var(--picto-large) / 2);
}
.arbre-ligne:has(> .arbre-chevron.vide) { --coupe: 0px; }

/* La boîte commence au premier niveau et finit sous le pictogramme : ses
   ancêtres, son propre trait (coupé par sa case), et le tiret vers la plaque. */
.arbre-ligne:has(> .icone)::before {
  --propre: calc((var(--depth, 0) - 1) * var(--pas));
  left: calc(var(--axe) + var(--pas));
  width: calc(var(--depth, 0) * var(--pas));
  /* LES TRAITS VERTICAUX VIENNENT DE `traceBranches` (arbre.js), qui sait si
     une branche continue sous la ligne — la CSS seule ne le sait pas, et les
     branches finies couraient jusqu'au bas du dernier enfant. Posés au pixel
     entier (voir plus haut), un dégradé par trait ne décroche plus. Avant que
     le script passe, seul le tiret paraît. */
  background:
    var(--traits, none),
    linear-gradient(var(--trait), var(--trait))
      calc(var(--propre) + var(--coupe)) center
      / max(0px, calc(var(--pas) - var(--picto-large) / 2 - var(--coupe))) 1px no-repeat;
}

/* Un dossier ouvert fait descendre sa branche DE SON PICTOGRAMME, là où ses
   enfants portent leur case — non plus de sa propre case. */
.arbre-ligne.is-open:has(> .icone)::after {
  left: calc(var(--axe) + (var(--depth, 0) + 1) * var(--pas));
  top: calc(50% + var(--picto-haut) / 2);
}
.arbre-ligne:hover > .icone { color: inherit; }
.arbre-ligne.choisie > .icone { color: inherit; }

/* LES RÉSERVES N'ONT PLUS À SUIVRE LA LIGNE. Elles le devaient tant que le
   pictogramme était posé à même le panneau : survolée ou désignée, la ligne
   changeait de fond sous lui et ses évidements se couvraient de petits carrés
   sombres. La plaque a rendu ce fond CONSTANT — voir `.icone-tampon`. */

.arbre-nom {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* L'intitulé est parfois un bouton — là où le chevron et le nom sont deux
   cibles distinctes. Il ne doit alors rien garder d'un bouton. */
button.arbre-nom {
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

/* `.arbre-compte` EST PARTI, des quatre arbres à la fois. Il disait « ce
   dossier tient trois choses » — un renseignement qu'on ne va jamais chercher,
   posé là où l'œil cherche un NOM. La règle s'en va avec lui : une déclaration
   que plus rien n'émet finit par décrire un écran qui n'existe plus. */
