/* ── Défilement : UN motif pour toutes les zones. Barre native masquée (piste invisible) ; un curseur fin dessiné
   par-dessus n'apparaît que pendant le défilement et s'efface en fondu une seconde après. Il reste dans les bornes de
   la zone qui défile, qui s'arrête au-dessus du champ de saisie. ── */
.scroll, .nav-scroll, .side-body, #input, .ta { scrollbar-width: none; }
.scroll::-webkit-scrollbar, .nav-scroll::-webkit-scrollbar, .side-body::-webkit-scrollbar, #input::-webkit-scrollbar, .ta::-webkit-scrollbar { display: none; } /* Safari < 18.2 */
.thumb { position: absolute; top: 0; left: 0; width: var(--thumb-w); border-radius: calc(var(--thumb-w) / 2); background: var(--thumb); opacity: 0; pointer-events: none; z-index: 6; transition: opacity 400ms ease-out, background-color var(--move) ease-out; }
.thumb.on { opacity: 1; pointer-events: auto; transition-duration: 120ms; }
.thumb:hover, .thumb.drag { background: var(--thumb-hover); }

/* ── Trois colonnes sous UNE barre globale. --l et --r portent l'état replié ; seul le conteneur change de largeur. ── */
/* Les deux largeurs de colonne sont DECLAREES, et c'est ce qui rend le repli
   animable. Une propriete personnalisee non declaree change de facon DISCRETE :
   une transition posee sur `grid-template-columns` qui la lit par var() est
   re-ciblee a chaque image et ne progresse jamais. Mesure du 2026-10-04 : au
   repli, --l passait bien a 92px pendant que la grille calculee restait gelee a
   264px — la sidebar ne se repliait PAS DU TOUT, et retirer la transition la
   faisait tomber a 92px sur-le-champ. On anime donc la LONGUEUR, et la grille
   la suit.
   initial-value est une valeur morte : #app pose toujours --l et --r lui-meme,
   et @property interdit un var() ici — y recopier le jeton de largeur en
   dupliquerait la source. */
@property --l { syntax: '<length>'; inherits: true; initial-value: 0px; }
@property --r { syntax: '<length>'; inherits: true; initial-value: 0px; }
#app { --l: var(--nav); --bar-in: var(--s2); --r-open: var(--side); --r: var(--r-open); height: 100vh; height: 100dvh; display: grid; grid-template-rows: var(--bar) minmax(0, 1fr); grid-template-columns: var(--l) minmax(0, 1fr) var(--r); transition: --l var(--move) ease-out, --r var(--move) ease-out; }
#app.l-off { --l: var(--nav-c); }
#app.r-off { --r: var(--rail); }
#app.r-none { --r: 0px; }
#app.r-none #side { box-shadow: none; }
/* Largeur de la colonne gauche : on tire son bord (poignee #nav-grip), du meme geste que dans Yumi Code.
   Les DEUX bornes vivent dans tokens.css et nulle part ailleurs : --nav = clamp(--nav-min, --nav-user,
   --nav-max). La page ne pose que --nav-user ; c'est le CSS qui borne, donc une fenetre qui retrecit
   ramene la colonne a la moitie de l'ecran sans une ligne de JavaScript. Les bornes sont DECLAREES en
   longueur pour que la poignee puisse les lire en pixels (aria-valuemin / aria-valuemax). */
@property --nav-min { syntax: '<length>'; inherits: true; initial-value: 0px; }
@property --nav-max { syntax: '<length>'; inherits: true; initial-value: 0px; }
/* DANS la colonne, collee a son filet : un pixel de plus a droite et #nav deborde (scrollWidth), ce que le
   test du repli mesure. */
