/* 11-noeuds.css — les nœuds de la toile : les cercles, leurs étiquettes, leurs états.
 *
 * ── L'ORDRE COMPTE ─────────────────────────────────────────────────────────────────
 * Douzième tranche. `server/feuilles.py` trie NUMÉRIQUEMENT — c'est ce qui empêche « 11- »
 * de se ranger avant « 9- ».
 *
 * ── POURQUOI CE FICHIER EXISTE ─────────────────────────────────────────────────────
 * `10-toile.css` a franchi le plafond de 400 lignes en gagnant la hauteur du portrait. Le
 * plafond existe pour provoquer exactement ça, et la coupe suit la frontière que le dessin
 * trace déjà : les ARÊTES sont en SVG et vivent dans la tranche 10, les NŒUDS sont en HTML
 * et vivent ici. Ce n'est pas un découpage par taille, c'est le même partage que celui du
 * balisage — `toile-svg.js` a les deux mêmes moitiés.
 *
 * Ce qui reste dans la tranche 10 et qu'il faut aller y lire : le cadre (`.toile`, et
 * pourquoi il cesse d'être carré sur un téléphone) et le mappage des cinq couleurs de
 * pilier (`.pil-*`), qui ne peuvent peindre qu'un trait.
 */

/* — les nœuds — */

.toile-noeuds {
  position: absolute;
  inset: 0;
  list-style: none;
  margin: 0;
  padding: 0;
}

/* `--x` et `--y` sont des centièmes, `--r` un rayon en centièmes : la largeur vaut donc deux
 * fois le rayon. Les pourcentages se rapportent à `.toile`, qui est carrée — c'est ce qui
 * fait qu'un seul nombre suffit pour les deux axes. */
.noeud {
  position: absolute;
  left: calc(var(--x, 50) * 1%);
  top: calc(var(--y, 50) * 1%);
  width: calc(var(--r, 5) * 2%);
  transform: translate(-50%, -50%);
}

.noeud > a,
.noeud > button {
  display: block;
  position: relative;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-decoration: none;
  cursor: pointer;
}

/* La cible tactile, et la raison pour laquelle elle est un pseudo-élément.
 *
 * Le plus petit nœud fait 33 px de diamètre sur un téléphone — en dessous du plancher de 44.
 * Agrandir le cercle mentirait : sa taille EST la mesure du trafic. On agrandit donc la
 * zone sensible sans toucher au dessin. C'est invisible, et c'est le but. */
.noeud > a::after,
.noeud > button::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: max(100%, var(--tap));
  height: max(100%, var(--tap));
  border-radius: 50%;
}

.n-tile {
  display: grid;
  place-items: center;
  width: 100%;
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--panel);
  border: 2px solid var(--lv, var(--line));
  /* `position` porte le halo et l'anneau de pulsation, qui sont des pseudo-éléments POSÉS
   * DEHORS (`inset: -6px`). Un `overflow: hidden` ici — le réflexe pour garder une image
   * dans un cercle — les rognerait tous les deux : le halo disparaîtrait sans un mot. Ce
   * n'est pas nécessaire, l'image fait 58 % et ne touche jamais le bord. */
  position: relative;
}

/* Un anneau vert sur chaque nœud qui va bien, c'est six alarmes qui disent « tout va » —
 * et une couleur qu'on voit partout cesse d'être vue. Le calme se dessine par l'ABSENCE,
 * comme l'onglet Santé qui ne pose aucun bouton sur une ligne verte. Restent le orange et
 * le rouge, qui désignent alors vraiment quelque chose. */
.noeud.lv-ok .n-tile,
.noeud.lv-unknown .n-tile { border-color: var(--line); }

.n-tile img { width: 58%; height: 58%; object-fit: contain; }

/* Le repli en initiales, comme dans la grille : trois de ces sites n'ont réellement aucune
 * icône, et l'absence doit se voir sans faire honte. */
.n-tile i {
  font-style: normal;
  font-weight: var(--w-gras);
  font-size: var(--t2);
  color: var(--mist);
}

/* — la forge —
 * Où vit le code, dit par sa marque plutôt que par un mot. Posée SUR le cercle et non dans
 * l'étiquette : celle-ci est déjà coupée à treize caractères, et lui prendre « github »
 * retirerait du nom, qui est ce qu'on lit.
 *
 * En bas à droite, débordant légèrement : c'est le coin le moins chargé — l'étiquette part
 * du bas CENTRÉ, le halo est concentrique, et rien d'autre ne s'y trouve. Le fond opaque
 * n'est pas décoratif, il détache la marque d'un trait qui passerait dessous.
 *
 * La couleur est `--mist`, pas celle de la marque. Deux raisons, et la seconde décide :
 * un violet GitLab et un noir GitHub côte à côte ajouteraient deux teintes à un écran qui
 * en porte déjà huit ; et une couleur de marque n'entre dans aucune des échelles de la
 * charte, donc elle s'écrirait en dur. Une marque se reconnaît à sa forme. */
