/* GÉNÉRÉ par landing/tools/sync-vendor.mjs depuis yumi-cli@37cc4483 (css/tokens.css --av-* + css/navigation.css) — ne pas modifier à la main. */
:root { --av-black: #48484a; --av-brown: #d08a3e; --av-red: #ff4b3a; --av-orange: #ff8c00; --av-yellow: #ffd600; --av-green: #00d65f; --av-cyan: #00d0f0; --av-blue: #3f8cff; --av-violet: #b46cff; --av-magenta: #ff3fae; --av-gray: #a3aab3; --av-ink: #1d1d1f; --av-edge: #f5f5f7; --room3d: 1.4; }
/* ── Sidebar gauche : chaque rangée est la même grille (monogramme | texte) ; au repli la colonne texte passe à 0. ── */
#nav { position: relative; grid-column: 1; grid-row: 1 / -1; display: flex; flex-direction: column; min-width: 0; min-height: 0; border-right: 1px solid var(--line); }
.nav-scroll { flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden; padding: var(--s2) 0; }
/* Les deux listes vivent cote a cote sur un rail ; la bascule fait glisser
   le rail, elle ne remplace pas une liste par l'autre. On peut donc
   entrevoir l'autre pendant le mouvement, comme une page qu'on tourne. */
/* 50 % CHACUNE d'un rail large de 200 % : chaque liste fait donc
   exactement la largeur de la colonne. Avec « 100% 100% » elles faisaient
   le DOUBLE, et le texte des bots ne se tronquait plus — on voyait la
   suite du message en faisant glisser (defaut du 2026-10-05). */
/* Yumi Code occupe la PREMIERE moitie du rail : au repos on regarde la
   seconde (les bots), et passer cote Code ramene le rail vers la droite —
   les bots sortent par la droite, le code entre par la gauche. L'inverse
   donnait la mauvaise direction (Nicolas, 2026-10-05). */
.nav-rail { display: grid; grid-template-columns: 50% 50%; width: 200%; transform: translateX(-50%); transition: transform 300ms cubic-bezier(.32, .72, 0, 1); }
#app.face-code .nav-rail { transform: translateX(0); }
.nav-pane { min-width: 0; overflow: hidden; } /* une liste ne deborde jamais sur l'autre */
@media (prefers-reduced-motion: reduce) { .nav-rail { transition: none; } }
.ng { display: grid; grid-template-columns: var(--mono-w) 1fr; gap: var(--s3); align-items: center; width: 100%; padding: 0 var(--s3) 0 var(--nav-pad); min-width: 0; transition: grid-template-columns var(--move) ease-out; }
#app.l-off .ng { grid-template-columns: var(--mono-w) 0fr; gap: 0; }
.ng > .text { min-width: 0; overflow: hidden; white-space: nowrap; display: flex; align-items: baseline; gap: var(--s2); }
.nav-item { position: relative; height: var(--row); text-align: left; }
#app.l-off .l1, #app.l-off .pv { visibility: hidden; }
#app.l-off .wa { position: absolute; left: calc(var(--nav-pad) + var(--mono-w) - 10px); top: 2px; z-index: 2; }
.nav-item:hover { background: var(--hover); }
.nav-item[aria-current="true"] { background: var(--current); }
.nav-item[aria-current="true"] .label { color: var(--accent); font-weight: 600; }
.nav-item .label { flex: none; font-size: var(--t-body); font-weight: 500; }
.nav-item.two { height: 52px; }
.nav-item.two > .text { flex-direction: column; align-items: stretch; justify-content: center; gap: 1px; }
.l1 { display: flex; align-items: baseline; gap: var(--s2); min-width: 0; }
.l1 .label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.l1 .when { flex: none; color: var(--ink-2); font-size: var(--t-label); font-variant-numeric: tabular-nums; }
.l2 { display: flex; align-items: center; gap: 6px; min-width: 0; color: var(--ink-2); font-size: var(--t-meta); }
.l2 .pv { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Non lus, comme WhatsApp : pastille verte à droite de la 2e ligne (ronde pour 1 chiffre, pilule au-delà), heure en vert, nom et aperçu en gras. */
.wa { flex: none; min-width: 20px; height: 20px; padding: 0 6px; border-radius: 10px; background: var(--wa); color: var(--wa-ink); font-size: 12px; font-weight: 700; line-height: 20px; text-align: center; font-variant-numeric: tabular-nums; }
.wa.one { width: 20px; padding: 0; }
.wait { flex: none; min-width: 20px; height: 20px; padding: 0 6px; border-radius: 10px; background: var(--warn-bg, var(--ink)); color: var(--warn-ink, var(--bg)); font-size: 12px; font-weight: 700; line-height: 20px; text-align: center; font-variant-numeric: tabular-nums; }
.wait.one { width: 20px; padding: 0; }
.nav-item.unread .label, .nav-item.unread .pv { color: var(--ink); font-weight: 600; }
.nav-item.unread .when { color: var(--wa); font-weight: 600; }
.new-sep { display: flex; align-items: center; gap: var(--s3); margin-top: var(--s4); color: var(--ink-2); font-size: var(--t-label); font-weight: 600; text-transform: uppercase; letter-spacing: .06em; }
.new-sep::before, .new-sep::after { content: ''; flex: 1; height: 1px; background: var(--line); }
/* Modèle : liste groupée par fournisseur, une ligne = nom + indication discrète (rapide, puissant, local ; coût relatif). */
.model-wrap { position: relative; }
.model-field { display: inline-flex; align-items: center; justify-content: space-between; gap: var(--s2); min-width: 240px; text-align: left; }
.model-field .icon { width: 14px; height: 14px; transform: rotate(90deg); color: var(--ink-2); }
.model-menu { top: calc(100% + 4px); left: 0; width: min(360px, calc(100vw - 32px)); max-height: 60vh; overflow-y: auto; }
#model-menu { top: auto; bottom: calc(100% + 6px); left: auto; right: 0; }
.model-opt { gap: 10px; } .model-opt .mo-name { font-weight: 500; } .model-opt .ck { margin-left: auto; }
.model-opt .ck { width: 16px; height: 16px; color: var(--accent); flex: none; }
.sub-model { position: relative; display: flex; align-items: center; min-width: 0; }
#new-btn.hl { background: var(--accent-fill); color: var(--on-accent); } /* aucun bot : le « + » est l'action à faire */
.l2.live { color: var(--accent); }
/* Omnibar (W.7b-6) : dans la barre du haut, largeur bornée (--omni-w), loupe DANS le champ. */
/* L'omnibar est centree sur l'ECRAN, pas sur la colonne du milieu : sa
   place ne depend donc d'AUCUNE colonne. Elle ne bouge plus quand on
   bascule de visage, quand on replie la colonne de gauche ou celle de
   droite (demande de Nicolas, 2026-10-05).
   Elle sort du flux, donc elle ne peut plus pousser les outils de la
   barre ; sa largeur garde 220 px de chaque bord pour ne jamais venir
   par-dessus eux. */
.omni { position: fixed; z-index: 15; top: calc((var(--bar) - var(--mono-w)) / 2); left: 50%; transform: translateX(-50%); flex: none; width: min(var(--omni-w), calc(100vw - 440px)); max-width: var(--omni-w); min-width: 0; height: var(--mono-w); display: flex; align-items: center; }
.omni-box { flex: 1; min-width: 0; height: var(--mono-w); display: grid; grid-template-columns: var(--mono-w) minmax(0, 1fr); align-items: center; overflow: hidden; border-radius: var(--r2); background: var(--surface); box-shadow: var(--ring); }
.omni-box:focus-within { box-shadow: var(--ring-focus); }
#search-btn { width: var(--mono-w); height: var(--mono-w); display: inline-flex; align-items: center; justify-content: center; border-radius: var(--r2); color: var(--ink-2); }
#search { width: 100%; min-width: 0; height: 100%; padding: 0 10px 0 0; border: 0; background: none; color: var(--ink); font: var(--t-body) var(--sans); }
#search:focus-visible { outline: none; }
#search::placeholder { color: var(--ink-2); }
/* Liste déroulante de l'omnibar : actions de création puis bots et salles ; pas de fond qui avale le clic. */
.omni-list { top: calc(100% + 4px); left: 0; min-width: 100%; }
.omni-list .pop-item { cursor: pointer; }
.omni-list .pop-item[aria-selected="true"] { background: var(--hover); font-weight: 400; }
.omni-list .icon { color: var(--ink-2); }
.omni-none { padding: var(--s2) 10px; color: var(--ink-2); font-size: var(--t-meta); }
.none { margin: 0; min-height: var(--row); }
.none .text { color: var(--ink-2); font-size: var(--t-meta); white-space: normal; }
/* Création inline : même grille que les entrées ; la salle ajoute ses membres sous le champ, un seul Valider.
   W.7b-5 : le champ reprend l'anneau de l'omnibar (box-shadow --ring-focus, pas outline).
   L'outline:focus-visible globale + overflow:hidden de .ng > .text rognaient l'anneau
   en points bleus aux coins (gauche et droite du champ). */
.new-row { height: var(--row); }
.new-row .text { align-items: center; gap: var(--s1); overflow: visible; }
.new-row .field { border: 0; height: var(--mono-w); appearance: none; -webkit-appearance: none; box-shadow: var(--ring); }
.new-row:focus-within .field { outline: none; box-shadow: var(--ring-focus); }
.new-room { padding: var(--s1) var(--s3) var(--s3) calc(var(--nav-pad) + var(--mono-w) + var(--s3)); }
.new-room legend { padding: 0; margin-bottom: var(--s1); color: var(--ink-2); font-size: var(--t-label); font-weight: 600; text-transform: uppercase; letter-spacing: .06em; }
.new-room fieldset { border: 0; margin: 0; padding: 0; }
.member { display: flex; align-items: center; gap: var(--s2); height: 28px; font-size: var(--t-body); cursor: pointer; }
.member input { width: 16px; height: 16px; margin: 0; accent-color: var(--accent-fill); }
.new-room .btn { margin-top: var(--s2); }
/* Avatar de bot (ANALYSE §8) : forme + volume (3 dégradés : linéaire 140°, reflet haut-gauche, ombre bas-droite) + deux
   yeux. Forme et couleur DÉRIVÉES de l'identifiant. Yeux STATIQUES qui disent l'état : ouverts = en ligne, fermés = hors
   ligne, regard de côté = au travail. Aucune animation. Salle : icône de groupe. Humain : initiale. */
.av { --eye: #1d1d1f; position: relative; font-size: var(--mono-w); width: 1em; height: 1em; display: inline-flex; flex: none; }
.av svg { width: 100%; height: 100%; display: block; overflow: visible; }
.av.sm { font-size: 20px; } .av.md { font-size: 28px; } .av.lg { font-size: 56px; } .av.xl { font-size: 72px; }
.av.photo img { width: 100%; height: 100%; display: block; object-fit: cover; border-radius: 50%; }
.av.photo[data-mask="rounded"] img { border-radius: 28%; } .av.photo[data-mask="square"] img { border-radius: 8%; }
/* Icône de groupe : avatars des membres, au plus 3, et « +N » en 4e case. */
.av.grp .cell { position: absolute; font-size: .5em; width: 1em; height: 1em; }
.g1 .c0 { font-size: 1em; left: 0; top: 0; }
.g2 .cell { font-size: .56em; top: 22%; } .g2 .c0 { left: 0; } .g2 .c1 { left: 44%; }
.g3 .c0 { left: 25%; top: 0; } .g3 .c1 { left: 0; top: 50%; } .g3 .c2 { left: 50%; top: 50%; }
.g4 .c0 { left: 0; top: 0; } .g4 .c1 { left: 50%; top: 0; } .g4 .c2 { left: 0; top: 50%; } .g4 .c3 { left: 50%; top: 50%; }
.av.grp .cell.more { width: 50%; height: 50%; display: flex; align-items: center; justify-content: center; border-radius: 50%; background: var(--mono-bg); color: var(--ink); font-size: .3em; font-weight: 600; font-variant-numeric: tabular-nums; }
/* États des yeux : repos = clignement rare et irrégulier ; hors ligne = fermés, immobiles ; au travail = animations
   d'état (demandées par Nicolas : elles disent ce que fait le bot). transform uniquement ; reduced-motion = pose fixe. */
.av-all, .av-eyes, .av-eye { transform-box: fill-box; transform-origin: center; }
/* Regard (--gx, --gy : position du regard, repère 40×40) et paupière (--bk) sont des variables ANIMÉES. Chaque œil prend
   la largeur d'un point sur une sphère, LA MÊME pour les deux yeux : (1 − ((centre de la paire − centre) / course)²)^0,75 — pleine au milieu, de plus en plus fine
   vers le bord, quel que soit ce qui le pousse (regard, pointeur, profil). --ex = x de l'œil, --cx/--rr = la forme.
   SONDES : le regard n'est PAS un transform animé de .av-eyes — lire --gx/--gy (getComputedStyle) ou la largeur réelle
   de .av-eye ; et le contour du corps est .av-line > *, celui d'un accessoire .acc-line > * (pas un [stroke] au hasard). */
@property --gx { syntax: '<number>'; inherits: true; initial-value: 0; }
@property --gy { syntax: '<number>'; inherits: true; initial-value: 0; }
@property --bk { syntax: '<number>'; inherits: false; initial-value: 1; }
.av-eyes { transform: translate(calc(var(--gx) * 1px), calc(var(--gy) * 1px)); }
.acc-face .acc-anim { translate: calc(var(--gx) * 1px) calc(var(--gy) * 1px); } /* moustaches : suivent le regard comme la bouche (translate indépendant de leur frétillement) */
.av-eye { transform: scale(pow(max(.01, 1 - pow((var(--pc, 20) + var(--gx) - var(--cx, 20)) / var(--rp, 8), 2)), .75), var(--bk)); } /* les DEUX yeux pareil, d'après la place de la paire ; ^.75 : quasi pleins au milieu, presque un trait au bord */
/* Trois états lisibles sur l'avatar seul : DORT (yeux fermés, respiration lente), AU TRAVAIL (yeux ouverts animés + effets
   autour), HORS LIGNE (gris, yeux fermés, immobile). Plus de pastille. */
.av { isolation: isolate; }
.av > svg, .av > img, .av > canvas.av3d { position: relative; z-index: 1; }
.av > canvas.av3d { width: 100%; height: 100%; display: block; } /* avatar 3D (avatar3d.js) */
.av[data-state="off"] .av-eye, .av[data-state="sleep"] .av-eye, .av[data-state]:not([data-state="off"]):not([data-state="sleep"]):not([data-state="on"]) .av-lid { display: none; } /* [data-state] : le conteneur d'un groupe n'a pas d'état */
.av[data-state="off"] > svg, .av.photo[data-state="off"] img { filter: grayscale(1) opacity(.5); }
.av[data-state="sleep"] .av-all { transform-origin: 50% 90%; animation: av-breathe calc(4s * var(--k, 1)) ease-in-out infinite; }
.av.photo[data-state="sleep"] img { animation: av-breathe calc(4s * var(--k, 1)) ease-in-out infinite; }
.av[data-state="sleep"] .av-lid { animation: av-fade .45s ease-out 1; }
.av[data-state]:not([data-state="off"]):not([data-state="sleep"]):not([data-state="on"]) .av-eye { animation: av-blink var(--bl, 5s) linear var(--bd, 0s) infinite, av-open .35s ease-out 1; } /* se réveille, puis cligne */
.av[data-state="long"] .av-eyes, .av[data-state="long"] .acc-face { --gy: 1.4; }
@keyframes av-breathe { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.03); } }
@keyframes av-open { from { --bk: .08; } to { --bk: 1; } }
@keyframes av-fade { from { opacity: 0; } to { opacity: 1; } }
/* Effets autour de l'avatar (ANALYSE §8, « Agent morphs ») : orbite (travaille), radar (cherche), anneau (tâche longue).
   L'orbite existe en deux couches identiques : derrière l'avatar (atténuée, moitié haute) et devant (moitié basse) = 3D. */
.fx { position: absolute; inset: -32%; pointer-events: none; display: none; }
.fx-back { z-index: 0; opacity: .5; clip-path: inset(0 0 50% 0); } .fx-front { z-index: 2; clip-path: inset(50% 0 0 0); }
.av[data-state="working"][data-fx="orbit"] .fx-back, .av[data-state="working"][data-fx="orbit"] .fx-front, .av[data-state="working"] .fx-work { display: block; }
.orb { position: absolute; left: 50%; top: 50%; width: 0; height: 0; transform: rotate(var(--tilt)); }
.ox { position: absolute; transform: translateX(calc(var(--r) * -1)); animation: av-ox calc(var(--t) / 2) ease-in-out infinite alternate; animation-delay: var(--d0, 0s); }
.oy { position: absolute; width: var(--sz); height: var(--sz); margin: calc(var(--sz) / -2); border-radius: 50%; opacity: var(--op);
  background: color-mix(in oklab, var(--av, var(--accent)) 55%, var(--ink)); animation: av-oy calc(var(--t) / 2) ease-in-out infinite alternate; animation-delay: calc(var(--d0, 0s) - var(--t) / 4); }
/* Orbite connectée : mêmes billes, reliées par des segments dessinés en direct (SVG mis à jour à chaque image). */
.av[data-state="working"][data-fx="link"] .fx-link { display: block; }
.fx-link.lk-back { z-index: 0; opacity: .55; } .fx-link.lk-front { z-index: 2; }
.fx-link circle { fill: color-mix(in oklab, var(--av, var(--accent)) 55%, var(--ink)); }
.fx-link line { stroke: color-mix(in oklab, var(--av, var(--accent)) 55%, var(--ink)); stroke-width: .018; stroke-linecap: round; }
@keyframes av-ox { from { transform: translateX(calc(var(--r) * -1)); } to { transform: translateX(var(--r)); } }
@keyframes av-oy { from { transform: translateY(calc(var(--ry) * -1)) scale(.6); } to { transform: translateY(var(--ry)) scale(1.25); } }
.fx.radar { inset: -20%; z-index: 0; border-radius: 50%; } .av[data-state="searching"] .radar, .av[data-state="working"][data-fx="radar"] .radar { display: block; }
.radar::before, .radar::after, .prog::before, .prog::after { content: ''; position: absolute; inset: 0; border-radius: 50%; }
.radar::before { box-shadow: inset 0 0 0 .025em color-mix(in oklab, var(--accent) 45%, transparent); }
.radar::after { background: conic-gradient(from 0deg, transparent 0turn .7turn, color-mix(in oklab, var(--accent) 55%, transparent) 1turn);
  -webkit-mask: radial-gradient(closest-side, transparent 66%, #000 68%); mask: radial-gradient(closest-side, transparent 66%, #000 68%); animation: av-spin 1.6s linear infinite; }
.fx.prog { inset: -14%; z-index: 2; } .av[data-state="long"] .prog, .av[data-state="working"][data-fx="prog"] .prog { display: block; }
.prog::before { box-shadow: inset 0 0 0 .05em var(--line); }
.prog::after { border: .05em solid transparent; border-top-color: var(--accent); border-right-color: var(--accent); animation: av-spin 1.3s linear infinite; }
.zz { position: absolute; right: -6%; top: -12%; z-index: 3; font: 600 .22em/1 var(--sans); color: var(--ink-2); opacity: 0; pointer-events: none; display: none; }
.zz::after { content: 'z'; }
.av.lg[data-state="sleep"] .zz, .av.xl[data-state="sleep"] .zz { display: block; animation: av-z 5.5s ease-out infinite; }
/* Galerie d'effets (éditeur d'icône) : halo, aura, étincelles, bulles, comète, pixels, ondes, constellation. Tailles en em :
   proportionnés à l'avatar, jamais plus de .72 em du centre (la moitié d'une ligne de liste). transform/opacity seulement. */
.fx-work { z-index: 2; } .fx-work.aura, .fx-work.const { z-index: 0; }
.fx-work > span, .halo::before, .aura::before, .comet::before { position: absolute; }
.halo::before { content: ''; inset: 15%; border-radius: 50%; box-shadow: 0 0 0 .035em color-mix(in oklab, var(--av) 70%, transparent), 0 0 .2em .05em color-mix(in oklab, var(--av) 50%, transparent); animation: av-halo 2.4s ease-in-out infinite; }
.aura::before { content: ''; inset: 8%; border-radius: 50%; background: radial-gradient(closest-side, color-mix(in oklab, var(--av) 60%, transparent) 55%, transparent); animation: av-aura 3s ease-in-out infinite; }
.spk { left: 50%; top: 50%; width: .045em; height: .13em; margin: -.065em -.0225em; border-radius: .03em; background: color-mix(in oklab, var(--av) 50%, var(--ink)); transform: rotate(var(--a)) translateY(-.6em); opacity: .9; animation: av-spark 1.6s ease-out infinite; animation-delay: var(--dl); }
.s0 { --a: 20deg; --dl: 0s; } .s1 { --a: 80deg; --dl: -.3s; } .s2 { --a: 140deg; --dl: -.9s; } .s3 { --a: 200deg; --dl: -.5s; } .s4 { --a: 260deg; --dl: -1.2s; } .s5 { --a: 320deg; --dl: -.7s; }
.bub { left: calc(50% + var(--x)); top: 50%; width: var(--s); height: var(--s); margin: calc(var(--s) / -2); border-radius: 50%; box-shadow: inset 0 0 0 .025em color-mix(in oklab, var(--av) 60%, var(--ink)); transform: translateY(.2em); opacity: .8; animation: av-bub var(--t) ease-in infinite; animation-delay: var(--dl); }
.b0 { --x: -.55em; --s: .12em; --t: 2.2s; --dl: 0s; } .b1 { --x: .58em; --s: .09em; --t: 2.8s; --dl: -.9s; } .b2 { --x: -.62em; --s: .07em; --t: 2.5s; --dl: -1.6s; } .b3 { --x: .5em; --s: .1em; --t: 3.1s; --dl: -2.3s; }
/* Comète : traînée et tête sur le MÊME rayon (--cr) autour du MÊME centre. La traînée est un anneau d'épaisseur --cw dont le
   milieu tombe exactement sur --cr ; la tête est centrée sur ce rayon. Tout tourne ensemble (un seul élément en rotation). */
.comet { --cr: 35%; --cw: .05em; animation: av-spin 1.8s linear infinite; }
.comet::before { content: ''; inset: calc(50% - var(--cr) - var(--cw) / 2); border-radius: 50%; background: conic-gradient(from 0deg, transparent 0turn .6turn, color-mix(in oklab, var(--av) 75%, transparent) 1turn);
  -webkit-mask: radial-gradient(closest-side, transparent calc(100% - var(--cw)), #000 calc(100% - var(--cw) + .5px), #000 calc(100% - .5px), transparent 100%);
  mask: radial-gradient(closest-side, transparent calc(100% - var(--cw)), #000 calc(100% - var(--cw) + .5px), #000 calc(100% - .5px), transparent 100%); }
.comet .head { left: 50%; top: calc(50% - var(--cr)); width: .12em; height: .12em; margin: -.06em; border-radius: 50%; background: color-mix(in oklab, var(--av) 45%, var(--ink)); }
.pxl { left: 50%; top: 50%; width: .09em; height: .09em; margin: -.045em; border-radius: .015em; background: color-mix(in oklab, var(--av) 55%, var(--ink)); transform: translate(var(--tx), var(--ty)); animation: av-pix 1.4s ease-in infinite alternate; animation-delay: var(--dl); } /* aller vers le centre, retour : ils rentrent DANS l'avatar puis repartent */
.av[data-state="working"][data-fx="pxlink"] .fx-link { display: block; }
.fx-link rect { fill: color-mix(in oklab, var(--av, var(--accent)) 55%, var(--ink)); }
.wv { inset: 20%; border-radius: 50%; box-shadow: inset 0 0 0 .03em color-mix(in oklab, var(--av) 65%, transparent); transform: scale(1.1); opacity: .6; animation: av-wave 2.4s ease-out infinite; animation-delay: var(--dl); }
.w0 { --dl: 0s; } .w1 { --dl: -.8s; } .w2 { --dl: -1.6s; }
/* Sonar : une impulsion part du centre, une seconde plus faible la suit, puis un silence avant le ping suivant. */
.snr { left: 50%; top: 50%; width: 1.2em; height: 1.2em; margin: -.6em; border-radius: 50%; box-shadow: inset 0 0 0 .035em color-mix(in oklab, var(--av) 60%, var(--ink)); transform: scale(.8); opacity: calc(var(--op) * .7); animation: av-sonar 2.8s ease-out infinite; animation-delay: var(--dl); }
.n0 { --op: .95; --dl: 0s; } .n1 { --op: .4; --dl: .32s; }
@keyframes av-sonar { 0% { transform: scale(.06); opacity: var(--op); } 42% { transform: scale(1.3); opacity: 0; } 100% { transform: scale(1.3); opacity: 0; } }
.const svg { width: 100%; height: 100%; animation: av-spin 14s linear infinite; }
.const polyline { fill: none; stroke: color-mix(in oklab, var(--av) 55%, var(--ink)); stroke-width: .018; stroke-linejoin: round; opacity: .7; }
.const circle { fill: color-mix(in oklab, var(--av) 45%, var(--ink)); }
@keyframes av-halo { 0%, 100% { transform: scale(.97); opacity: .55; } 50% { transform: scale(1.05); opacity: 1; } }
@keyframes av-aura { 0%, 100% { transform: scale(.94); opacity: .5; } 50% { transform: scale(1.04); opacity: 1; } }
@keyframes av-spark { 0% { transform: rotate(var(--a)) translateY(-.46em) scale(.4); opacity: 0; } 30% { opacity: 1; } 100% { transform: rotate(var(--a)) translateY(-.7em) scale(1); opacity: 0; } }
@keyframes av-bub { 0% { transform: translateY(.45em) scale(.5); opacity: 0; } 20% { opacity: .9; } 100% { transform: translateY(-.66em) scale(1); opacity: 0; } }
@keyframes av-pix { 0% { transform: translate(calc(var(--tx) * 1.15), calc(var(--ty) * 1.15)) rotate(90deg) scale(.8); opacity: 0; } 15% { opacity: 1; } 65% { opacity: .95; } 100% { transform: translate(0, 0) rotate(0deg) scale(.3); opacity: 0; } }
@keyframes av-wave { 0% { transform: scale(.9); opacity: .9; } 100% { transform: scale(1.45); opacity: 0; } }
.fx-grid { display: grid; grid-template-columns: repeat(4, 80px); gap: 4px; }
.fx-btn { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 12px 2px 6px; border-radius: var(--r2); font-size: var(--t-label); color: var(--ink-2); }
.fx-btn .av { font-size: 30px; }
.fx-btn:hover { background: var(--hover); }
.fx-btn[aria-pressed="true"] { background: var(--current); box-shadow: inset 0 0 0 2px var(--accent); color: var(--ink); }
@keyframes av-spin { to { transform: rotate(1turn); } }
@keyframes av-z { 0%, 62% { opacity: 0; transform: translate(0, 0); } 72% { opacity: .9; } 100% { opacity: 0; transform: translate(.1em, -.5em); } }
.av[data-state="thinking"] .av-eyes, .av[data-state="thinking"] .acc-face { --gy: -2.4; animation: av-think calc(2.6s * var(--k, 1)) ease-in-out infinite; }
.av[data-state="working"] .av-eyes, .av[data-state="working"] .acc-face { --gx: 2; animation: var(--lk, av-look) calc(1.7s * var(--k, 1)) ease-in-out infinite; }
.av[data-state="working"] > svg { transform-origin: 50% 70%; animation: var(--tl, av-tilt1) calc(3.2s * var(--k, 1)) ease-in-out infinite; } /* tête qui penche au hasard */
.av[data-state="working"] .av-all, .av.photo[data-state="working"] img { animation: av-bob calc(.85s * var(--k, 1)) ease-in-out infinite; }
.av[data-state="searching"] .av-eyes, .av[data-state="searching"] .acc-face { --gx: 2.6; animation: av-scan calc(1.9s * var(--k, 1)) linear infinite; }
.av[data-state="writing"] .av-eyes, .av[data-state="writing"] .acc-face { --gy: 2; }
.av[data-state="writing"] .av-all, .av.photo[data-state="writing"] img { animation: av-hop .5s ease-in-out infinite; }
.av[data-state="sending"] .av-all, .av.photo[data-state="sending"] img { animation: av-bounce .9s ease-out 1; }
.av[data-state="sending"] .av-eye + .av-eye { animation: av-wink .9s ease-out 1; }
@keyframes av-think { 0%, 100% { --gx: calc(-1.6 * var(--g, 1)); --gy: calc(-2.2 * var(--g, 1)); } 35% { --gx: calc(1.8 * var(--g, 1)); --gy: calc(-2.8 * var(--g, 1)); } 65% { --gx: calc(.4 * var(--g, 1)); --gy: calc(-1.6 * var(--g, 1)); } }
@keyframes av-look { 0%, 100% { --gx: calc(-2.2 * var(--g, 1)); --gy: calc(.3 * var(--g, 1)); } 45%, 55% { --gx: calc(2.2 * var(--g, 1)); --gy: calc(.3 * var(--g, 1)); } }
@keyframes av-bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-1px); } }
@keyframes av-scan { 0% { --gx: calc(-2.8 * var(--g, 1)); --gy: 0; } 38%, 50% { --gx: calc(2.8 * var(--g, 1)); --gy: 0; } 88%, 100% { --gx: calc(-2.8 * var(--g, 1)); --gy: 0; } }
@keyframes av-hop { 0%, 100% { transform: translateY(0); } 35% { transform: translateY(-1.6px); } 70% { transform: translateY(0); } }
@keyframes av-bounce { 0% { transform: translateY(0); } 30% { transform: translateY(-3.2px); } 55% { transform: translateY(0); } 72% { transform: translateY(-1px); } 100% { transform: translateY(0); } }
@keyframes av-wink { 0%, 62% { --bk: 1; } 76% { --bk: .08; } 100% { --bk: 1; } }
.av[data-c="black"] { --av: var(--av-black); --eye: #ffffff; } .av[data-c="brown"] { --av: var(--av-brown); } .av[data-c="red"] { --av: var(--av-red); }
.av[data-c="orange"] { --av: var(--av-orange); } .av[data-c="yellow"] { --av: var(--av-yellow); } .av[data-c="green"] { --av: var(--av-green); }
.av[data-c="cyan"] { --av: var(--av-cyan); } .av[data-c="blue"] { --av: var(--av-blue); } .av[data-c="violet"] { --av: var(--av-violet); }
.av[data-c="magenta"] { --av: var(--av-magenta); } .av[data-c="gray"] { --av: var(--av-gray); }
/* Couleur libre : --avc = la couleur choisie ; sa variante sombre s'en dérive (color-mix en oklab) ;
   les yeux (--eye-l / --eye-d) sont choisis en JS pour garder un contraste d'au moins 4,5:1 dans chaque thème. */
.av[data-c="custom"] { --av: var(--avc); --eye: var(--eye-l); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .av[data-c="custom"] { --av: color-mix(in oklab, var(--avc) 86%, #ffffff); --eye: var(--eye-d); } }
:root[data-theme="dark"] .av[data-c="custom"] { --av: color-mix(in oklab, var(--avc) 86%, #ffffff); --eye: var(--eye-d); }
.av-eye { fill: var(--skin-eye, var(--eye)); } .av-lid { fill: none; stroke: var(--skin-eye, var(--eye)); stroke-width: 1.8; stroke-linecap: round; }
/* Rendus (data-skin) : mêmes formes, seul ce bloc change. --paint = aplat, --edge = contour, --skin-eye = yeux et traits. */
.av-fill > * { fill: var(--paint, var(--av)); stroke: var(--paint, var(--av)); stroke-linejoin: round; } .av-line > * { fill: var(--edge, var(--av-edge)); stroke: var(--edge, var(--av-edge)); stroke-linejoin: round; stroke-width: calc(var(--sw0, 0) + var(--lw, 3.2)); } /* --lw : épaisseur réglable (AV_LINE par défaut) */
.av-fill > .open, .av-line > .open { fill: none; stroke-linecap: round; }
.av-shade { display: none; }
.gl-a { stop-color: #ffffff; stop-opacity: .22; } .gl-b { stop-color: #000000; stop-opacity: .16; }
.gh-a { stop-color: #ffffff; stop-opacity: .55; } .gh-b { stop-color: #ffffff; stop-opacity: 0; }
.gs-a { stop-color: #000000; stop-opacity: .26; } .gs-b { stop-color: #000000; stop-opacity: 0; }
.av[data-outline="off"]:not([data-skin="neon"]):not([data-skin="line"]) .av-line { display: none; } /* contour = option, sauf rendus dessinés au contour */ .av[data-skin="soft"] .av-shade { display: inline; }
.av[data-skin="pastel"] { --paint: color-mix(in oklab, var(--av) 42%, #ffffff); --skin-eye: var(--av-ink); }
.av[data-skin="neon"] { --paint: color-mix(in oklab, var(--av) 16%, var(--surface)); --edge: var(--av); --skin-eye: var(--av); } .av[data-skin="neon"] .av-line { filter: drop-shadow(0 0 1.5px var(--av)); }
.av[data-skin="line"] { --paint: var(--surface); --edge: var(--av); --skin-eye: var(--av); }
.av[data-skin="comic"] .av-line, .av[data-skin="comic"][data-outline="off"] .av-fill { filter: drop-shadow(1.8px 1.8px 0 var(--av-edge)); } /* contour : sombre en clair, clair en sombre (autocollant) */
.av[data-c="black"][data-skin="neon"], .av[data-c="black"][data-skin="line"] { --edge: var(--ink); --skin-eye: var(--ink); }
.mono .icon { color: var(--ink-2); }
.mono { position: relative; width: var(--mono-w); height: var(--mono-w); border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; background: var(--mono-bg); color: var(--ink); font: 600 var(--t-label)/1 var(--sans); letter-spacing: .02em; flex: none; }
.mono.room, .mono.tool { border-radius: var(--r2); }
.mono.draft { background: none; box-shadow: inset 0 0 0 1.5px var(--ink-2); color: var(--ink-2); }
.nav-foot { flex: none; border-top: 1px solid var(--line); padding-top: 6px; }
.account { position: relative; padding-bottom: 6px; }
#account-menu { bottom: calc(100% - 2px); left: var(--s3); min-width: 220px; }

.av-ink { fill: var(--skin-eye, var(--eye)); } .av-ink.l { fill: none; stroke: var(--skin-eye, var(--eye)); stroke-width: 1.6; stroke-linecap: round; }
/* Vie des yeux au repos (data-state on) : le regard erre (haut, bas, côté, en diagonale), clignement, et de temps en temps
   un sourire en deux demi-lunes (^^ : la paupière retournée). Motif, rythme et décalage par bot : lifeVars (avatars.js). */
.av[data-state="on"] .av-eyes, .av[data-state="on"] .acc-face { animation: var(--gz, av-gaze1) var(--gt, 9s) ease-in-out var(--gd, 0s) infinite; }
.av[data-state="on"] .av-eye { animation: av-blink var(--bl, 5s) linear var(--bd, 0s) infinite, av-joy var(--jy, 20s) linear var(--jd, 0s) infinite; }
.av[data-state="on"] .av-lid { opacity: 0; transform: scaleY(-1); transform-box: fill-box; transform-origin: center; animation: av-joy-lid var(--jy, 20s) linear var(--jd, 0s) infinite; }
@keyframes av-blink { 0%, 94% { --bk: 1; } 96% { --bk: .1; } 98%, 100% { --bk: 1; } }
@keyframes av-joy { 0%, 80% { opacity: 1; } 81%, 91% { opacity: 0; } 92%, 100% { opacity: 1; } }
@keyframes av-joy-lid { 0%, 80% { opacity: 0; } 81%, 91% { opacity: 1; } 92%, 100% { opacity: 0; } }
@keyframes av-gaze1 { 0%, 22% { --gx: 0; --gy: 0; } 26%, 40% { --gx: calc(2 * var(--g, 1)); --gy: calc(-2 * var(--g, 1)); } 44%, 62% { --gx: 0; --gy: 0; } 66%, 78% { --gx: calc(-2.4 * var(--g, 1)); --gy: 0; } 82%, 100% { --gx: 0; --gy: 0; } }
@keyframes av-gaze2 { 0%, 30% { --gx: 0; --gy: 0; } 34%, 46% { --gx: 0; --gy: calc(-2.4 * var(--g, 1)); } 50%, 58% { --gx: 0; --gy: 0; } 62%, 74% { --gx: 0; --gy: calc(2 * var(--g, 1)); } 78%, 100% { --gx: 0; --gy: 0; } }
@keyframes av-gaze3 { 0%, 18% { --gx: 0; --gy: 0; } 22%, 34% { --gx: calc(-2 * var(--g, 1)); --gy: calc(-1.8 * var(--g, 1)); } 38%, 52% { --gx: calc(2.4 * var(--g, 1)); --gy: 0; } 56%, 100% { --gx: 0; --gy: 0; } }
@keyframes av-gaze4 { 0%, 40% { --gx: 0; --gy: 0; } 44%, 56% { --gx: calc(1.6 * var(--g, 1)); --gy: calc(1.8 * var(--g, 1)); } 60%, 70% { --gx: calc(-1.6 * var(--g, 1)); --gy: calc(-2 * var(--g, 1)); } 74%, 100% { --gx: 0; --gy: 0; } }
@keyframes av-look2 { 0%, 100% { --gx: calc(-2 * var(--g, 1)); --gy: calc(-1.6 * var(--g, 1)); } 45%, 55% { --gx: calc(2 * var(--g, 1)); --gy: calc(1.2 * var(--g, 1)); } }
@keyframes av-look3 { 0%, 100% { --gx: calc(-1.6 * var(--g, 1)); --gy: calc(1.6 * var(--g, 1)); } 30% { --gx: 0; --gy: calc(1.8 * var(--g, 1)); } 60% { --gx: calc(1.6 * var(--g, 1)); --gy: calc(1.6 * var(--g, 1)); } }
@keyframes av-tilt1 { 0%, 100% { transform: rotate(0); } 25% { transform: rotate(-7deg); } 60% { transform: rotate(5deg); } }
@keyframes av-tilt2 { 0%, 100% { transform: rotate(3deg); } 50% { transform: rotate(-5deg); } }
@keyframes av-tilt3 { 0%, 40%, 100% { transform: rotate(0); } 20% { transform: rotate(8deg); } 70% { transform: rotate(-4deg); } }
/* Accessoires : --acc-fill = couleur, --acc-edge = contour (défaut : celui du bot) ; contour optionnel (data-line). */
.acc-line > * { fill: var(--acc-edge, var(--av-edge)); stroke: var(--acc-edge, var(--av-edge)); stroke-linejoin: round; stroke-linecap: round; stroke-width: calc(var(--sw0, 0) + 2.4); }
.acc-fill > * { fill: var(--acc-fill); stroke: var(--acc-fill); stroke-linejoin: round; stroke-linecap: round; }
.acc-line > .open, .acc-fill > .open { fill: none; } .acc-line > .open { stroke-width: calc(var(--sw0, 0) + 1.4); } /* monture : contour fin, sinon un bandeau */ .av-acc[data-line="off"] .acc-line { display: none; }
.acc-anim { transform-box: fill-box; transform-origin: 50% 100%; }
.av[data-state="sending"] .acc-head .acc-anim { animation: acc-tip .9s ease-out 1; } /* il soulève son chapeau */
.av[data-state="working"] .acc-head .acc-anim { animation: acc-bob calc(.85s * var(--k, 1)) ease-in-out -.12s infinite; } /* le chapeau suit avec un temps de retard */
.av[data-state="writing"] .acc-neck .acc-anim { transform-origin: 50% 50%; animation: acc-wiggle .5s ease-in-out infinite; }
@keyframes acc-tip { 0%, 100% { transform: none; } 35%, 55% { transform: translate(0, -3px) rotate(-10deg); } }
@keyframes acc-bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-.9px); } }
@keyframes acc-wiggle { 0%, 100% { transform: rotate(0); } 50% { transform: rotate(7deg); } }
/* Vie propre des accessoires au repos, en plus du corps, au rythme du bot (lifeVars : --jy/--jd = son sourire,
   --gt/--gd = son regard) : le nœud penche de temps en temps, la cravate se balance,
   les lunettes grossissent et rebondissent au sourire, le chapeau se soulève, la moustache frétille. */
.av[data-state="on"] .acc-neck .acc-anim { transform-origin: 50% 50%; animation: acc-bow-live var(--jy, 20s) ease-in-out var(--jd, 0s) infinite; }
.av[data-state="on"] .av-acc[data-k="tie"] .acc-anim { transform-origin: 50% 0; animation: acc-tie-live var(--gt, 9s) ease-in-out var(--gd, 0s) infinite; }
.av[data-state="on"] .acc-eyes .acc-anim { transform-origin: 50% 50%; animation: acc-pop var(--jy, 20s) ease-in-out var(--jd, 0s) infinite; }
.av[data-state="on"] .acc-head .acc-anim { animation: acc-hat-live var(--gt, 9s) ease-in-out var(--gd, 0s) infinite; }
.av[data-state="on"] .acc-face .acc-anim { transform-origin: 50% 50%; animation: acc-stache var(--jy, 20s) ease-in-out var(--jd, 0s) infinite; }
@keyframes acc-bow-live { 0%, 20%, 34%, 100% { transform: rotate(0); } 24% { transform: rotate(-14deg); } 29% { transform: rotate(10deg); } } /* au repos : penche, ne tourne PAS (Nicolas, 02/10) */
/* Le tour de ventilateur, c'est au TRAVAIL seulement ; la cravate, elle, se balance plus vite. */
.av[data-state="working"] .acc-neck .acc-anim { transform-origin: 50% 50%; animation: acc-fan calc(7s * var(--k, 1)) ease-in-out infinite; } /* de temps en temps seulement */
.av[data-state="working"] .av-acc[data-k="tie"] .acc-anim { transform-origin: 50% 0; animation: acc-tie-live 1.1s ease-in-out infinite; }
@keyframes acc-fan { 0%, 82% { transform: rotate(0); } 96%, 100% { transform: rotate(720deg); } }
@keyframes acc-tie-live { 0%, 55%, 100% { transform: rotate(0); } 30% { transform: rotate(7deg); } 40% { transform: rotate(-5deg); } 50% { transform: rotate(2deg); } }
@keyframes acc-pop { 0%, 80%, 92%, 100% { transform: scale(1); } 83% { transform: scale(1.28); } 86% { transform: scale(.92); } 89% { transform: scale(1.08); } }
@keyframes acc-hat-live { 0%, 58%, 72%, 100% { transform: none; } 62% { transform: translateY(-1.6px) rotate(-7deg); } 67% { transform: translateY(0) rotate(3deg); } }
@keyframes acc-stache { 0%, 80%, 92%, 100% { transform: scaleX(1); } 83% { transform: scaleX(1.25); } 86% { transform: scaleX(.9); } 89% { transform: scaleX(1.1); } }
/* Mains (emplacement hands) : chaque main (.hl / .hr, dans le calque contour ET le calque couleur) bouge selon l'état ;
   trajets vers le menton (--h-chin, --h-mid) et la visière (--h-eye) calculés d'après la forme (accSVG). */
.acc-hands .hl, .acc-hands .hr { transform-box: fill-box; transform-origin: center; }
.av[data-state="on"] .acc-hands .hl { animation: h-idle 3.2s ease-in-out infinite; } .av[data-state="on"] .acc-hands .hr { animation: h-idle 3.2s ease-in-out -1.6s infinite; }
.av[data-state="sleep"] .acc-hands .hl, .av[data-state="sleep"] .acc-hands .hr { transform: translateY(2.5px); }
.av[data-state="thinking"] .acc-hands .hr { animation: h-chin 2.6s ease-in-out infinite; }
.av[data-state="searching"] .acc-hands .hr { animation: h-visor 1.9s ease-in-out infinite; }
.av[data-state="working"] .acc-hands .hl { animation: h-work .45s ease-in-out infinite; } .av[data-state="working"] .acc-hands .hr { animation: h-work .45s ease-in-out -.22s infinite; }
.av[data-state="writing"] .acc-hands .hl { animation: h-type .26s linear infinite; } .av[data-state="writing"] .acc-hands .hr { animation: h-type .26s linear -.13s infinite; }
.av[data-state="sending"] .acc-hands .hr { animation: h-wave 1s ease-in-out infinite; }
@keyframes h-idle { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-.8px); } }
@keyframes h-chin { 0%, 100% { transform: translate(var(--h-chin), var(--h-mid)); } 50% { transform: translate(var(--h-chin), calc(var(--h-mid) - 1px)); } }
@keyframes h-visor { 0%, 100% { transform: translate(calc(var(--h-chin) + 1px), var(--h-eye)); } 50% { transform: translate(calc(var(--h-chin) + 3px), var(--h-eye)); } }
@keyframes h-work { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-2.4px); } }
@keyframes h-type { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(1.6px); } }
@keyframes h-wave { 0%, 100% { transform: translate(1px, -9px) rotate(0); } 25% { transform: translate(2.5px, -10px) rotate(14deg); } 75% { transform: translate(-.5px, -10px) rotate(-14deg); } }
/* Pieds (emplacement feet) : avec des pieds (data-feet), écrire = MARCHER, chercher = COURIR (de profil : les yeux
   partent sur le côté, l'œil du fond s'efface presque), travailler = SAUTER façon Kirby (tassement, étirement, les pieds
   se décollent un peu, réception écrasée). Décision du 25/09 (planche capybara) : le saut est le travail actif. */
.acc-feet .fl, .acc-feet .fr { transform-box: fill-box; transform-origin: center; }
.av[data-feet][data-state="writing"] .av-all { animation: ft-bob .5s ease-in-out infinite; }
.av[data-feet][data-state="searching"] .av-all { transform-origin: 50% 90%; animation: ft-run-body .3s ease-in-out infinite; }
.av[data-feet][data-state="writing"] .acc-feet .fl, .av[data-feet][data-state="searching"] .acc-feet .fl { animation: ft-step var(--ft, 1s) linear infinite; }
.av[data-feet][data-state="writing"] .acc-feet .fr, .av[data-feet][data-state="searching"] .acc-feet .fr { animation: ft-step var(--ft, 1s) linear calc(var(--ft, 1s) / -2) infinite; }
.av[data-feet][data-state="writing"] { --ft: 1s; } .av[data-feet][data-state="searching"] { --ft: .6s; }
.av[data-feet][data-state="writing"] .av-all .av-eyes, .av[data-feet][data-state="searching"] .av-all .av-eyes, .av[data-feet][data-state="writing"] .av-all .acc-face, .av[data-feet][data-state="searching"] .av-all .acc-face { animation: none; --gx: var(--prof, 4); --gy: 0; } /* de profil : au bord (l'œil s'y amincit tout seul) */
.av[data-feet][data-state="writing"] .acc-hands .hl, .av[data-feet][data-state="searching"] .acc-hands .hl { animation: h-swing var(--ft, 1s) ease-in-out infinite; }
.av[data-feet][data-state="writing"] .acc-hands .hr, .av[data-feet][data-state="searching"] .acc-hands .hr { animation: h-swing var(--ft, 1s) ease-in-out calc(var(--ft, 1s) / -2) infinite; }
.av[data-feet][data-state="working"] > svg { animation: none; }
.av[data-feet][data-state="working"] .av-all { transform-origin: 50% 100%; animation: ft-jump .9s cubic-bezier(.3, 0, .5, 1) infinite; }
.av[data-feet][data-state="working"] .acc-feet .fl, .av[data-feet][data-state="working"] .acc-feet .fr { animation: ft-dangle .9s ease-in-out infinite; }
@keyframes ft-bob { 0%, 50%, 100% { transform: translateY(0); } 25%, 75% { transform: translateY(-1px); } }
@keyframes ft-run-body { 0%, 100% { transform: rotate(7deg) translateY(0); } 50% { transform: rotate(7deg) translateY(-1.4px); } }
/* Foulée : le pied posé glisse loin vers l'arrière AU SOL (0 → 55 %), puis revient loin vers l'avant en se levant à peine.
   --fo rapproche les deux pieds sous le corps (vue de profil). */
.acc-feet .fl { --fo: 3.5px; } .acc-feet .fr { --fo: -3.5px; }
@keyframes ft-step { 0%, 100% { transform: translate(calc(var(--fo, 0px) + 4.5px), 0); } 55% { transform: translate(calc(var(--fo, 0px) - 4.5px), 0); } 78% { transform: translate(var(--fo, 0px), -1.3px); } }
@keyframes h-swing { 0%, 100% { transform: translateX(1.4px); } 50% { transform: translateX(-1.4px); } }
@keyframes ft-jump { 0% { transform: scale(1.14, .86); } 18% { transform: scale(.9, 1.12) translateY(-3px); } 45% { transform: scale(1) translateY(-8px); } 72% { transform: scale(.96, 1.05) translateY(-1px); } 82% { transform: scale(1.14, .86) translateY(0); } 100% { transform: scale(1.14, .86); } }
@keyframes ft-dangle { 0%, 18%, 82%, 100% { transform: translateY(0); } 45% { transform: translateY(1.6px); } }
/* Regard plus ample (--g, d'après la forme) et deux parcours de plus : les yeux qui roulent en arc, le double regard. */
@keyframes av-gaze5 { 0%, 30%, 100% { --gx: 0; --gy: 0; } 36% { --gx: calc(-2.4 * var(--g, 1)); --gy: calc(-1.4 * var(--g, 1)); } 42% { --gx: 0; --gy: calc(-2.4 * var(--g, 1)); } 48% { --gx: calc(2.4 * var(--g, 1)); --gy: calc(-1.4 * var(--g, 1)); } 56% { --gx: 0; --gy: 0; } }
@keyframes av-gaze6 { 0%, 50%, 100% { --gx: 0; --gy: 0; } 53%, 58% { --gx: calc(2.4 * var(--g, 1)); --gy: 0; } 61%, 66% { --gx: calc(-2.4 * var(--g, 1)); --gy: 0; } 70% { --gx: 0; --gy: 0; } }
/* Spin : le visage (yeux, traits, lunettes, moustache) part en haut à gauche en s'amincissant, passe DERRIÈRE (masqué),
   revient par l'autre bord et se recentre — une boule qui tourne sur elle-même. --sr = rayon d'après la forme.
   Au repos : de temps en temps (--sp, --sd tirés par bot) ; au travail sans pieds : plus souvent. */
.av-face { transform-box: view-box; transform-origin: 50% 50%; }
.av[data-state] .av-face.still { animation: none; } /* forme à oreilles / antennes : ne tourne pas */
.av[data-state="on"] .av-face { animation: av-facespin var(--sp, 30s) ease-in-out var(--sd, 0s) infinite; }
.av[data-state="working"]:not([data-feet]) .av-face { animation: av-facespin calc(7s * var(--k, 1)) ease-in-out infinite; }
@keyframes av-facespin { /* visage : sortie vers -(sx, sy)·sr en aplatissant l'axe (qx, qy), masqué derrière, retour par +(sx, sy)·sr */
  0%, 86%, 100% { transform: none; opacity: 1; }
  89% { transform: translate(calc(var(--sr, 12px) * var(--sx, 1) * -.72), calc(var(--sr, 12px) * var(--sy, .42) * -.72)) scale(calc(1 - var(--qx, 1) * .38), calc(1 - var(--qy, 0) * .38)); opacity: 1; }
  90.8% { transform: translate(calc(var(--sr, 12px) * var(--sx, 1) * -1), calc(var(--sr, 12px) * var(--sy, .42) * -1)) scale(calc(1 - var(--qx, 1) * .92), calc(1 - var(--qy, 0) * .92)); opacity: 1; }
  90.9% { transform: translate(calc(var(--sr, 12px) * var(--sx, 1) * -1), calc(var(--sr, 12px) * var(--sy, .42) * -1)) scale(calc(1 - var(--qx, 1) * .92), calc(1 - var(--qy, 0) * .92)); opacity: 0; }
  93.4% { transform: translate(calc(var(--sr, 12px) * var(--sx, 1)), calc(var(--sr, 12px) * var(--sy, .42))) scale(calc(1 - var(--qx, 1) * .92), calc(1 - var(--qy, 0) * .92)); opacity: 0; }
  93.5% { transform: translate(calc(var(--sr, 12px) * var(--sx, 1)), calc(var(--sr, 12px) * var(--sy, .42))) scale(calc(1 - var(--qx, 1) * .92), calc(1 - var(--qy, 0) * .92)); opacity: 1; }
  95.5% { transform: translate(calc(var(--sr, 12px) * var(--sx, 1) * .72), calc(var(--sr, 12px) * var(--sy, .42) * .72)) scale(calc(1 - var(--qx, 1) * .38), calc(1 - var(--qy, 0) * .38)); opacity: 1; }
}
/* Les yeux suivent le pointeur (data-look, --mx/--my posés par avatars.js) : remplace le parcours de regard, suit en douceur. */
.av[data-look][data-state] .av-all .av-eyes, .av[data-look][data-state] .av-all .acc-face { animation: none; --gx: var(--mx, 0); --gy: var(--my, 0); transition: --gx .12s ease-out, --gy .12s ease-out; }
.av[data-look][data-state] .av-face { animation: none; } /* yeux accrochés au pointeur : pas de spin (Nicolas, 03/10) */
/* Yeux kawaii (data-face, d'après la mascotte ゆみたん de vpn.3d-expert.fr) : un STYLE DE VISAGE, indépendant du rendu —
   vrais yeux foncés avec un reflet blanc, joues roses, petite bouche en « u » (si la forme n'en a pas). Se combine avec
   tout rendu (pop, doux, pastel, comic…). Néon et trait gardent leurs yeux de couleur ; corps noir vif : yeux blancs. */
.av-glint, .av-blush, .av-kmouth { display: none; }
.av[data-face="kawaii"]:not([data-skin="neon"]):not([data-skin="line"]) { --skin-eye: #3a2b4d; }
.av[data-face="kawaii"][data-c="black"]:is([data-skin="pop"], [data-skin="comic"], [data-skin="soft"]) { --skin-eye: #ffffff; }
.av[data-face="kawaii"] .av-glint, .av[data-face="kawaii"] .av-blush, .av[data-face="kawaii"] .av-kmouth { display: inline; }
.av-glint { fill: #ffffff; } .av-blush { fill: #ff9ec0; opacity: .85; } .av-kmouth { fill: none; stroke: var(--skin-eye, var(--eye)); stroke-width: 1.3; stroke-linecap: round; }
.av[data-face="kawaii"][data-state="off"] .av-glint, .av[data-face="kawaii"][data-state="sleep"] .av-glint { display: none; } /* yeux fermés : pas de reflet */
.av[data-face="kawaii"][data-state="on"] .av-glint { animation: av-joy var(--jy, 20s) linear var(--jd, 0s) infinite; } /* le reflet s'efface quand il sourit */
/* Rendus « corps » inspirés de la mascotte : nuage = boule presque blanche teintée, lueur rose, ombre mauve, sans contour ;
   mochi = blanc teinté, fin contour de la couleur du bot. */
.av[data-skin="cloud"], .av[data-skin="mochi"] { --paint: color-mix(in oklab, var(--av) 16%, #ffffff); }
.av[data-skin="cloud"] .av-line { display: none; } .av[data-skin="cloud"] .av-shade { display: inline; }
.av[data-skin="cloud"] .gl-a { stop-color: #ffffff; stop-opacity: 0; } .av[data-skin="cloud"] .gl-b { stop-color: #ffd6e8; stop-opacity: .5; }
.av[data-skin="cloud"] .gs-a { stop-color: #ffb8d5; stop-opacity: .55; } .av[data-skin="cloud"] .gh-a { stop-color: #ffffff; stop-opacity: .9; }
.av[data-skin="cloud"]:not([data-state="off"]) > svg { filter: drop-shadow(0 .05em .1em rgba(169, 140, 240, .35)); }
.av[data-skin="mochi"] { --edge: var(--av); --lw: 2; }

/* Visage Code : les projets, puis leurs sessions dessous. Meme rangee que
   les bots (.ng), pour que la bascule ne change pas la facon de lire. */
.code-ws { display: flex; align-items: center; gap: var(--s2); height: var(--row); padding: 0 var(--s3) 0 var(--nav-pad); color: var(--ink-2); font-size: var(--t-meta); text-transform: uppercase; letter-spacing: .04em; }
.code-ws .icon { width: 16px; height: 16px; flex: none; }
.code-ss { height: var(--row); text-align: left; }
#app.l-off .code-ws .code-nm { display: none; }
.code-note { margin: 0; padding: var(--s3) var(--nav-pad); color: var(--ink-2); font-size: var(--t-meta); }
.code-note.err { color: var(--err); }
.code-acts { display: flex; gap: var(--s2); padding: var(--s2) var(--nav-pad) var(--s3); }
#app.l-off .code-acts { display: none; }
/* Nouvelle session dans un projet : discrete, a droite de son nom, et elle se rabat avec lui. */
.code-ws .code-nm { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.code-ws .code-new { flex: none; }
#app.l-off .code-ws .code-new { display: none; }