.nav-grip { position: absolute; top: 0; bottom: 0; right: 0; width: 6px; z-index: 5; cursor: col-resize; touch-action: none; }
.nav-grip::after { content: ""; position: absolute; top: 0; bottom: 0; right: 0; width: 2px; background: var(--accent); opacity: 0; transition: opacity var(--move) ease-out; }
.nav-grip:hover::after, .nav-grip:focus-visible::after, #app.nav-sizing .nav-grip::after { opacity: 1; }
.nav-grip:focus-visible { outline: none; }
/* Pendant le geste la grille suit le pointeur image par image : la transition de --l la ferait trainer. */
#app.nav-sizing { transition: none; user-select: none; -webkit-user-select: none; }
#app.l-off .nav-grip { display: none; }
/* Colonne gauche sur toute la hauteur. La barre commence APRÈS elle (W.7b-6) :
   ≡ + « + » + avatar + omnibar | loupe de fil, version, langue, thème. W.39 : le nom
   de conversation n'est plus dans la barre (avatar seul, nom dans la colonne de droite). */
.bar { grid-column: 2 / -1; grid-row: 1; display: grid; grid-template-columns: minmax(0, 1fr) max(var(--r), var(--settings-w)); align-items: center; background: var(--surface); border-bottom: 1px solid var(--line); min-width: 0; container-type: inline-size; }
.bar-main { display: flex; align-items: center; gap: var(--s2); min-width: 0; padding: 0 var(--s3) 0 var(--bar-in); }
.bar-tools { display: flex; align-items: center; flex: none; gap: var(--s1); }
.brand { position: relative; flex: none; height: var(--bar); border-bottom: 1px solid transparent; display: flex; align-items: center; padding: 0 var(--s3) 0 var(--nav-pad); min-width: 0; overflow: visible; }
/* Bascule de produit (C6) : deux segments, meme grille que les rangees de la
   colonne (.ng) — colonne d icone fixe, colonne de texte qui passe a 0fr au
   repli. L icone du segment actif ne bouge donc pas d un pixel ; c est le
   TEXTE qui se rabat, comme un livre. */
/* Un VRAI interrupteur : une piste, deux moities EGALES, une pastille qui
   glisse de l'une a l'autre. Les moities egales sont ce qui permet a la
   pastille de faire exactement 50 % sans qu'on mesure quoi que ce soit en
   JavaScript, au repli comme a l'ouverture. */
/* La piste est un fond CLAIR, pas le gris de l'interrupteur : le libelle
   inactif s'y lit. Sur --switch-off (un gris moyen) il etait gris sur
   gris, releve par la contre-revue le 2026-10-05. */
.prod { position: relative; display: grid; grid-template-columns: 1fr 1fr; align-items: center; min-width: 0; padding: 2px; border-radius: calc(var(--field) / 2); background: var(--mono-bg); }
.prod-knob { position: absolute; top: 2px; bottom: 2px; left: 2px; width: calc(50% - 2px); border-radius: calc(var(--field) / 2); background: var(--surface); box-shadow: 0 1px 3px rgba(0, 0, 0, .3); transition: transform var(--hop) var(--hop-ease); }
#app.face-code .prod-knob { transform: translateX(100%); }
/* Visage Code : la colonne de gauche reste ENTIERE (son rail glisse vers les projets, navigation.css) ;
   l'interface de Yumi Code prend la place du fil et de la colonne de droite. De la barre il ne reste que
   ≡ : « + », l'avatar et l'omnibar sont des outils des bots. */
/* Hors du visage Code le cadre reste RENDU, invisible et hors flux. En display:none le navigateur gele
   les transitions de l interface qu il charge : precharge, son ecran de chargement ne s effacait qu au premier clic. */
/* #app est le bloc conteneur du cadre hors flux : il occupe alors EXACTEMENT sa zone de grille, donc la
   meme boite cache que montre. Sans ca il prenait toute la fenetre tant qu'il etait cache, changeait de
   largeur a la bascule, et l'interface qu'il charge s'en plaignait (« ResizeObserver loop », mesure
   du 2026-10-12). */