.n-forge {
  position: absolute;
  right: -1px;
  bottom: -1px;
  display: grid;
  place-items: center;
  width: 42%;
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--panel);
  box-shadow: 0 0 0 1px var(--line);
}

.n-forge svg {
  width: 62%;
  height: 62%;
  fill: var(--mist);
}

/* Un nœud sans mesure n'a pas de fond : la pastille en emprunte un au conteneur, sinon la
 * marque se lit par-dessus le trait pointillé qu'elle chevauche. */
.noeud.sans-mesure .n-forge { background: var(--ground); }

/* Le halo : ce que le produit rapporte, ou coûte.
 *
 * Plafonné à une opacité de .25 et un rayon de 12 px, et ce plafond est la décision. Six
 * halos rouges à pleine puissance font une alarme que l'écran ne peut pas tenir — et un
 * écran qui crie en permanence, on cesse de le regarder. Le halo est une teinte, pas un
 * anneau : l'anneau, c'est la bordure, et elle dit autre chose. */
.n-tile::after {
  content: "";
  position: absolute;
  inset: -6px;
  border-radius: 50%;
  box-shadow: 0 0 10px 1px var(--halo-c, transparent);
  opacity: calc(var(--halo, 0) * .22);
  pointer-events: none;
}

.rent-pos { --halo-c: var(--ok); }
.rent-neg { --halo-c: var(--error); }
/* `nil` couvre DEUX cas — « exactement zéro » et « pas mesuré ». On perd la distinction ici,
 * et c'est l'étiquette chiffrée qui la rend : « 0 € » et « — € » ne se lisent pas pareil. */
.rent-nil { --halo-c: transparent; }

/* Jamais mesuré. Le pointillé est le vocabulaire que la charte emploie déjà pour un
 * emplacement vide, et il dit la bonne chose : NON MESURÉ, pas désert. Grand Roque n'a pas
 * de clé de lecture ; sa case doit se voir vide, pas petite. */
.noeud.sans-mesure .n-tile {
  border-style: dashed;
  background: none;
}

/* — les étiquettes —
 * Sorties du flux : le cercle est la mesure, le texte ne doit pas la déformer.
 *
 * Et ÉTROITES, ce qui est la vraie contrainte. Sur un anneau, ce qui se cogne, ce sont les
 * voisins angulaires : à sept emplacements sur 430 px, chacun dispose de 99 px. Un nom
 * rendu à sa largeur naturelle — « felixrevert.fr », « Nouvelle app » — en fait 100 et
 * recouvre le suivant. Quatorze caractères, coupés, tiennent dans 93 : c'est ce que demande
 * la ligne de chiffres — à treize, « 3 /j · −12 € » perdait son euro, et un montant amputé
 * de son unité est pire qu'un montant absent. Le pas angulaire en fait 112, donc il reste du
 * jeu. Le nom entier reste au survol, dans le `title`.
 *
 * La `font-size` est posée ICI et pas seulement sur le `b`, et ce n'est pas de la mise en
 * forme : `ch` est la largeur du zéro DANS LA POLICE DE L'ÉLÉMENT. Sans elle, la boîte
 * héritait des 14 px du corps de page et « 11ch » valait 101 px au lieu de 60 — la largeur
 * qu'on croyait avoir corrigée. */
.n-txt {
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  width: 14ch;
  margin-top: var(--s1);
  font-size: var(--t1);
  text-align: center;
  line-height: var(--lh-titre);
}

