/* Nyx Design System — « Midnight Glass ». Bloc :root canonique, miroir de
   design/nyx-design-system.css (cf. docs/DESIGN-SYSTEM.md). Synchroniser les deux. */
:root{--bg0:#070711;--bg1:#0b0b18;--bg2:#0c0c1a;--field:#0e0e1c;--card:rgba(255,255,255,.04);--card2:rgba(255,255,255,.06);--line:rgba(255,255,255,.09);--ink:#eceefb;--dim:#9a9ec2;--faint:#7e83a8;--violet:#a78bfa;--violet2:#7c3aed;--cyan:#22d3ee;--green:#34d399;--amber:#fbbf24;--red:#fb7185;--blue:#60a5fa;--grad-mark:conic-gradient(from 200deg,var(--violet2),#1e1b4b,var(--cyan),var(--violet2));--grad-accent:linear-gradient(90deg,var(--violet),var(--cyan));--grad-action:linear-gradient(135deg,var(--violet2),var(--cyan));--grad-me:linear-gradient(135deg,var(--violet2),#5b21b6);--grad-page:radial-gradient(900px 500px at 78% -8%,rgba(124,58,237,.22),transparent 60%),linear-gradient(180deg,var(--bg1),var(--bg0));--grad-surface:linear-gradient(180deg,var(--bg1),var(--bg0));--r-xs:7px;--r-sm:9px;--r-md:11px;--r-lg:14px;--r-xl:18px;--r-pill:999px;--shadow-brand:0 6px 22px rgba(124,58,237,.45);--shadow-drawer:0 0 44px rgba(0,0,0,.55)}
*{box-sizing:border-box}html,body{margin:0;height:100%}
body{background:var(--grad-page);color:var(--ink);font:15px/1.55 ui-sans-serif,system-ui,-apple-system,Inter,Roboto;-webkit-font-smoothing:antialiased;display:flex;flex-direction:column;height:100dvh}
header{position:relative;z-index:30;display:flex;align-items:center;gap:11px;padding:12px 16px;padding-top:max(12px,env(safe-area-inset-top));padding-left:max(16px,env(safe-area-inset-left));padding-right:max(16px,env(safe-area-inset-right));border-bottom:1px solid var(--line);backdrop-filter:blur(12px)}
.mark{width:36px;height:36px;border-radius:var(--r-md);background:url("/mark-nyx.png") center/cover no-repeat,var(--bg1);box-shadow:var(--shadow-brand);font-size:0;color:transparent;flex:0 0 auto}
/* Fond étoilé animé de l'écran de connexion. C'est un <img> et non un
   background-image : dans un <img>, l'animation CSS embarquée dans le SVG est
   garantie de tourner, ce qui n'est pas vrai d'une image de fond selon le moteur.
   aria-hidden + alt vide : c'est un décor, un lecteur d'écran n'a rien à en dire. */
.login-bg{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:0;pointer-events:none;user-select:none}
.login-card{position:relative;z-index:1}
/* marque Nyx sur l'écran de connexion, même fichier source que le .mark 36px (voir design/assets/brand/nyx-mark.svg) */
.login-logo{width:112px;height:112px;border-radius:28px;object-fit:cover;display:block;margin:0 0 16px;box-shadow:0 12px 44px rgba(124,58,237,.42);border:1px solid var(--line)}
h1{font-size:16px;margin:0;font-weight:700}h1 .g{background:var(--grad-accent);-webkit-background-clip:text;background-clip:text;color:transparent}
.pill{margin-left:auto;font-weight:700;font-size:11px;letter-spacing:.5px;padding:6px 11px;border-radius:999px;border:1px solid var(--line)}
.weekchip{display:none;align-items:center;gap:5px;margin-left:8px;font-weight:600;font-size:11px;padding:5px 10px;border-radius:999px;border:1px solid var(--line);background:var(--card);color:var(--dim);cursor:pointer;white-space:nowrap}
.weekchip b{color:var(--ink);font-weight:700}
.weekchip.w-ok b{color:var(--green)}.weekchip.w-warn b{color:var(--amber)}.weekchip.w-hot b{color:var(--red)}
.b-RUNNING{background:rgba(52,211,153,.13);color:var(--green)}.b-DONE{background:rgba(96,165,250,.15);color:var(--blue)}.b-IDLE{background:rgba(255,255,255,.06);color:var(--dim)}.b-STUCK{background:rgba(251,113,133,.15);color:var(--red)}
/* état de publication de la branche du chat */
.pub{display:inline-block;font-size:10.5px;font-weight:700;letter-spacing:.3px;padding:2px 7px;border-radius:999px;border:1px solid var(--line);vertical-align:middle}
.pub-merged{background:rgba(52,211,153,.13);color:var(--green)}
.pub-pending{background:rgba(251,191,36,.13);color:var(--amber)}
.pub-conflict,.pub-rejected{background:rgba(251,113,133,.15);color:var(--red)}
.pub-failed{background:rgba(255,255,255,.06);color:var(--dim)}
/* LE VOILE DES DEUX COLONNES LATÉRALES (2026-09-11). Depuis que le fond du cockpit est un ciel
   étoilé (sky.js), il passe DERRIÈRE tout, y compris sous la liste des chats et sous la colonne
   Équipe — deux surfaces de texte dense qu'on lit toute la journée. Sans voile, les étoiles montent
   entre les lignes et les trois zones de l'écran cessent de se distinguer les unes des autres.
   Ce voile les sépare et calme le ciel là où il y a à lire, tout en le laissant pleinement visible
   au centre, qui est l'endroit où il a quelque chose à raconter.
   Un fond semi-opaque et PAS un `backdrop-filter` : un flou sur une colonne pleine hauteur est un
   filtre recalculé à chaque image, et c'est exactement ce qui a fait tomber le ciel à 34 images par
   seconde pendant sa conception (cf. l'en-tête de design/assets/brand/sky.mjs). */
#tabs,#live{background:rgba(7,7,17,.46)}

/* barre de chats */
#tabs{display:flex;gap:7px;padding:9px 12px;overflow-x:auto;border-bottom:1px solid var(--line);scrollbar-width:none}
#tabs::-webkit-scrollbar{display:none}
.tab{flex:0 0 auto;display:flex;align-items:center;gap:6px;padding:6px 11px;border-radius:999px;border:1px solid var(--line);background:var(--card);font-size:12.5px;color:var(--dim);cursor:pointer;white-space:nowrap;max-width:200px;overflow:hidden}
.tab.on{border-color:var(--violet);color:var(--ink);background:rgba(124,58,237,.16)}
/* Le même balisage sert aux deux modes : en pilule horizontale (mobile) les conteneurs .stxt/.smeta
   s'effacent (display:contents) et tout redevient inline ; en sidebar desktop ils reprennent leur
   rôle de bloc titre + ligne de méta (cf. @media min-width:901px). */
.tab .stxt,.tab .smeta{display:contents}
.tab .stitle{color:var(--ink);font-weight:600;overflow:hidden;text-overflow:ellipsis;flex:0 1 auto;min-width:0}
.sini{display:none}
/* méta discrète dans un onglet (auteur, alerte contexte). Ex-`.tab .proj` : le projet unique d'un fil
   n'existe plus, un fil porte 0..N repos rendus en puces `.rchip`. */
.tab .tmeta{font-size:10.5px;color:#9398b4;flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tab .pub{flex:0 0 auto}
/* Puce de repo : un fil affiche ce qu'il a TOUCHÉ (une puce par repo lié), avec son accès et son état de
   publication. Un fil sans repo n'affiche rien — c'est l'état normal d'une conversation. */
.rchip{display:inline-flex;align-items:center;gap:3px;flex:0 0 auto;font-size:10px;font-weight:600;line-height:1.6;padding:0 6px;border-radius:999px;border:1px solid rgba(124,58,237,.45);background:rgba(124,58,237,.14);color:#c4b5fd;max-width:130px;overflow:hidden;white-space:nowrap}
.rchip.ro{border-color:rgba(148,163,184,.35);background:rgba(148,163,184,.12);color:#94a3b8}
.rchip .rk{overflow:hidden;text-overflow:ellipsis;min-width:0}
.rchip .rx{cursor:pointer;opacity:.55;font-weight:700;padding-left:1px}
.rchip .rx:hover{opacity:1;color:var(--red)}
.repo-add{flex:0 0 auto;font:inherit;font-size:10.5px;font-weight:700;line-height:1.6;padding:0 8px;border-radius:999px;border:1px dashed var(--line);background:none;color:var(--dim);cursor:pointer}
.repo-add:hover{border-color:var(--violet);color:var(--ink)}
.repo-pick{display:flex;align-items:center;gap:6px;flex-wrap:wrap;flex:1 1 100%;margin-top:5px;padding-top:6px;border-top:1px dashed var(--line)}
.repo-pick select{font:inherit;font-size:12px;padding:4px 8px;border-radius:8px;border:1px solid var(--line);background:var(--field);color:var(--ink);max-width:190px}
/* MENUS DÉROULANTS — règle unique pour tout le cockpit.
   La liste qui s'ouvre au clic est dessinée par le SYSTÈME, pas par la page : elle ne sait pas rendre
   un fond semi-transparent et retombe alors sur du BLANC, sous un texte clair hérité de la page —
   d'où le « écriture blanche sur fond blanc » signalé le 2026-07-31 sur le compte facturé, qui posait
   `background:var(--card)` (rgba blanc à 4 %). Deux exigences, et elles vont ensemble :
     - `color-scheme:dark` : dit au navigateur de dessiner ses widgets natifs en sombre. C'est ce qui
       tient sur macOS/iOS, où même un fond opaque ne repeint pas la popup ;
     - un fond OPAQUE (`--field`, le token des champs de saisie) + des `option` explicitement peintes,
       pour les moteurs qui, eux, respectent le style des options.
   La règle est ici et pas en style inline : le même piège attendait le prochain menu ajouté. */
select{color-scheme:dark}
select option,select optgroup{background:var(--field);color:var(--ink)}
/* compte facturé d'une automatisation (panneau Automatisations) */
.billsel{font:inherit;padding:2px 6px;border-radius:var(--r-xs);border:1px solid var(--line);background:var(--field);color:var(--ink);max-width:100%}
.repo-pick button{font:inherit;font-size:12px;font-weight:600;padding:4px 11px;border-radius:8px;border:1px solid var(--violet);background:rgba(124,58,237,.16);color:var(--ink);cursor:pointer}
.repo-pick button.ghost{border-color:var(--line);background:none;color:var(--dim);font-weight:500}
/* sélecteur de compte Claude (token de session) — pastille violette compacte dans le header */
.acct-sel{flex:0 0 auto;appearance:none;-webkit-appearance:none;padding:6px 26px 6px 11px;border-radius:999px;border:1px solid var(--violet);background:rgba(124,58,237,.16) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%23a78bfa' fill='none' stroke-width='1.5'/%3E%3C/svg%3E") no-repeat right 9px center;color:var(--ink);font-size:12px;font-weight:600;cursor:pointer;outline:0;max-width:168px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.acct-sel option{background:var(--bg1);color:var(--ink)}
.acct-i{flex:0 0 auto;margin-left:6px;width:20px;height:20px;border-radius:999px;border:1px solid var(--violet);background:rgba(124,58,237,.16);color:var(--violet);font-size:12px;font-weight:700;font-style:italic;line-height:1;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;padding:0;font-family:Georgia,'Times New Roman',serif}
.acct-i:hover{background:rgba(124,58,237,.3)}
/* Prêt de compte, à même le header : le bouton n'apparaît QUE lorsque le compte affiché par le
   sélecteur est LE MIEN — il agit sur ce compte-là, et sur aucun autre. Vert = déjà prêté (c'est un
   état, pas une action), violet = privé, comme le sélecteur à côté. */
#acctInfo{position:fixed;inset:0;z-index:90;display:none;align-items:center;justify-content:center;background:rgba(3,3,10,.62);backdrop-filter:blur(4px);padding:20px}
#acctInfo.on{display:flex}
#acctInfo .card{max-width:440px;width:100%;background:var(--grad-surface);border:1px solid var(--line);border-radius:var(--r-lg);box-shadow:var(--shadow-drawer);padding:20px 20px 18px;max-height:86vh;overflow:auto}
#acctInfo h3{margin:0 0 6px;font-size:16px}
#acctInfo p,#acctInfo li{color:var(--dim);font-size:13px;line-height:1.6}
#acctInfo ol{margin:12px 0 0;padding-left:20px}
#acctInfo li{margin-bottom:9px}
#acctInfo code{background:var(--field);border:1px solid var(--line);border-radius:6px;padding:1px 5px;font-size:12px;color:var(--ink)}
#acctInfo .note{margin-top:14px;font-size:12px;color:var(--faint);border-top:1px solid var(--line);padding-top:10px}
#acctInfo .x{float:right;background:none;border:none;color:var(--dim);font-size:22px;cursor:pointer;line-height:1;margin:-6px -4px 0 0}
#acctInfo textarea{width:100%;box-sizing:border-box;margin-top:10px;min-height:64px;resize:vertical;background:var(--field);border:1px solid var(--line);border-radius:10px;color:var(--ink);font-size:12px;font-family:ui-monospace,monospace;padding:9px 10px}
#acctInfo .row2{display:flex;gap:8px;margin-top:10px}
#acctInfo .btn-primary{flex:1;height:42px;border:0;border-radius:11px;background:var(--grad-action);color:#fff;font-weight:700;cursor:pointer}
#acctInfo .btn-primary:disabled{opacity:.5;cursor:default}
#acctInfo .btn-ghost{height:42px;padding:0 14px;border:1px solid var(--line);border-radius:11px;background:none;color:var(--dim);cursor:pointer}
/* Prêt de compte : geste distinct de la liaison (il engage TON forfait pour les autres), donc séparé
   par un filet et libellé en toutes lettres — jamais un interrupteur muet à côté de « Délier ». */
#acctInfo .sharebox{margin-top:12px;border-top:1px solid var(--line);padding-top:12px}
#acctInfo .btn-ghost.wide{width:100%}
/* `flex:1` du .btn-primary ne s'applique que dans .row2 : hors d'un conteneur flex il rendrait un
   bouton à la largeur du texte, dépareillé sous le bouton pleine largeur juste au-dessus. */
#acctInfo .btn-primary.wide{width:100%;margin-top:10px}
#acctInfo .sharenote{margin:8px 0 0;font-size:12px;color:var(--faint)}
#acctInfo details{margin-top:14px;border-top:1px solid var(--line);padding-top:10px}
#acctInfo summary{cursor:pointer;color:var(--dim);font-size:12.5px;font-weight:600}
#acctInfo .msg{margin-top:10px;font-size:12px;min-height:16px}
.sdot{width:7px;height:7px;border-radius:50%;flex:0 0 7px}
/* Deux gestes de rangement, empilés : la croix MASQUE (réversible, local), la poubelle SUPPRIME
   (définitif, côté daemon, avec confirmation). La poubelle vivait avant sous le pouce en haut, ce qui
   la rendait introuvable quand on cherchait juste à faire de la place. */
/* EN RANGÉE dans la barre horizontale, EN COLONNE dans la colonne de gauche (règle plus bas). Une
   troisième action est arrivée avec les dossiers : empilée, elle épaississait la barre du mobile
   d'un tiers (mesuré à 390px, pilules de 44 à 50px de haut). */
.tab .tabacts{display:flex;flex-direction:row;align-items:center;gap:2px;margin-left:2px;flex:0 0 auto}
.tab .tabacts .tabx,.tab .tabacts .tabdel,.tab .tabacts .tabfld{padding:5px 4px}
.tab .tabx,.tab .tabdel,.tab .tabfld{color:#9398b4;font-size:11px;opacity:.7;padding:0 3px;border-radius:5px;cursor:pointer;flex:0 0 auto;line-height:1}
.tab .tabx:hover,.tab .tabfld:hover{opacity:1;color:#fff;background:rgba(255,255,255,.12)}
.tab .tabdel:hover{opacity:1;color:var(--red);background:rgba(251,113,133,.16)}
.tab.hiddent{opacity:.55;border-style:dashed}
/* Entrée vers l'Historique, au pied de la liste des chats (elle a remplacé le bouton de la barre du
   haut et la pastille des masqués). Pilule pleine et non pointillée : c'est un ACCÈS permanent, pas
   un raccourci conditionnel. */
.tab.histtab{color:var(--ink);font-weight:600;cursor:pointer}
.tab.histtab:hover{border-color:var(--violet)}
.tab.newconv{background:rgba(124,58,237,.18);border-color:var(--violet);color:var(--ink);font-weight:700}
/* ---- Dossiers en barre HORIZONTALE (sous 901px) : une pilule par dossier, dépliable sur place.
   Le glisser-déposer n'existe pas ici (on range au doigt par l'icône dossier d'un chat), mais voir
   ses dossiers et les ouvrir, si : le rangement est le même sur tous les appareils, l'affichage non. */
.fldr{flex:0 0 auto;display:flex;align-items:center;gap:5px;padding:6px 10px;border-radius:999px;
      border:1px dashed var(--line);background:var(--card);font-size:12.5px;color:var(--ink);cursor:pointer;white-space:nowrap}
.fldr.open{border-style:solid;border-color:var(--violet)}
.fldr .fchev{display:none}
.fldr .fico{display:grid;place-items:center;color:var(--dim)}
.fldr .fn{padding:0 5px;border-radius:6px;background:var(--card2);color:#9398b4;font-size:10.5px}
.fldr .tabacts{flex-direction:row;gap:2px}
/* On n'entre que dans UN dossier à la fois : sa pilule de tête ramène à toutes les conversations. */
.tab.fldback{background:rgba(124,58,237,.18);border-color:var(--violet);color:var(--ink);font-weight:700}
.fldbody{display:contents} /* jamais déplié en rangée : la pilule du dossier sert d'entrée */
.fldempty,.fldmore{flex:0 0 auto;align-self:center;color:var(--faint);font-size:11px;white-space:nowrap}
.fldmore{cursor:pointer;text-decoration:underline dotted}
/* ---- Menu « Ranger dans… ». Il vit sur <body>, hors de #tabs : le poll reconstruit la liste toutes
   les 4 s et emporterait le menu ouvert avec elle. ---- */
.fldmenu{position:fixed;z-index:120;min-width:210px;max-width:min(280px,92vw);max-height:60vh;overflow:auto;
         padding:5px;border:1px solid var(--line);border-radius:12px;background:var(--card);
         box-shadow:0 18px 40px rgba(0,0,0,.45)}
.fldmenu .fmt{padding:6px 8px 7px;font-size:10.5px;color:var(--faint);border-bottom:1px solid var(--line);
              margin-bottom:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.fldmenu .fmi{display:flex;align-items:center;gap:8px;width:100%;min-height:38px;padding:7px 9px;border:0;
              border-radius:9px;background:none;color:var(--ink);font:inherit;font-size:12.5px;text-align:left;cursor:pointer}
.fldmenu .fmi span{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.fldmenu .fmi:hover{background:var(--card2)}
.fldmenu .fmi.on{color:var(--violet);font-weight:700}
.kcard.clickable{cursor:pointer}.kcard.clickable:hover{border-color:var(--violet)}
.kcard .krow{display:flex;align-items:center;gap:8px}
.hsearch{width:100%;box-sizing:border-box;margin-bottom:6px;padding:8px 11px;border-radius:10px;border:1px solid var(--line);background:var(--card);color:var(--ink);font-size:13px;outline:0}
/* Extrait du message qui matche, dans une carte de l'Historique. Bridé à trois lignes : au-delà, une
   seule conversation remplit l'écran et on ne peut plus comparer les résultats entre eux. */
.hsnip{display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;
       margin:5px 0 2px;font-size:11.5px;line-height:1.45;color:var(--dim)}
.hsnip b{color:var(--faint)}
.kcard mark,.hsnip mark{background:rgba(124,58,237,.32);color:var(--ink);border-radius:3px;padding:0 1px}
.hidetag{font-size:9.5px;font-weight:700;padding:1px 6px;border-radius:5px;background:rgba(255,255,255,.08);color:var(--faint)}
.s-running{background:var(--green);box-shadow:0 0 7px var(--green)}.s-idle{background:var(--faint)}.s-stuck{background:var(--red);box-shadow:0 0 7px var(--red)}.s-queued{background:var(--amber)}.s-done{background:var(--blue)}
/* pastilles d'état d'une conversation : 🟢 fini · 🟠 en cours · 🔴 problème (stuck) */
.c-done{background:var(--green);box-shadow:0 0 6px rgba(52,211,153,.55)}.c-busy{background:var(--amber);box-shadow:0 0 7px var(--amber);animation:cpulse 1.4s ease-in-out infinite}.c-stuck{background:var(--red);box-shadow:0 0 7px var(--red);animation:cpulse 1.4s ease-in-out infinite}
@keyframes cpulse{0%,100%{opacity:1}50%{opacity:.45}}
/* pastilles de santé (panneau Statut système) */
.s-up{background:var(--green);box-shadow:0 0 7px var(--green)}.s-lag{background:var(--amber);box-shadow:0 0 7px var(--amber)}.s-down{background:var(--red);box-shadow:0 0 7px var(--red)}.s-unknown{background:var(--faint)}
/* ---- bannière système (#sysbar, cf. renderSysBar dans 04-fil.js) -------------------------------
   Entre le header et le shell, dans le flux : elle pousse le contenu au lieu de le recouvrir, donc
   elle ne masque jamais un message et ne peut pas défiler hors de l'écran. `flex:0 0 auto` sur un
   body en colonne : elle prend sa hauteur, le shell garde le reste. Le JS pose display:flex et la
   classe de ton (amber/red/green) ; sans ton, elle est masquée.
   min-width:0 sur le bloc de texte, sinon un sha ou un nom de module long pousse le bouton hors de
   l'écran au lieu de passer à la ligne. Le bouton reste au bout sur une ligne large, et passe
   dessous en mobile (flex-wrap). */
#sysbar{flex:0 0 auto;z-index:29;display:none;align-items:center;flex-wrap:wrap;gap:8px 12px;
  padding:9px max(14px,env(safe-area-inset-left)) 9px max(14px,env(safe-area-inset-right));
  border-bottom:1px solid var(--line);font-size:12.5px;line-height:1.45}
#sysbar .sb-txt{flex:1 1 260px;min-width:0}
#sysbar .sb-l1{font-weight:600}
/* Ligne 2 : la conséquence pour le chat ouvert. Volontairement plus discrète que la ligne 1 —
   l'alerte est l'état système, celle-ci n'en est que la traduction locale. */
#sysbar .sb-l2{margin-top:2px;font-weight:400;color:var(--dim)}
#sysbar .sb-act{flex:0 0 auto;padding:5px 13px;min-height:32px;border-radius:9px;border:1px solid currentColor;
  background:transparent;color:inherit;font:inherit;font-size:12.5px;font-weight:600;cursor:pointer}
#sysbar .sb-act:hover{background:rgba(255,255,255,.07)}
/* Mobile : le texte du moteur périmé fait cinq lignes sur 390 px et mangerait le haut du fil. On le
   borne à DEUX lignes — pas une : la première s'arrête au milieu de la liste des modules, avant
   l'ancienneté (« depuis 42 min »), qui est justement ce qui fait décider de reprendre le moteur ou
   d'attendre. Le reste s'ouvre d'un tap sur le texte (panneau Statut, cf. renderSysBar). */
@media (max-width:640px){
  #sysbar .sb-txt{cursor:pointer}
  #sysbar .sb-l1{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
}
#sysbar.amber{color:var(--amber);background:rgba(251,191,36,.12);border-bottom-color:rgba(251,191,36,.30)}
#sysbar.red{color:var(--red);background:rgba(251,113,133,.13);border-bottom-color:rgba(251,113,133,.32)}
#sysbar.green{color:var(--green);background:rgba(52,211,153,.12);border-bottom-color:rgba(52,211,153,.30)}
/* Coquille sous le header : en mobile tout s'empile (barre de chats horizontale au-dessus du fil),
   en desktop (>=901px) la barre de chats devient la colonne de gauche — cf. le bloc sidebar en fin de CSS. */
#shell{flex:1;display:flex;flex-direction:column;min-height:0;min-width:0}
#sbgrip{display:none}
#main{flex:1;display:flex;flex-direction:column;min-height:0;min-width:0}
#threadWrap{flex:1;position:relative;display:flex;flex-direction:column;min-height:0}
#thread{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:14px}
#jumpBtn{position:absolute;right:16px;bottom:16px;z-index:20;display:none;align-items:center;gap:7px;background:var(--violet);color:#fff;border:none;border-radius:999px;padding:8px 14px;font:inherit;font-size:13px;font-weight:600;cursor:pointer;box-shadow:0 6px 20px rgba(0,0,0,.4);transition:filter .15s,transform .15s}
#jumpBtn:not(.hasnew){padding:9px 11px}
#jumpBtn:not(.hasnew) .jbTxt{display:none}
#jumpBtn:hover{filter:brightness(1.1)}
#jumpBtn:active{transform:translateY(1px)}
#scrollDateBubble{position:absolute;top:10px;left:50%;transform:translateX(-50%);background:rgba(30,20,50,.72);color:#e8e0f8;font-size:12px;font-weight:500;padding:4px 13px;border-radius:12px;pointer-events:none;z-index:15;opacity:0;transition:opacity .25s ease;white-space:nowrap;backdrop-filter:blur(4px)}
#scrollDateBubble.vis{opacity:1}
#jumpBtn .arw{font-size:15px;line-height:1;font-weight:700}
.msg-wrap{display:flex;flex-direction:column;width:fit-content}
.msg-wrap.me{align-self:flex-end;align-items:flex-end;max-width:86%}
.msg-wrap.nyx{align-self:flex-start;align-items:flex-start;max-width:86%}
.msg-wrap.router{align-self:flex-start;align-items:flex-start}
.msg{padding:10px 14px;border-radius:18px;white-space:pre-wrap;word-break:break-word;font-size:14.5px;line-height:1.45}
.msg.me{background:var(--grad-me);color:#fff;border-bottom-right-radius:4px}
.msg.nyx{background:var(--card2);border:1px solid var(--line);border-bottom-left-radius:4px}
.msg.router{color:var(--dim);font-size:12.5px;font-style:italic;padding:3px 14px;border:0;background:transparent}
/* ÉVÉNEMENT D'ACQUISITION DE REPO (reply kind:"acquire"). Un tour qui n'a fait que demander un repo au
   daemon n'est pas une réponse : c'est une étape, suivie d'un réveil automatique où le vrai travail est
   fait. Rendu en une ligne discrète. Cette forme ne vaut QUE pour l'annonce seule (cf. acqEnLigne dans
   04-fil.js) : un tour qui a acquis un repo ET travaillé reste une bulle pleine, jamais un repli. */
.msg-wrap.acq{align-self:flex-start;align-items:flex-start;max-width:86%}
.msg.acq{color:var(--dim);font-size:12.5px;padding:3px 14px;border:0;background:transparent;white-space:normal}
.tag{display:inline-block;font-size:10px;font-weight:700;letter-spacing:.3px;color:var(--violet);background:rgba(124,58,237,.14);border-radius:5px;padding:1px 6px;margin-bottom:5px}
.t{font-size:11px;color:var(--faint);opacity:.85;margin-top:4px;padding:0 4px;line-height:1.3}
/* Barre de retour sous une réponse de Nyx (miroir des apps Android / iOS) : pouce haut / pouce bas,
   puis Fable / ramifier / détail / lecture sous la DERNIÈRE réponse seulement. Un pouce est un verdict
   enregistré sur le bus, pas un message envoyé dans le fil — il ne coûte rien et vaut sur n'importe
   quel tour, d'où sa présence sous chacun. */
.msg-acts{display:flex;flex-wrap:wrap;align-items:center;gap:2px;margin-top:1px;padding:0 1px;min-width:0}
/* Rangées des réponses PLUS ANCIENNES : présentes mais effacées, sinon deux icônes sous chaque bulle
   noieraient le fil. Elles reviennent au survol, et restent visibles si un verdict y est posé (`:has`)
   — un vote déjà donné doit se voir sans avoir à chercher. */
.msg-acts.dim{opacity:.28;transition:opacity .15s}
.msg-wrap:hover .msg-acts.dim,.msg-acts.dim:focus-within,.msg-acts.dim:has(.on-up),.msg-acts.dim:has(.on-down){opacity:1}
.msg-act{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;width:34px;height:34px;padding:0;border:0;border-radius:var(--r-sm);background:none;color:var(--faint);cursor:pointer;transition:background .15s,color .15s}
.msg-act svg{width:18px;height:18px;display:block;fill:currentColor;pointer-events:none}
.msg-act:hover{background:var(--card2);color:var(--ink)}
.msg-act.on-up{color:var(--violet)}
.msg-act.on-down{color:var(--red)}
.msg-act.on-tts{color:var(--violet)}
/* La voix de Nyx se prépare (un aller-retour à /api/voice, ~1 s) : le bouton pulse. Sans ce signal,
   un clic reste sans effet visible pendant une seconde et on reclique — donc on paie deux fois. */
.msg-act.tts-busy{animation:ttsPulse 1s ease-in-out infinite}
@keyframes ttsPulse{0%,100%{opacity:1}50%{opacity:.35}}
.msg-act.on-branch{color:var(--cyan)}
.msg-act.on-fable{color:var(--cyan)}
.msg-act:disabled,.msg-act:disabled:hover{cursor:default;opacity:.4;background:none}
/* Le POURQUOI d'un verdict : apparaît une fois un pouce posé, jamais avant. Rétrécissable et tronqué
   (min-width:0 + ellipsis) : une raison un peu longue doit se couper, pas pousser les pouces hors de
   l'écran sur un mobile étroit. */
.msg-why{flex:0 1 auto;min-width:0;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-left:4px;padding:0 8px;height:26px;border:1px dashed var(--line);border-radius:var(--r-sm);background:none;color:var(--faint);font:inherit;font-size:11.5px;cursor:pointer;transition:color .15s,border-color .15s}
.msg-why:hover{color:var(--ink);border-color:var(--dim)}
/* Pointeur grossier (mobile/tablette) : cibles tactiles à 44px, jamais des icônes de 34px au pouce.
   Les rangées `dim` y sont RELEVÉES : sans survol, .28 rendrait les pouces des anciennes réponses
   quasi invisibles et surtout indécouvrables — le geste n'existerait que sur ordinateur. */
@media (hover:none){.msg-acts{gap:0;margin-top:0}.msg-acts.dim{opacity:.6}.msg-act{width:44px;height:44px}.msg-why{height:32px}.msg-act:hover{background:none;color:var(--faint)}.msg-act.on-up:hover{color:var(--violet)}.msg-act.on-down:hover{color:var(--red)}.msg-act.on-tts:hover{color:var(--violet)}.msg-act.on-branch:hover{color:var(--cyan)}.msg-act.on-fable:hover{color:var(--cyan)}}
/* « Détail de la réponse » (bouton i) */
/* Fenêtre du verdict d'un pouce (raison + portée). Même coquille que #msgInfo : une seule idée de
   ce qu'est un modal centré dans ce cockpit, sinon les deux dérivent au premier ajustement. */
#fbAsk{position:fixed;inset:0;z-index:96;display:none;align-items:center;justify-content:center;background:rgba(3,3,10,.62);backdrop-filter:blur(4px);padding:20px}
#fbAsk.on{display:flex}
#fbAsk .card{max-width:440px;width:100%;background:var(--grad-surface);border:1px solid var(--line);border-radius:var(--r-lg);box-shadow:var(--shadow-drawer);padding:18px}
#fbAsk h3{margin:0 0 10px;font-size:16px}
#fbAsk .x{float:right;background:none;border:none;color:var(--dim);font-size:22px;cursor:pointer;line-height:1;margin:-6px -4px 0 0;padding:0 6px}
#fbAsk textarea{width:100%;box-sizing:border-box;background:rgba(255,255,255,.05);border:1px solid var(--line);border-radius:10px;color:var(--ink);padding:10px;font:inherit;font-size:13px;resize:vertical}
.fb-scope{display:flex;flex-direction:column;gap:8px;margin:12px 0 4px}
.fb-scope label{display:flex;gap:9px;align-items:flex-start;cursor:pointer;font-size:13px;line-height:1.35}
.fb-scope input{margin-top:3px;flex:0 0 auto}
.fb-scope b{display:block;color:var(--ink);font-weight:600}
.fb-scope em{display:block;color:var(--dim);font-style:normal;font-size:11.5px}
/* Cible MONTRÉE mais éteinte (aucun projet à viser), même parti pris que .dec-opt:disabled dans
   Décisions : la masquer ferait chercher pourquoi elle manque ici et pas sur le message d'à côté. */
.fb-scope label.off{opacity:.42;cursor:not-allowed}
.fb-err{min-height:16px;margin:6px 0 0;font-size:12px;color:#fb7185}
.fb-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:10px}
#msgInfo{position:fixed;inset:0;z-index:95;display:none;align-items:center;justify-content:center;background:rgba(3,3,10,.62);backdrop-filter:blur(4px);padding:20px}
#msgInfo.on{display:flex}
#msgInfo .card{max-width:420px;width:100%;background:var(--grad-surface);border:1px solid var(--line);border-radius:var(--r-lg);box-shadow:var(--shadow-drawer);padding:18px 18px 16px;max-height:86vh;overflow:auto}
#msgInfo h3{margin:0 0 8px;font-size:16px}
#msgInfo .x{float:right;background:none;border:none;color:var(--dim);font-size:22px;cursor:pointer;line-height:1;margin:-6px -4px 0 0;padding:0 6px}
.mi-row{display:flex;align-items:baseline;justify-content:space-between;gap:12px;min-width:0;padding:8px 0;border-bottom:1px solid var(--line);font-size:13px}
.mi-row span{color:var(--dim);flex:0 0 auto}
.mi-row b{color:var(--ink);font-weight:600;text-align:right;flex:0 1 auto;min-width:0;overflow-wrap:anywhere}
.mi-note{margin-top:12px;font-size:12px;color:var(--faint);line-height:1.5}
.msg a{color:var(--violet);text-decoration:underline;text-underline-offset:2px;word-break:break-all}
.msg a:hover{opacity:.8}
.msg img{max-width:200px;border-radius:10px;margin-bottom:6px;display:block}
/* LES PIÈCES JOINTES D'UN MESSAGE QU'ON VIENT D'ENVOYER, ET LEUR ANNEAU DE TÉLÉVERSEMENT.
   Tant qu'un fichier n'est pas en ligne, il est voilé et porte son anneau ; l'anneau qui disparaît
   EST le signal « c'est prêt ». Un document (PDF, vocal) n'a pas d'aperçu : il prend la même place,
   en pastille nom + icône, pour qu'il ait lui aussi une trace à l'écran pendant son envoi. */
.msg .pieces{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:6px}
.msg .piece{position:relative;display:inline-flex;align-items:center;gap:6px;line-height:0}
.msg .piece img{margin-bottom:0}
.msg .piece.doc{line-height:1.2;font-size:12.5px;padding:7px 11px;border-radius:var(--r-sm);border:1px solid var(--line);background:var(--card2);max-width:220px}
.msg .piece.doc .nom{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Le voile pendant l'envoi : assez pour qu'on lise « pas encore parti », pas assez pour cacher
   l'image qu'on vient de choisir. */
.msg .piece.envol img{opacity:.42}
.msg .piece.doc.envol{opacity:.75;padding-right:46px}
/* La part FAITE est en cyan et la part restante en noir profond : la première version opposait
   `--violet` à un blanc translucide, et sur une image violette (l'aperçu d'une photo, le fond d'une
   bulle) les deux se confondaient — la capture de contrôle montrait un anneau uniforme dont on ne
   pouvait pas lire la fraction. L'épaisseur (7px) vient du même constat : à 3px il n'y avait rien à
   lire. */
.up-ring{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:44px;height:44px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;box-shadow:0 2px 10px rgba(0,0,0,.45);
  background:conic-gradient(var(--cyan) calc(var(--p,0) * 1%),rgba(7,7,17,.72) 0)}
.msg .piece.doc .up-ring{left:auto;right:9px;transform:translateY(-50%);width:30px;height:30px}
.msg .piece.doc .up-ring i{font-size:9px;letter-spacing:-.5px}
.up-ring::after{content:'';position:absolute;inset:7px;border-radius:50%;background:rgba(7,7,17,.9)}
.msg .piece.doc .up-ring::after{inset:5px}
.up-ring i{position:relative;z-index:1;font-style:normal;font-size:10.5px;font-weight:700;color:var(--ink);letter-spacing:-.3px}
/* Avant le premier octet confirmé il n'y a aucune fraction à montrer, et un anneau vide immobile se
   lit comme « rien ne se passe » : il tourne alors, et se fige en jauge dès qu'un pourcentage arrive. */
.up-ring.wait{background:conic-gradient(var(--cyan) 28%,rgba(7,7,17,.72) 0);animation:upSpin .9s linear infinite}
@keyframes upSpin{to{transform:translate(-50%,-50%) rotate(360deg)}}
.msg .piece.doc .up-ring.wait{animation-name:upSpinDoc}
@keyframes upSpinDoc{to{transform:translateY(-50%) rotate(360deg)}}
/* Mouvement réduit : l'anneau ne tourne plus mais reste visible — c'est lui qui porte l'information,
   la rotation n'en est que l'accent. */
@media (prefers-reduced-motion:reduce){.up-ring.wait{animation:none}}
.msg img.genimg{max-width:min(360px,100%);max-height:380px;width:auto;height:auto;cursor:zoom-in;border:1px solid var(--line)}
/* Une vidéo publiée dans une réponse : mêmes bornes qu'une image, pour qu'une bulle garde sa forme
   quelle que soit la définition du fichier. Le curseur ne dit PAS « zoom » : ici on lit sur place,
   avec les commandes du lecteur. */
.msg video.genvid{max-width:min(360px,100%);max-height:380px;width:auto;height:auto;border:1px solid var(--line);border-radius:8px;background:#000;display:block}
/* Bouton doc cliquable dans le chat (ouvre la preview) */
.doc-btn{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;margin:3px 0;border:1px solid var(--line);border-radius:var(--r-sm);background:var(--card2);color:var(--ink);text-decoration:none;font-size:13px;font-weight:600;cursor:pointer}
.doc-btn:hover{border-color:var(--violet);background:rgba(124,58,237,.14);opacity:1}
/* Bouton « ouvrir ce chat » dans une bulle (lien nyxthread:, posé par /branch). Cyan = ramification,
   comme le pouce de branche. Cible tactile 44px au doigt, plus compacte à la souris. */
/* `.msg a` (violet souligné, break-all) l'emporterait sur `.thread-btn` seul : on qualifie donc la
   règle pour qu'un bouton reste un bouton dans une bulle — pas de soulignement, et un libellé coupé
   aux espaces plutôt qu'en plein milieu d'un mot si la largeur se resserre. */
.thread-btn,.msg a.thread-btn{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;margin:3px 0;min-height:34px;border:1px solid var(--cyan);border-radius:var(--r-sm);background:rgba(34,211,238,.12);color:var(--ink);text-decoration:none;font-size:13px;font-weight:600;cursor:pointer;word-break:normal;overflow-wrap:anywhere}
.thread-btn:hover,.msg a.thread-btn:hover{background:rgba(34,211,238,.24);opacity:1}
@media (hover:none){.thread-btn,.msg a.thread-btn{min-height:44px;padding:10px 14px}.thread-btn:hover,.msg a.thread-btn:hover{background:rgba(34,211,238,.12)}}
/* Titres markdown rendus (chat + preview) */
.md-h{font-weight:700;color:var(--ink);line-height:1.25;margin:14px 0 6px}
.md-h1{font-size:22px;border-bottom:1px solid var(--line);padding-bottom:6px}
.md-h2{font-size:18px}.md-h3{font-size:16px;color:var(--violet)}.md-h4,.md-h5,.md-h6{font-size:14px;color:var(--dim)}
/* Trait de separation markdown (---), le meme geste que sur les deux telephones */
.md-hr{height:1px;background:var(--line);border:0;margin:14px 0}
/* Overlay preview document (apercu type VSCode) */
#docov{position:fixed;inset:0;z-index:80;display:none;flex-direction:column;background:rgba(4,4,12,.72);backdrop-filter:blur(6px)}
#docov .docpanel{margin:auto;width:min(820px,94vw);height:min(88vh,900px);display:flex;flex-direction:column;background:var(--grad-surface);border:1px solid var(--line);border-radius:var(--r-lg);box-shadow:var(--shadow-drawer);overflow:hidden}
#docov .dochead{display:flex;align-items:center;gap:10px;padding:12px 16px;border-bottom:1px solid var(--line);flex:0 0 auto}
#docov .dochead b{font-size:14px;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}
#docov .docclose{background:var(--card2);border:1px solid var(--line);color:var(--ink);border-radius:var(--r-sm);width:30px;height:30px;font-size:16px;cursor:pointer;flex:0 0 auto}
#docov .docclose:hover{border-color:var(--violet)}
#docov .docbody{padding:22px 26px;overflow-y:auto;-webkit-overflow-scrolling:touch;font-size:14px;line-height:1.6;color:var(--ink);word-wrap:break-word}
#docov .docbody code{background:rgba(255,255,255,.1);padding:1px 5px;border-radius:5px;font-size:13px}
@media(max-width:640px){#docov .docpanel{width:100vw;height:100vh;border-radius:0;border:0}#docov .docbody{padding:16px 16px}}
/* Pieces jointes cliquables dans une bulle (images -> miniature, autres -> chip ouvrable) */
.atts{display:flex;flex-wrap:wrap;gap:8px;margin-top:6px}
.msg.me .atts{justify-content:flex-end}
.att-img{max-width:min(240px,58vw);max-height:240px;border-radius:10px;object-fit:cover;cursor:pointer;border:1px solid rgba(255,255,255,.16);display:block;background:rgba(255,255,255,.05)}
.att-file{display:inline-flex;align-items:center;gap:8px;max-width:240px;padding:8px 12px;border-radius:10px;border:1px solid rgba(255,255,255,.2);background:rgba(255,255,255,.09);color:#fff;font:inherit;font-size:13px;font-weight:600;cursor:pointer;text-align:left}
.att-file:hover{border-color:#fff;background:rgba(255,255,255,.14)}
.att-file .att-ico{font-size:18px;flex:0 0 auto;line-height:1}
.att-file .att-nm{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Visualiseur de piece jointe (image / video / audio / pdf) plein ecran */
#mediaov{position:fixed;inset:0;z-index:85;display:none;flex-direction:column;background:rgba(4,4,12,.86);backdrop-filter:blur(6px)}
#mediaov .mpanel{margin:auto;width:min(1000px,96vw);height:min(92vh,960px);display:flex;flex-direction:column;background:var(--grad-surface);border:1px solid var(--line);border-radius:var(--r-lg);box-shadow:var(--shadow-drawer);overflow:hidden}
#mediaov .mhead{display:flex;align-items:center;gap:10px;padding:12px 16px;border-bottom:1px solid var(--line);flex:0 0 auto}
#mediaov .mhead b{font-size:14px;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}
#mediaov .mclose{background:var(--card2);border:1px solid var(--line);color:var(--ink);border-radius:var(--r-sm);width:30px;height:30px;font-size:16px;cursor:pointer;flex:0 0 auto}
#mediaov .mclose:hover{border-color:var(--violet)}
#mediaov .mbody{flex:1;display:flex;overflow:auto;padding:14px;min-height:0}
#mediaov .mbody img,#mediaov .mbody video{margin:auto;max-width:100%;max-height:100%;object-fit:contain;border-radius:8px}
#mediaov .mbody audio{margin:auto;width:min(92%,520px)}
#mediaov .mbody iframe{flex:1;border:0;width:100%;height:100%;background:#fff;border-radius:8px}
@media(max-width:640px){#mediaov .mpanel{width:100vw;height:100vh;border-radius:0;border:0}}
.md-tblw{overflow-x:auto;margin:8px 0;border:1px solid var(--line);border-radius:10px;-webkit-overflow-scrolling:touch}
.md-tbl{border-collapse:collapse;white-space:normal;font-size:13px;min-width:100%}
.md-tbl th,.md-tbl td{padding:7px 11px;text-align:left;vertical-align:top;border-bottom:1px solid var(--line);border-right:1px solid var(--line)}
.md-tbl th:last-child,.md-tbl td:last-child{border-right:0}
.md-tbl tbody tr:last-child td{border-bottom:0}
.md-tbl thead th{background:rgba(255,255,255,.06);font-weight:700;color:var(--ink);white-space:nowrap}
.md-tbl tbody tr:nth-child(even){background:rgba(255,255,255,.025)}
/* Mobile : on abandonne le scroll horizontal (mauvais en chat) pour un empilement cle/valeur lisible. */
@media (max-width:560px){
  .md-tblw{overflow-x:visible;border:0;margin:8px 0}
  .md-tbl{display:block;font-size:13.5px}
  .md-tbl thead{display:none}
  .md-tbl tbody{display:block}
  .md-tbl tr{display:block;border:1px solid var(--line);border-radius:10px;margin:0 0 8px;background:rgba(255,255,255,.03)}
  .md-tbl tbody tr:nth-child(even){background:rgba(255,255,255,.03)}
  .md-tbl td{display:flex;justify-content:space-between;gap:14px;border:0;border-bottom:1px solid var(--line);padding:6px 11px;text-align:right!important}
  .md-tbl tr td:last-child{border-bottom:0}
  .md-tbl td::before{content:attr(data-label);font-weight:700;color:var(--dim);text-align:left;white-space:nowrap;flex:0 0 auto}
  .md-tbl td:empty{display:none}
}
.think{align-self:flex-start;color:var(--dim);font-size:13px;font-style:italic;padding:6px 14px}
/* Vague : une bande claire balaie les lettres de la ligne « Nyx travaille… », les éclaircit une à une puis
   elles reprennent leur couleur (repris de ShimmerText de nyx-android : base=--dim, highlight=--ink, 1.5s). */
.think .wave{background:linear-gradient(90deg,var(--dim) 40%,var(--ink) 50%,var(--dim) 60%);background-size:300% 100%;-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;color:transparent;animation:nyxwave 1.5s linear infinite}
@keyframes nyxwave{0%{background-position:150% 0}100%{background-position:-150% 0}}
@media (prefers-reduced-motion:reduce){.think .wave{animation:none;-webkit-text-fill-color:var(--dim);color:var(--dim)}}
.stepswrap{align-self:flex-start;padding:2px 14px 0}
/* Appended even when empty (see renderPending, 04-fil.js): until a step lands it must take no height
   whatsoever. */
.stepswrap:empty{padding:0}
.steps{display:flex;flex-direction:column;gap:1px}
.step{color:var(--faint);font-size:12px;font-family:ui-monospace,Menlo,monospace;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:min(70vw,520px)}
.wfs{display:flex;flex-direction:column;gap:4px;margin-top:3px}
.wf{display:flex;flex-direction:column;gap:1px;max-width:min(80vw,520px);border-left:2px solid var(--violet);padding:2px 0 2px 8px;background:linear-gradient(90deg,rgba(124,58,237,.10),transparent)}
.wf .wfhead{font-size:12px;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.wf .wfhead b{color:var(--violet);font-weight:700}
.wf .wfmeta{font-size:11px;color:var(--dim);font-family:ui-monospace,Menlo,monospace;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.wf .wfdot{display:inline-block;width:6px;height:6px;border-radius:999px;background:var(--cyan);box-shadow:0 0 6px var(--cyan);margin-right:5px;animation:cpulse 1.4s ease-in-out infinite}
.wf .wfage{color:var(--faint);font-size:11px;font-family:ui-monospace,Menlo,monospace;margin-left:6px}
.wf .wferr{color:var(--red)}
/* Frise des phases déclarées par le workflow. Aucune n'est « on » tant que le daemon ne SAIT pas
   laquelle tourne : la frise grise dit « deux étapes prévues », jamais « on en est là ». */
.wf .wfph{font-size:11px;margin-top:1px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.wf .wfph .on{color:var(--violet);font-weight:700}
.wf .wfph .off{color:var(--faint)}
.wf .wfph .sep{color:var(--faint);margin:0 5px}
/* Avancement du run (agents rendus + morts / agents vus). Barre plutôt que fraction : on la lit sans
   la lire, au milieu d'une conversation. */
.wf .wfbar{height:3px;border-radius:999px;background:rgba(148,163,184,.18);margin:3px 0 2px;overflow:hidden}
.wf .wfbar i{display:block;height:100%;background:var(--violet);transition:width .4s ease}
.wf .wfags{display:flex;flex-direction:column;gap:1px;margin-top:2px}
/* Une ligne d'agent tient en trois zones : glyphe, libellé, et à droite ce qu'il fait. Seul le LIBELLÉ
   se rétrécit (min-width:0) : l'activité et le chrono sont ce qui bouge, les tronquer d'abord — ce que
   faisait l'ellipsis unique de l'ancienne ligne — revenait à couper la seule information vivante. */
.wf .wfag{display:flex;align-items:baseline;gap:6px;font-size:11px;font-family:ui-monospace,Menlo,monospace;max-width:min(78vw,500px)}
.wf .wfag .g{flex:none;width:9px;text-align:center}
.wf .wfag .n{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.wf .wfag .d{flex:none;margin-left:auto;padding-left:8px;color:var(--faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:52%}
.wf .wfag.run .n{color:var(--cyan)}
.wf .wfag.done .n{color:var(--dim)}
.wf .wfag.err .n{color:var(--red)}
.empty{color:var(--faint);text-align:center;margin:auto;font-size:14px;max-width:320px}
.composer{border-top:1px solid var(--line);padding:10px 12px;padding-bottom:calc(env(safe-area-inset-bottom) + 10px);padding-left:max(12px,env(safe-area-inset-left));padding-right:max(12px,env(safe-area-inset-right));background:rgba(7,7,17,.8);backdrop-filter:blur(12px)}
.target{font-size:11.5px;color:var(--dim);margin-bottom:7px;display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.target b{color:var(--violet)}
/* « 🌿 Ramification » : même gabarit que les boutons de la barre cible, en cyan (la couleur de la
   ramification) — ce n'est pas une action d'arrêt. */
.stopbtn.branchbtn{margin-left:0;color:var(--cyan);background:rgba(34,211,238,.12);border-color:rgba(34,211,238,.45)}
.stopbtn.branchbtn:hover{background:rgba(34,211,238,.24)}
.stopbtn{margin-left:auto;flex:0 0 auto;display:inline-flex;align-items:center;gap:5px;font-size:11.5px;font-weight:700;color:var(--red);background:rgba(251,113,133,.12);border:1px solid rgba(251,113,133,.45);border-radius:999px;padding:3px 11px;cursor:pointer}
.forcebtn{margin-left:6px;color:var(--amber);background:rgba(251,191,36,.10);border-color:rgba(251,191,36,.45)}
.stopbtn:hover{background:rgba(251,113,133,.2)}
.stopbtn:disabled{opacity:.5;cursor:default}
.attach{display:flex;flex-wrap:wrap;align-items:center;gap:8px;font-size:12px;color:var(--cyan);margin-bottom:8px}
.attach .chip{display:inline-flex;align-items:center;gap:6px;max-width:180px;padding:3px 8px 3px 3px;border-radius:9px;background:rgba(34,211,238,.1);border:1px solid rgba(34,211,238,.22)}
.attach .chip>span:not(.x){overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.attach img{width:34px;height:34px;border-radius:7px;object-fit:cover;flex:0 0 auto}
.attach .x{cursor:pointer;color:var(--red);margin-left:2px;flex:0 0 auto}
.dl-btn{display:inline-block;margin:4px 0;padding:6px 12px;border-radius:9px;border:1px solid var(--violet);background:rgba(124,58,237,.16);color:var(--ink);font-size:13px;font-weight:600;text-decoration:none;cursor:pointer}
.dl-btn:hover{background:rgba(124,58,237,.28)}
/* position:relative -> repère d'ancrage de l'infobulle du cadenas (.tip), qui doit rester bornée par le
   champ de saisie quelle que soit la largeur d'écran. */
.row{position:relative;display:flex;align-items:flex-end;gap:8px;background:var(--field);border:1px solid var(--line);border-radius:var(--r-xl);padding:7px 8px 7px 12px}
#box{flex:1;background:transparent;border:0;color:var(--ink);font:15px ui-sans-serif;resize:none;max-height:140px;outline:0;padding:6px 0}
.ic{width:38px;height:38px;border-radius:50%;border:0;display:grid;place-items:center;cursor:pointer;font-size:18px;flex:0 0 38px;background:rgba(255,255,255,.07);color:var(--ink)}
.ic.rec{background:var(--red);color:#fff;animation:pulse 1.1s infinite}
/* ---- cadenas « mode discussion » (composer) ----
   Le composer est une rangée flex à largeur bornée : le bouton doit être NON rétrécissable (flex:0 0)
   comme ses voisins .ic, sinon il pousse le textarea hors du champ sur mobile. Il hérite de .ic pour ça.
   L'état verrouillé se voit à la couleur ET à l'icône (🔒 vs 🔓) : jamais la couleur seule, elle est
   invisible pour un daltonien et absente en capture d'écran commentée. */
#lock.on{background:rgba(251,191,36,.18);color:var(--amber);box-shadow:inset 0 0 0 1px rgba(251,191,36,.5)}
/* Le composer devient ambré quand le cadenas est mis : le signal est à l'échelle du champ de saisie,
   donc impossible à rater au moment d'écrire — c'est là que l'information compte. */
.row.locked{border-color:rgba(251,191,36,.45)}
.hint.locked{color:var(--amber)}
/* Infobulle maison : `title` natif n'apparaît qu'après une temporisation et JAMAIS au tactile, or ce
   bouton a besoin d'être expliqué au premier contact. Ancrée au-dessus du bouton, non cliquable. */
/* `static` et non `relative` : c'est la RANGÉE (.row) qui doit servir de repère à l'infobulle, pas le
   bouton. Ancrer sur le bouton la fait déborder — mesuré aux deux extrémités avant correction : centrée
   elle sortait à gauche à 390px, ancrée à gauche du bouton elle sortait à droite à 320px. Bornée par la
   rangée, elle ne peut sortir d'aucun côté, à n'importe quelle largeur. */
.tipwrap{position:static;flex:0 0 auto;display:flex}
.tip{position:absolute;bottom:calc(100% + 9px);left:8px;right:8px;max-width:330px;
  padding:9px 11px;border-radius:var(--r-md);border:1px solid var(--line);background:var(--bg2);
  color:var(--ink);font-size:12px;line-height:1.45;box-shadow:var(--shadow-drawer);z-index:40;
  opacity:0;visibility:hidden;transition:opacity .12s ease;pointer-events:none}
.tip b{display:block;margin-bottom:3px}
.tip.show{opacity:1;visibility:visible}
@keyframes pulse{50%{opacity:.55}}
#send{background:var(--grad-action);color:#fff}
#send:disabled{opacity:.4}
.hint{color:var(--faint);font-size:11px;text-align:center;margin-top:6px}
/* ---- navbar / drawer écosystème ---- */
.menu-btn{display:inline-flex;align-items:center;justify-content:center;gap:7px;height:34px;padding:0 12px;border-radius:9px;border:1px solid var(--line);background:var(--card);color:var(--ink);cursor:pointer;font-size:13px;white-space:nowrap}
.menu-btn:hover{border-color:var(--violet)}
/* Icones de nav — direction "Neon runique" : glyphes vectoriels (sprite <symbol> en tete de <body>).
   Un seul trace par icone, recolore par currentColor -> suit le theme (dim au repos, violet au survol,
   cyan quand le groupe est ouvert). Remplace les emojis systeme, qui ignoraient la palette. */
.menu-btn .ic-e{display:block;width:17px;height:17px;line-height:0;flex:0 0 auto}
/* Bouton d'icone SEULE, glyphe TEXTE (pause/reprise d'un job dans le panneau Automatisations) : c'est le
   seul .menu-btn du cockpit dont l'icone n'est pas un <symbol> SVG. Reutiliser .ic-e le decalait vers le
   haut de son bouton (constat owner 2026-07-31) : son line-height:0 est calibre pour un SVG, il place un
   glyphe texte sur une ligne de hauteur nulle. D'ou une classe a part, centree par le flex, et un bouton
   carre pour que le padding horizontal ne fasse pas paraitre l'icone decalee. */
.menu-btn.ic-only{padding:0;width:34px;flex:0 0 auto}
.menu-btn .ic-t{display:inline-flex;align-items:center;justify-content:center;width:100%;height:100%;line-height:1;font-size:13px;flex:0 0 auto}
/* Badge d'attente d'un ordre d'automatisation (ordre depose, pas encore confirme par le bus). Ambre et
   non violet comme .badge : il ne dit pas un etat, il dit qu'on ne le connait pas encore. */
.item .nm .badge.wait{background:rgba(226,168,59,.16);color:#e2a83b}
.menu-btn .ic-e svg{display:block;width:100%;height:100%;fill:none;stroke:var(--dim);stroke-width:2.2;stroke-linecap:square;stroke-linejoin:miter}
.menu-btn:hover .ic-e svg{stroke:var(--violet)}
.navgroup.open .grp-trig .ic-e svg{stroke:var(--cyan)}
.menu-btn.logoutb .ic-e svg{stroke:currentColor}
.ic-sprite{position:absolute;width:0;height:0;overflow:hidden}
/* Glyphe inline dans un flux de texte (badges, puces de repo, libellés de bouton). Il suit la couleur de
   son porteur via currentColor — ce qu'un emoji ne sait pas faire. Classe `.icg` et non `.ico` : `.ico`
   est déjà pris par les pastilles carrées des tiroirs (.item .ico), la réutiliser les casserait. */
.icg{display:inline-block;vertical-align:-.14em;fill:none;stroke:currentColor;stroke-width:2.2;stroke-linecap:square;stroke-linejoin:miter}
/* Teintes de statut, pour les glyphes posés au rendu d'un message (cf. statusGlyphs). Reprend la
   sémantique du design system : green = réussi, amber = attention, red = bloqué. */
.icg--ok{stroke:var(--green)}
.icg--warn{stroke:var(--amber)}
.icg--bad{stroke:var(--red)}
.menu-btn .lbl{font-weight:600}
/* Déconnexion : action secondaire/destructive -> style ghost discret (pas une carte de nav), accent danger au survol */
.menu-btn.logoutb{background:transparent;border-color:transparent;color:var(--faint);margin-left:2px}
.menu-btn.logoutb:hover{border-color:var(--red);color:var(--red);background:rgba(251,113,133,.10)}
.menu-btn.logoutb .lbl{font-weight:500}
.navbtns{display:flex;align-items:center;gap:8px;margin-left:auto}
.burger{display:none}
/* ---- menus regroupés (3 déroulants : Espace de travail / Système / Moi) ---- */
.navgroup{position:relative;display:inline-flex}
.grp-trig .caret{font-size:10px;opacity:.6;margin-left:-2px}
.navgroup.open .grp-trig{border-color:var(--violet)}
.submenu{position:absolute;top:calc(100% + 6px);right:0;min-width:196px;flex-direction:column;gap:4px;padding:8px;background:var(--grad-surface);border:1px solid var(--line);border-radius:12px;box-shadow:0 14px 44px rgba(0,0,0,.55);z-index:46;display:none}
.navgroup.open .submenu{display:flex}
.submenu .menu-btn{width:100%;height:38px;justify-content:flex-start}
/* PLUS AUCUN TIROIR LATÉRAL. `.scrim`, `.drawer` (menu de gauche, déjà mort : plus aucun élément ne
   le portait) et `.kdrawer` (treize panneaux à droite) ont été retirés le 2026-08-10 — demande de
   Tim, « c'est vraiment très chiant le truc qui s'ouvre sur le côté ». Tout ce qui s'ouvrait par le
   bord s'ouvre désormais AU CENTRE, dans la coquille unique `.nmodal` (cf. plus bas). Le voile n'est
   plus un élément séparé : il est porté par la coquille, ce qui supprime le second voile qui se
   superposait au premier sur les écrans déjà en modale.
   Les classes de CONTENU qui vivaient dans ces tiroirs (`.sec`, `.item`, `.kcard`…) restent : elles
   décrivent des listes, pas une enveloppe, et servent telles quelles dans la modale. */
.sec{margin-top:15px}
.sec-t{font-size:10.5px;font-weight:700;letter-spacing:.7px;text-transform:uppercase;color:var(--faint);padding:0 6px;margin-bottom:6px;display:flex;align-items:center;gap:7px}
.sec-t .n{margin-left:auto;background:rgba(255,255,255,.06);border-radius:999px;padding:1px 7px;font-size:10px;color:var(--dim);letter-spacing:0}
.item{display:flex;gap:10px;padding:8px 7px;border-radius:11px;align-items:flex-start}
.item:hover{background:var(--card)}
.item .ico{width:27px;height:27px;border-radius:8px;display:grid;place-items:center;font-size:14px;flex:0 0 27px;background:rgba(255,255,255,.05);border:1px solid var(--line)}
.item .meta{min-width:0;flex:1}
.item .nm{font-size:13px;font-weight:600;color:var(--ink);display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.item .nm .badge{font-size:9.5px;font-weight:700;letter-spacing:.3px;padding:1px 5px;border-radius:5px;background:rgba(124,58,237,.16);color:var(--violet)}
.item .ds{font-size:11.5px;color:var(--dim);line-height:1.4;margin-top:2px}
.item .live{width:7px;height:7px;border-radius:50%;background:var(--green);box-shadow:0 0 7px var(--green);flex:0 0 7px;margin-top:7px}
.item.act{cursor:pointer;align-items:center}
.sec-t.toggle{cursor:pointer;color:var(--dim)}
/* ---- panneau Docs & notes (base de connaissance) ---- */
.kcard{border:1px solid var(--line);background:var(--card);border-radius:12px;padding:11px 12px;margin:9px 0}
.kcard .kt{font-size:13.5px;font-weight:700;display:flex;align-items:center;gap:7px;flex-wrap:wrap}
.kcard .ktype{font-size:9.5px;font-weight:700;padding:1px 6px;border-radius:5px;background:rgba(124,58,237,.16);color:var(--violet)}
.kcard .kmeta{font-size:10.5px;color:var(--faint);margin-top:3px}
.kcard .ksum{font-size:12.5px;color:var(--dim);margin-top:6px;line-height:1.45}
.kcard .ktodos{margin-top:8px;font-size:12px;color:var(--ink)}
.kcard .ktodos ul{margin:3px 0 0;padding-left:18px}.kcard .ktodos li{margin:2px 0}
/* ---- responsive : header dense (6 boutons + pill) sur petit écran ---- */
/* sous 900px : les boutons se replient dans un menu burger */
@media (max-width:900px){
  .burger{display:inline-flex;margin-left:auto}
  .navbtns{position:absolute;top:calc(100% + 6px);right:12px;margin-left:0;flex-direction:column;align-items:stretch;min-width:212px;padding:8px;gap:4px;background:var(--grad-surface);border:1px solid var(--line);border-radius:12px;box-shadow:0 14px 44px rgba(0,0,0,.55);z-index:45;display:none}
  .navbtns.open{display:flex}
  .navbtns .menu-btn{width:100%;height:40px;justify-content:flex-start}
  /* dans le burger : les groupes s'ouvrent en accordéon inline (pas en flottant) */
  .navgroup{width:100%;flex-direction:column;align-items:stretch}
  .navgroup .grp-trig{width:100%;height:40px;justify-content:flex-start}
  .navgroup .grp-trig .caret{margin-left:auto}
  .submenu{position:static;padding:2px 0 4px 16px;border:0;box-shadow:none;background:none;min-width:0}
}
@media (max-width:560px){
  header{gap:7px;padding:10px 12px}
  .mark{width:32px;height:32px;border-radius:9px;font-size:16px}
  h1{font-size:15px;min-width:0;overflow:hidden;text-overflow:ellipsis}
  .burger{padding:0 10px;height:31px}
  header #weekChip{margin-left:6px;padding:5px 8px;font-size:10px}
  header #acctSel{margin-left:6px;max-width:124px;font-size:11px;padding:5px 22px 5px 9px}
  .msg-wrap.me,.msg-wrap.nyx{max-width:90%}
  #thread{padding:13px 12px}
}
/* ---- barre de chats en COLONNE À GAUCHE (desktop >=901px), façon Claude/ChatGPT ----
   Même #tabs, même renderTabs() : sous 901px on garde la barre horizontale en pilules (mobile),
   au-dessus on rend une liste verticale groupée par date. Le HTML est identique dans les deux modes,
   seul le CSS change ; renderTabs() ajoute juste les en-têtes de date quand la sidebar est active. */
@media (min-width:901px){
  #shell{flex-direction:row}
  #tabs{flex:0 0 var(--sbw,264px);width:var(--sbw,264px);flex-direction:column;align-items:stretch;gap:1px;
        padding:8px 8px 16px;overflow-x:hidden;overflow-y:auto;border-bottom:0;border-right:1px solid var(--line)}
  #tabs.mini{--sbw:60px;padding:8px 6px 16px}
  /* en-tête collant : « + Nouveau chat » reste visible quand la liste défile */
  .sbhead{position:sticky;top:-8px;z-index:2;display:flex;align-items:center;gap:6px;margin:-8px -8px 6px;padding:8px;
          background:rgba(11,11,24,.94);backdrop-filter:blur(10px)}
  #tabs.mini .sbhead{margin:-8px -6px 6px;padding:8px 6px;flex-direction:column}
  .snew{flex:1 1 auto;min-width:0;display:flex;align-items:center;justify-content:center;gap:6px;height:36px;padding:0 12px;
        border-radius:10px;border:1px solid var(--violet);background:rgba(124,58,237,.18);color:var(--ink);
        font-size:13px;font-weight:700;cursor:pointer;white-space:nowrap;overflow:hidden}
  .snew:hover{background:rgba(124,58,237,.3)}
  .snew.on{box-shadow:0 0 0 1px var(--violet) inset}
  #tabs.mini .snew{width:44px;flex:0 0 auto;padding:0}
  #tabs.mini .snew .lbl{display:none}
  .stog{flex:0 0 auto;width:30px;height:30px;border-radius:9px;border:1px solid var(--line);background:none;color:var(--dim);
        font:inherit;font-size:14px;line-height:1;cursor:pointer;display:grid;place-items:center;padding:0}
  .stog:hover{border-color:var(--violet);color:var(--ink)}
  /* en-tête de groupe de date (Aujourd'hui, Hier, 7 derniers jours…) */
  .sgrp{padding:11px 9px 4px;font-size:10px;font-weight:700;letter-spacing:.5px;text-transform:uppercase;color:var(--faint)}
  #tabs.mini .sgrp{display:none}
  /* ---- Dossiers : le rangement choisi, au-dessus de la liste par date ---- */
  .sgrp.fldhead{display:flex;align-items:center;justify-content:space-between;padding-top:4px}
  .fldadd{width:22px;height:22px;display:grid;place-items:center;padding:0;border:1px solid var(--line);
          border-radius:7px;background:none;color:var(--dim);cursor:pointer}
  .fldadd:hover{border-color:var(--violet);color:var(--ink)}
  #tabs .fldr{display:flex;align-items:center;gap:6px;padding:6px 9px;border-radius:10px;border:1px solid transparent;
              cursor:pointer;font-size:12.5px;color:var(--ink)}
  #tabs .fldr:hover{background:var(--card2)}
  /* Cible de dépôt survolée : le liseré dit où le chat va tomber, sinon on lâche à l'aveugle. */
  #tabs .fldr.dropok,#tabs .fldbody.dropok,#tabs .sdated.dropok{background:rgba(124,58,237,.14);border-color:var(--violet)}
  #tabs .sdated{display:flex;flex-direction:column;gap:1px;border:1px solid transparent;border-radius:10px}
  #tabs .fldr .fchev{display:grid;place-items:center;color:var(--faint);transition:transform .12s}
  #tabs .fldr.open .fchev{transform:rotate(90deg)}
  #tabs .fldr .fico{display:grid;place-items:center;color:var(--dim)}
  #tabs .fldr .fname{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:600}
  #tabs .fldr .fn{flex:0 0 auto;padding:0 5px;border-radius:6px;background:var(--card2);color:#9398b4;font-size:10.5px}
  #tabs .fldr .tabacts{flex-direction:row;gap:3px;margin-top:0}
  #tabs .fldr .tabx,#tabs .fldr .tabdel{opacity:0;transition:opacity .12s}
  #tabs .fldr:hover .tabx,#tabs .fldr:hover .tabdel{opacity:.65}
  /* Le contenu d'un dossier est décalé : on doit voir d'un coup d'œil ce qui est rangé et ce qui ne l'est pas. */
  #tabs .fldbody{display:flex;flex-direction:column;gap:1px;margin:1px 0 4px 10px;padding-left:6px;
                 border-left:1px solid var(--line);border-radius:0 10px 10px 0}
  .fldempty{padding:6px 10px;color:var(--faint);font-size:11px;line-height:1.4}
  .fldmore{padding:5px 10px;color:var(--dim);font-size:11px;cursor:pointer;text-decoration:underline dotted}
  .fldmore:hover{color:var(--ink)}
  #tabs.mini .fldhead,#tabs.mini .fldr,#tabs.mini .fldbody,#tabs.mini .fldempty,#tabs.mini .fldmore{display:none}
  /* La ligne qu'on traîne reste visible mais s'efface : on suit son déplacement sans la perdre. */
  #tabs .tab.dragging{opacity:.45}
  /* une ligne = une conversation. La pilule .tab devient une ligne pleine largeur. */
  #tabs .tab{flex:0 0 auto;width:auto;max-width:none;display:flex;align-items:flex-start;gap:8px;white-space:normal;
             padding:7px 9px;border-radius:10px;border:1px solid transparent;background:none}
  #tabs .tab:hover{background:var(--card2)}
  #tabs .tab.on{background:rgba(124,58,237,.16);border-color:var(--violet)}
  #tabs .tab .sdot{margin-top:5px}
  #tabs .tab .stxt{display:block;flex:1 1 auto;min-width:0}
  #tabs .tab .stitle{display:block;font-size:13px;font-weight:600;color:var(--ink);line-height:1.35;
                     overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  #tabs .tab .smeta{display:flex;flex-wrap:wrap;align-items:center;gap:4px;margin-top:3px;font-size:10.5px;color:#9398b4}
  #tabs .tab .smeta:empty{display:none}
  #tabs .tab .tmeta{flex:0 1 auto}
  /* actions discrètes : croix et poubelle n'apparaissent qu'au survol (ou sur le chat courant) */
  #tabs .tab .tabacts{flex-direction:column;gap:1px;margin-top:2px}
  #tabs .tab .tabacts .tabx,#tabs .tab .tabacts .tabdel,#tabs .tab .tabacts .tabfld{padding:0 3px}
  #tabs .tab .tabx,#tabs .tab .tabdel,#tabs .tab .tabfld{opacity:0;transition:opacity .12s}
  #tabs .tab:hover .tabx,#tabs .tab.on .tabx,#tabs .tab:hover .tabdel,#tabs .tab.on .tabdel,
  #tabs .tab:hover .tabfld,#tabs .tab.on .tabfld{opacity:.65}
  #tabs .tab.histtab{justify-content:center;align-items:center;gap:7px;margin-top:8px;padding:8px 9px;
                     border-radius:10px;border-color:var(--line);background:var(--card);font-size:12.5px}
  /* mode replié : on ne garde que la pastille d'état et l'initiale du chat */
  .sini{display:none;width:26px;height:26px;border-radius:8px;background:var(--card2);border:1px solid var(--line);
        color:var(--ink);font-size:12px;font-weight:700;align-items:center;justify-content:center;flex:0 0 26px}
  #tabs.mini .tab{justify-content:center;padding:6px 0;gap:4px}
  #tabs.mini .tab .stxt,#tabs.mini .tab .tabacts{display:none}
  #tabs.mini .sini{display:inline-flex}
  /* colonne repliée : il ne reste que le sablier, comme « ＋ Nouveau chat » ne garde que son signe */
  #tabs.mini .tab.histtab .lbl{display:none}
  /* Poignée sur le bord droit de la colonne : clic = replier/déplier, glisser = redimensionner.
     Largeur réelle de 9px « annulée » par une marge négative pour ne pas décaler le fil. */
  #sbgrip{display:block;flex:0 0 9px;width:9px;margin-left:-9px;position:relative;z-index:6;cursor:col-resize;touch-action:none}
  #sbgrip::after{content:'';position:absolute;top:0;bottom:0;left:3px;width:3px;border-radius:3px;background:transparent;transition:background .15s}
  #sbgrip:hover::after,#sbgrip.on::after{background:var(--violet)}
  body.sbresize{cursor:col-resize;user-select:none;-webkit-user-select:none}
  body.sbresize #sbgrip::after{background:var(--violet)}
  /* ---- Recherche dans les chats (barre + résultats) ----
     La liste vit dans son propre conteneur (#sblist) pour que le battement du poll la reconstruise
     SANS toucher au champ : sinon la saisie et le curseur sautent toutes les 4 secondes. */
  .sbfind{position:relative;display:flex;align-items:center;margin:0 0 6px}
  .sbfind .sfi{position:absolute;left:9px;display:grid;place-items:center;color:var(--faint);pointer-events:none}
  .sbfind input{width:100%;box-sizing:border-box;height:32px;padding:0 26px 0 28px;border-radius:9px;
                border:1px solid var(--line);background:var(--card);color:var(--ink);font:inherit;font-size:12.5px;outline:0}
  .sbfind input:focus{border-color:var(--violet)}
  .sbfind input::placeholder{color:var(--faint)}
  .sbfind input::-webkit-search-cancel-button{display:none} /* on a notre propre croix, alignée au thème */
  .sfx{position:absolute;right:5px;width:20px;height:20px;display:grid;place-items:center;padding:0;
       border:0;border-radius:6px;background:none;color:var(--dim);cursor:pointer}
  .sfx:hover{background:var(--card2);color:var(--ink)}
  #sblist{display:flex;flex-direction:column;gap:1px;min-width:0}
  .sfind-n{padding:6px 9px 4px;font-size:10px;font-weight:700;letter-spacing:.5px;text-transform:uppercase;color:var(--faint)}
  .sfind-warn{margin:0 4px 6px;padding:6px 8px;border-radius:8px;background:rgba(226,168,59,.12);color:#e2a83b;font-size:11px;line-height:1.35}
  .sfind-empty{padding:14px 10px;color:var(--dim);font-size:12px;line-height:1.45}
  /* Une carte de résultat : titre, extrait du message qui matche, méta. L'extrait est bridé à trois
     lignes — au-delà il pousse les autres résultats hors de l'écran et on ne compare plus rien. */
  #tabs .tab.sres{align-items:flex-start;cursor:pointer}
  #tabs .tab.sres .ssnip{display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;
                         margin-top:3px;font-size:11.5px;line-height:1.4;color:var(--dim);white-space:normal}
  #tabs .tab.sres .ssnip b{color:var(--faint);font-weight:700}
  #tabs .tab.sres mark{background:rgba(124,58,237,.32);color:var(--ink);border-radius:3px;padding:0 1px}
  #tabs .tab.sres .rchip{padding:1px 5px;border-radius:5px;background:var(--card2);color:#9398b4;font-size:10px}
  #tabs.mini .sbfind{display:none}
}
@media (max-width:380px){
  header{gap:5px;padding:9px 10px}
  .burger .lbl{display:none} /* on garde juste l'icône ☰ pour gagner de la place */
  h1 .g{font-size:0} /* gagne de la place : on garde la pastille 𝗡, le titre cède en dernier */
  header #acctSel{max-width:104px}
}

/* ══ LA COQUILLE DE MODALE — LA SEULE ENVELOPPE DU COCKPIT ═══════════════════════════════════
   TOUT ce qui s'ouvre par-dessus le cockpit passe par ici : les treize anciens tiroirs latéraux
   (Repos, Docs, Statut, Écosystème, Nouveautés, Historique, Ce qui consomme, Comptes, Décisions,
   Secrets, Alertes ntfy, Profil, Google) comme les écrans déjà en modale (Équipe, Rôles, Testing,
   Social, le vote, le Pacte, le journal).

   POURQUOI UNE SEULE. Jusqu'au 2026-08-10 il y en avait DEUX, et toutes les deux s'appelaient
   `.nmodal` : la première ici (voile absent, `display:flex`, centrage), la seconde 700 lignes plus
   bas pour le vote (voile, `display:block`, défilement de la page). La cascade donnait raison à la
   seconde, donc les modales Équipe/Rôles/Testing/Social tournaient avec des règles écrites pour une
   autre : sans `margin:auto` dans un conteneur en `block`, leur feuille se collait au bord gauche.
   Deux lectures d'une même classe qui peuvent diverger en silence sont un défaut, pas un style.

   LA MÉCANIQUE RETENUE est celle du vote, parce que c'est la référence que Tim a demandée : le
   voile et le défilement sont portés par `.nmodal`, la feuille grandit avec son contenu et c'est la
   modale qui défile — pas un corps interne à hauteur bornée. Un écran long (le Statut, l'historique)
   se parcourt donc comme une page, sans double barre de défilement.

   LARGEUR : une seule variable, `--nw`, posée sur la modale (`style="--nw:860px"`). Le contenu
   décide — une colonne de texte se lit à 660 px, une matrice personnes × rôles réclame 960 px. */
.nmodal{position:fixed;inset:0;z-index:70;display:none;overflow-y:auto;overscroll-behavior:contain;
  background:rgba(4,4,12,.66);backdrop-filter:blur(3px);padding:24px 14px 40px}
.nmodal.on{display:block}
/* Deux noms pour UNE feuille. `.nsheet` (en-tête compact : pastille + titre + méta) et
   `.nmodal-card` (en-tête riche : titre, sous-titre, dégradé) sont deux formes d'EN-TÊTE, pas deux
   enveloppes : la coquille est décrite une fois, ici, et rien d'autre ne la redéfinit. */
/* `clip` ET PAS `hidden`, depuis le 2026-08-21, et la différence n'est pas cosmétique : les deux
   rognent le contenu aux coins arrondis à l'identique, mais `hidden` fait de la feuille un CONTENEUR
   DE DÉFILEMENT, ce qui annule tout `position:sticky` posé à l'intérieur — le sticky se réfère au
   premier ancêtre défilable, trouve cette feuille, et elle ne défile jamais (c'est `.nmodal` qui
   défile, au-dessus). Mesuré sur l'écran des accès : en-tête à -1076 px avec `hidden`, collé à 24 px
   avec `clip`, tout le reste inchangé. Aucun code ne défile cette coquille (aucun `scrollTop`) et
   aucun panneau n'y flotte d'élément, donc le contexte de formatage que `hidden` créait ne servait
   à rien ; ce qu'il coûtait, lui, se voit ci-dessus. */
.nsheet,.nmodal-card{position:relative;width:min(var(--nw,660px),100%);margin:0 auto;
  background:linear-gradient(180deg,#12121f,#0b0b16);border-radius:22px;
  box-shadow:0 30px 90px rgba(0,0,0,.7),0 0 0 1px rgba(255,255,255,.06);
  overflow:clip;animation:nmodalIn .2s cubic-bezier(.4,0,.2,1)}
@media (prefers-reduced-motion:reduce){.nsheet,.nmodal-card{animation:none}}
/* En-tête compact. La croix porte tout son style ICI : elle le tenait de `.drawer-h .x`, supprimé
   avec les tiroirs — sans quoi elle retomberait sur le bouton gris natif du navigateur. */
.nmh{display:flex;align-items:center;gap:11px;padding:15px 20px;border-bottom:1px solid var(--line)}
.nmh b{font-size:15px}
.nmh .mark{width:30px;height:30px;font-size:15px;border-radius:9px;flex:0 0 auto}
.nmh .x{margin-left:auto;flex:0 0 auto;cursor:pointer;color:var(--dim);font-size:18px;
  background:none;border:0;padding:4px;line-height:1}
.nmh .x:hover{color:var(--ink)}
.nmb{padding:18px 20px 22px}
/* Métadonnée technique : même grammaire que les libellés de Kōten, en monospace discret. */
.nmono{font-family:ui-monospace,Menlo,monospace;font-size:11px;color:var(--faint);letter-spacing:.4px}
/* Bandeau de chiffres. grid-auto-fit : passe de 4 à 2 colonnes sans media query. */
.nstats{display:grid;grid-template-columns:repeat(auto-fit,minmax(132px,1fr));gap:11px;margin-bottom:20px}
.nstat{border:1px solid var(--line);border-radius:12px;padding:12px 14px;background:var(--card)}
.nstat .n{font-size:24px;font-weight:700;line-height:1.1}
.nstat .n.hi{background:var(--grad-accent);-webkit-background-clip:text;background-clip:text;color:transparent}
.nstat .l{font-size:10.5px;color:var(--faint);margin-top:2px;letter-spacing:.6px;font-family:ui-monospace,Menlo,monospace}
/* Une tuile MÈNE à ce qu'elle compte : elle doit donc se voir comme un bouton avant d'être cliquée
   (un chiffre qui ne réagit qu'au survol se lit comme de la décoration). `.on` = la vue ou le filtre
   courant, `.morte` = un compteur à zéro, qui n'ouvre rien et le montre. */
.nstat.act{cursor:pointer;transition:border-color .15s,background .15s,transform .1s;-webkit-tap-highlight-color:transparent}
.nstat.act:hover{border-color:rgba(167,139,250,.34);background:var(--card2)}
.nstat.act:active{transform:translateY(1px)}
.nstat.act:focus-visible{outline:2px solid var(--violet);outline-offset:2px}
.nstat.act.on{border-color:rgba(167,139,250,.5);background:var(--card2)}
.nstat.act.morte{cursor:default;opacity:.55}
.nstat.act.morte:hover{border-color:var(--line);background:var(--card)}
.nstat.act.morte:active{transform:none}
/* Titre de section + action de retour sur la même ligne (annuaire, board filtré). */
.nhead{display:flex;align-items:center;gap:10px;margin:0 0 12px;min-width:0}
.nhead .nst{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
.nhead .ntab{margin-left:auto;flex:0 0 auto;cursor:pointer}
/* Fiche de personne dans l'annuaire : l'avatar porte le point « au travail ». */
.nav.lg{width:34px;height:34px;font-size:13px;margin-left:0;color:var(--ink);font-weight:600}
.npf{position:relative;display:inline-flex;flex:0 0 auto}
.npf .nwd{position:absolute;right:-1px;bottom:-1px;width:10px;height:10px;border-radius:50%;
  background:var(--green);border:2px solid var(--card);box-sizing:border-box}
.ncard.open .npf .nwd{border-color:var(--card2)}
/* Nom et sous-titre EMPILÉS : sur une seule ligne ils se disputent la largeur, et à 390 px c'est le
   nom qui se fait écraser au profit du sous-titre (mesuré). C'est aussi la forme de la carte mobile. */
.npb{display:flex;flex-direction:column;gap:2px;flex:1 1 auto;min-width:0}
.npn{display:flex;align-items:center;gap:7px;min-width:0}
.npn b{font-size:14.5px;font-weight:700;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
/* Sous-titre secondaire : il rétrécit et s'ellipse, jamais le badge ni le chevron. En --dim et non
   en --faint (que porte .nmono) : sur le fond d'une carte dépliée, --faint tombe à 4.35 de contraste
   pour un minimum WCAG AA de 4.5 (mesuré). C'est du contenu, pas de la chrome. */
.nsub{flex:0 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dim)}
/* Carte dépliable (reprise du board Kōten : en-tête cliquable + corps révélé). */
.ncard{border:1px solid var(--line);border-radius:14px;background:var(--card);margin-bottom:10px;overflow:hidden}
.ncard.open{border-color:rgba(167,139,250,.34)}
.nch{display:flex;align-items:center;gap:10px;padding:14px 16px;cursor:pointer;min-width:0}
.ncard.open .nch{background:var(--card2)}
.nch:hover{background:var(--card2)}
.ncn{font-size:16px;font-weight:700;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
/* Le chevron est une AFFORDANCE (il dit que la carte s'ouvre), pas une decoration : mesure a 4.32 de
   contraste en --faint pour un minimum WCAG AA de 4.5, sur un glyphe de 12 px. */
.ncaret{flex:0 0 auto;color:var(--dim);font-size:12px;transition:transform .2s}
.ncard.open .ncaret{transform:rotate(180deg)}
.ncb{border-top:1px solid var(--line);padding:14px 16px}
/* Badge d'état. TOUJOURS rendu, même à zéro : un repère à la même place sur chaque ligne se
   balaie des yeux, alors qu'un badge qui apparaît/disparaît oblige à relire. */
/* En --dim et non en --faint : le badge ne dit plus un total decoratif mais le chiffre du mois, qui
   est le contenu de la ligne. Mesure a 390 px sur la carte depliee, --faint y tombait a 4.06 de
   contraste pour un minimum WCAG AA de 4.5 - sur du 11 px, c'est illisible. */
.nbadge{display:inline-flex;align-items:center;gap:6px;padding:3px 9px;border-radius:999px;
  border:1px solid var(--line);font-size:11px;color:var(--dim);flex:0 0 auto;white-space:nowrap}
.nbadge.live{border-color:rgba(52,211,153,.35);background:rgba(52,211,153,.10);color:var(--green)}
/* Le total de toujours, accolé au chiffre du mois : présent pour donner l'échelle, effacé pour ne pas
   se disputer la lecture avec lui. */
.nbadge .tot{font-weight:400;opacity:.62;margin-left:1px}
/* Le mois affiché, en tête de l'annuaire, au-dessus de ses douze colonnes. Les deux flèches et le
   bouton « mois en cours » qui vivaient ici sont partis le 2026-08-21 : trois commandes pour le geste
   que les colonnes font déjà, et aucune ne tenait dans la largeur d'un téléphone. */
.nmoisbar{display:flex;align-items:center;gap:8px;margin:0 0 12px}
.nmoisbar .nmt{font-family:ui-monospace,Menlo,monospace;font-size:12px;letter-spacing:.5px;color:var(--dim);
  min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:0 1 auto}
/* Douze mois d'un coup d'oeil. Colonnes cliquables : c'est le geste le plus court pour comparer deux
   mois, et le seul depuis que les flèches sont parties. */
.nbars{display:flex;align-items:flex-end;gap:4px;height:56px;margin:2px 0 12px}
.nbar{flex:1 1 0;min-width:0;display:flex;flex-direction:column;justify-content:flex-end;align-items:center;
  gap:4px;height:100%;cursor:pointer;border-radius:5px;padding:2px 0}
.nbar i{display:block;width:100%;border-radius:3px;background:var(--line);transition:background .15s}
.nbar em{font-style:normal;font-size:9px;font-family:ui-monospace,Menlo,monospace;color:var(--faint);
  line-height:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}
.nbar:hover i{background:var(--dim)}
.nbar.on i{background:var(--grad-action);background-color:rgba(167,139,250,.9)}
.nbar.on em{color:var(--ink)}
/* Le détail du mois. Les lignes de code viennent en dernier et en gris : c'est un volume, pas un
   score — un refactor qui en supprime 3 000 est un bon mois. */
.nmois{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 12px}
.nmc{flex:1 1 64px;min-width:0;border:1px solid var(--line);border-radius:10px;padding:7px 9px;
  display:flex;flex-direction:column;gap:2px}
.nmc b{font-size:15px;font-weight:700;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.nmc span{font-size:10px;font-family:ui-monospace,Menlo,monospace;color:var(--faint);letter-spacing:.3px}
.nmc.lignes{flex:1 1 130px}
.nmc.lignes b{font-size:12px;font-weight:600;color:var(--dim)}
.nmc.lignes s{text-decoration:none;opacity:.7}
.nbadge.warn{border-color:rgba(251,191,36,.32);color:var(--amber)}
.ndot{position:relative;width:6px;height:6px;flex:0 0 auto}
.ndot i{position:absolute;inset:0;border-radius:50%;background:currentColor}
.ndot i:first-child{animation:nping 1.7s cubic-bezier(0,0,.2,1) infinite;opacity:.6}
@keyframes nping{75%,100%{transform:scale(2.6);opacity:0}}
@media (prefers-reduced-motion:reduce){.ndot i:first-child{animation:none}}
/* Onglets internes (pilules) et onglets de tête (attachés au bord du corps). */
.ntabs{display:flex;gap:5px;flex-wrap:wrap;margin-bottom:13px}
.ntab{font:inherit;font-size:11.5px;padding:6px 12px;border-radius:999px;border:1px solid var(--line);
  background:none;color:var(--dim);cursor:pointer;font-family:ui-monospace,Menlo,monospace;letter-spacing:.3px}
.ntab.on{border-color:transparent;background:var(--grad-action);color:#fff;font-weight:600}
/* Le compteur d'un onglet ACTIF est du blanc sur le dégradé d'action : à 80 % d'opacité il tombait
   à 2,67 de contraste (mesure du 2026-08-06, minimum AA 4.5) sur un chiffre de 11,5 px. L'effacer
   avait du sens sur un onglet au repos, pas sur celui qu'on regarde. */
.ntab .c{margin-left:6px;opacity:.8}
.ntab.on .c{opacity:1}
.ntabbar{display:flex;gap:5px;padding:13px 20px 0;flex-wrap:wrap;flex:0 0 auto}
.ntabbar .ntab{border-radius:999px 999px 0 0;border-bottom:0;padding:7px 13px}
/* Avatars en pile : la superposition dit « groupe » sans prendre la largeur d'une liste. */
.navs{margin-left:auto;display:flex;flex:0 0 auto}
.nav{width:24px;height:24px;border-radius:50%;background:var(--card2);border:2px solid var(--bg1);
  display:inline-flex;align-items:center;justify-content:center;font-size:10.5px;color:var(--dim);
  margin-left:-7px;flex:0 0 auto;object-fit:cover}
.nav:first-child{margin-left:0}
/* Ligne de liste (présence, roadmap, bug, commit). */
.nrow{display:flex;align-items:center;gap:9px;padding:7px 0;border-bottom:1px solid rgba(255,255,255,.05);min-width:0}
.nrow:last-child{border-bottom:0}
.ntxt{font-size:12.5px;color:var(--dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;flex:1 1 auto}
.nago{margin-left:auto;flex:0 0 auto;font-size:10.5px;color:var(--faint)}
.nvide{font-size:12px;color:var(--faint);font-style:italic;padding:4px 0}
/* Une ligne ouvrable doit s'annoncer comme telle, sinon rien ne distingue le fil qu'on peut ouvrir
   de celui qu'on ne peut que lire. */
.nrow.clickable{cursor:pointer}
.nrow.clickable:hover .ntxt{color:var(--ink)}
.nrow.clickable:focus-visible{outline:2px solid var(--violet);outline-offset:-2px;border-radius:6px}
/* Historique des commits : la liste grandit à chaque « voir plus » et pousserait tout le reste de la
   modale hors de l'écran. On la borne et on la fait défiler SUR ELLE-MÊME, pour que les onglets de la
   carte restent atteignables quelle que soit la profondeur d'historique déroulée. `overscroll-behavior`
   empêche le défilement de se propager à la modale une fois arrivé au bout de la liste. */
.nscroll{max-height:46vh;overflow-y:auto;overscroll-behavior:contain}
.nmore{display:flex;justify-content:center;padding:10px 0 2px}
.npuce{width:6px;height:6px;border-radius:50%;flex:0 0 auto}
/* Matrice de rôles. */
.ntable{width:100%;border-collapse:collapse;margin-top:14px}
.ntable th{font-size:10.5px;color:var(--faint);font-family:ui-monospace,Menlo,monospace;letter-spacing:.5px;
  text-align:center;padding:0 0 9px;font-weight:400}
.ntable th:first-child{text-align:left}
.ntable td{padding:9px 0;border-top:1px solid rgba(255,255,255,.06)}
.ntable td.c{text-align:center;width:92px}
.nwho{display:flex;align-items:center;gap:9px;min-width:0}
.nnm{font-size:13.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
/* L'adresse sous le nom, sur l'écran des rôles. En bloc et non en ligne : c'est une SECONDE
   identité, pas une suite du nom, et les coller ferait lire « Elenaelena@exemple.com ». */
.admail{display:block;font-size:11px;opacity:.62;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ntag{font-size:10px;color:var(--violet);border:1px solid rgba(167,139,250,.35);border-radius:999px;padding:1px 7px;flex:0 0 auto}
/* LA LIGNE DE PROJET de l'écran des accès. Elle n'a pas d'interrupteurs — un droit se pose par dépôt,
   jamais par projet — et elle se lisait donc comme une ligne de dépôt vide : même gris, même taille.
   « À quoi servent ces lignes ? » (owner, 2026-08-21). La pastille `.ntag`, déjà l'étiquette du
   cockpit, dit qu'elles sont d'une autre nature ; le filet un peu plus marqué au-dessus ouvre le
   groupe. Forme retenue parmi trois, cf. docs/mockups/roles-acces-un-seul-tableau.html. */
.ntable tr.adgrp td{border-top:1px solid rgba(255,255,255,.12);padding:16px 0 8px}
.ntable tr.adgrp:first-child td{border-top:none;padding-top:2px}
.ntable tr.adgrp .ntag{font-family:ui-monospace,Menlo,monospace;letter-spacing:.5px;padding:1px 9px}
/* L'EN-TÊTE SUIT LE DÉFILEMENT, et c'est la contrepartie de ne l'écrire qu'une fois.
   Douze tableaux, c'était douze en-têtes — le défaut signalé — mais aussi le rappel des colonnes
   toujours sous les yeux. Un seul en-tête en haut d'une liste de 4 800 px, mesurée sur le périmètre
   réel du 2026-08-21, laisse défiler cinq interrupteurs anonymes : exactement le « c'est admin read
   write ? » qui avait fait ajouter l'en-tête en août, déplacé plus bas dans la page.
   `sticky` le garde visible sans le dupliquer. Le fond doit être OPAQUE et repris de `.nsheet` : un
   en-tête translucide laisse défiler les lignes derrière ses lettres. Réservé au tableau des accès
   (`.adacc`) : la matrice des rôles tient dans un écran et n'a rien à faire coller. */
.ntable.adacc th{position:sticky;top:0;z-index:2;background:#12121f;padding-top:9px}
/* Interrupteur : l'état se voit sans avoir à interpréter une couleur de fond. */
.nsw{width:34px;height:20px;border-radius:999px;border:1px solid var(--line);background:none;position:relative;
  cursor:pointer;display:inline-block;padding:0;vertical-align:middle}
.nsw i{position:absolute;top:2px;left:2px;width:14px;height:14px;border-radius:50%;background:var(--faint);transition:.15s}
.nsw.on{border-color:transparent;background:var(--green)}
.nsw.on i{left:16px;background:#fff}
/* EN ATTENTE, ET C'EST UNE COULEUR À PART. Poser un droit passe par GitHub : entre le clic et la
   réponse, l'interrupteur ne bougeait pas du tout, donc le geste se lisait comme un bouton mort.
   Il bouge maintenant tout de suite en AMBRE — « demandé, pas encore confirmé » — et ne devient VERT
   que quand GitHub a répondu. L'écran ne ment donc pas en attendant : il dit qu'il attend. */
.nsw.att{border-color:transparent;background:var(--amber);animation:cpulse 1.4s ease-in-out infinite}
.nsw.att i{background:#fff}
.nsw[disabled]{opacity:.45;cursor:progress}
/* Un interrupteur en attente reste lisible : il est désactivé le temps de l'aller-retour, mais
   l'estomper le rendrait invisible juste au moment où il porte l'information. */
.nsw.att[disabled]{opacity:1;cursor:progress}
/* Sections de bas de modale (inviter, administrateurs). */
.nsec{margin-top:22px;padding-top:16px;border-top:1px solid var(--line)}
.nst{font-size:11px;color:var(--faint);font-family:ui-monospace,Menlo,monospace;letter-spacing:.5px;margin-bottom:9px}
.nfield{width:100%;font:inherit;font-size:13.5px;padding:9px 11px;border-radius:10px;border:1px solid var(--line);
  background:var(--field);color:var(--ink);box-sizing:border-box;min-width:0}
.nrow2{display:flex;gap:8px;min-width:0}
.nbtn{font:inherit;font-size:13px;padding:9px 14px;border-radius:10px;border:0;background:var(--grad-action);
  color:#fff;font-weight:600;cursor:pointer;flex:0 0 auto}
.nbtn.ghost{background:none;border:1px solid var(--line);color:var(--dim)}
.nbtn.danger{background:none;border:1px solid rgba(251,113,133,.4);color:var(--red)}
.nbtn.sm{font-size:11.5px;padding:5px 11px;border-radius:9px}
.nbtn[disabled]{opacity:.55;cursor:progress}
/* Fiche d'élément à traiter (soumission, bug, testeur, paiement). */
.nitem{border:1px solid var(--line);border-radius:12px;background:var(--card);padding:13px 15px;margin-bottom:9px}
.nitop{display:flex;align-items:flex-start;gap:9px;min-width:0;margin-bottom:3px}
/* Le titre d'une FICHE s'enroule sur deux lignes au lieu d'être coupé net. Mesuré le 2026-08-06 à
   390 px : « Problème de signature électronique sur les longs documents » s'arrêtait à « les longs
   d… ». Sur une ligne de liste la troncature est un choix de densité, mais ici le titre EST ce
   qu'on juge avant de valider un paiement — le couper au milieu fait décider sans savoir. */
.niti{font-size:13.5px;font-weight:600;min-width:0;overflow:hidden;overflow-wrap:anywhere;
  display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-clamp:2}
.niby{font-size:11.5px;color:var(--faint);margin-bottom:10px}
.ninote{font-size:12.5px;color:var(--dim);background:var(--field);border-radius:9px;padding:8px 11px;margin-bottom:10px;
  overflow-wrap:anywhere}
.nacts{display:flex;gap:6px;flex-wrap:wrap}
/* Sur téléphone la modale prend l'écran : à 390 px, une carte flottante avec 14 px de marge de
   chaque côté ne gagne rien et fait perdre 28 px de contenu. Coins droits, pas de marge, et la
   hauteur suit le contenu — c'est la modale qui défile, comme sur grand écran. */
@media (max-width:640px){
  .nmodal{padding:0}
  .nsheet,.nmodal-card{width:100%;min-height:100dvh;border-radius:0}
  .nmb{padding:14px 14px 20px}
  .ntable td.c{width:72px}
}

/* ---- Détail d'un bug (déplié en place) -----------------------------------------------------
   En place plutôt qu'en sous-modale : empiler une modale sur une modale fait perdre le fil, et
   on veut pouvoir comparer deux rapports en les ouvrant tous les deux. */
.nbug{cursor:pointer}
.nbug:hover .ntxt{color:var(--ink)}
.nbugd{padding:2px 0 10px}
.nbugd .nkv{display:flex;gap:10px;padding:4px 0;min-width:0}
.nbugd .nk{flex:0 0 92px;font-family:ui-monospace,Menlo,monospace;font-size:10.5px;color:var(--faint);
  letter-spacing:.4px;padding-top:2px}
.nbugd .nv{flex:1 1 auto;min-width:0;font-size:12.5px;color:var(--dim);white-space:pre-wrap;overflow-wrap:anywhere}
/* Attendu / obtenu : le cœur du rapport, on les distingue par la couleur du filet. */
.nbugd .nv.att{border-left:2px solid rgba(52,211,153,.4);padding-left:9px}
.nbugd .nv.obt{border-left:2px solid rgba(251,113,133,.45);padding-left:9px}
.nshots{display:flex;gap:8px;flex-wrap:wrap;margin-top:8px}
.nshots img{width:104px;height:104px;object-fit:cover;border-radius:9px;border:1px solid var(--line);
  background:var(--field);cursor:zoom-in}

/* ===== Social : le ping et son effet (cf. 11-social.js) ======================================
   L'emote n'a rien à voir avec les conversations : elle pop sur l'ÉCRAN, par-dessus tout, puis
   disparaît sans laisser de trace. Le calque est donc au-dessus des modales (z-index 60) et de
   l'écran de connexion (100), et ne capte aucun clic — on doit pouvoir continuer à taper pendant
   qu'une volée passe. */
#pingfx{position:fixed;inset:0;z-index:200;pointer-events:none;overflow:hidden}
.pemote{position:absolute;line-height:1;will-change:transform,opacity;opacity:0;
  filter:drop-shadow(0 6px 18px rgba(0,0,0,.55))}
/* L'image se dimensionne en `em` : la taille aléatoire est portée par le font-size du parent, donc
   un glyphe et une image custom suivent exactement la même mécanique. */
.pemote img{width:1em;height:1em;object-fit:contain;display:block}
.pemote span{display:block}
/* Deux animations superposées : `pemotePop` porte l'échelle, la montée et l'inclinaison (avec son
   rebond) ; `pemoteDrift` porte la rotation lente et continue. Les fondre en une seule keyframe
   obligerait à réécrire la rotation à chaque étape et tuerait le rebond. */
@keyframes pemotePop{
  0%  {opacity:0;transform:translate3d(0,14px,0) scale(.35) rotate(var(--r0))}
  16% {opacity:1;transform:translate3d(0,0,0) scale(1.18) rotate(calc(var(--r0) * .45))}
  28% {opacity:1;transform:translate3d(0,0,0) scale(1) rotate(calc(var(--r0) * .2))}
  72% {opacity:1}
  100%{opacity:0;transform:translate3d(0,-46px,0) scale(.94) rotate(var(--r1))}
}
@keyframes pemoteDrift{0%{transform:rotate(0deg)}100%{transform:rotate(var(--spin))}}
/* Bandeau signé : l'owner a tranché pour un ping signé — sans le nom, muter quelqu'un serait
   impossible, et une emote anonyme en plein écran est juste inquiétante. */
.pingsig{position:absolute;top:74px;left:50%;transform:translateX(-50%);display:flex;align-items:center;
  gap:9px;padding:9px 16px 9px 9px;border-radius:999px;background:rgba(12,12,26,.82);
  border:1px solid rgba(167,139,250,.5);box-shadow:0 10px 34px rgba(124,58,237,.42);
  backdrop-filter:blur(14px);font-size:13.5px;font-weight:600;color:var(--ink);opacity:0;
  animation:pingsigIn 2.6s cubic-bezier(.2,.9,.25,1) forwards;max-width:calc(100% - 32px)}
.pingsig b{color:var(--violet)}
.pingsig .pav{width:26px;height:26px;border-radius:999px;background:var(--grad-me);display:grid;
  place-items:center;font-size:12px;font-weight:800;flex:0 0 auto}
@keyframes pingsigIn{
  0%  {opacity:0;transform:translateX(-50%) translateY(-14px) scale(.9)}
  9%  {opacity:1;transform:translateX(-50%) translateY(0) scale(1)}
  82% {opacity:1;transform:translateX(-50%) translateY(0) scale(1)}
  100%{opacity:0;transform:translateX(-50%) translateY(-10px) scale(.97)}
}
/* Mouvement réduit : une volée d'emotes qui tournent est franchement pénible pour qui y est
   sensible. On garde l'information (l'emote et QUI l'envoie), on retire le tournoiement. */
@media (prefers-reduced-motion:reduce){
  .pemote{animation-duration:1.1s!important}
  .pemote span{animation:none!important}
  @keyframes pemotePop{0%{opacity:0}12%{opacity:1}70%{opacity:1}100%{opacity:0}}
}

/* ----- Écran Social ------------------------------------------------------------------------- */
/* Recherche d'emote. Même dessin que la barre des chats (.sbfind), mais ses classes vivent dans le
   media query de la colonne latérale : les réutiliser ici donnerait un champ sans style sur mobile.
   D'où un jeu de classes propre — le dessin est repris, pas la portée. */
.sofind{position:relative;display:flex;align-items:center;margin:0 0 9px}
.sofind .sofi{position:absolute;left:9px;display:grid;place-items:center;color:var(--faint);pointer-events:none}
.sofind input{width:100%;box-sizing:border-box;height:32px;padding:0 26px 0 28px;border-radius:9px;
  border:1px solid var(--line);background:var(--card);color:var(--ink);font:inherit;font-size:12.5px;outline:0}
.sofind input:focus{border-color:var(--violet)}
.sofind input::placeholder{color:var(--faint)}
.sofind input::-webkit-search-cancel-button{display:none} /* on a notre propre croix, alignée au thème */
.sofx{position:absolute;right:5px;width:20px;height:20px;display:grid;place-items:center;padding:0;
  border:0;border-radius:6px;background:none;color:var(--dim);cursor:pointer}
.sofx:hover{background:var(--card2);color:var(--ink)}
/* Compteur « 3 sur 27 », affiché seulement pendant une recherche. */
.sonb{margin-left:7px;font-weight:600;color:var(--faint);letter-spacing:0}
/* Le vide d'une recherche : il DOIT se lire, sinon une palette vide passe pour une panne. */
.sovide{padding:9px 2px;color:var(--dim);font-size:12px}
.sopal{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:10px}
.soem{width:46px;height:46px;border-radius:12px;border:1px solid var(--line);background:var(--card);
  font-size:23px;line-height:1;cursor:pointer;display:grid;place-items:center;padding:0;color:var(--ink)}
.soem:hover{border-color:rgba(167,139,250,.45)}
.soem.on{border-color:var(--violet);background:rgba(124,58,237,.18);box-shadow:0 0 0 1px rgba(167,139,250,.35)}
.soem img{width:26px;height:26px;object-fit:contain}
/* Aperçu de l'emote choisie, collé SOUS la palette : c'est le seul endroit où on la voit en grand,
   et il doit rester dans le champ de vision du clic. Il vivait dans le titre « ENVOYER … À », en bas
   de l'écran : il fallait scroller après avoir cliqué une vignette pour voir ce qu'on avait choisi.
   Bornes DURES sur l'image : le fichier source fait 192 px et sortait à sa taille native tant que
   rien ne le contraignait, ce qui coupait le titre en deux. */
.sopv{display:flex;align-items:center;gap:12px;min-width:0;margin:0 0 16px;padding:10px 12px;
  border:1px solid var(--line);border-radius:12px;background:var(--card)}
.sopvi{flex:0 0 auto;width:72px;height:72px;display:grid;place-items:center;font-size:44px;line-height:1}
.sopvi img{max-width:72px;max-height:72px;object-fit:contain}
.sopvt{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font-size:13px;color:var(--dim)}
/* Vignette d'un intertitre (« ENVOYER … À ») : un rappel de ce qui part, pas un aperçu. */
.dsem{display:inline-grid;place-items:center;vertical-align:-4px}
.dsem img{width:18px;height:18px;object-fit:contain}
/* Ajout d'une emote : replié par défaut, c'est un geste rare face à « envoyer un ping ». */
.soadd{border:1px solid var(--line);border-radius:12px;background:var(--card);padding:10px 12px}
.soaddt{background:none;border:0;color:var(--dim);font:inherit;font-size:12.5px;font-weight:600;
  cursor:pointer;padding:0;text-align:left;width:100%}
.soadd.open .soaddt{color:var(--ink)}
.soaddb{margin-top:11px}
.sorow2{display:flex;gap:8px;margin-bottom:8px;flex-wrap:wrap}
.sorow2>*{flex:1 1 150px;min-width:0}
.soaddb input[type=text],.soaddb input:not([type]),.soaddb input{font:inherit;font-size:13px;padding:8px 10px;
  border-radius:9px;border:1px solid var(--line);background:var(--field);color:var(--ink);outline:0;width:100%}
.sofile{font:inherit;font-size:12.5px;padding:8px 11px;border-radius:9px;border:1px dashed var(--line);
  background:var(--card);color:var(--ink);cursor:pointer}
.sofn{font-size:12px;color:var(--dim);margin-bottom:8px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Aperçu de l'image retenue, à côté de son origine : « image choisie » ne permet pas de vérifier
   qu'on n'a pas cliqué la mauvaise vignette dans une grille. */
.sofnimg{width:20px;height:20px;border-radius:5px;object-fit:cover;vertical-align:-5px;margin-right:5px}
.sosave{width:100%;height:40px;border:0;border-radius:10px;background:var(--grad-action);color:#fff;
  font-weight:700;font:inherit;font-size:13.5px;cursor:pointer}
.sosave:disabled{opacity:.55;cursor:default}
.socat{border:1px solid var(--line);border-radius:12px;background:var(--card);padding:2px 12px}
.socatr{display:flex;align-items:center;gap:9px;padding:7px 0;border-bottom:1px solid rgba(255,255,255,.05);min-width:0}
.socatr:last-child{border-bottom:0}
.socatp{font-size:20px;line-height:1;flex:0 0 auto;display:grid;place-items:center;width:24px}
.socatp img{width:22px;height:22px;object-fit:contain}
.sodel{background:none;border:0;color:var(--faint);font-size:13px;cursor:pointer;padding:2px 6px;flex:0 0 auto}
.sodel:hover{color:var(--red)}
/* Ligne d'une personne. min-width:0 sur le conteneur + .ntxt rétrécissable : un nom long ne doit
   pas pousser les boutons Ping / mute hors de l'écran. */
.sop .nmono{flex:0 0 auto}
.somuted{color:var(--amber)!important}
.sopg{flex:0 0 auto;font:inherit;font-size:12.5px;font-weight:700;padding:6px 14px;border-radius:999px;
  border:0;background:var(--grad-action);color:#fff;cursor:pointer}
.sopg:active{transform:scale(.96)}
.somute{flex:0 0 auto;background:none;border:1px solid var(--line);border-radius:999px;font-size:12px;
  line-height:1;padding:5px 8px;cursor:pointer;color:var(--dim)}
.somute.on{border-color:rgba(251,191,36,.5);background:rgba(251,191,36,.14)}
/* Retour d'information de l'écran Social. Il est DANS la modale et pas dans le bandeau du chat :
   flash() écrit sous le champ de saisie, donc derrière le voile de la modale, là où l'utilisateur
   ne peut rien lire — on cliquait « Ajouter au catalogue » sans jamais savoir ce qui s'était passé. */
.somsg{flex:0 0 auto;margin:0;padding:0 20px 14px;font-size:12.5px;font-weight:600;min-height:18px;
  overflow-wrap:anywhere}
/* Précision sous le bouton d'image : dire d'emblée qu'aucune taille n'est à respecter évite
   l'aller-retour « trop lourde » qui a bloqué l'owner. */
.sofile .sohint{display:block;font-size:11px;font-weight:400;color:var(--faint);margin-top:2px}

/* ---- colonne « En ce moment » (12-live.js) -----------------------------------------------------
   Elle est dans le shell, jamais dans un panneau : l'activité de l'équipe doit se voir sans qu'on
   la cherche. Mobile d'abord : bandeau horizontal glissant, sous la barre des chats. Desktop
   (>=901px, plus bas) : colonne à droite du fil. Un seul DOM pour les deux, comme #tabs. */
#tabs{order:0}
#main{order:2}
/* Le fond de cette colonne était un blanc à 1,5 %, qui ÉCLAIRCISSAIT. Il est remplacé par le voile
   sombre commun aux deux colonnes (déclaré en tête de fichier) : sur un ciel étoilé, éclaircir
   revenait à faire ressortir les étoiles là où on lit, exactement l'inverse du but. */
#live{order:1;display:flex;flex-direction:column;min-width:0;border-bottom:1px solid var(--line)}
.lvh{flex:0 0 auto;display:flex;align-items:center;gap:8px;padding:8px 12px}
.lvh b{font-size:10.5px;font-weight:700;letter-spacing:.6px;color:var(--dim)}
.lvcount{font-size:10.5px;font-weight:700;color:var(--faint);background:var(--card);border:1px solid var(--line);
  border-radius:999px;padding:1px 8px;min-width:20px;text-align:center}
.lvcount.on{color:var(--green);background:rgba(52,211,153,.14);border-color:transparent}
.lvtog{margin-left:auto;flex:0 0 auto;width:24px;height:24px;border-radius:8px;border:1px solid var(--line);
  background:none;color:var(--dim);font:inherit;font-size:13px;line-height:1;cursor:pointer;display:grid;place-items:center;padding:0}
.lvtog:hover{border-color:var(--violet);color:var(--ink)}
.lvbody{flex:1 1 auto;display:flex;gap:6px;padding:0 10px 9px;overflow-x:auto;overflow-y:hidden;scrollbar-width:none}
.lvbody::-webkit-scrollbar{display:none}
/* Une personne au travail. min-width:0 sur le bloc de texte : sans lui, un titre de fil long pousse
   le chrono hors de la carte au lieu de se faire tronquer. */
/* L'ITEM = la personne ET son dépli de chantiers, dans un seul bloc. C'est lui qui est l'élément du
   bandeau horizontal en mobile : sans ce conteneur, les chantiers dépliés partaient À CÔTÉ de la
   carte au lieu de dessous (le bandeau est un flex row), ce que l'ancien CSS masquait au lieu de
   corriger. Un dépli qu'on ouvre et qui ne montre rien serait pire que pas de dépli du tout. */
.lvitem{flex:0 0 auto;max-width:270px;min-width:0;display:flex;flex-direction:column;gap:2px}
.lvp{display:flex;align-items:flex-start;gap:9px;padding:8px 10px;border-radius:11px;
  background:var(--card);border:1px solid var(--line)}
.lvp.go{cursor:pointer}
.lvp.go:hover{border-color:var(--violet);background:var(--card2)}
/* L'avatar porte DEUX informations distinctes, et il faut les garder distinctes :
   - le FOND (dégradé `.on`) dit que quelque chose tourne — un agent, ou la personne qui code ;
   - la PASTILLE dit si la PERSONNE est là. Verte en ligne, ambre absente, rien hors ligne.
   Les confondre était le défaut d'origine : un worker qui moulinait pour quelqu'un d'endormi
   allumait le même vert que sa présence réelle. Repliée en rail, la pastille reste lisible parce
   qu'elle est la seule chose qui subsiste. */
.lvav{flex:0 0 auto;position:relative;width:28px;height:28px;border-radius:999px;display:grid;place-items:center;
  font-size:11px;font-weight:700;color:var(--dim);background:rgba(255,255,255,.08)}
.lvav.on{color:#fff;background:var(--grad-me)}
.lvav img{width:100%;height:100%;border-radius:999px;object-fit:cover;display:block}
.lvav.st-en-ligne::after,.lvav.st-absent::after{content:"";position:absolute;right:-1px;bottom:-1px;
  width:9px;height:9px;border-radius:999px;border:2px solid var(--bg1)}
.lvav.st-en-ligne::after{background:var(--green)}
.lvav.st-absent::after{background:var(--amber)}
/* LA PASTILLE D'APPAREIL (2026-08-20). Même coin, même taille utile que le point ci-dessus, qu'elle
   REMPLACE — d'où `.lvav.dev::after{display:none}` : superposés, les deux marqueurs se mangeraient.
   La couleur reste celle de l'état, le pictogramme dit l'appareil. Le disque de fond est celui du
   panneau et pas le picto lui-même : sur une photo de profil claire, un trait vert de neuf pixels
   sans fond devient illisible, ce que le point plein n'avait pas comme problème. */
.lvav.dev::after{display:none}
.lvdev{position:absolute;right:-3px;bottom:-3px;width:13px;height:13px;border-radius:999px;
  background:var(--bg1);border:1.5px solid var(--bg1);display:grid;place-items:center}
.lvdev svg{width:100%;height:100%;display:block}
.lvdev.st-en-ligne{color:var(--green)}
.lvdev.st-absent{color:var(--amber)}
/* Hors ligne : l'avatar s'efface au lieu de disparaître. La personne existe, elle n'est pas là. */
.lvp.st-hors-ligne .lvav{opacity:.45}
.lvp.st-hors-ligne .lvnn{color:var(--dim)}
.lvb2{min-width:0;flex:1 1 auto;display:flex;flex-direction:column;gap:2px}
/* LA LIGNE DU NOM PASSE A DEUX LIGNES PLUTOT QUE D'ECRASER LE NOM. Elle porte jusqu'a trois badges
   incompressibles (« toi », le niveau, « 2 en cours ») et, sur un roster, le nom est ce qu'on vient
   lire : mesure, « Timothe Legrand-Puylapelle » tombait a 78 px pour 150 px necessaires, soit
   « Timothe Le… », et meme « Quang Nguyen » y passait. Le `min-width` donne au nom un plancher : en
   dessous, ce sont les badges qui vont a la ligne suivante, et le nom retrouve toute la largeur. Un
   nom court ne declenche rien — la carte ne grandit que quand elle en a besoin. */
.lvn{display:flex;align-items:center;flex-wrap:wrap;gap:5px;min-width:0;font-size:12.5px;font-weight:600;color:var(--ink);white-space:nowrap}
.lvnn{flex:0 1 auto;min-width:min(100%,6em);overflow:hidden;text-overflow:ellipsis}
.lvme{flex:0 0 auto;font-size:9px;font-weight:700;letter-spacing:.3px;text-transform:uppercase;color:var(--dim);
  background:rgba(255,255,255,.08);border-radius:999px;padding:1px 5px}
.lvsrc{flex:0 0 auto;font-size:9px;font-weight:700;letter-spacing:.3px;text-transform:uppercase;border-radius:999px;padding:1px 5px}
.lvsrc.nyx{background:rgba(124,58,237,.24);color:#c4b5fd}
.lvsrc.loc{background:rgba(34,211,238,.16);color:#67e8f9}
/* Niveau d'expérience. Volontairement le badge le plus discret de la ligne : c'est un jeu, et un
   niveau qui crierait plus fort que la tâche en cours ferait croire à un classement de performance. */
.lvlv{flex:0 0 auto;font-size:9px;font-weight:700;letter-spacing:.2px;border-radius:999px;padding:1px 5px;
  background:rgba(226,168,59,.16);color:#e2a83b;font-variant-numeric:tabular-nums}
/* Le compteur d'agents. Il porte l'information que la colonne perdait entièrement : combien de
   workers tournent pour cette personne, indépendamment du fait qu'elle soit là ou non. */
/* Pas d'uppercase ici, contrairement aux autres badges : « 2 EN COURS » mesure une dizaine de pixels
   de plus que « 2 en cours » et, à ce prix-là, il poussait la ligne du nom à passer sur deux lignes
   pour des noms qui tenaient. Le badge de niveau (« Nv 5 ») est déjà en casse mixte. */
.lvag{flex:0 0 auto;white-space:nowrap;font-size:9px;font-weight:700;letter-spacing:.2px;
  border-radius:999px;padding:1px 5px;
  background:rgba(124,58,237,.24);color:#c4b5fd;font-variant-numeric:tabular-nums}
/* Bouton d'emote : TOUJOURS visible, et c'est une correction du 2026-08-05. Il était en `opacity:0`
   révélé au survol — donc littéralement inexistant sur un écran tactile, où il n'y a pas de survol,
   et illisible même à la souris (retour owner). Un geste qu'on ne voit pas est un geste que personne
   ne fait. Il porte donc un fond et une bordure au repos, et s'affirme au survol et au focus. */
.lvemo{flex:0 0 auto;align-self:center;width:26px;height:26px;padding:0;border-radius:8px;
  border:1px solid var(--line);background:var(--card2);color:var(--dim);font:inherit;font-size:14px;
  line-height:1;cursor:pointer;transition:color .12s,border-color .12s,background .12s}
.lvp:hover .lvemo{color:var(--ink)}
.lvemo:hover,.lvemo:focus-visible{border-color:var(--violet);color:var(--violet);background:rgba(124,58,237,.16)}
/* Le caret du dépli. Seule marque qui doit survivre à la fermeture : elle dit qu'il y a quelque
   chose à ouvrir. Il pivote à l'ouverture, comme tous les dépliants du cockpit. */
.lvcar{flex:0 0 auto;align-self:center;color:var(--faint);font-size:11px;line-height:1;
  transition:transform .14s,color .12s}
.lvcar.on{transform:rotate(180deg);color:var(--violet)}
.lvp.go:hover .lvcar{color:var(--ink)}
/* Le dépli : les chantiers de la personne, en retrait sous sa carte. */
.lvsub{display:flex;flex-direction:column;gap:3px;margin:1px 0 4px 22px;min-width:0}
/* Un chantier, en détail. Deux à trois lignes : l'en-tête (projet, nature, chrono), la description,
   puis l'activité brute du worker. Le filet vertical le rattache visuellement à la personne —
   sans lui, trois chantiers ressemblent à trois personnes, le défaut qu'on répare depuis le 5 août. */
.lvsl{display:flex;flex-direction:column;gap:2px;min-width:0;padding:6px 9px;
  border-left:2px solid var(--line);border-radius:0 9px 9px 0;background:rgba(255,255,255,.025)}
.lvsl.go{cursor:pointer}
.lvsl.go:hover{border-left-color:var(--violet);background:var(--card2)}
.lvslh{display:flex;align-items:center;flex-wrap:wrap;gap:5px;min-width:0}
/* L'activité brute (« exécute les tests ») : ce qu'il tape, pas ce qu'il fait. Donc en second, gris,
   et jamais au prix de la description. */
.lvsla{min-width:0;font-size:10.5px;line-height:1.4;color:var(--faint);white-space:normal;overflow-wrap:anywhere}
/* La description N'EST PAS TRONQUÉE : un dépli s'ouvre en hauteur, il n'a aucune raison de couper
   « Refonte de la colonne Équipe du… » — c'est précisément la phrase qu'on vient lire. `overflow-wrap`
   plutôt que `word-break` : une longue branche git sans espace (`feat/paiement-apple-in-app`) se coupe
   au besoin, mais les mots ordinaires restent entiers. */
.lvslt{min-width:0;font-size:11.5px;line-height:1.4;color:var(--ink);white-space:normal;overflow-wrap:anywhere}
/* Le PROJET de la sous-ligne. Sans lui, deux chantiers d'une même personne ne montrent que deux
   titres de tâche et la colonne ne dit toujours pas SUR QUOI elle est — le défaut que la sous-liste
   était censée réparer (retour owner du 2026-08-06). Il ne rétrécit pas et c'est la tâche qui se
   tronque : le projet est justement ce qu'on vient lire. Bornée quand même, un slug à rallonge ne
   doit pas manger toute la sous-ligne. Violet = un agent Nyx, cyan = la personne à son clavier,
   mêmes couleurs que les badges de la ligne du dessus. */
.lvslp{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font-size:9.5px;font-weight:700;letter-spacing:.3px;text-transform:uppercase;border-radius:999px;padding:1px 6px;
  background:rgba(255,255,255,.09);color:var(--ink)}
/* Palette d'emotes, ancrée sous le bouton par popAncrer (01-socle.js). Grille souple : le catalogue
   d'équipe grandit, une largeur fixe finirait par déborder. */
.lvpal{position:fixed;z-index:90;display:grid;grid-template-columns:repeat(6,32px);gap:4px;padding:8px;
  width:max-content;max-width:min(260px,92vw);overflow:auto;background:var(--grad-surface);
  border:1px solid var(--line);border-radius:12px;box-shadow:var(--shadow-drawer)}
.lvpalb{width:32px;height:32px;padding:0;display:grid;place-items:center;border-radius:8px;
  border:1px solid transparent;background:none;font-size:17px;line-height:1;cursor:pointer}
.lvpalb:hover{background:var(--card2);border-color:var(--violet)}
.lvpalb img{width:22px;height:22px;object-fit:contain;display:block}
.lvt{font-size:12px;color:var(--ink);opacity:.92;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* La ligne du bas porte la méta, les étoiles ET le chrono. Seule la méta se laisse tronquer : le
   chrono et le compteur d'étoiles ont une largeur qu'on ne peut pas couper à moitié. min-width:0 sur
   le conteneur, sinon le texte long pousse le chrono hors de la carte au lieu de se raccourcir. */
/* Même règle que la ligne du nom : quand « 2 projets en cours », « voir plus » et le chrono ne tiennent
   pas ensemble, c'est le chrono qui passe à la ligne — pas le texte qui se fait couper en « 2 projets en
   cou… ». Un compte tronqué ne compte plus rien. */
.lvm{display:flex;align-items:center;flex-wrap:wrap;gap:4px;min-width:0;font-size:10.5px;color:var(--dim)}
.lvmt{flex:0 1 auto;min-width:min(100%,9em);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.lvm .lvago{margin-left:auto}
/* --dim et non --faint : mesuré sous le seuil AA (4,36 à 4,48 pour 4,5 exigé) sur le fond de la
   colonne, plus clair en bandeau mobile qu'en colonne à cause du dégradé violet de la page. */
.lvago{flex:0 0 auto;align-self:center;font-size:10.5px;color:var(--dim);white-space:nowrap;font-variant-numeric:tabular-nums}
.lvvide{flex:0 0 auto;align-self:center;font-size:11.5px;color:var(--faint);padding:4px 2px}
/* ---- Étoiles d'encouragement + panneau « Où en est X » (cf. 12-live.js) ----
   L'étoile sur la carte est volontairement discrète : c'est un signal d'équipe, pas un score. */
/* « voir plus » : le mot qui dit qu'il y a un dépli. Le caret le dit déjà à qui sait le lire ; sur une
   colonne qu'on balaie, un mot se remarque là où un chevron passe inaperçu. */
.lvmore{flex:0 0 auto;font-size:10px;font-weight:600;color:var(--violet);white-space:nowrap}
.lvp.go:hover .lvmore{text-decoration:underline}
.lvstar{flex:0 0 auto;margin-left:6px;font-size:10px;color:#e2a83b;white-space:nowrap}
.lvstar.on{font-weight:700}
#lvov{position:fixed;inset:0;z-index:80;display:none;flex-direction:column;background:rgba(4,4,12,.72);backdrop-filter:blur(6px)}
#lvov .lvpanel{margin:auto;width:min(520px,94vw);max-height:min(86vh,860px);display:flex;flex-direction:column;
               background:var(--grad-surface);border:1px solid var(--line);border-radius:var(--r-lg);box-shadow:var(--shadow-drawer);overflow:hidden}
#lvov .lvhead{display:flex;align-items:center;gap:10px;padding:12px 16px;border-bottom:1px solid var(--line);flex:0 0 auto}
#lvov .lvhead b{flex:1;min-width:0;font-size:14px;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
#lvov .lvclose{flex:0 0 auto;width:30px;height:30px;border-radius:var(--r-sm);border:1px solid var(--line);
               background:var(--card2);color:var(--ink);font-size:15px;cursor:pointer}
#lvov .lvclose:hover{border-color:var(--violet)}
#lvov .lvbody2{padding:16px 18px 18px;overflow-y:auto;-webkit-overflow-scrolling:touch}
.lvsum-top{display:flex;align-items:center;gap:11px;margin-bottom:12px}
.lvsum-nom{font-size:14.5px;font-weight:700;color:var(--ink)}
.lvsum-sub{margin-top:2px;font-size:11.5px;color:var(--faint)}
.lvsum-tache{padding:10px 12px;border-radius:10px;background:var(--card2);border:1px solid var(--line);
             font-size:13px;line-height:1.45;color:var(--ink)}
.lvsum-li{margin:12px 0 0;padding-left:18px;font-size:12.5px;line-height:1.6;color:var(--dim)}
.lvsum-t{margin:16px 0 7px;font-size:10px;font-weight:700;letter-spacing:.5px;text-transform:uppercase;color:var(--faint)}
/* Journal d'actions : une ligne par étape, tronquée. Trois lignes qui débordent valent moins qu'une
   liste qu'on parcourt d'un coup d'œil, et c'est un survol qu'on vient chercher ici. */
.lvsteps{display:flex;flex-direction:column;gap:3px}
.lvstep{padding:5px 9px;border-radius:8px;background:rgba(255,255,255,.035);font-size:11.5px;color:var(--dim);
        overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-variant-numeric:tabular-nums}
.lvstep.ok{color:#6ee7b7}
.lvstep.ko{color:#fca5a5}
.lvsum-note{margin-top:14px;font-size:11.5px;line-height:1.5;color:var(--faint)}
.lvstarbtn{margin-top:14px;width:100%;display:flex;align-items:center;justify-content:center;gap:8px;
           height:38px;border-radius:10px;border:1px solid var(--line);background:var(--card2);
           color:var(--ink);font:inherit;font-size:13px;font-weight:600;cursor:pointer}
.lvstarbtn:hover{border-color:#e2a83b}
.lvstarbtn.on{border-color:#e2a83b;background:rgba(226,168,59,.14);color:#e2a83b}
.lvstarbtn .n{padding:1px 7px;border-radius:999px;background:rgba(255,255,255,.1);font-size:11px}
/* « Ouvrir la conversation » : même gabarit que le bouton d'encouragement, teinte de la marque. Les
   deux se suivent dans la fiche et doivent se lire comme deux actions du même écran, pas comme un
   bouton et un lien. Il n'apparaît que sur un fil qu'on a le droit de lire (`ouvrable`). */
.lvopenbtn{margin-top:14px;width:100%;display:flex;align-items:center;justify-content:center;
           height:38px;border-radius:10px;border:1px solid var(--line);background:var(--card2);
           color:var(--ink);font:inherit;font-size:13px;font-weight:600;cursor:pointer}
.lvopenbtn:hover{border-color:var(--violet);background:rgba(167,139,250,.14);color:var(--violet)}
/* EN BANDEAU MOBILE, la place va à ceux qui sont là : les hors-ligne sont masqués. Ils réapparaissent
   dès qu'on a une colonne, plus bas. C'est le même DOM dans les deux cas — ni second rendu, ni
   seconde liste à tenir d'accord avec la première. Le dépli des chantiers, lui, N'EST PLUS MASQUÉ :
   il n'apparaît que sur un clic explicite, et un geste qui ne produit rien à l'écran est un bug. */
.lvitem.st-hors-ligne{display:none}
#live.mini .lvbody{display:none}
@media (min-width:901px){
  #live{order:3;flex:0 0 var(--lvw,300px);width:var(--lvw,300px);border-bottom:0;border-left:1px solid var(--line);min-height:0}
  #live.mini{--lvw:58px}
  .lvh{padding:10px 12px;border-bottom:1px solid var(--line)}
  .lvbody{flex-direction:column;overflow-x:hidden;overflow-y:auto;padding:9px}
  .lvitem{flex:0 0 auto;max-width:none;width:100%}
  .lvvide{align-self:flex-start;padding:6px 2px}
  /* La colonne a la hauteur pour montrer TOUTE l'équipe, y compris qui n'est pas là — c'est la
     moitié de l'information qu'on vient y chercher. La ligne hors ligne est allégée : pas de fond,
     pas de bordure, elle ne doit pas peser autant qu'une personne au travail. */
  .lvitem.st-hors-ligne{display:flex}
  .lvp.st-hors-ligne{background:none;border-color:transparent;padding-top:5px;padding-bottom:5px}
  /* Repliée, la colonne dit toujours QUI est là : on ne garde que les pastilles et le compteur.
     Une colonne repliée qui n'afficherait plus rien reviendrait à la cacher. Les hors-ligne en
     sortent : un rail de 58 px doit répondre « qui est dispo », pas dérouler l'annuaire. */
  #live.mini .lvh{flex-direction:column;gap:6px;padding:10px 6px}
  #live.mini .lvh b{display:none}
  #live.mini .lvtog{margin-left:0}
  #live.mini .lvbody{display:flex;align-items:center;padding:9px 6px}
  #live.mini .lvp{justify-content:center;padding:6px;background:none;border-color:transparent}
  #live.mini .lvb2,#live.mini .lvago,#live.mini .lvvide,#live.mini .lvemo,#live.mini .lvcar{display:none}
  #live.mini .lvitem.st-hors-ligne,#live.mini .lvsub{display:none}
}

/* ===== « Mes photos » du profil (07-profil.js) ============================================= */
/* Même grille souple que le sélecteur : autant de colonnes que la largeur permet, sans déborder
   à 390 px. Le tiroir du profil est étroit, d'où des cases plus petites. */
.pfgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(74px,1fr));gap:8px}
/* C'est un <button> : chaque case OUVRE le fichier (agrandir, télécharger, partager). D'où le
   `padding:0` et le `font:inherit`, que le navigateur pose sinon, et le focus visible — au clavier
   comme au doigt, une case est un geste, pas une image décorative. */
.pfcell{position:relative;aspect-ratio:1;border:1px solid var(--line);border-radius:10px;
  background:var(--field);overflow:hidden;display:flex;align-items:center;justify-content:center;
  padding:0;font:inherit;color:inherit;cursor:pointer}
.pfcell:hover{border-color:var(--violet)}
.pfcell:focus-visible{outline:2px solid var(--violet);outline-offset:2px}
.pfcell img{width:100%;height:100%;object-fit:cover}
.pfnoimg{font-size:22px;opacity:.65}
/* Fond OPAQUE et non un dégradé : par-dessus une image quelconque le contraste dépend de l'image,
   donc il n'est jamais garanti (mesuré à 2.57 pour 4.5 exigé sur le sélecteur, même erreur). */
.pfcap{position:absolute;left:0;right:0;bottom:0;font-size:9px;color:#e9e9f2;background:#15151f;
  padding:2px 3px;display:flex;align-items:center;gap:3px;min-width:0}
/* min-width:0 + flex:0 1 auto : sans ça un espace/famille long pousse le marqueur « éq. » hors de
   la case au lieu de se tronquer (le piège flexbox déjà rencontré sur les onglets). */
.pfcapt{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pfteam{flex:0 0 auto;margin-left:auto;font-size:8px;font-weight:700;color:#c9b8ff}

/* ===== Sélecteur d'image (13-picker.js) ==================================================== */
/* Un seul écran pour les deux chemins : « Mon appareil » et « La bibliothèque » (la GED). */
.pkwrap{position:fixed;inset:0;z-index:80;background:rgba(0,0,0,.62);backdrop-filter:blur(3px);
  display:flex;align-items:center;justify-content:center;padding:16px}
.pkbox{width:min(560px,100%);max-height:min(78vh,620px);display:flex;flex-direction:column;
  background:var(--panel,var(--card));border:1px solid var(--line);border-radius:16px;overflow:hidden}
.pkhead{display:flex;align-items:center;gap:10px;padding:13px 15px;border-bottom:1px solid var(--line);
  min-width:0}                                   /* min-width:0 : sinon un titre long pousse la croix dehors */
.pkhead b{font-size:14px;flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pkx{flex:0 0 auto;width:28px;height:28px;border:0;border-radius:8px;background:var(--field);
  color:var(--dim);font:inherit;cursor:pointer}
.pktabs{display:flex;gap:6px;padding:11px 15px 0}
.pktab{flex:0 1 auto;min-width:0;font:inherit;font-size:12.5px;padding:7px 12px;border-radius:9px;
  border:1px solid var(--line);background:var(--card);color:var(--dim);cursor:pointer;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* L'onglet ACTIF doit dominer. Au premier jet il prenait `--field` (#0e0e1c, plus SOMBRE que
   `--card`), donc l'onglet inactif ressortait davantage que l'actif — vu sur la capture, pas dans
   les mesures, qui ne savent pas dire « c'est à l'envers ». */
.pktab.on{background:var(--card2);color:var(--ink);font-weight:700;border-color:var(--violet)}
.pkrech{margin:10px 15px 0;font:inherit;font-size:13px;padding:8px 10px;border-radius:9px;
  border:1px solid var(--line);background:var(--field);color:var(--ink);outline:0}
.pkcorps{flex:1 1 auto;min-height:120px;overflow-y:auto;padding:13px 15px}
.pkmsg{padding:0 15px 13px;font-size:12px;color:var(--dim);min-height:16px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Grille souple : autant de colonnes que la largeur permet, sans jamais déborder à 390 px. */
.pkgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(88px,1fr));gap:9px}
.pkcell{position:relative;padding:0;border:1px solid var(--line);border-radius:11px;background:var(--field);
  cursor:pointer;overflow:hidden;aspect-ratio:1;display:flex;align-items:center;justify-content:center}
.pkcell img{width:100%;height:100%;object-fit:contain;padding:7px;box-sizing:border-box}
.pkcell:hover{border-color:var(--accent,var(--ink))}
.pkcell.pkko{opacity:.55;cursor:default}
/* Le badge REMPLACE la légende au lieu de se superposer : les deux étaient posés en bas de la
   cellule et se chevauchaient, on lisait « introuvable » par-dessus « nyx/brand ». Vu sur la
   capture — un chevauchement à l'intérieur d'un pseudo-élément qu'aucune mesure ne remonte. */
.pkcell.pkko .pkcap{display:none}
.pkcell.pkko::after{content:'introuvable';position:absolute;inset:auto 0 0;font-size:10px;
  background:#4a1622;color:#ffdde3;padding:3px 0}
/* Légende sur fond OPAQUE, et pas sur un dégradé translucide comme au premier jet : par-dessus une
   image quelconque, le contraste dépend de l'image, donc il n'est jamais garanti. Mesuré par
   nyxd/ui-review.sh sur les trois largeurs — 2.57 pour 4.5 exigé, sur les 7 vignettes. Le fond
   opaque le rend indépendant du contenu ; la taille reste petite mais lisible. */
.pkcap{position:absolute;left:0;right:0;bottom:0;font-size:10px;color:#e9e9f2;
  background:#15151f;padding:3px 4px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pklocal{display:flex;flex-direction:column;align-items:center;gap:9px;padding:22px 0}
.pkbtn{font:inherit;font-size:13px;padding:9px 14px;border-radius:10px;border:1px solid var(--line);
  background:var(--card);color:var(--ink);cursor:pointer}
.pkbig{padding:13px 20px;border-style:dashed;font-size:14px}
.pkbtn:disabled{opacity:.55;cursor:default}
.pkvide{text-align:center;color:var(--dim);font-size:13px;padding:26px 8px;line-height:1.5}
.pkvide.pkerr{color:var(--red)}
.pkhint{font-size:11.5px;color:var(--faint)}
/* Un fichier SANS vignette (PDF, .docx, vocal) : `put.py` n'en fabrique que pour les images, et ce
   n'est pas un échec. La case affiche alors son icône plutôt qu'un cadre vide qu'on cliquerait pour
   rien. */
.pkico{font-size:28px;line-height:1;opacity:.85}
/* « Mes fichiers » est en CONSULTATION : ses entrées sont privées, donc sans URL — leur fichier vit
   dans un bucket dont la clé ne quitte pas la VM. On ne peut pas les joindre à un message, et le
   curseur ne doit pas laisser croire le contraire. */
.pkcell.pkro{cursor:default}
.pkcell.pkro:hover{border-color:var(--line)}
/* Bascule « pour l'équipe / pour moi » de l'écran Bibliothèque. Deux boutons plutôt qu'une case à
   cocher : publier n'est pas un détail, et l'état choisi doit se lire d'un coup d'œil sans avoir à
   interpréter une coche. Même règle que les onglets — l'ACTIF domine. */
.pkvis{display:flex;gap:8px;flex-wrap:wrap;justify-content:center}
.pkvisb{flex:0 1 auto;min-width:0;font:inherit;font-size:12.5px;padding:8px 13px;border-radius:9px;
  border:1px solid var(--line);background:var(--card);color:var(--dim);cursor:pointer;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pkvisb.on{background:var(--card2);color:var(--ink);font-weight:700;border-color:var(--violet)}

/* ============================== SON (14-son.js) =======================   Le bouton de la barre porte une teinte propre : demande explicite de l'owner, il doit se REPÉRER
   vite, parce qu'on le cherche au moment précis où une réunion commence et où il faut couper en
   trois secondes. L'état coupé vire au rouge et l'icône se barre — l'information utile n'est pas
   « il y a des réglages de son », c'est « est-ce que ça va sonner, là, maintenant ». */
.menu-btn.son-btn{border-color:rgba(34,211,238,.42);background:rgba(34,211,238,.10)}
.menu-btn.son-btn .ic-e svg{stroke:var(--cyan)}
.menu-btn.son-btn.off{border-color:rgba(251,113,133,.45);background:rgba(251,113,133,.10)}
.menu-btn.son-btn.off .ic-e svg{stroke:var(--red)}
.menu-btn.son-btn.off .lbl{color:var(--red)}
/* panneau */
#sonbody{padding:16px;overflow-y:auto}
.son-mute{width:100%;font:inherit;font-weight:700;font-size:13px;padding:13px 14px;border-radius:var(--r-md);border:1px solid var(--line);background:var(--card2);color:var(--ink);cursor:pointer;text-align:left;line-height:1.4}
.son-mute:hover{border-color:var(--violet)}
.son-mute.on{border-color:rgba(251,113,133,.5);background:rgba(251,113,133,.12);color:var(--red)}
.son-note{font-size:11.5px;color:var(--faint);line-height:1.5;margin:9px 0 0}
.son-h{font-size:11px;font-weight:700;letter-spacing:.5px;text-transform:uppercase;color:var(--dim);margin:22px 0 8px}
.son-row{display:flex;align-items:center;gap:12px;padding:11px 12px;border:1px solid var(--line);border-radius:var(--r-md);background:var(--card);margin-bottom:8px;cursor:pointer}
.son-row:hover{border-color:rgba(124,58,237,.4)}
/* min-width:0 : sans lui, le texte non rétrécissable pousse la case à cocher hors du panneau */
.son-txt{display:flex;flex-direction:column;gap:2px;flex:1 1 auto;min-width:0}
.son-txt b{font-size:13px;font-weight:600}
.son-txt span{font-size:11.5px;color:var(--faint);line-height:1.4}
.son-check{flex:0 0 auto;width:19px;height:19px;accent-color:var(--violet);cursor:pointer}
/* ---- file de DÉCISIONS (panneau générique : une automatisation propose, on coche) ---- */
.dec-card{border:1px solid var(--line);background:linear-gradient(180deg,rgba(255,255,255,.05),rgba(255,255,255,.02));
  border-radius:var(--r-lg);padding:14px;margin:12px 0;box-shadow:0 8px 30px rgba(0,0,0,.28)}
.dec-h{display:flex;align-items:center;gap:8px;min-width:0}
/* Le titre s'ENROULE, il ne se coupe plus : « 20 leçon(s) attendent ton avis… » ne disait pas ce qui
   attendait. Sur une carte qui tient toute la largeur du panneau, un titre sur deux lignes coûte
   dix pixels ; un titre tronqué coûte un aller-retour pour comprendre de quoi il s'agit. */
.dec-h b{flex:1 1 auto;min-width:0;font-size:16px;font-weight:750;letter-spacing:-.2px}
.dec-job{flex:0 0 auto;font-size:11px;color:var(--dim);border:1px solid var(--line);border-radius:999px;padding:1px 7px}
/* À QUI S'ADRESSE LA CARTE : plein, pas en contour, contrairement au nom du job juste à côté. Quand
   deux cartes du même producteur se suivent le même jour, c'est le SEUL repère qui les sépare au
   premier coup d'œil — le rendre aussi discret que le reste, c'était le défaut signalé le 11/08. */
.dec-a{flex:0 0 auto;font-size:11px;font-weight:650;border-radius:var(--r-pill);padding:1px 8px;
  color:var(--dim);background:var(--line)}
.dec-a.moi{color:var(--violet);background:rgba(167,139,250,.16);box-shadow:inset 0 0 0 1px rgba(167,139,250,.38)}
.dec-wait{flex:0 0 auto;font-size:11px;color:var(--amber,#e0a33e)}
.dec-why{color:var(--dim);font-size:13px;margin:6px 0 8px;white-space:pre-wrap}
.dec-items{display:flex;flex-direction:column;gap:8px;margin-bottom:10px}
.dec-item{display:flex;gap:8px;align-items:flex-start;padding:7px 9px;border:1px solid var(--line);
  border-radius:8px;background:var(--card2);cursor:pointer;min-width:0}
.dec-item:hover{border-color:var(--violet)}
.dec-item input{margin-top:3px;flex:0 0 auto}
.dec-lbl{min-width:0;display:flex;flex-direction:column;gap:2px}
.dec-lbl em{color:var(--dim);font-size:12px;font-style:normal}
.dec-eff{font-style:normal;font-size:11px;color:var(--dim);border:1px solid var(--line);border-radius:999px;padding:0 6px}
.dec-note{width:100%;box-sizing:border-box;background:var(--card2);color:var(--ink);
  border:1px solid var(--line);border-radius:8px;padding:6px 8px;font:inherit;font-size:13px;resize:vertical}
.dec-actions{display:flex;gap:8px;align-items:center;margin-top:8px;flex-wrap:wrap}
.dec-ok,.dec-rien{border:1px solid var(--line);background:none;color:var(--dim);
  border-radius:var(--r-pill);padding:9px 16px;cursor:pointer;font:inherit;font-size:12.5px}
/* Le bouton qui ENGAGE se voit : valider écrit dans le corpus de comportement de toute la maison,
   il n'a pas à ressembler à « rien pour cette fois », qui est son contraire. */
.dec-ok{border:0;background:var(--grad-action);color:#fff;font-weight:800;box-shadow:0 4px 14px rgba(124,58,237,.35)}
.dec-ok:disabled,.dec-rien:disabled{opacity:.5;cursor:default}
/* ---- une proposition RICHE : phrase entière, réglage visible, dossier dépliable ----
   La forme suit les cartes de vote (`.feat`), sur demande de l'owner : deux écrans où l'on tranche
   au même endroit du cockpit n'ont aucune raison de se ressembler de loin et de diverger de près. */
.dec-bar{display:flex;align-items:center;gap:10px;margin:2px 0 9px;font-size:12px;color:var(--faint)}
.dec-cnt b{color:var(--ink)}
.dec-all{margin-left:auto;font:inherit;font-size:11.5px;font-weight:700;color:var(--dim);background:none;
  border:1px solid var(--line);border-radius:var(--r-pill);padding:3px 10px;cursor:pointer}
.dec-all:hover{color:var(--ink);border-color:var(--violet)}
.dec-lsn{border:1px solid var(--line);border-radius:var(--r-md);background:rgba(255,255,255,.028);transition:border-color .15s,background .15s}
/* ACQUIS = VERT, ET FRANC (owner, 2026-08-12 : « quand c'est validé ça devrait être encadré en vert »).
   Le violet servait à « coché, pas encore envoyé » — un état qui n'existe plus depuis que le verdict
   se pose d'un clic. Il ne reste que des états FINAUX, et chacun a sa couleur : vert on garde, barré
   on jette, ambre on renvoie à réécrire. */
.dec-lsn.pris,.dec-item.pris{border-color:rgba(52,211,153,.55);background:rgba(52,211,153,.09)}
.dec-lsn.revu,.dec-item.revu{border-color:rgba(251,191,36,.45);background:rgba(251,191,36,.07)}
.dec-rev{font-size:10.5px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--amber);
  border:1px solid rgba(251,191,36,.45);border-radius:var(--r-pill);padding:1px 7px;margin-left:7px;white-space:nowrap}
/* LES TROIS GESTES. Ils vivent sur la ligne, alignés à droite du texte : c'est le seul endroit où la
   décision se prend en lisant la proposition, et plus au fond de la carte. */
.dec-actes{display:flex;flex-direction:column;gap:4px;flex:0 0 auto;align-self:flex-start}
.dec-act{font-size:11px;font-weight:600;border-radius:var(--r-pill);padding:3px 10px;cursor:pointer;
  border:1px solid var(--line);background:transparent;color:var(--dim);white-space:nowrap}
.dec-act.go:hover{color:var(--green);border-color:rgba(52,211,153,.55);background:rgba(52,211,153,.10)}
.dec-act.rev:hover{color:var(--amber);border-color:rgba(251,191,36,.5);background:rgba(251,191,36,.10)}
.dec-act.no:hover{color:var(--red);border-color:rgba(251,113,133,.5);background:rgba(251,113,133,.10)}
/* Annuler sa réponse : le seul geste qui DÉFAIT, donc il ne prend aucune des trois couleurs de verdict.
   Il reste gris au survol pour se lire comme un retour en arrière et pas comme un quatrième avis. */
.dec-act.annul:hover{color:var(--fg);border-color:var(--line-strong,var(--line));background:rgba(255,255,255,.06)}
/* Déposé, pas encore confirmé par le bus. Discret par construction : le verdict est DÉJÀ affiché dans
   son état final, ceci ne dit que « c'est en route », pas « attends ». */
.dec-encours{flex:0 0 auto;align-self:flex-start;font-size:13px;opacity:.65}
.dec-lsn.open{border-color:rgba(167,139,250,.4)}
.dec-lsn-h{display:flex;gap:10px;align-items:flex-start;padding:12px}
.dec-lsn-txt{flex:1 1 auto;min-width:0}
.dec-lsn-p{margin:0;font-size:13.6px;line-height:1.5;color:var(--ink);cursor:pointer}
.dec-lsn-p code{background:rgba(255,255,255,.08);border-radius:5px;padding:1px 5px;font-size:12.5px}
.dec-cb{flex:0 0 auto;margin:3px 0 0;width:17px;height:17px;accent-color:var(--violet2);cursor:pointer}
.dec-chev{flex:0 0 auto;margin-top:1px;font:inherit;font-size:11px;color:var(--faint);background:none;
  border:1px solid var(--line);border-radius:var(--r-pill);padding:3px 9px;cursor:pointer;white-space:nowrap}
.dec-lsn.open .dec-chev{color:var(--violet);border-color:rgba(167,139,250,.45)}
/* LE RÉGLAGE : trois choix, un seul actif, lisible sans rien ouvrir — c'est l'information qui décide
   du clic (pour qui cette leçon va valoir), elle ne peut pas vivre derrière un dépli. */
.dec-set{display:flex;align-items:center;gap:9px;margin-top:10px;flex-wrap:wrap}
.dec-set-k{font-size:10.5px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:var(--faint)}
.dec-seg{display:inline-flex;background:rgba(255,255,255,.04);border:1px solid var(--line);border-radius:var(--r-pill);padding:3px;gap:2px}
.dec-opt{font:inherit;font-size:11.5px;font-weight:700;color:var(--dim);background:none;border:0;
  border-radius:var(--r-pill);padding:5px 11px;cursor:pointer;white-space:nowrap}
.dec-opt:hover:not(:disabled):not(.on){color:var(--ink);background:rgba(255,255,255,.06)}
.dec-opt.on{background:var(--grad-action);color:#fff;box-shadow:0 3px 10px rgba(124,58,237,.4)}
.dec-opt:disabled{opacity:.32;cursor:not-allowed}
.dec-flag{font-size:9.5px;font-weight:800;border-radius:var(--r-pill);padding:2px 7px;color:var(--faint);
  background:rgba(255,255,255,.05);border:1px solid var(--line)}
.dec-flag.mod{color:var(--amber);background:rgba(251,191,36,.14);border-color:rgba(251,191,36,.3)}
.dec-set-aide{margin:6px 0 0;font-size:11.3px;color:var(--faint);line-height:1.45}
.dec-tgs{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}
.dec-tg{font-size:11px;font-weight:600;color:var(--faint);background:rgba(255,255,255,.045);
  border:1px solid var(--line);border-radius:var(--r-pill);padding:3px 9px}
/* Le dossier : un point par ligne, deux colonnes quand la place le permet. Il était rendu en une
   seule phrase collée aux « · », donc illisible dès qu'il portait plus de trois choses. */
.dec-pts{display:none;border-top:1px dashed rgba(255,255,255,.10);margin:0 12px;padding:11px 0 13px;
  grid-template-columns:1fr 1fr;gap:9px 16px}
.dec-lsn.open .dec-pts{display:grid}
.dec-pt{display:flex;gap:8px;align-items:flex-start;font-size:12.4px;color:var(--dim);line-height:1.45;min-width:0}
.dec-pt i{flex:0 0 18px;font-style:normal;text-align:center}
.dec-pt b{color:var(--ink);font-weight:650;font-size:12.5px}
.dec-pt span{min-width:0;overflow-wrap:anywhere}
.dec-pt.cite{grid-column:1/-1}
.dec-pt blockquote{margin:0;border-left:2px solid rgba(167,139,250,.5);padding:2px 0 2px 10px;font-style:italic}
@media(max-width:640px){.dec-pts{grid-template-columns:1fr}}
.dec-sep{margin:18px 0 6px;color:var(--dim);font-size:12px;text-transform:uppercase;letter-spacing:.06em}
/* ---- une décision DÉJÀ PRISE : la même carte, en sourdine, et toujours jouable ----
   Elle n'est plus un résumé d'une ligne (`.dec-done`, supprimé le 2026-08-10) : on doit pouvoir la
   relire en entier et cocher ce qu'on n'avait pas retenu. Elle se distingue donc par sa TEMPÉRATURE,
   pas par sa forme — même mise en page, moins de contraste, un liseré qui dit « c'est du passé ».
   Les deux formes auraient sinon divergé au premier changement du dossier d'une leçon. */
.dec-card.prise{border-left:2px solid rgba(167,139,250,.35);background:rgba(255,255,255,.02)}
.dec-card.prise .dec-h b{opacity:.9}
.dec-card.prise .dec-by{font-size:11px;color:var(--dim);opacity:.8;margin-left:auto}
/* Ce qui est acquis se lit comme acquis : coché, éteint, non cliquable, et DIT. Sans le mot, une case
   grisée se lit comme un bug d'affichage plutôt que comme une décision déjà appliquée. */
.dec-vu{font-size:10.5px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--cyan);
  border:1px solid rgba(34,211,238,.4);border-radius:999px;padding:1px 7px;margin-left:7px;white-space:nowrap}
.dec-lsn.fige,.dec-item.fige{opacity:.72}
.dec-lsn.fige .dec-lsn-p,.dec-item.fige{cursor:default}
/* UNE LIGNE REFUSÉE RESTE LISIBLE. Elle s'efface sans disparaître, et son motif s'affiche : c'est lui
   la donnée à relire plus tard pour comprendre pourquoi une proposition rate. La faire disparaître
   donnerait l'impression d'avoir perdu quelque chose, et effacerait la seule trace utile. */
.dec-non{font-size:10.5px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--red);
  border:1px solid rgba(251,113,133,.4);border-radius:var(--r-pill);padding:1px 7px;margin-left:7px;white-space:nowrap}
.dec-non-m{display:block;font-size:12px;color:var(--dim);font-style:italic;margin-top:3px}
.dec-lsn.hors,.dec-item.hors{opacity:.5}
.dec-lsn.hors .dec-lsn-p,.dec-item.hors .dec-lbl b{text-decoration:line-through}
/* Ce qui n'a pas encore été regardé : le seul nombre qui dise le travail restant. */
.dec-reste{font-weight:700;color:var(--ink)}
.dec-note-vue{font-size:12.5px;color:var(--ink);opacity:.85;border-left:2px solid rgba(167,139,250,.4);
  padding:2px 0 2px 9px;margin:2px 0 8px;font-style:italic}
/* Le lien vers le chat d'où sort la leçon. `.thread-btn` porte déjà la forme (01-socle.js l'utilise
   pour les ramifications) : on n'en refait pas une seconde, on l'espace juste dans le dossier. */
.dec-fil{display:inline-flex;margin-top:5px}
.dec-coupe{font-size:11.5px;margin:8px 0 2px}
/* Le rappel de ce qui attend sur d'autres journées : compact, avec le moyen d'y aller. Il ne DÉVERSE
   pas les cartes — depuis que rien n'est reproposé, ce qui traîne peut faire des semaines de listes. */
.dec-rappel{display:flex;align-items:center;gap:9px;flex-wrap:wrap;font-size:12.5px;color:var(--dim);
  border:1px dashed rgba(167,139,250,.4);border-radius:9px;padding:8px 11px;margin:2px 0 12px}
.dec-rappel b{color:var(--ink)}
.dec-rappel-j{color:var(--faint);font-size:11.5px}
.dec-saut{margin-left:auto;border:1px solid var(--line);background:none;color:var(--ink);font:inherit;
  font-size:12px;border-radius:var(--r-pill);padding:5px 12px;cursor:pointer}
.dec-saut:hover{border-color:var(--violet);background:rgba(124,58,237,.14)}
/* La barre d'archives : « ‹ Décidé hier › », et le rang de la journée dans la pile. Les flèches
   réutilisent `.jr-arrow` (le journal) : même geste, même bouton, une seule définition à maintenir. */
.dec-jours{display:flex;align-items:center;gap:10px;text-transform:none;letter-spacing:0}
.dec-jour-nom{font-size:12.5px;color:var(--ink);font-weight:650}
.dec-jour-n{margin-left:auto;font-size:11px;color:var(--faint);font-variant-numeric:tabular-nums}

/* boutons de son sur une ligne du catalogue d'emotes (11-social.js) */
.soson{flex:0 0 auto;font:inherit;font-size:12px;line-height:1;padding:5px 7px;border-radius:7px;border:1px solid var(--line);background:none;color:var(--dim);cursor:pointer}
.soson:hover{border-color:var(--violet);color:var(--ink)}
.soson.on{border-color:rgba(34,211,238,.45);color:var(--cyan)}
.soson:disabled{opacity:.45;cursor:default}
#soSonName:not(:empty){display:flex;align-items:center;gap:7px;margin-top:6px;font-size:12px;color:var(--cyan)}

/* ══ L'EXPÉRIENCE — l'en-tête riche de la coquille ════════════════════════════
   Demande de l'owner le 2026-07-31 pour le vote du matin, qui est un moment à part : on l'ouvre, on
   tranche, on repart. C'est cette forme-là que Tim a désignée le 2026-08-10 pour TOUT le cockpit, et
   la coquille commune (`.nmodal` / `.nmodal-card`) est décrite plus haut, une seule fois. Ce qui
   suit ne décrit plus que l'EN-TÊTE riche — titre, sous-titre, croix ronde — par opposition à
   l'en-tête compact `.nmh` des écrans de liste.
   Le modal s'ouvre SEUL à la première connexion du jour et peut être fermé ; la pastille de
   notification, elle, reste tant que le vote n'est pas fait (cf. 16-exp.js). */
/* Le VOTE est large, le PROFIL reste étroit. Deux moments différents dans la même coquille : on
   départage une liste (il faut la voir d'un coup, en grille), ou on lit sa propre fiche (une
   colonne se lit mieux). Demande owner du 2026-08-01, « une modale 3x plus large et 3 par ligne ». */
.nmodal-card.wide{--nw:1240px}
@keyframes nmodalIn{from{opacity:0;transform:translateY(10px) scale(.99)}to{opacity:1;transform:none}}
.nmodal-head{position:relative;padding:20px 22px 17px;border-bottom:1px solid var(--line);background:radial-gradient(420px 150px at 22% -30%,rgba(124,58,237,.35),transparent 70%)}
.nmodal-x{position:absolute;top:16px;right:16px;width:30px;height:30px;border-radius:999px;border:1px solid var(--line);background:rgba(255,255,255,.04);color:var(--dim);font-size:15px;line-height:1;display:flex;align-items:center;justify-content:center;cursor:pointer}
.nmodal-x:hover{color:var(--ink);border-color:var(--violet)}
.nmodal-title{font-size:21px;font-weight:800;margin:0;letter-spacing:-.3px}
.nmodal-title .g{background:var(--grad-accent);-webkit-background-clip:text;background-clip:text;color:transparent}
.nmodal-sub{font-size:12.5px;color:var(--dim);margin:4px 0 0}
.nmodal-body{padding:18px 22px 24px}

/* Le mot EXP, mis en avant partout — jamais « XP ». */
.exp{font-weight:800;letter-spacing:.4px;background:var(--grad-accent);-webkit-background-clip:text;background-clip:text;color:transparent}

/* jetons de vote du jour + réserve */
.jetonbar{display:flex;align-items:center;gap:12px;margin-top:14px}
.nmodal-card.wide .jetonbar{padding:11px 14px;border:1px solid var(--line);border-radius:var(--r-lg);background:rgba(255,255,255,.03)}
.jetons{display:flex;gap:6px;flex:0 0 auto}
.jeton{width:26px;height:26px;border-radius:999px;background:var(--grad-action);box-shadow:0 3px 12px rgba(124,58,237,.55);display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:800;color:#fff}
.jeton.off{background:rgba(255,255,255,.07);box-shadow:none;color:var(--faint)}
.jb-txt{font-size:12.5px;color:var(--dim);line-height:1.4;min-width:0}
.jb-txt b{color:var(--ink)}

/* ── LA LISTE DU VOTE, EN GRILLE ──────────────────────────────────────────────
   Trois cartes par ligne (owner, 2026-08-01). Une liste d'une colonne obligeait à faire défiler
   une douzaine de lignes pour trois votes : on ne compare pas ce qu'on ne voit pas ensemble.
   La grille dégrade proprement — 3 colonnes, puis 2, puis 1 sur mobile. */
.featgrid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px;align-items:stretch}
@media(max-width:1080px){.featgrid{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media(max-width:720px){.featgrid{grid-template-columns:minmax(0,1fr)}}

/* une feature votable : carte VERTICALE, pied collé en bas.
   `flex-direction:column` + `.f-foot{margin-top:auto}` est ce qui aligne les boutons d'une même
   ligne quelle que soit la longueur du titre. Avant, le bouton suivait le texte et sautillait
   d'une carte à l'autre. */
.feat{display:flex;flex-direction:column;background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);padding:15px 16px 13px;transition:.15s;min-width:0;height:100%}
.feat:hover{border-color:rgba(124,58,237,.4);background:var(--card2);transform:translateY(-1px)}
.feat.mine{opacity:.62}
.feat.mine:hover{border-color:var(--line);background:var(--card);transform:none}
.feat.voted{border-color:rgba(52,211,153,.35);background:rgba(52,211,153,.06)}
.feat.voted:hover{transform:none}
.f-top{display:flex;align-items:center;gap:7px;margin-bottom:10px;min-width:0}
/* Le PROJET est un repère, pas une étiquette : il se lit en clair à gauche. Enfermé dans un badge
   il pesait autant que la nature du travail, alors qu'il ne sert qu'à situer (owner, 2026-08-01). */
.f-proj{font-size:12px;font-weight:700;color:var(--dim);letter-spacing:.2px;flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.f-top .pill{flex:0 0 auto;max-width:52%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-left:auto}
.f-top .p-auto + .p-classe{margin-left:0}
/* La NATURE du travail, à droite. Elle a remplacé le montant en EXP : sur une liste où l'on
   départage, un prix fait se ressembler deux travaux très différents. */
.p-classe{border-color:rgba(124,58,237,.4);background:rgba(124,58,237,.13);color:#c4b5fd;font-weight:700}
.f-body{flex:1 1 auto;min-width:0}
/* Titre borné à 3 lignes et description à 4 : sans cette borne, une carte bavarde étire toute sa
   ligne de grille et le reste flotte dans le vide. */
.f-title{font-size:16.5px;font-weight:750;line-height:1.35;margin:0 0 8px;letter-spacing:-.25px;color:var(--ink);display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
.f-why{font-size:12.5px;color:var(--dim);line-height:1.5;margin:0 0 10px;display:-webkit-box;-webkit-line-clamp:4;-webkit-box-orient:vertical;overflow:hidden}
/* DE QUOI UN LOT EST FAIT. La pastille du haut ne porte plus qu'un compte de livraisons : aucun mot
   n'est juste pour un ensemble de quatorze correctifs et deux fonctionnalités. La composition se lit
   ici, et se déplie au clic sur la liste des livraisons, une par ligne, avec la phrase du juge.
   Bornée à 3 lignes repliée : elle ne doit pas pousser le pied de carte hors de la grille. */
.f-compo{font-size:11.5px;color:var(--violet);font-weight:600;line-height:1.45;margin:0 0 10px;cursor:pointer;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
.f-compo:hover{color:#c4b5fd}
.f-compo .chev{font-size:9px;margin-left:5px;display:inline-block;transition:transform .15s}
.f-compo.ouvert .chev{transform:rotate(180deg)}
.f-detail{list-style:none;margin:0 0 10px;padding:9px 0 0;border-top:1px solid var(--line)}
.f-detail li{display:flex;gap:8px;align-items:flex-start;padding:5px 0;font-size:11.5px;line-height:1.45;color:var(--dim)}
.d-cran{flex:0 0 auto;font-size:9.5px;font-weight:800;letter-spacing:.2px;padding:2px 7px;border-radius:999px;border:1px solid rgba(124,58,237,.4);background:rgba(124,58,237,.13);color:#c4b5fd;white-space:nowrap;margin-top:1px}
.d-proj{color:var(--faint);font-weight:700}
.f-foot{display:flex;align-items:center;gap:10px;margin-top:auto;padding-top:11px;border-top:1px solid var(--line);min-width:0}
/* L'AUTEUR : son icône, son nom EN DESSOUS (owner, 2026-08-01). Une initiale seule ne dit pas
   pour qui on vote, or c'est exactement la question posée à cet écran. */
.f-who{display:flex;flex-direction:column;align-items:center;gap:4px;flex:0 0 auto;max-width:74px}
.f-who .avname{font-size:11.5px;font-weight:700;color:var(--ink);line-height:1;max-width:74px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.f-who .avduo{display:flex}
.f-votes{font-size:11.5px;color:var(--faint);font-weight:600;flex:0 0 auto;white-space:nowrap}
/* Un ÉTAT, jamais un bouton : pas de fond, pas de bord, pas de curseur pointeur. « Ton travail »
   et « tu as voté » ne sont pas des actions, ils ne doivent pas en avoir l'air. */
.f-etat{margin-left:auto;flex:0 0 auto;font-size:12px;font-weight:700;color:var(--faint);white-space:nowrap}
.f-etat.ok{color:var(--green)}
.vbtn{flex:0 0 auto;margin-left:auto;font:inherit;font-size:12.5px;font-weight:800;padding:9px 16px;border-radius:999px;border:0;background:var(--grad-action);color:#fff;cursor:pointer;white-space:nowrap;box-shadow:0 4px 14px rgba(124,58,237,.35)}
.vbtn:disabled{opacity:.6;cursor:default}
.vbtn.done{background:none;border:1px solid rgba(52,211,153,.4);color:var(--green);cursor:default;box-shadow:none}
.vbtn.na{background:none;border:1px solid var(--line);color:var(--faint);cursor:default;box-shadow:none;font-weight:600}
.p-proj{border-color:rgba(124,58,237,.45);background:rgba(124,58,237,.14);color:#c4b5fd}
.p-auto{border-color:rgba(34,211,238,.4);background:rgba(34,211,238,.12);color:#67e8f9}
.p-exp{border-color:rgba(52,211,153,.35);background:rgba(52,211,153,.12);color:var(--green);font-weight:800}
.p-mine{border-color:rgba(255,255,255,.12);background:rgba(255,255,255,.05);color:var(--faint)}

/* le niveau, dans la vue profil */
.lvlwrap{display:flex;align-items:center;gap:16px;margin-bottom:16px}
.lvlbadge{width:74px;height:74px;border-radius:20px;flex:0 0 auto;display:flex;flex-direction:column;align-items:center;justify-content:center;background:var(--grad-action);box-shadow:0 10px 30px rgba(124,58,237,.5)}
.lvlbadge b{font-size:31px;font-weight:800;line-height:1;color:#fff;letter-spacing:-1px}
.lvlbadge span{font-size:8px;letter-spacing:1.4px;font-weight:800;color:rgba(255,255,255,.8);margin-top:3px}
.lvlt{flex:1 1 auto;min-width:0}
.lvlt .big{font-size:27px;font-weight:800;letter-spacing:-.6px;line-height:1.1}
.lvlt .big .exp{font-size:16px;vertical-align:2px;margin-left:3px}
.lvlt .nxt{font-size:12.5px;color:var(--dim);margin-top:3px}
.xbar{height:10px;border-radius:999px;background:rgba(255,255,255,.06);overflow:hidden;margin:0 0 7px}
.xbar i{display:block;height:100%;border-radius:999px;background:var(--grad-accent);box-shadow:0 0 14px rgba(124,58,237,.6);transition:width .4s}
.xbarlab{display:flex;justify-content:space-between;font-size:11.5px;color:var(--faint)}
.xsplit{display:flex;gap:9px;margin-bottom:2px}
.xsplit .card{flex:1 1 0;margin:0;padding:13px 11px;background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg)}
.xsplit b{display:block;font-size:21px;font-weight:800;letter-spacing:-.5px;margin-bottom:3px}
.xsplit span{font-size:11.5px;color:var(--dim);line-height:1.35;display:block}
.gain{display:flex;gap:12px;align-items:flex-start;padding:11px 0;border-bottom:1px solid var(--line)}
.gain:last-child{border-bottom:0}
/* `white-space:nowrap` : la ligne porte aussi le calcul du boost (« +250 (100 +150 %) ») et il ne
   doit pas se couper en deux entre le montant et sa justification. */
.gain-xp{flex:0 0 auto;font-size:14px;font-weight:800;color:var(--green);min-width:58px;text-align:right;padding-top:1px;white-space:nowrap}
.gain-t{font-size:13.5px;line-height:1.4;margin:0;font-weight:600}
.gain-w{font-size:11.5px;color:var(--faint);line-height:1.45;margin:2px 0 0}

/* ── LE BOOST D'EXP ────────────────────────────────────────────────────────────────────────────
   UNE SEULE DÉFINITION POUR LES CINQ ENDROITS qui le montrent (le profil, le journal du matin, la
   colonne « En ce moment », l'annuaire, la notification). Cinq styles proches auraient produit cinq
   pastilles légèrement différentes pour une seule et même chose.

   POURQUOI CE N'EST PAS DORÉ, alors que c'est la couleur qu'on donne d'instinct à un bonus : l'or est
   DÉJÀ pris par le passage de palier et par le saut de plusieurs niveaux (`jr-lvl-x`, `jr-bar-plein`),
   qui sont les seuls moments exceptionnels MÉRITÉS du système. Le vert dit « gagné », le rouge dit
   « erreur », le bleu dit « terminé ». Aucune couleur n'était libre : le boost prend donc le dégradé
   de la marque, qui dit « décidé par la maison » et ne vole le sens de personne.

   ET C'EST POUR ÇA QUE LA BARRE NE CHANGE PAS DE COULEUR MAIS REÇOIT UN HALO. Le remplissage des
   barres d'EXP est déjà ce dégradé-là : le repeindre ne se distinguerait de rien. Le halo, lui, ne
   peut se confondre ni avec le remplissage doré du palier, ni avec la barre ordinaire.

   LE POURCENTAGE ET PAS UN MULTIPLICATEUR : les boosts se cumulent (Tim, 2026-08-27) et s'ajoutent,
   donc « +50 % » et « +100 % » se lisent comme « +150 % », là où « ×1,5 » et « ×2 » ne s'additionnent
   pas à l'œil. Un « ×N » avait d'ailleurs été retiré de l'onglet Expérience le 2026-08-11 parce qu'il
   se lisait comme un multiplicateur de gain : c'est exactement ce que celui-ci est. */
.boost{display:inline-flex;align-items:center;flex:0 0 auto;font-size:10.5px;font-weight:800;letter-spacing:.3px;
  padding:3px 9px;border-radius:999px;background:var(--grad-action);color:#fff;
  box-shadow:0 0 12px rgba(34,211,238,.45);white-space:nowrap}
.boost-s{font-size:9.5px;padding:2px 7px}
/* Le halo d'une barre d'EXP boostée. Posé sur la BOÎTE et pas sur le remplissage : une barre presque
   vide doit se signaler autant qu'une barre pleine, et un halo sur un remplissage de 3 % ne se voit
   pas. */
.xbar.boostee,.jr-bar.boostee{box-shadow:0 0 0 1px rgba(34,211,238,.35),0 0 14px rgba(34,211,238,.4)}
/* L'anneau autour d'un visage boosté. Deux ombres : la première redécoupe le fond entre l'avatar et
   l'anneau, sinon le cyan bave sur la pastille d'initiale. */
.av-boost{box-shadow:0 0 0 2px var(--bg1),0 0 0 4px var(--cyan),0 0 14px rgba(34,211,238,.55)}
/* La composition du boost, dans le profil. Elle existe parce que les boosts se cumulent ET portent
   une portée : deux boosts de portées différentes font DEUX totaux (+150 % en codant, +50 % en
   votant) et aucune pastille ne peut dire les deux. Le détail ne vit qu'ici. */
.bcompo{border:1px solid rgba(34,211,238,.35);background:rgba(34,211,238,.06);border-radius:var(--r-lg);
  padding:11px 13px;margin:0 0 16px}
.bcompo-t{display:flex;align-items:center;gap:9px;font-size:13px;font-weight:700}
.bcompo-t .sp{margin-left:auto;font-size:11.5px;color:var(--dim);font-weight:400}
.bcompo ul{list-style:none;margin:9px 0 0;padding:0}
.bcompo li{display:flex;align-items:baseline;gap:10px;font-size:12.5px;padding:3px 0;color:var(--dim)}
.bcompo li b{color:var(--cyan);font-weight:800;flex:0 0 auto;min-width:54px}
.bcompo li .q{flex:1 1 auto;color:var(--ink);min-width:0}
.bcompo li .d{flex:0 0 auto;font-size:11px;color:var(--faint);white-space:nowrap}
/* Le calcul écrit à côté d'un gain boosté (« 100 +150% »). En retrait de couleur : c'est une
   justification, pas un second montant — deux chiffres de même poids se liraient comme deux gains. */
.bcalc{color:var(--faint);font-weight:600;font-size:.86em}
/* L'annonce d'un boost qui vient de tomber, dans le journal du matin. Neutre exprès : ce n'est ni un
   gain (vert) ni un palier (or), c'est une explication. */
.jr-bfin{margin:12px 0 0;padding:10px 13px;border-radius:var(--r-md);font-size:12.5px;color:var(--dim);
  background:var(--card);border:1px solid var(--line)}
.jr-bfin b{color:var(--ink)}
.jr-bfin .n{color:var(--cyan);font-weight:800}

/* pastille « il te reste des votes », sur le bouton du menu */
.expdot{display:inline-flex;align-items:center;justify-content:center;min-width:16px;height:16px;padding:0 4px;margin-left:6px;border-radius:999px;background:var(--grad-action);color:#fff;font-size:10px;font-weight:800;vertical-align:1px}
/* Pastille d'initiale d'une personne, dans les listes d'expérience. Les teintes distinguent d'un
   coup d'oeil qui a livré quoi, et la variante sombre marque le travail « maison » (celui que
   personne n'a demandé), qui ne concourt pas. */
.av{width:28px;height:28px;border-radius:999px;flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;font-size:11.5px;font-weight:800;color:#fff;text-transform:uppercase}
.av-t{background:linear-gradient(135deg,var(--violet2),#5b21b6)}
.av-d{background:linear-gradient(135deg,#0891b2,#0e7490)}
.av-q{background:linear-gradient(135deg,#b45309,#92400e)}
.av-n{background:linear-gradient(135deg,#334155,#1e293b);color:var(--cyan)}

/* ── LE PODIUM ET SES CHAPEAUX ────────────────────────────────────────────────────────────────
   Les trois premiers du classement d'expérience portent un chapeau sur leur avatar, dans les TROIS
   listes qui montrent l'équipe : la colonne « En ce moment » (`.lvav`), l'annuaire de l'écran Équipe
   (`.npf`, déjà positionné pour son point vert) et le classement du profil d'expérience (`.avw`, qui
   n'existe que pour ça).

   AUCUNE COULEUR DE CHAPEAU ICI, et c'est délibéré. Chaque dessin ne nomme que des rôles (`--hat-a`
   la masse, `--hat-b` l'ombre et les bandeaux, `--hat-c` l'éclat, `--hat-d` un second éclat), et
   c'est le CHAPEAU qui dit avec quoi ils sont peints (api/_hats.js), posé en `style` sur le badge.
   Une palette écrite ici serait un second détenteur de la couleur d'un chapeau, et la version dorée
   réservée au premier n'aurait aucun moyen d'exister sans dupliquer les dessins.

   ELLE DÉBORDE DE L'AVATAR EXPRÈS, et c'est ce qui la fait lire comme un chapeau POSÉ dessus plutôt
   que comme un badge collé au bord. Un débordement de 8 px vers le haut suffit, la carte a de la
   marge ; au-delà il mordrait sur la ligne du dessus quand deux cartes se suivent.

   `align-self:flex-start` SUR `.avw` N'EST PAS DÉCORATIF : la carte du classement est une colonne
   flex, où un enfant s'étire par défaut sur toute la largeur. Sans lui, le conteneur mesure la carte
   entière et le chapeau, centré sur LUI, se pose au milieu de la ligne au lieu de la tête (mesuré à
   l'écran, pas supposé). */
/* UNE PALETTE DE REPLI, ET ELLE N'EST PAS DÉCORATIVE. Un dessin ne nomme que des rôles ; si personne
   ne les peint, `fill:var(--hat-a)` devient invalide et le navigateur retombe sur le NOIR — mesuré :
   `rgb(0, 0, 0)`, donc un chapeau invisible sur le fond sombre du cockpit, sans la moindre erreur
   nulle part. Ça arrive dès que la palette n'accompagne pas le dessin : réponse d'`/api/exp` gardée
   en mémoire depuis une version précédente, panne partielle, onglet resté ouvert pendant un
   déploiement. Le repli est l'or, la livrée de la couronne — le style posé sur le badge le remplace
   dès qu'il existe. */
.hatw,.hatopt,.lvcrown{--hat-a:#FBBF24;--hat-b:#D97706;--hat-c:#FDE68A;--hat-d:#FCD34D}
.avw{position:relative;display:inline-flex;flex:0 0 auto;align-self:flex-start}
.hatw{position:absolute;left:50%;top:-9px;width:26px;height:16px;transform:translateX(-52%) rotate(-16deg);
  transform-origin:50% 100%;pointer-events:none;padding:0;border:0;background:none;line-height:0;
  filter:drop-shadow(0 1px 1px rgba(0,0,0,.45))}
.hatw svg{width:100%;height:100%;display:block;overflow:visible}
/* Cliquable UNIQUEMENT sur sa propre tête : ailleurs c'est un ornement, et un curseur « main » sur
   un élément qui ne réagit pas est un mensonge d'interface. */
.hatw.act{pointer-events:auto;cursor:pointer;-webkit-tap-highlight-color:transparent}
.hatw.act:hover{transform:translateX(-52%) rotate(-16deg) scale(1.14)}
.hatw.act:focus-visible{outline:2px solid var(--violet);outline-offset:3px;border-radius:4px}
/* Sur la carte de l'annuaire, l'avatar est plus grand (34 px) : le chapeau suit, sinon il flotte. */
.npf .hatw{width:30px;height:18px;top:-11px}
/* Dans la colonne « En ce moment », l'avatar fait 28 px et le conteneur de la liste ROGNE ce qui sort
   de lui (`.lvbody`, overflow). Mesuré : à -8 px, le chapeau de la PREMIÈRE ligne dépassait de 1,7 px
   et se faisait couper le haut. Il est donc plus petit et remonte moins — ce qui le fait aussi moins
   mordre sur la ligne du dessus, les cartes étant serrées ici. */
.lvav .hatw{width:23px;height:14px;top:-5px}

/* La petite couronne du PREMIER, POSÉE SUR son badge de niveau dans la colonne « En ce moment »
   (demande de l'owner : elle était à côté, elle passe dessus). Le badge devient donc la tête qui la
   porte, et l'enveloppe `.lvlvw` n'existe que pour lui servir de repère — sans elle, une couronne en
   absolu se placerait par rapport à la carte entière.

   Légèrement inclinée, comme les chapeaux des avatars : c'est ce qui la fait lire comme posée plutôt
   que comme un pictogramme collé. Elle ne prend aucune place dans la ligne (`position:absolute`), donc
   le nom ne perd pas un caractère au passage. */
.lvlvw{position:relative;display:inline-flex;flex:0 0 auto}
.lvcrown{position:absolute;left:50%;top:-7px;width:15px;height:10px;line-height:0;pointer-events:none;
  transform:translateX(-50%) rotate(-12deg);filter:drop-shadow(0 1px 1px rgba(0,0,0,.45))}
.lvcrown svg{width:100%;height:100%;display:block;overflow:visible}

/* Le choix du chapeau : un popover ancré sous la couronne (`popAncrer`, 01-socle.js), comme tout ce
   qui se déplie sous un bouton dans ce cockpit. Au-dessus des modales, parce qu'il s'ouvre depuis
   l'une d'elles (le profil d'expérience).

   FOND OPAQUE (`--grad-surface`), COMME LES DEUX AUTRES POPOVERS du cockpit (`.lvpal`, `.popcard`).
   Il était en `--card`, qui est un blanc à 4 % : posé au-dessus de la colonne « En ce moment », il
   laissait voir les lignes à travers et devenait illisible — les libellés des chapeaux se mélangeaient
   aux noms de l'équipe (capture de l'owner, 2026-08-18). `--card` est fait pour une surface POSÉE
   DANS une carte, jamais pour ce qui flotte au-dessus du reste. */
.hatpop{position:fixed;z-index:2200;width:250px;max-width:calc(100vw - 20px);overflow:auto;
  background:var(--grad-surface);border:1px solid var(--line);border-radius:14px;padding:12px;
  box-shadow:0 18px 40px rgba(0,0,0,.45)}
.hatpop-t{font-size:11px;letter-spacing:.6px;text-transform:uppercase;color:var(--faint);margin:0 0 10px}
.hatgrid{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}
.hatopt{display:flex;flex-direction:column;align-items:center;gap:5px;padding:9px 4px;cursor:pointer;
  border:1px solid var(--line);border-radius:11px;background:var(--card2);color:var(--dim);font:inherit}
.hatopt svg{width:34px;height:22px;display:block}
.hatopt em{font-style:normal;font-size:9.5px;line-height:1.2;text-align:center}
.hatopt:hover{border-color:rgba(167,139,250,.34)}
.hatopt.on{border-color:var(--violet);color:var(--ink);background:rgba(167,139,250,.12)}
.hatopt:focus-visible{outline:2px solid var(--violet);outline-offset:2px}
/* Ce que le rang FERME, dit sous la grille plutôt que deviné devant deux cases manquantes. */
.hatpop-n{margin:10px 0 0;font-size:10.5px;line-height:1.35;color:var(--faint)}
/* « voir les autres » en bas de la liste du vote */
.more{width:100%;font:inherit;font-size:12.5px;font-weight:700;padding:12px;border-radius:var(--r-md);border:1px dashed var(--line);background:none;color:var(--dim);cursor:pointer;margin-top:4px}
.more:hover{border-color:var(--violet);color:var(--ink)}

/* ---- POPOVERS DE LA BARRE : Notifications et Son ----

   « une card qui se déplie en dessous de la cloche, à l'image des notifs sur téléphone », puis
   « pour l'onglet Son il faudrait faire pareil, que ça pop en dessous pas sur le côté » (owner,
   2026-07-31). Les deux partagent .popcard : deux blocs séparés auraient divergé à la première
   retouche, et c'est le même objet d'interface.

   Ce que ça remplace : deux tiroirs latéraux. Ces deux-là ne sont PAS devenus des modales le
   2026-08-10 avec les treize autres, et c'est délibéré : une modale est un LIEU où l'on va (Repos,
   Docs, Décisions, Secrets) et elle tire un voile sombre sur le cockpit entier ; régler son volume
   ou jeter un oeil à ses notifications n'est pas y aller, c'est un geste de passage. D'où : pas de
   voile, on ferme au clic ailleurs ou à Échap, et on reste exactement où on était.

   POSITION EN JS, PAS ICI (popAncrer, 01-socle.js) : `top`/`left`/`max-height` sont écrits à chaque
   ouverture depuis le rectangle réel du bouton, qui se déplace selon la largeur et les droits. Un
   `right` en dur, tenté d'abord, décrochait visuellement le popover de son bouton. */
.popcard{position:fixed;z-index:60;width:392px;max-width:calc(100vw - 20px);
  background:var(--grad-surface);border:1px solid var(--line);border-radius:14px;box-shadow:0 18px 50px rgba(0,0,0,.55);
  display:none;flex-direction:column;overflow:hidden;transform-origin:top left;animation:popcard-in .16s cubic-bezier(.2,.8,.3,1)}
.popcard.on{display:flex}
.sonpop{width:352px}
@keyframes popcard-in{from{opacity:0;transform:translateY(-6px) scale(.97)}to{opacity:1;transform:none}}
/* Le mouvement dit « ça sort de ce bouton-là » ; c'est une aide à la compréhension, pas une
   décoration. On le coupe pour qui a demandé moins d'animation, sans rien perdre du contenu. */
@media (prefers-reduced-motion:reduce){.popcard{animation:none}}
.popcard-h{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:11px 14px 9px;border-bottom:1px solid var(--line);flex:0 0 auto}
.popcard-h b{font-size:13px;font-weight:650;letter-spacing:.01em}
.popcard-x{background:none;border:0;color:inherit;font:inherit;font-size:15px;line-height:1;opacity:.5;cursor:pointer;padding:2px 4px}
.popcard-x:hover{opacity:1}
/* Défilement DANS la carte : la hauteur est plafonnée par popAncrer selon la place réelle sous le
   bouton. Sans ça, quinze notifications poussent le bas de la carte hors de l'écran. */
.popcard-body{overflow-y:auto;padding:8px 10px 10px;flex:1 1 auto;min-height:0}

/* ---- LA CLOCHE (17-notifs.js) ----
   Le compteur reprend .expdot (même forme, même dégradé) plutôt qu'un troisième dialecte de pastille :
   le cockpit en a déjà deux, un point vert pour « il y a du neuf » et un compteur pour « il t'en reste
   N ». Une notification est du second type, on ne réinvente donc rien. */
.not-dot{display:inline-flex;align-items:center;justify-content:center;min-width:16px;height:16px;padding:0 4px;margin-left:6px;border-radius:999px;background:var(--grad-action);color:#fff;font-size:10px;font-weight:800;vertical-align:1px}
.not-sec{font-size:10.5px;text-transform:uppercase;letter-spacing:.06em;opacity:.5;margin:10px 4px 6px}
.not-sec:first-child{margin-top:2px}
/* Une notification est une CARTE et un BOUTON : elle mène quelque part, et le curseur doit le dire
   avant le clic. min-width:0 sur le texte, sinon un titre long pousse l'heure hors de la carte au lieu
   de se tronquer (règle maison, déjà payée ailleurs). */
.not-item{display:flex;align-items:flex-start;gap:9px;width:100%;text-align:left;background:var(--card);border:1px solid var(--line);border-radius:12px;padding:10px 11px;margin:0 0 7px;color:inherit;font:inherit;cursor:pointer;transition:transform .12s ease,border-color .12s ease}
.not-item:hover{border-color:rgba(124,58,237,.4);transform:translateY(-1px)}
.not-item:active{transform:none}
.not-item .not-pt{width:7px;height:7px;border-radius:50%;flex:0 0 7px;margin-top:6px;background:var(--grad-action)}
.not-item.act .not-pt{background:var(--amber);box-shadow:0 0 7px var(--amber)}
.not-item.lue .not-pt{background:var(--line);box-shadow:none}
.not-item.lue{opacity:.5;background:none}
.not-txt{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1 1 auto}
.not-txt b{font-weight:650;font-size:13px;line-height:1.35}
.not-sub{font-size:11.5px;opacity:.65;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.not-quand{font-size:10.5px;opacity:.5;flex:0 0 auto;margin-top:2px;white-space:nowrap}
.not-tout{display:block;width:100%;margin:2px 0 4px;background:none;border:1px solid var(--line);border-radius:9px;padding:6px 10px;color:inherit;font:inherit;font-size:11.5px;cursor:pointer;opacity:.75}
.not-tout:hover{opacity:1;background:var(--card)}

/* Le panneau Son passe du tiroir à la carte : ses réglages étaient déjà des cases à cocher, seule
   l'enveloppe change. On resserre les espacements, une carte n'a pas la hauteur d'un tiroir. */
.sonpop .son-mute{font-size:12.5px;padding:10px 12px}
.sonpop .son-h{margin:14px 0 7px}
.sonpop .son-row{padding:9px 10px;margin-bottom:6px}
.sonpop .son-note{font-size:11px;margin:7px 0 0}

/* Plusieurs auteurs sur une même feature : les pastilles se chevauchent légèrement, ce qui se lit
   comme « à plusieurs » sans prendre la largeur de trois avatars côte à côte. */
.avduo{display:inline-flex;flex:0 0 auto}
.avduo .av{margin-right:-9px;box-shadow:0 0 0 2px #12121f}
.f-who .av{width:32px;height:32px;font-size:13px}
/* Vraie photo de profil. Même gabarit que la pastille d'initiale, pour que le pied ne bouge pas
   selon que le profil est rempli ou non. */
.av-img{object-fit:cover;background:var(--card2)}
.avduo .av:last-child{margin-right:0}

/* ── LE PACTE DE LA NUIT ──────────────────────────────────────────────────────
   Un document contractuel qu'on lit en entier, dans la coquille de modal déjà
   utilisée par l'expérience. Deux partis pris :
   - le TEXTE est posé sur un fond de lecture, plus calme que le reste du cockpit :
     on ne lit pas trois cents lignes d'engagement sur la même surface qu'un
     tableau de bord ;
   - les RÉPONSES vivent à la fin du document, pas dans un bandeau collant. Il
     faut être arrivé en bas pour cliquer, autant que ce soit le mouvement naturel
     de la lecture plutôt qu'une règle imposée par un bouton grisé en surimpression. */
.pacte-doc{font-size:14px;line-height:1.72;color:var(--ink)}
.pacte-doc .md-h{font-weight:800;letter-spacing:-.2px;margin:22px 0 8px}
.pacte-doc .md-h1{font-size:20px}
.pacte-doc .md-h2{font-size:17px;padding-top:14px;border-top:1px solid var(--line)}
.pacte-doc .md-h3{font-size:14.5px;color:var(--dim)}
.pacte-doc pre{font-size:11.5px;line-height:1.5}
.pacte-doc table{font-size:13px}
.pacte-flash{margin:0 0 16px;padding:10px 13px;border-radius:var(--r-md);border:1px solid rgba(226,168,59,.4);background:rgba(226,168,59,.08);color:var(--ink);font-size:13px}
.pacte-foot{padding:16px 22px 22px;border-top:1px solid var(--line);background:rgba(255,255,255,.02)}
.pacte-actions{display:flex;flex-wrap:wrap;gap:10px}
/* Les deux réponses sont côte à côte et de même poids visuel : « en discuter » n'est
   pas un renoncement qu'on cache, c'est une réponse qu'on propose. */
.pacte-actions .vbtn{margin-left:0}
.gbtn{flex:0 0 auto;font:inherit;font-size:12.5px;font-weight:800;padding:9px 16px;border-radius:999px;border:1px solid var(--line);background:rgba(255,255,255,.04);color:var(--ink);cursor:pointer;white-space:nowrap}
.gbtn:hover:not(:disabled){border-color:var(--violet)}
.gbtn:disabled{opacity:.5;cursor:default}
.pacte-lire{margin:10px 0 0;font-size:12.5px;color:var(--dim)}
.pacte-etat{display:flex;align-items:center;gap:7px;font-size:13px;color:var(--ink)}
.pacte-etat i{color:var(--dim);font-style:normal;font-size:12px}
@media (max-width:520px){
  .pacte-actions{flex-direction:column}
  .pacte-actions .vbtn,.pacte-actions .gbtn{width:100%;text-align:center}
}

/* ── LE JOURNAL DU MATIN ──────────────────────────────────────────────────────
   La nuit écoulée, dans la coquille de modal déjà utilisée par l'expérience et
   le pacte. Quatre partis pris, tous dictés par ce qu'il remplace — un email de
   standup qu'on finissait par ne plus ouvrir :

   - EN ONGLETS, PAS EN LONG. Les pastilles réutilisent `.tab`/`.tab.on`, le même
     objet que les onglets de conversation : on ne réapprend pas un vocabulaire
     visuel pour un écran qu'on ouvre une fois par jour.
   - LE BANDEAU DE CHIFFRES EST UNE GRILLE, jamais une ligne flex. À 390px,
     quatre colonnes déborderaient ou seraient rognées ; `minmax(0,1fr)` + un
     passage en 2x2 garantit qu'aucun chiffre ne sort de l'écran.
   - LES TEXTES SECONDAIRES SONT TRONQUÉS (`text-overflow:ellipsis`) et leurs
     conteneurs portent `min-width:0` : sans ça, un résumé d'automatisation un
     peu long élargit la ligne flex et pousse l'heure hors du cadre.
   - LES FLÈCHES D'ARCHIVES sont GRISÉES en bout de course, pas masquées : un
     bouton qui disparaît fait douter d'avoir mal cliqué. */

/* Plus large que le pacte (un document se lit en colonne étroite), bien plus
   étroit que la grille de vote (`.wide`, 1240px) : ici on lit des paragraphes,
   pas une grille à comparer. */
#jrmodal .nmodal-card{--nw:900px}
.jr-head{padding-bottom:16px}
.jr-headrow{display:flex;align-items:center;gap:13px;min-width:0;padding-right:38px}
.jr-headtxt{flex:1 1 auto;min-width:0}
.jr-headtxt .nmodal-sub{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.jr-days{display:flex;gap:6px;flex:0 0 auto}
.jr-arrow{width:32px;height:32px;border-radius:999px;border:1px solid var(--line);background:rgba(255,255,255,.04);color:var(--ink);font-size:17px;line-height:1;display:flex;align-items:center;justify-content:center;cursor:pointer;font-family:inherit}
.jr-arrow:hover:not(:disabled){border-color:var(--violet);background:rgba(124,58,237,.16)}
/* Grisée, et surtout SANS curseur pointeur : le bout des archives se voit avant
   de se découvrir en cliquant. */
.jr-arrow:disabled{opacity:.32;cursor:default}

.jr-band{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px;padding:14px 22px;border-bottom:1px solid var(--line);background:linear-gradient(90deg,rgba(124,58,237,.30),rgba(34,211,238,.16))}
.jr-stat{min-width:0;text-align:center}
.jr-stat b{display:block;font-size:21px;font-weight:800;letter-spacing:-.5px;color:var(--ink);line-height:1.15;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.jr-stat span{display:block;font-size:10.5px;color:var(--dim);letter-spacing:.3px;margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* La rangée d'onglets DÉFILE sur mobile plutôt que de passer à la ligne : cinq
   matières sur deux lignes, et le contenu de l'onglet actif serait poussé sous
   la ligne de flottaison au moment même où on vient le chercher. */
.jr-tabs{display:flex;gap:8px;padding:12px 22px;border-bottom:1px solid var(--line);overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch}
.jr-tabs::-webkit-scrollbar{display:none}
.jr-tabs .tab{font-family:inherit}
/* Le libellé hérite du gris de `.tab` et ne s'allume que sur l'onglet actif. Le `.stitle` des
   onglets de conversation force l'encre : réutilisé ici, il ferait briller autant les quatre
   matières qu'on ne regarde pas que celle qu'on lit. */
.tab .jr-t{font-weight:700;flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis}
.tab.on .jr-t{color:var(--ink)}
/* Le compteur est DISCRET : il situe (« treize avancées »), il n'annonce pas.
   À zéro il s'efface encore, sans disparaître — un onglet sans compteur du tout
   se lirait comme un onglet cassé. */
.tab .jr-n{font-style:normal;font-size:10.5px;font-weight:700;color:var(--faint);background:rgba(255,255,255,.07);border-radius:999px;padding:1px 6px;flex:0 0 auto}
.tab.on .jr-n{color:var(--ink);background:rgba(124,58,237,.34)}
.tab .jr-n.zero{opacity:.45}

.jr-vide{text-align:left;font-size:13px;color:var(--dim);margin:6px 0 0;line-height:1.6}
.jr-card{background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);padding:14px 16px;margin-bottom:12px;min-width:0}
.jr-tete{display:flex;align-items:center;gap:9px;min-width:0;margin-bottom:10px}
.jr-nom{font-size:14.5px;font-weight:750;color:var(--ink);letter-spacing:-.2px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.jr-meta{font-size:11.5px;color:var(--faint);margin-left:auto;flex:0 0 auto;white-space:nowrap}
.jr-badge{flex:0 0 auto;font-size:11px;font-weight:700;letter-spacing:.2px;padding:3px 9px;border-radius:999px;border:1px solid var(--line);background:rgba(255,255,255,.04);color:var(--dim);max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* Le travail : le projet TITRE la puce, les avancées vivent dessous. Un badge de
   projet répété sur chaque ligne noierait la seule chose qu'on lit vraiment. */
.jr-proj{margin-top:9px;min-width:0}
.jr-puces{margin:7px 0 0;padding-left:17px;font-size:13px;line-height:1.6;color:var(--ink)}
.jr-puces li{margin-bottom:4px}
.jr-puces li::marker{color:var(--violet)}

/* L'expérience : le total en VERT. C'est un gain, et le vert est déjà la couleur
   du gain partout ailleurs dans le cockpit (`.f-etat.ok`, la pastille d'un run
   réussi) — le lire en violet demanderait de réapprendre ce que ça veut dire. */
.jr-xtot{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;margin-bottom:14px;min-width:0}
.jr-xtot b{font-size:32px;font-weight:800;letter-spacing:-1px;color:var(--green);line-height:1}
.jr-xtot-l{font-size:12.5px;color:var(--dim);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.jr-lvls{display:flex;flex-direction:column;gap:8px;margin-bottom:14px}
.jr-lvl{display:flex;align-items:center;gap:9px;padding:9px 13px;border-radius:var(--r-md);border:1px solid rgba(52,211,153,.35);background:rgba(52,211,153,.08);font-size:13px;color:var(--ink);min-width:0}
.jr-lvl b{font-weight:750}
/* UN SAUT DE PLUSIEURS NIVEAUX se distingue du passage ordinaire : c'est rare (un palier coûte
   ~3 000 EXP et grimpe), et une nuit à trois paliers rendue comme une nuit à un seul efface
   exactement ce qu'il y avait à fêter. Doré et non vert : le vert dit « gagné » partout dans le
   cockpit, l'or dit « exceptionnel », et les deux doivent rester lisibles côte à côte. */
.jr-lvl-x{border-color:rgba(255,204,77,.42);background:linear-gradient(100deg,rgba(255,204,77,.13),rgba(52,211,153,.06))}
/* L'ÉCLAT qui traverse le bandeau à son apparition. Il ne décore pas : il DÉSIGNE. Le passage de
   niveau est le seul événement de la nuit qu'on vient fêter, et posé sans mouvement au milieu de
   quatre autres blocs, il se lit comme un encadré de plus. `overflow:hidden` le contient. */
.jr-lvl{position:relative;overflow:hidden}
.jr-lvl::after{content:"";position:absolute;inset:0;pointer-events:none;transform:translateX(-130%);
  background:linear-gradient(100deg,transparent 32%,rgba(255,255,255,.13) 50%,transparent 68%)}
.jr-lvl-eclat::after{animation:jrEclat .9s ease-out}
@keyframes jrEclat{to{transform:translateX(130%)}}
.jr-lvl-de{color:var(--dim);font-variant-numeric:tabular-nums}
.jr-lvl-a{color:#ffcc4d;font-weight:800;font-variant-numeric:tabular-nums}
.jr-xrow{padding:12px 0;border-top:1px solid var(--line);min-width:0}
.jr-xtete{display:flex;align-items:center;gap:9px;min-width:0}
.jr-gain{margin-left:auto;flex:0 0 auto;font-size:13px;font-weight:800;color:var(--green);white-space:nowrap}
/* `position:relative` et PAS d'`overflow:hidden` : le reflet et la gerbe d'étincelles sont posés
   dedans, et la gerbe doit pouvoir en sortir — coupée à six pixels de haut, elle ne se voit pas.
   Le remplissage garde sa forme par son propre `border-radius`, il n'a jamais eu besoin du
   rognage du parent (il ne dépasse pas 100 % par construction). */
.jr-bar{position:relative;height:6px;border-radius:999px;background:rgba(255,255,255,.07);margin-top:9px}
.jr-bar i{display:block;height:100%;border-radius:999px;background:var(--grad-action)}
/* L'INSTANT DU PALIER. La barre vire à l'or le temps d'un battement au moment où elle se vide et
   que le niveau s'incrémente : sans ce marqueur, une barre qui repart de zéro se lit comme un bug
   d'affichage plutôt que comme un niveau franchi. La largeur, elle, est pilotée en JavaScript
   (20-journal.js) et n'a AUCUNE transition CSS — deux moteurs d'animation sur la même propriété se
   battraient, et la barre traînerait derrière le compteur de niveau. */
.jr-bar-plein i{animation:jrPalier .42s ease-out}
@keyframes jrPalier{0%{background:linear-gradient(90deg,#ffcc4d,#ffe9a8);box-shadow:0 0 14px rgba(255,204,77,.6)}100%{background:var(--grad-action)}}
/* LE REFLET qui suit la tête de la barre pendant le remplissage. Sans lui, une barre qui change de
   longueur et une barre qui SE REMPLIT se ressemblent : c'est le point mobile qui dit lequel des
   deux on regarde. Éteint à l'arrivée (opacité pilotée en JS), il ne laisse aucune trace. */
.jr-shine{position:absolute;top:0;bottom:0;width:26px;margin-left:-13px;pointer-events:none;opacity:0;
  transition:opacity .25s ease;background:linear-gradient(90deg,transparent,rgba(255,255,255,.8),transparent)}
/* LES ÉTINCELLES du palier franchi. `overflow:visible` sur la boîte : la gerbe doit sortir de la
   barre, sinon on ne voit qu'une ligne de points coupée à sa hauteur de six pixels. */
/* Toutes les étincelles naissent au MÊME point : le bout droit de la barre, là où le palier vient
   d'être atteint. C'est le code JS qui les disperse en éventail (cf. `jrEtincelles`) ; les répartir
   ici le long de la barre donnait une rangée de pointillés, qui se lisait comme un défaut. */
.jr-parts{position:absolute;inset:0;overflow:visible;pointer-events:none}
.jr-parts i{position:absolute;right:0;top:50%;width:3px;height:3px;margin:-1.5px -1.5px 0 0;
  border-radius:50%;background:#ffe9a8;box-shadow:0 0 5px rgba(255,204,77,.95)}
.jr-niv{font-variant-numeric:tabular-nums}
.jr-niv b{color:var(--ink);font-weight:800}
.jr-niv-pop{animation:jrNivPop .5s cubic-bezier(.2,1.6,.4,1)}
@keyframes jrNivPop{0%{transform:scale(1)}35%{transform:scale(1.22)}100%{transform:scale(1)}}
/* La prochaine marche, sous la barre. C'est ce qu'on vient chercher le lendemain : un pourcentage
   ne dit pas combien il reste, ce chiffre-là si. */
.jr-xpied{display:flex;justify-content:flex-end;margin-top:6px;font-size:11.5px;color:var(--dim);font-variant-numeric:tabular-nums}
.jr-reste b{color:var(--ink);font-weight:700}
/* Mouvement coupé dans le système : le JavaScript n'anime plus rien (20-journal.js le vérifie),
   et les deux marqueurs de palier s'effacent avec lui. Un vertige n'est pas un goût. */
@media (prefers-reduced-motion:reduce){
  .jr-bar-plein i,.jr-niv-pop,.jr-lvl-eclat::after{animation:none}
  .jr-shine{display:none}
}
/* LE SURPLUS DE GAINS est présent dans le DOM et masqué, jamais absent : la liste reste trouvable
   par la recherche du navigateur même repliée, et déplier ne demande aucun re-render — donc aucune
   position de lecture perdue (règle 5 en tête de 20-journal.js). */
/* `.jr-det .jr-det-plus` et non `.jr-det-plus` seul : la règle doit BATTRE `.jr-det li{display:flex}`
   qui la suit dans ce fichier. Une classe seule (0,1,0) perd contre une classe + un élément (0,1,1),
   et le masquage ne s'appliquait pas — les 78 gains restaient visibles, repliés comme dépliés.
   Invisible dans le HTML rendu, invisible dans la batterie qui lit ce HTML : il a fallu compter les
   `li` réellement affichés dans un vrai navigateur. */
.jr-det .jr-det-plus{display:none}
.jr-det.deplie .jr-det-plus{display:flex}
.jr-voir{margin-top:7px;background:none;border:0;padding:2px 0;cursor:pointer;font:inherit;
  font-size:12px;color:var(--violet);text-align:left}
.jr-voir:hover{text-decoration:underline}
/* Les deux libellés sont écrits dans le bouton, c'est `aria-expanded` qui décide lequel se voit.
   Le JavaScript n'a donc aucun texte à mémoriser ni à réécrire — et l'état affiché ne peut pas
   diverger de l'état annoncé au lecteur d'écran, puisque c'est le même attribut qui porte les deux. */
.jr-voir-moins,.jr-voir[aria-expanded="true"] .jr-voir-plus{display:none}
.jr-voir[aria-expanded="true"] .jr-voir-moins{display:inline}
.jr-det{list-style:none;margin:9px 0 0;padding:0;font-size:12px;color:var(--dim)}
.jr-det li{display:flex;align-items:baseline;gap:8px;padding:3px 0;min-width:0}
.jr-dm{flex:0 0 auto;font-weight:700;color:var(--green);font-variant-numeric:tabular-nums}
/* Le « quoi » vient d'un message de commit : c'est ce qui déborde. On le tronque,
   la ligne garde sa forme et le projet reste lisible à droite. */
.jr-dq{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.jr-dp{flex:0 0 auto;color:var(--faint);font-size:11px;white-space:nowrap}
.jr-plus{color:var(--faint);font-style:italic}
.jr-vote{display:flex;align-items:center;gap:12px;margin:0 0 14px;padding:12px 15px;border-radius:var(--r-lg);border:1px solid rgba(124,58,237,.42);background:rgba(124,58,237,.12);min-width:0}
.jr-vote-t{min-width:0;flex:1 1 auto;font-size:13px;color:var(--ink)}
.jr-vote-t span{display:block;font-size:11.5px;color:var(--dim);margin-top:2px}


/* La veille : puces NUMÉROTÉES, pour qu'on puisse dire « le point 3 de ce matin ». */
.jr-sec{margin-bottom:18px}
.jr-sect{font-size:12px;font-weight:800;letter-spacing:.6px;text-transform:uppercase;color:var(--dim);margin:0 0 10px}
.jr-puce{display:flex;gap:11px;padding:11px 0;border-top:1px solid var(--line);min-width:0}
.jr-num{flex:0 0 auto;width:22px;height:22px;border-radius:999px;background:rgba(124,58,237,.18);border:1px solid rgba(124,58,237,.4);color:#c4b5fd;font-size:11px;font-weight:800;display:flex;align-items:center;justify-content:center}
.jr-puce-c{min-width:0;flex:1 1 auto}
.jr-puce-c b{display:block;font-size:13.5px;font-weight:700;color:var(--ink);line-height:1.4}
.jr-puce-c p{font-size:12.5px;line-height:1.6;color:var(--dim);margin:4px 0 0}
.jr-src{display:inline-block;margin-top:5px;font-size:11.5px;color:var(--cyan);text-decoration:none;border-bottom:1px solid rgba(34,211,238,.35)}
.jr-src:hover{color:var(--ink);border-bottom-color:var(--violet)}

/* Les automatisations : pastille verte ou rouge, et l'heure. L'heure est ce qui
   permet de rapprocher un échec d'autre chose qui s'est passé cette nuit-là. */
.jr-run{display:flex;align-items:center;gap:10px;padding:11px 0;border-top:1px solid var(--line);min-width:0}
.jr-dot{flex:0 0 auto;width:8px;height:8px;border-radius:999px;background:var(--green);box-shadow:0 0 8px rgba(52,211,153,.6)}
.jr-dot.ko{background:var(--red);box-shadow:0 0 8px rgba(251,113,133,.6)}
.jr-run-t{flex:1 1 auto;min-width:0;font-size:12.5px;color:var(--dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.jr-h{flex:0 0 auto;font-size:11.5px;color:var(--faint);font-variant-numeric:tabular-nums;white-space:nowrap}
.jr-dette .jr-nom{flex:1 1 auto}

@media(max-width:560px){
  /* 2x2 plutôt que quatre colonnes rognées : à 390px, quatre chiffres côte à côte
     tiennent sur deux caractères chacun, ce qui n'est plus un chiffre. */
  .jr-band{grid-template-columns:repeat(2,minmax(0,1fr));padding:12px 16px;gap:10px}
  .jr-stat b{font-size:18px}
  .jr-tabs{padding:10px 16px}
  .jr-headrow{gap:10px;padding-right:34px}
  .jr-xtot b{font-size:26px}
  /* Le bouton de vote passe sous son texte : côte à côte, l'un des deux serait
     tronqué et ce serait le texte qui explique pourquoi voter. */
  .jr-vote{flex-direction:column;align-items:stretch}
  .jr-vote .vbtn{margin-left:0;width:100%}
}

/* ── LE RETOUR D'ATTENTE, POSÉ PAR `agir()` (01-socle.js) ────────────────────────────────────────
   Une seule apparence pour tous les boutons du cockpit, parce qu'un geste ne doit pas se signaler
   différemment selon l'écran où il se trouve — c'est très exactement ce que le lot A corrige.

   ELLE N'EFFACE RIEN. Le libellé et l'icône du bouton restent en place et le « … » s'ajoute après :
   remplacer le contenu, c'était perdre l'icône au retour, et se battre avec les écrans qui
   redessinent leur liste pendant l'attente.

   `::after` ne rend rien sur un <input> ni sur un <select>. Ce n'est pas un oubli : ces éléments-là
   sont soit en régime instantané (le sélecteur de compte Claude), soit accompagnés d'un bouton qui,
   lui, porte la classe. L'opacité et le curseur, eux, s'appliquent partout. */
.agir-attente{opacity:.62;cursor:progress}
.agir-attente::after{content:'…';margin-left:.35em;animation:agirAttente 1s ease-in-out infinite}
@keyframes agirAttente{0%,100%{opacity:1}50%{opacity:.25}}

/* ─────────────────────────────────────────────────────────────────────────────────────────────────
   LE TIROIR TÉLÉPHONE (24-panneau-telephone.js, dessiné dans docs/mockups/panneau-appareil.html).
   Il vit DANS le shell, entre le fil et la colonne d'équipe, et pas au-dessus : la décision du
   2026-09-01 est qu'un téléphone ne recouvre jamais la conversation depuis laquelle on l'a ouvert.
   Le bord gauche violet est ce qui le colle au fil plutôt que de le faire flotter à côté.

   IL N'A PAS DE CROIX, ET C'EST LA MAQUETTE QUI LE DIT. Trois gestes qui ne se confondent pas : le
   bouton du composer OUVRE la session, la flèche REPLIE sans rien couper (l'appareil reste allumé,
   la place tenue), « Rendre la place » TERMINE. Une croix se serait confondue avec la troisième et
   aurait rendu la place à chaque fois qu'on voulait juste regarder la conversation.

   REPLIER EST TOTAL : le tiroir disparaît et le fil reprend toute la largeur. Volontairement à
   l'inverse de la colonne « En ce moment », qui garde un rail de 58 px parce qu'elle a encore
   quelque chose à dire une fois repliée — un téléphone caché n'a rien à dire. On le rouvre par le
   bouton du composer, qui ramène le tiroir sans rien relancer.

   TOUT VIENT DU THÈME, rien n'est écrit à la main. Le panneau flottant d'avant se peignait ses
   propres bruns (#141210, #3a332c) qui n'existent nulle part ailleurs dans le cockpit : il ne
   ressemblait à rien de la maison, et le jour où le thème bouge il aurait été le seul à ne pas
   suivre. */
/* SOUS 901 px LE SHELL EST UNE COLONNE, et le tiroir doit donc PARTAGER la hauteur au lieu de la
   prendre. Sans les deux propriétés ci-dessous il pousse le fil hors de l'écran : mesuré le
   2026-09-04 à 390 et 768 px, la conversation devenait entièrement invisible et la barre de saisie
   remontait par-dessus, alors que la même page sans tiroir était saine aux trois largeurs. Il ne
   RECOUVRE pas pour autant — la décision du 2026-09-01 est qu'on ouvre un téléphone depuis un fil et
   qu'on doit continuer à lire ce fil, à toutes les largeurs. Il s'empile, borné, et la flèche le
   replie quand on veut toute la place. */
#tel{order:2;flex:0 0 auto;max-height:52vh;display:flex;flex-direction:column;min-width:0;
  background:var(--grad-surface);border-top:1px solid var(--line);border-left:2px solid rgba(167,139,250,.4)}
#tel[hidden]{display:none}
.telh{flex:0 0 auto;display:flex;align-items:center;gap:8px;padding:9px 12px;border-bottom:1px solid var(--line)}
.telh b{font-size:10.5px;font-weight:700;letter-spacing:.6px;color:var(--dim)}
.telh .teletat{font-size:11px;color:var(--violet);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.telbody{flex:1;min-height:0;display:flex;flex-direction:column;gap:9px;padding:11px 12px;overflow:auto}
/* La barre de provenance : ce que l'écran montre, dit en permanence. Elle ne porte QUE ce dont le
   cockpit a la source — un badge qui affiche un tiret parce que personne ne l'alimente apprend à
   ne plus lire la barre. */
.telprov{display:flex;flex-wrap:wrap;gap:5px;align-items:center;font-size:10.5px}
.telprov .chip{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border-radius:var(--r-pill);
  border:1px solid var(--line);background:var(--card);color:var(--dim);font-weight:600;white-space:nowrap}
.telprov .chip.on{border-color:rgba(124,58,237,.45);background:rgba(124,58,237,.14);color:#c4b5fd}
.telprov .chip.mono{font-family:ui-monospace,monospace}
/* Ce que le TÉLÉPHONE porte, à côté des dépôts du chat. Volontairement en retrait : les pastilles
   disent sur quoi on travaille, cette phrase dit ce que l'appareil exécute, et confondre les deux est
   exactement ce que cette ligne existe pour éviter. */
.telprov .telporte{flex:1 1 100%;color:var(--muted,#9aa0aa);font-family:ui-monospace,monospace;font-size:10px;
  line-height:1.35;word-break:break-word}
/* L'écran lui-même. Fond très sombre et non pur noir : un téléphone éteint et une vidéo qui n'est
   pas encore arrivée doivent se distinguer d'un trou dans la page. */
/* `position:relative` porte les vignettes : depuis qu'une place tient l'iPhone ET l'Android, le cadre
   montre un grand écran et un petit par-dessus, dans le même conteneur. Rien ne se déplace dans le
   DOM quand on échange — une <video> déplacée d'un parent à l'autre se remet en pause dans plusieurs
   navigateurs, et l'échange rendrait une image figée. */
.telecran{position:relative;flex:0 0 auto;border:1px solid var(--line);border-radius:var(--r-lg);
  background:#05050c;overflow:hidden;display:flex;align-items:center;justify-content:center;min-height:220px}
/* CONTAIN et non COVER : on montre l'écran ENTIER. Recadrer donnerait une image plus flatteuse et
   ferait juger un cadrage qui n'existe pas sur l'appareil. */
.tel-ecran{width:100%;height:100%;object-fit:contain;display:block;background:#05050c}
/* UNE BOÎTE PAR LIAISON dans le cadre : le chat affiché montre la sienne, les autres restent montées
   mais cachées. Cachée, elle ne reçoit aucun clic — c'est ce qui empêche de piloter le téléphone d'un
   chat depuis un autre, ce qui était possible tant que l'écran était un élément unique de la page. */
.telvue{width:100%;height:100%}
.telvue[hidden]{display:none}
/* UN GRAND, LES AUTRES EN VIGNETTE, et un clic échange. Mesuré dans docs/device-panel-plan.md : deux
   écrans lisibles côte à côte demanderaient un tiroir de 680 px, ce qui ne laisse que 196 px de
   conversation sur un écran de 1440 — mort. On voit les deux bouger, on lit celui qui compte, le
   tiroir reste à sa largeur. */
/* SA LARGEUR EST FIXE, ET SA HAUTEUR VIENT DE L'APPAREIL. En pourcentage du cadre, elle allait du
   simple au quadruple : le cadre n'a pas de hauteur propre, il prend celle de l'écran en grand, donc
   un iPhone de 913 px de haut donnait une « vignette » de 347 px. Vu à la mesure le 2026-09-10, dans
   un vrai navigateur. 92 px sur un tiroir de 340 laissent lire un écran d'accueil sans manger celui
   qu'on regarde. */
.telvue.mini{position:absolute;top:8px;right:8px;width:92px;height:auto;
  border:1px solid rgba(167,139,250,.5);border-radius:var(--r-sm);background:#05050c;overflow:hidden;
  cursor:pointer;box-shadow:0 2px 10px rgba(0,0,0,.5);z-index:2}
.telvue.mini .tel-ecran{height:auto}
.telvue.mini:hover{border-color:var(--violet)}
/* LA VIDÉO D'UNE VIGNETTE NE REÇOIT AUCUN POINTEUR : ses écouteurs transforment un clic en appui sur
   le téléphone, et un clic sur la vignette veut dire « montre-la en grand », jamais « tape ici ». Le
   geste lui-même est refusé plus haut (24-panneau-telephone.js, `peutToucher`) ; ceci dirige le clic
   vers la boîte, qui porte l'échange. */
.telvue.mini .tel-ecran{pointer-events:none}
.telpied{display:flex;gap:6px;align-items:center;flex-wrap:wrap}
.telbtn{font:inherit;font-size:11px;font-weight:600;padding:4px 10px;border-radius:var(--r-sm);
  border:1px solid var(--line);background:var(--card);color:var(--dim);cursor:pointer}
.telbtn:hover{color:var(--ink);border-color:rgba(167,139,250,.45)}
.telbtn.fin{margin-left:auto}
.telnote{font-size:10.5px;color:var(--faint);line-height:1.45}
/* La case « rendre la place automatiquement » et ce qu'elle explique. Le libellé est au même niveau
   que les boutons : c'est une décision sur la session, pas un réglage rangé ailleurs. */
.telveille{display:flex;align-items:center;gap:6px;font-size:11px;color:var(--dim);cursor:pointer}
.telveille input{accent-color:var(--violet);cursor:pointer;margin:0}
/* Le décompte que le PONT annonce. Il se lit en un coup d'œil ou il ne sert à rien : c'est la seule
   ligne du tiroir qui prévient d'une coupure à venir. */
.telreste{font-size:10.5px;font-weight:600;color:var(--violet);line-height:1.45}
/* LE RAPPEL : un point sur 📱 quand un chat tient un téléphone. Le bouton reste visible partout, y
   compris liste repliée, donc c'est le seul endroit qui puisse porter ce signal en permanence. */
#phoneBtn.tel-on{color:var(--violet);position:relative}
#phoneBtn.tel-on::after{content:'';position:absolute;top:2px;right:2px;width:6px;height:6px;
  border-radius:50%;background:var(--violet);box-shadow:0 0 0 2px var(--bg)}
@media (min-width:901px){
  /* En large, le tiroir est une colonne à droite du fil, avant celle de l'équipe. Sa largeur est
     celle d'un téléphone lisible sans écraser la conversation. */
  #tel{order:2;flex:0 0 var(--telw,340px);width:var(--telw,340px);max-height:none;border-top:0;
    border-radius:0 var(--r-lg) var(--r-lg) 0;box-shadow:var(--shadow-drawer)}
  .telecran{min-height:0;flex:1}
}