#app { position: relative; }
.code-frame { grid-column: 2 / -1; grid-row: 2; width: 100%; height: 100%; border: 0; position: absolute; visibility: hidden; }
#app.face-code .code-frame { position: static; visibility: visible; }
#app.face-code #new-btn, #app.face-code .bar-main .conv, #app.face-code .omni, #app.face-code #find, #app.face-code #center, #app.face-code #side { display: none; }
/* Les DEUX libelles sont a l'encre pleine : ce qui designe l'actif est la
   pastille sous lui, comme sur un interrupteur segmente. Un libelle
   eclairci pour « inactif » coutait le contraste sans rien dire de plus. */
.prod-seg { position: relative; z-index: 1; display: grid; grid-template-columns: var(--mono-w) 1fr; align-items: center; height: calc(var(--field) - 4px); padding-right: var(--s2); color: var(--ink); transition: grid-template-columns var(--move) ease-out; }
#app.l-off .prod-seg { grid-template-columns: var(--mono-w) 0fr; padding-right: 0; } /* le creux du libelle part avec lui : sinon le segment garde 8 px de vide */
.prod-seg .icon { justify-self: center; }
.prod-nm { min-width: 0; overflow: hidden; white-space: nowrap; text-align: left; font-weight: 600; font-size: var(--t-body); }
.prod-seg[aria-pressed="false"] .icon { opacity: .55; } /* seule l icone s efface : le texte garde son contraste */
.prod-seg[aria-busy="true"] { opacity: .5; } /* attente : etat statique, aucune animation */
@media (prefers-reduced-motion: reduce) { .prod-knob { transition: none; } }
.prod-err { position: absolute; left: var(--nav-pad); right: var(--s3); top: calc(var(--bar) - 4px); z-index: 10; padding: 6px 8px; border: 1px solid var(--line); border-radius: var(--r2); background: var(--surface); box-shadow: var(--shadow); color: var(--err); font-size: var(--t-meta); }
/* ── Dans l'app de bureau du Mac (data-shell, pose par bootstrap.js quand la fenetre s'annonce) il n'y a plus
   de barre de titre : les trois boutons de la fenetre (--lights-w, a --nav-pad du bord) entrent dans NOTRE
   barre, en tete de la colonne de gauche, comme chez Grok Bot. La bascule se range a leur droite, en icones
   seules et a place FIXE : elle ne bouge ni au repli de la colonne ni d'un visage a l'autre. Colonne repliee,
   elle depasse du rail et les outils de la barre lui laissent la place. Toute la barre deplace la fenetre,
   sauf ses commandes. En plein ecran les boutons disparaissent (data-shell="full") : la bascule reprend sa place. ── */
:root[data-shell="darwin"] { --lights-w: 52px; --prod-x: calc(var(--nav-pad) + var(--lights-w) + var(--s3)); --prod-w: calc(2 * var(--mono-w) + 4px); }
:root[data-shell="darwin"] .prod { position: fixed; z-index: 3; top: calc((var(--bar) - var(--field)) / 2); left: var(--prod-x); width: var(--prod-w); }
:root[data-shell="darwin"] .prod-seg { grid-template-columns: var(--mono-w) 0fr; padding-right: 0; }
:root[data-shell="darwin"] .prod-err { left: 0; right: auto; width: max-content; max-width: 60vw; }
:root[data-shell="darwin"] .bar-main { transition: padding-left var(--move) ease-out; }
/* --bar-in est le retrait gauche de la barre : .bar-main le lit, et l'omnibar aussi (navigation.css), pour savoir
   ou finissent les outils de gauche. */
:root[data-shell="darwin"] #app.l-off { --bar-in: calc(var(--prod-x) + var(--prod-w) + var(--s2) - var(--nav-c)); }
/* Fenetre secondaire (data-solo, bootstrap.js) : sa colonne ne montre que ses Mi (ou son dossier de projet), comme la
   barre d'onglets d'une fenetre de Chrome. Rien n'y ouvre autre chose : ni « + », ni omnibar, ni bascule de visage, ni
   pied de colonne. Ces regles suivent celles du dessus, qui ont la meme specificite : l'ordre les fait gagner. */