.n-txt b {
  display: block;
  font-weight: var(--w-fort);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Les deux chiffres de l'ancienne grille, sur UNE ligne — comme dans la grille, et pour une
 * raison différente qu'il vaut mieux écrire que redécouvrir.
 *
 * Dans la grille, une ligne parce qu'empilés ils poussaient le sixième produit sous la ligne
 * de flottaison. Ici il n'y a pas de ligne de flottaison. Empilés, ils tenaient d'ailleurs
 * mieux en largeur — et c'est ce qu'on avait fait d'abord.
 *
 * Ce qui a tranché, c'est la collision RADIALE, mesurée à l'écran : trois lignes sous un
 * nœud descendent de treize centièmes, l'anneau des produits est à 23 et celui des machines
 * à 41 — « Lane / 9 /j / −16 € » atterrissait dans le disque de `vps-1a91c5b3`. Sur deux
 * lignes, six centièmes, et il reste cinq de dégagement. La largeur, elle, est déjà tenue
 * par la coupe de `.n-txt` ; la hauteur ne l'était par rien. */
.n-kpi {
  display: block;
  font-size: var(--t1);
  color: var(--mist);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Le point qui les sépare est POSÉ, pas écrit dans le balisage : le composant rend deux
 * `<span>` et n'a pas à savoir comment on les ponctue. */
.n-kpi span + span::before { content: " · "; color: var(--mist); font-weight: var(--w-normal); }

.n-kpi .pos { color: var(--ok);    font-weight: var(--w-fort); }
.n-kpi .neg { color: var(--error); font-weight: var(--w-fort); }
.n-kpi .nil { color: var(--mist); }

/* — le centre —
 * Lane respire, et c'est la SEULE chose qui bouge quand il ne se passe rien. À un chantier
 * toutes les onze heures, une toile immobile et une toile en panne se ressemblent beaucoup ;
 * ce souffle dit « le système tourne, il n'y a simplement rien à signaler ». Une seule
 * transformation composite, donc un coût de batterie négligeable. */
.noeud.centre .n-tile {
  background: var(--accent-bg);
  border-color: var(--accent);
  animation: toile-souffle 4s ease-in-out infinite;
}

.noeud.centre .n-tile i { color: var(--accent); font-size: var(--t5); }

@keyframes toile-souffle { 50% { transform: scale(1.03); } }

/* — les machines —
 * Elles ne sont pas des cibles : il n'y a pas d'écran machine à l'accueil, et un nœud qui
 * réagit au doigt sans mener nulle part apprend à se méfier des nœuds. */
.noeud.machine .n-tile {
  background: var(--panel2);
  border-width: 1px;
}

.noeud.machine .n-txt b {
  font-size: var(--t1);
  font-weight: var(--w-moyen);
  color: var(--mist);
}

/* — créer une application —
 * La même tuile pointillée que dans la grille, devenue un emplacement libre dans l'anneau.
 * L'ajout est au même endroit et à la même taille que ce qu'on ajoute, donc il se trouve
 * sans être expliqué. */
.noeud.neuf .n-tile {
  border-style: dashed;
  background: none;
}

.noeud.neuf .n-tile i { color: var(--accent); font-size: var(--t5); }

/* — un agent travaille ici —
 * L'anneau qui s'écarte, en plus du fil qui défile : sur un téléphone, le fil peut être
 * masqué par le pouce, le nœud non. */
.noeud.vif .n-tile {
  border-color: var(--accent);
}

.noeud.vif .n-tile::before {
  content: "";
  position: absolute;
  inset: -2px;
  border-radius: 50%;
  border: 2px solid var(--accent);
  animation: toile-pulse 1.8s ease-out infinite;
  pointer-events: none;
}

@keyframes toile-pulse {
  70%, 100% { transform: scale(1.45); opacity: 0; }
}

/* — la phrase, et la légende — */

/* Un fil qui s'allume ne dit pas CE QUE fait l'agent. La phrase vient du serveur, entière ;
 * le navigateur l'affiche, il ne la compose pas. */
.toile-phrase {
  margin: var(--s4) auto 0;
  max-width: var(--toile-max);
  text-align: center;
  font-size: var(--t3);
  line-height: var(--lh-corps);
  color: var(--mist);
}

.toile-phrase b { color: var(--text); font-weight: var(--w-fort); }

/* La position et la taille se lisent seules ; les cinq teintes, non. La légende reprend les
 * icônes EXACTES de la barre d'onglets, donc elle enseigne un vocabulaire déjà connu d'un
 * autre écran — zéro dessin nouveau à apprendre. */
.toile-legende {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--s1) var(--s3);
  list-style: none;
  margin: var(--s3) auto 0;
  max-width: var(--toile-max);
  padding: 0;
  font-size: var(--t1);
  color: var(--mist);
}

.toile-legende li { display: flex; align-items: center; gap: var(--s1); }

.toile-legende svg {
  width: 14px;
  height: 14px;
  fill: none;
  stroke: var(--pil);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* — moins de mouvement —
 * Le `prefers-reduced-motion` de `tokens.css` écrase les DURÉES, globalement et par
 * `!important`. Tout état qui n'existe QUE par le mouvement disparaît donc en silence : sans
 * ce bloc, quelqu'un qui a demandé moins d'animation ne voit plus du tout qu'un agent
 * travaille. Le réglage d'accessibilité lui retirerait une information — l'inverse du but.
 * D'où la règle : tout état animé a un jumeau statique. */
@media (prefers-reduced-motion: reduce) {
  .noeud.vif .n-tile { outline: 2px solid var(--accent); outline-offset: 3px; }
  .lien.agent.vif { stroke-dasharray: none; }
}