#app[data-solo] :is(#new-btn, .omni, .prod, .nav-foot) { display: none; }
:root[data-shell="darwin"] #app[data-solo].l-off { --bar-in: calc(var(--prod-x) - var(--nav-c)); }
/* Un seul Mi (solo-one, layout.js) : pas de colonne, la barre part du bord juste apres les boutons de la fenetre. */
#app[data-solo="mi"].solo-one { --l: 0px; }
#app[data-solo="mi"].solo-one :is(#nav, #menu) { display: none; }
:root[data-shell="darwin"] #app[data-solo="mi"].solo-one { --bar-in: var(--prod-x); }
/* La bande du haut est UNE barre sur toute la largeur, meme fond et meme filet : la bascule, qui depasse de la
   colonne quand elle est repliee, ne chevauche pas deux teintes. Le -1px couvre le filet vertical de la colonne. */
:root[data-shell="darwin"] .brand { background: var(--surface); border-bottom-color: var(--line); margin-right: -1px; }
:root[data-shell] :is(.brand, .bar) { -webkit-app-region: drag; }
:root[data-shell] :is(.brand, .bar) :is(button, input, a, .omni-box, .pop) { -webkit-app-region: no-drag; }
:root[data-shell] .nav-grip { -webkit-app-region: no-drag; } /* elle recouvre le bord de .brand, qui deplace la fenetre */
.build { color: var(--ink-2); font-family: var(--mono); font-size: var(--t-label); flex: none; }
.conv { display: flex; align-items: center; gap: var(--s2); padding: 0; min-width: 0; flex: none; }
.conv h1 { margin: 0; font-size: var(--t-h1); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.settings { grid-column: 2; display: flex; align-items: center; justify-content: flex-end; gap: var(--s3); padding: 0 var(--s4) 0 var(--s3); }
.lang-wrap { position: relative; flex: none; }
#lang { display: inline-flex; align-items: center; gap: 6px; height: var(--field); padding: 0 10px; border: 1px solid var(--line); border-radius: var(--r2); background: var(--surface); font: var(--t-meta) var(--mono); text-transform: uppercase; }
.flag { width: 16px; height: 12px; flex: none; border-radius: 2px; box-shadow: 0 0 0 1px var(--line); }
.pop { position: absolute; margin: 0; padding: var(--s1); list-style: none; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r3); box-shadow: var(--shadow); z-index: 20; }
#lang-menu { top: calc(100% + 4px); right: 0; min-width: 160px; }
.pop-item { display: flex; align-items: center; gap: 10px; width: 100%; height: 36px; padding: 0 10px; border-radius: var(--r2); white-space: nowrap; text-align: left; font-size: var(--t-body); }
.pop-item:hover, .pop-item:focus-visible { background: var(--hover); outline: none; }
.pop-item[aria-selected="true"] { font-weight: 600; }
/* Interrupteur de thème : DOM construit une fois, seul aria-checked change, le CSS fait glisser le curseur. */
.toggle { --track-w: 48px; --track-h: 28px; --knob: 24px; flex: none; width: var(--track-w); height: var(--field); display: inline-flex; align-items: center; border-radius: 14px; }
.toggle .track { position: relative; width: var(--track-w); height: var(--track-h); border-radius: calc(var(--track-h) / 2); background: var(--switch-off); transition: background-color 200ms ease-out; }
.toggle[aria-checked="true"] .track { background: var(--accent-fill); }
.toggle .knob { position: absolute; top: 2px; left: 2px; width: var(--knob); height: var(--knob); border-radius: 50%; background: #ffffff; box-shadow: 0 1px 3px rgba(0, 0, 0, .35); transform: translateX(0); transition: transform 200ms ease-out; }
.toggle[aria-checked="true"] .knob { transform: translateX(calc(var(--track-w) - var(--knob) - 4px)); }
.toggle .knob .icon { position: absolute; inset: 0; margin: auto; width: 14px; height: 14px; color: #1d1d1f; transition: opacity 200ms ease-out; }
.toggle .knob .on, .toggle[aria-checked="true"] .knob .off { opacity: 0; }
.toggle[aria-checked="true"] .knob .on { opacity: 1; }

