/* ==========================================================================
   Cybelia — l'interface client                    (§3.4, §4.7 · lot L5bis)
   ==========================================================================
   Les jetons ci-dessous sont repris **tels quels** des maquettes de Denis
   (`Chat.dc.html`, `Main.dc.html`, `Espaces.dc.html`). Ils ne sont pas
   retrouvés : ils sont respectés. Le nombre d'or y est déjà dessiné —
   `1.618fr / 1fr` pour le partage conversation / persona — et les
   respirations suivent Fibonacci : 8 · 13 · 21 · 34 px.

   Ces fichiers vivent **dans le dépôt** (§3.4, grief n°2). La v2 tenait son
   image de marque par un patch appliqué dans le conteneur, que chaque montée
   de version effaçait. Une marque qui ne survit pas à un `docker compose
   pull` n'est pas une marque, c'est une retouche.
   ========================================================================== */

* { box-sizing: border-box; }
body { margin: 0; }

/* `hidden` doit gagner, toujours. La règle du navigateur — `[hidden] {
   display: none }` — vient de sa feuille à lui, et la moindre règle d'auteur
   qui pose un `display` la bat : une bande `.jointes` en `display: flex`
   restait à l'écran, vide, alors que le script la croyait rangée (mesuré dans
   Chrome le 2026-08-27). Deux rustines ponctuelles existaient déjà, écrites
   chacune le jour où quelqu'un s'y est cogné ; celle-ci les remplace toutes,
   et évite la troisième. */
[hidden] { display: none !important; }

.app {
  --brand-500: #E31C60; --brand-300: #f0467f; --brand-700: #a30f44;
  --font-sans: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;
  --bg: #0b0b0d; --bg-sunken: #08080a; --surface: #141417; --surface-2: #1b1b1f;
  --surface-hover: #212126; --border: rgba(255,255,255,.08);
  --border-strong: rgba(255,255,255,.14);
  --text: #f4f4f5; --text-muted: #a1a1aa; --text-subtle: #71717a;
  --accent: #E31C60; --accent-soft: rgba(227,28,96,.14); --accent-ring: rgba(227,28,96,.40);
  --success: #34d399; --success-soft: rgba(16,185,129,.14);
  --warning: #fbbf24; --warning-soft: rgba(251,191,36,.14);
  --info: #60a5fa; --info-soft: rgba(59,130,246,.14);
  /* Le rouge des gestes qui ne se défont pas. Distinct de `--accent`, qui est
     la marque : effacer quelqu'un ne se peint pas de la couleur de la maison. */
  --danger: #ef4444; --danger-soft: rgba(239,68,68,.14);
  --sun: block; --moon: none;
  font-family: var(--font-sans); font-size: 14px; line-height: 1.5;
  color: var(--text); background: var(--bg); -webkit-font-smoothing: antialiased;
}

/* Le thème clair. Le choix vit dans `localStorage`, pas dans une session :
   c'est une préférence de l'œil qui regarde, pas du compte qui se connecte. */
.app[data-theme="light"] {
  --sun: none; --moon: block;
  --bg: #fafafa; --bg-sunken: #f4f4f5; --surface: #ffffff; --surface-2: #fafafa;
  --surface-hover: #f4f4f5; --border: #e4e4e7; --border-strong: #d4d4d8;
  --text: #18181b; --text-muted: #52525b; --text-subtle: #71717a;
  --accent-soft: #fde7ee; --success: #059669; --success-soft: #d1fae5;
  --warning: #b45309; --warning-soft: #fef3c7;
  --info: #2563eb; --info-soft: #dbeafe;
  --danger: #dc2626; --danger-soft: #fee2e2;
}

h1, h2, h3 { margin: 0; line-height: 1.2; font-weight: 600; letter-spacing: -.01em; }
p { margin: 0; }
a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--brand-300); text-decoration: underline; }
.ico { width: 17px; height: 17px; flex-shrink: 0; }

/* ── La coquille ────────────────────────────────────────────────────────── */

.app { display: grid; grid-template-columns: 248px minmax(0, 1fr);
       height: 100vh; overflow: hidden; }

/* Le bandeau d'incarnation prend une rangée à lui, sur toute la largeur
   (§4.7ter). 34 px — Fibonacci, et la hauteur d'une ligne qu'on ne peut pas
   ne pas voir sans être pour autant volée à l'écran de travail. */
.app.incarne { grid-template-rows: 34px minmax(0, 1fr); }
.incarnation { grid-column: 1 / -1; display: flex; align-items: center; gap: 8px;
               padding: 0 13px; font-size: 12.5px; color: #fff;
               background: linear-gradient(90deg, var(--brand-700), var(--brand-500)); }
.incarnation .ico { width: 15px; height: 15px; flex-shrink: 0; }
.incarnation b { font-weight: 620; }
.incarnation form { margin: 0; display: flex; }
.incarnation .bouton { height: 21px; padding: 0 8px; font-size: 11.5px;
                       color: #fff; background: rgba(255,255,255,.16);
                       border-color: rgba(255,255,255,.28); }
.incarnation .bouton:hover { background: rgba(255,255,255,.26); }

.barre { background: var(--bg-sunken); border-right: 1px solid var(--border);
         display: flex; flex-direction: column; min-height: 0; }
.marque { display: flex; align-items: center; gap: 10px; padding: 16px 18px; }
.sceau { width: 28px; height: 28px; border-radius: 7px; flex-shrink: 0;
         background: linear-gradient(135deg, var(--brand-500), var(--brand-700));
         display: grid; place-items: center; color: #fff; font-weight: 700; font-size: 14px; }
/* Le fichier de marque dessine déjà sa tuile et son dégradé : le sceau qui le
   porte ne doit pas en peindre un second dessous. */
.sceau.image { background: none; border-radius: 0; }
.societe { font-weight: 600; font-size: 13.5px; }
.editeur { font-size: 10px; color: var(--text-subtle); letter-spacing: .12em;
           text-transform: uppercase; font-weight: 520; }

.nav { padding: 8px 8px 4px; display: flex; flex-direction: column; gap: 2px; }
.nav a { display: flex; align-items: center; gap: 10px; padding: 7px 10px;
         border-radius: 9px; font-size: 13.5px; font-weight: 520;
         color: var(--text-muted); text-decoration: none; }
.nav a:hover { background: var(--surface-hover); color: var(--text); text-decoration: none; }
.nav a[aria-current] { background: var(--accent-soft); color: var(--accent); }

.groupe { font-size: 10px; font-weight: 600; letter-spacing: .08em;
          text-transform: uppercase; color: var(--text-subtle); padding: 4px 10px; }
/* Un groupe qui suit des entrées respire ; le premier n'a rien au-dessus. */
.nav .groupe:not(:first-child) { margin-top: 13px; }
.fils { flex: 1; padding: 10px 8px 16px; display: flex; flex-direction: column;
        gap: 2px; overflow-y: auto; min-height: 0; }
.fils a { display: block; padding: 7px 10px; border-radius: 9px; font-size: 13.5px;
          font-weight: 520; color: var(--text-muted); text-decoration: none;
          white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.fils a:hover { background: var(--surface-hover); color: var(--text); text-decoration: none; }
.fils a[aria-current] { background: var(--accent-soft); color: var(--accent); }
/* La croix d'un fil (§4.7, #56) : elle ne se montre qu'au survol ou au focus
   clavier. Toujours visible, elle ferait une colonne de croix qu'on finit par
   cliquer par mégarde ; jamais visible, elle n'existerait pas au clavier. */
.fils .fil { display: flex; align-items: center; border-radius: 9px; }
.fils .fil a { flex: 1; min-width: 0; }
.fils .fil .croix { opacity: 0; width: 26px; height: 26px; flex-shrink: 0;
                    border: none; background: transparent; border-radius: 8px;
                    color: var(--text-subtle); cursor: pointer; font-size: 13px;
                    margin-right: 4px; }
.fils .fil:hover .croix, .fils .fil .croix:focus-visible { opacity: 1; }
.fils .fil .croix:hover { background: var(--surface-hover); color: var(--accent); }

/* ── L'Atelier (§4.9quinquies, #12) ─────────────────────────────────────────
   Un rail court entre le bouton « Nouvelle conversation » et la liste des
   fils. `flex: none` est là pour une raison : `.fils` prend tout ce qui reste
   et défile ; sans cela, un atelier de vingt dossiers écraserait la liste des
   conversations au lieu de défiler avec elle. Le rail plafonne à 233 px —
   au-delà, il défile seul. */
.atelier { flex: none; padding: 0 8px; max-height: 233px; overflow-y: auto; }
.atelier-titre { display: flex; align-items: center; gap: 5px; padding-right: 4px; }
.atelier-titre span { flex: 1; }
.croix.plus { width: 21px; height: 21px; flex-shrink: 0; border: none;
              background: transparent; border-radius: 6px; cursor: pointer;
              color: var(--text-subtle); font-size: 16px; line-height: 1; }
.croix.plus:hover { background: var(--surface-hover); color: var(--accent); }

/* Même grammaire qu'un fil — un dossier est une ligne de la même colonne, il
   n'a pas à se dessiner autrement. Deux gestes au lieu d'un : renommer et
   fermer, cachés jusqu'au survol pour la raison déjà écrite plus haut. */
.projet { display: flex; align-items: center; border-radius: 9px; }
.projet .nom { flex: 1; min-width: 0; text-align: left; padding: 7px 10px;
               border: none; background: transparent; border-radius: 9px;
               font: inherit; font-size: 13.5px; font-weight: 520;
               color: var(--text-muted); cursor: pointer;
               white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.projet:hover { background: var(--surface-hover); }
.projet:hover .nom { color: var(--text); }
.projet[aria-current] { background: var(--accent-soft); }
.projet[aria-current] .nom { color: var(--accent); }
.projet .compte { font-size: 11px; color: var(--text-subtle); padding: 0 3px;
                  flex-shrink: 0; }
.projet .croix { opacity: 0; width: 21px; height: 21px; flex-shrink: 0;
                 border: none; background: transparent; border-radius: 6px;
                 color: var(--text-subtle); cursor: pointer; font-size: 12px;
                 margin-right: 2px; }
.projet:hover .croix, .projet .croix:focus-visible { opacity: 1; }
.projet .croix:hover { background: var(--surface); color: var(--accent); }

/* Le bandeau du dossier ouvert : il dit où l'on est, pas ce qu'on peut faire.
   Assez discret pour ne pas concurrencer l'en-tête juste au-dessus. */
.bandeau-dossier { display: flex; align-items: center; gap: 8px;
                   padding: 8px 21px; border-bottom: 1px solid var(--border);
                   background: var(--accent-soft); font-size: 13px; }
.bandeau-dossier .ico { width: 16px; height: 16px; color: var(--accent);
                        flex-shrink: 0; }
.bandeau-dossier b { color: var(--text); font-weight: 600;
                     white-space: nowrap; overflow: hidden;
                     text-overflow: ellipsis; max-width: 377px; }
.bandeau-dossier .aide { white-space: nowrap; }
.bandeau-dossier .bouton { padding: 4px 10px; font-size: 12px; }

/* ── La mémoire, ouverte (#11) ──────────────────────────────────────────────
   Une liste de lignes, pas un tableau : chaque souvenir est une phrase, et une
   phrase se lit sur toute la largeur. 377 px de haut — assez pour en voir une
   dizaine, assez peu pour que le champ d'ajout reste visible sans défiler. */
.souvenirs { display: flex; flex-direction: column; gap: 3px;
             max-height: 377px; overflow-y: auto; margin: 3px 0;
             border: 1px solid var(--border); border-radius: 9px; padding: 5px; }
.souvenir { display: flex; align-items: flex-start; gap: 8px;
            padding: 6px 8px; border-radius: 8px; }
.souvenir:hover { background: var(--surface-hover); }
.souvenir .dit { flex: 1; min-width: 0; font-size: 13px; line-height: 1.5;
                 color: var(--text); overflow-wrap: anywhere; }
.souvenir .gestes { display: flex; gap: 2px; flex-shrink: 0; }
.souvenir .croix { opacity: 0; width: 24px; height: 24px; border: none;
                   background: transparent; border-radius: 7px;
                   color: var(--text-subtle); cursor: pointer; font-size: 12px; }
.souvenir:hover .croix, .souvenir .croix:focus-visible { opacity: 1; }
.souvenir .croix:hover { background: var(--surface); color: var(--accent); }
.souvenir.choix { cursor: pointer; align-items: center; }
/* Un souvenir commun se distingue sans se cacher : il n'a pas de gestes, et
   un filet à gauche dit qu'il n'appartient pas à celui qui le lit. */
.souvenir.commun { border-left: 2px solid var(--border-strong);
                   border-radius: 0 9px 9px 0; }
.souvenir .dit .aide { color: var(--text-subtle); font-size: 12px; }
.ajout-souvenir { display: flex; gap: 5px; align-items: center; }
.ajout-souvenir input { flex: 1; min-width: 0; }

/* ── Le compte, et l'administration sous lui (#57) ──────────────────────────
   `position: relative` porte le menu : il s'ouvre **vers le haut**, parce
   qu'il n'y a rien en dessous. Le menu vit dans `.app`, donc dans la portée où
   la palette est déclarée — la leçon des boîtes transparentes vaut ici aussi,
   et c'est pour cela qu'on n'utilise pas la couche du dessus. */
.moi { border-top: 1px solid var(--border); padding: 10px 12px;
       display: flex; align-items: center; gap: 10px; position: relative; }
.pastille { width: 30px; height: 30px; border-radius: 50%; background: var(--surface-2);
            display: grid; place-items: center; font-size: 12.5px; font-weight: 600;
            border: 1px solid var(--border); flex-shrink: 0; }
.moi-bouton { flex: 1; min-width: 0; display: flex; align-items: center; gap: 10px;
              font: inherit; text-align: left; padding: 4px; margin: -4px;
              border: 0; background: transparent; color: inherit;
              border-radius: 10px; cursor: pointer; }
.moi-bouton:hover { background: var(--surface-hover); }
.moi-qui { min-width: 0; flex: 1; }
.moi-nom { font-size: 12.5px; font-weight: 520; white-space: nowrap;
           overflow: hidden; text-overflow: ellipsis; }
.moi-role { font-size: 11.5px; color: var(--text-subtle); white-space: nowrap;
            overflow: hidden; text-overflow: ellipsis; }
.moi-bouton .chevron { flex-shrink: 0; color: var(--text-subtle);
                       transition: transform .13s ease; }
.moi-bouton[aria-expanded="true"] .chevron { transform: rotate(180deg); }

/* 8 px au-dessus du bouton, et les mêmes bords que les boîtes : un menu qui
   se dessine autrement qu'une boîte apprend à croire qu'il est autre chose. */
.menu-compte { position: absolute; left: 8px; right: 8px; bottom: calc(100% - 2px);
               z-index: 21; padding: 5px; border-radius: 13px;
               background: var(--surface); border: 1px solid var(--border);
               box-shadow: 0 13px 34px rgba(0, 0, 0, .21);
               display: flex; flex-direction: column; gap: 2px; }
.menu-compte .groupe { padding: 5px 10px 2px; }
.menu-compte a, .menu-compte button { display: flex; align-items: center; gap: 10px;
              width: 100%; padding: 7px 10px; border-radius: 9px; border: 0;
              font: inherit; font-size: 13.5px; font-weight: 520; text-align: left;
              background: transparent; color: var(--text-muted);
              text-decoration: none; cursor: pointer; }
.menu-compte a:hover, .menu-compte button:hover {
              background: var(--surface-hover); color: var(--text);
              text-decoration: none; }
.menu-compte a[aria-current] { background: var(--accent-soft); color: var(--accent); }
/* L'épingle (Denis, 2026-08-28). Posée dans le coin, à la taille d'une icône
   et pas d'une entrée : c'est un interrupteur d'habitude, pas une destination.
   Elle se retourne de 34° quand elle tient — le même geste qu'une punaise
   qu'on enfonce — et prend la couleur d'accent, parce qu'un état qui ne se
   distingue que par une nuance de gris ne se distingue pas. */
.menu-compte .epingle { position: absolute; top: 5px; right: 5px;
              width: 26px; height: 26px; padding: 0;
              display: flex; align-items: center; justify-content: center;
              color: var(--text-subtle); border-radius: 8px; }
.menu-compte .epingle .ico { width: 15px; height: 15px;
              transition: transform .13s ease; }
.menu-compte .epingle[aria-pressed="true"] { color: var(--accent);
              background: var(--accent-soft); }
.menu-compte .epingle[aria-pressed="true"] .ico { transform: rotate(34deg); }

/* Se déconnecter n'est pas une entrée comme les autres : un trait l'en sépare,
   pour qu'on ne la clique pas en visant celle du dessus. */
.menu-compte form { margin-top: 4px; padding-top: 5px;
                    border-top: 1px solid var(--border); }

/* ── L'en-tête d'écran ──────────────────────────────────────────────────── */

.entete { height: 56px; border-bottom: 1px solid var(--border); flex-shrink: 0;
          display: flex; align-items: center; gap: 16px; padding: 0 24px; }
.chemin { display: flex; align-items: center; gap: 7px; font-size: 13.5px;
          color: var(--text-muted); min-width: 0; }
.chemin b { color: var(--text); font-weight: 520; white-space: nowrap;
            overflow: hidden; text-overflow: ellipsis; }
.pousse { flex: 1; }

.chip { display: inline-flex; align-items: center; gap: 6px; font-size: 11.5px;
        font-weight: 520; padding: 3px 9px; border-radius: 999px;
        background: var(--surface-2); color: var(--text-muted);
        border: 1px solid var(--border); }
.point { width: 6px; height: 6px; border-radius: 50%; background: var(--success);
         flex-shrink: 0; }

.bouton { font: inherit; font-size: 13.5px; font-weight: 520; padding: 8px 13px;
          border-radius: 9px; border: 1px solid var(--accent); background: var(--accent);
          color: #fff; cursor: pointer; display: inline-flex; align-items: center;
          justify-content: center; gap: 8px; }
.bouton:hover { background: var(--brand-700); border-color: var(--brand-700); }
.bouton.sobre { background: transparent; border-color: var(--border-strong);
                color: var(--text-muted); }
.bouton.sobre:hover { background: var(--surface-hover); color: var(--text); }
/* Une nue tient une icône **ou** un mot, et les deux étaient servis par la
   même largeur figée de 32 px : « Renvoyer le lien » et « Suspendre » se
   chevauchaient dans la colonne des comptes (Denis, le 2026-08-25). Un carré
   de 32 px est un **plancher**, pas une mesure : une icône seule ne l'atteint
   pas et reste carrée, un libellé pousse la boîte à sa propre largeur. */
.bouton.nue { min-width: 32px; height: 32px; padding: 0 8px; border-color: transparent;
              background: transparent; color: var(--text-muted); }
.bouton.nue:hover { background: var(--surface-hover); color: var(--text);
                    border-color: transparent; }
/* Ce qui ne se défait pas ne ressemble pas à ce qui se défait. La teinte
   `--danger` est déjà celle des refus ; on ne la sort que là. */
.bouton.rouge { background: var(--danger); border-color: var(--danger); color: #fff; }
.bouton.rouge:hover { filter: brightness(.92); }
.bouton.nue.rouge { background: transparent; border-color: transparent;
                    color: var(--danger); }
.bouton.nue.rouge:hover { background: var(--danger-soft); color: var(--danger); }

.theme-btn { width: 30px; height: 30px; border-radius: 9px; border: 1px solid var(--border);
             background: var(--surface); color: var(--text-muted); display: grid;
             place-items: center; cursor: pointer; flex-shrink: 0; }
.theme-btn:hover { background: var(--surface-hover); color: var(--text); }
.t-sun { display: var(--sun); } .t-moon { display: var(--moon); }

/* ── Discuter : le partage φ, 62 / 38 ───────────────────────────────────── */

.scene { display: grid; grid-template-columns: minmax(0, 1.618fr) minmax(0, 1fr);
         min-width: 0; min-height: 0; }
.scene[data-panneau="replie"] { grid-template-columns: minmax(0, 1fr) 34px; }
.colonne { display: flex; flex-direction: column; min-width: 0; min-height: 0;
           border-right: 1px solid var(--border); }
.echange { flex: 1; overflow-y: auto; padding: 21px 34px; display: flex;
           flex-direction: column; gap: 21px; }

.question { align-self: flex-end; max-width: 78%; background: var(--accent-soft);
            border: 1px solid rgba(227,28,96,.22); border-radius: 12px 12px 4px 12px;
            padding: 13px 16px; font-size: 14px; white-space: pre-wrap; }
.reponse { display: flex; gap: 13px; max-width: 92%; }
.visage { width: 32px; height: 32px; border-radius: 50%; flex-shrink: 0;
          background: linear-gradient(135deg, var(--brand-500), var(--brand-700));
          display: grid; place-items: center; color: #fff; font-size: 12.5px; font-weight: 700; }
.bulle { background: var(--surface); border: 1px solid var(--border);
         border-radius: 12px 12px 12px 4px; padding: 13px 16px; font-size: 14px;
         line-height: 1.65; white-space: pre-wrap; overflow-wrap: anywhere; }
/* Ce que la relecture a signalé (§4.5sexies). Discret **et** lisible : ce
   n'est pas une alerte — le texte au-dessus reste la réponse —, c'est une
   marge. D'où le filet à gauche plutôt qu'un cadre, et la teinte d'attention
   plutôt que celle du refus. Respirations Fibonacci (§5.6). */
.reserves { margin: 8px 0 0 13px; padding: 8px 13px; font-size: 13px;
            line-height: 1.55; color: var(--text-subtle);
            border-left: 2px solid var(--warning); }
.reserves b { display: block; font-size: 12px; letter-spacing: .02em;
              text-transform: uppercase; color: var(--text-subtle);
              margin-bottom: 5px; }
.reserves ul { margin: 0; padding-left: 21px; }
.reserves li { margin: 3px 0; }

/* Elle réfléchit — trois points qui traversent, pas un rectangle vide.
   *Recette de Denis, 2026-08-25.* Une bulle vide en attendant le premier jeton
   ne dit pas « je réfléchis », elle dit « il ne se passe rien » : c'est le
   moment précis où l'on croit que ça a planté. Trois points qui se lèvent l'un
   après l'autre disent le contraire sans rien promettre de faux.

   Le décalage entre deux points est φ⁻¹ de la durée du cycle divisée par trois
   — 0,21 s pour un cycle de 1,02 s : assez pour lire une vague, trop peu pour
   la voir clignoter. La vague part de la gauche, comme se lit une phrase. */
.points { display: inline-flex; align-items: center; gap: 5px; padding: 2px 0; }
.points i { width: 6px; height: 6px; border-radius: 50%; display: block;
            background: var(--text-subtle); opacity: .34;
            animation: vague 1.02s ease-in-out infinite; }
.points i:nth-child(2) { animation-delay: .21s; }
.points i:nth-child(3) { animation-delay: .42s; }
@keyframes vague {
  0%, 62%, 100% { opacity: .34; transform: translateY(0); }
  31%           { opacity: 1;   transform: translateY(-3px); }
}
/* Sur un poste réglé « moins d'animations », on ne bouge plus : on respire.
   Le témoin reste vivant sans faire bouger l'écran de qui l'a demandé. */
@media (prefers-reduced-motion: reduce) {
  .points i { animation: respire 1.62s ease-in-out infinite; }
  @keyframes respire { 0%, 100% { opacity: .34; } 50% { opacity: .89; } }
}

/* ── La roue d'un geste en cours ──────────────────────────────────────────
   Denis, 2026-08-29 : *« partout où je lance des actions […] je veux une
   petite roue qui tourne le temps de l'exécution. »*

   Trois choix, et chacun se voit :

   1. **`aria-busy`, pas une classe.** L'attribut existe pour dire exactement
      cela, un lecteur d'écran l'annonce, et une mesure au navigateur le lit
      sans rien connaître de notre feuille de style.
   2. **La roue s'ajoute, le mot reste.** Remplacer « Lancer » par une roue
      ferait sauter la largeur du bouton, donc la ligne, donc le tableau.
      L'`::before` pousse le libellé de 21 px et rien d'autre ne bouge.
   3. **Elle tourne en 0,89 s.** Un tour plus rapide se lit comme de la
      nervosité, plus lent comme une panne. L'arc ouvert fait 62 % du cercle —
      c'est ce qui donne à l'œil de quoi voir qu'il tourne.

   Le bouton est `disabled` pendant ce temps : la roue dit que ça travaille,
   la désactivation empêche de le redemander. L'un sans l'autre laisserait
   partir deux fois ce qui ne doit partir qu'une. */
.bouton[aria-busy="true"] { position: relative; cursor: progress; }
.bouton[aria-busy="true"]::before {
  content: ""; display: inline-block; vertical-align: -2px;
  width: 13px; height: 13px; margin-right: 8px; border-radius: 50%;
  border: 2px solid currentColor; border-top-color: transparent;
  opacity: .62; animation: tourne .89s linear infinite;
}
/* Une icône seule n'a pas de mot à pousser : la roue **remplace** le dessin,
   sinon le carré de 32 px déborde (leçon `.bouton.nue`). */
.bouton.nue[aria-busy="true"] { color: transparent; }
.bouton.nue[aria-busy="true"]::before {
  position: absolute; inset: 0; margin: auto; color: var(--text-subtle);
  border-color: currentColor; border-top-color: transparent;
}
@keyframes tourne { to { transform: rotate(360deg); } }
/* Sur un poste réglé « moins d'animations », la roue ne tourne pas : elle
   respire. Le geste reste visiblement en cours sans faire bouger l'écran de
   qui a demandé qu'il ne bouge plus. */
@media (prefers-reduced-motion: reduce) {
  .bouton[aria-busy="true"]::before {
    animation: respire-roue 1.62s ease-in-out infinite;
  }
  @keyframes respire-roue { 0%, 100% { opacity: .21; } 50% { opacity: .89; } }
}

/* Un bloc que le réglage de la maison a fermé : lisible, mais sans promesse. */
.coche.eteint { opacity: .62; }

/* Le même motif, en plus petit, dans une puce de tableau : le passage
   d'ingestion qui tourne vraiment (§4.3 — n'animer que ce qui bouge). */
.chip .points i { width: 4px; height: 4px; background: currentColor; }
.chip .points { gap: 3px; margin-right: 5px; vertical-align: middle; }

.retours, .gestes-reponse { margin-top: 10px; display: flex;
                            align-items: center; gap: 8px; flex-wrap: wrap; }
.gestes-reponse { margin-top: 8px; }
.retours button, .gestes-reponse button { font: inherit; font-size: 12.5px; padding: 4px 9px; border-radius: 9px;
                  border: 1px solid transparent; background: transparent;
                  color: var(--text-subtle); cursor: pointer;
                  display: inline-flex; align-items: center; gap: 6px; }
/* Cinq gestes sous chaque réponse : en toutes lettres ils pesaient plus que
   la réponse. En dessin, ils tiennent dans un carré, et le mot reste dans
   `title` (point 2 de la recette du 2026-08-30). */
.retours .geste, .gestes-reponse .geste { padding: 0; width: 28px; height: 28px;
                  justify-content: center; }
.retours .geste .ico, .gestes-reponse .geste .ico { width: 16px; height: 16px; }
.retours button:hover, .gestes-reponse button:hover {
  background: var(--surface-hover); color: var(--text); }
.gestes-reponse button[aria-pressed="true"] { color: var(--accent);
                                             background: var(--accent-soft); }
.retours button[aria-pressed="true"] { color: var(--accent); background: var(--accent-soft); }

.saisie { padding: 13px 34px 21px; flex-shrink: 0; }
/* Le cadre passe en colonne : le texte au-dessus, les gestes en dessous —
   c'est la disposition qui laisse la phrase respirer sur toute la largeur au
   lieu de la coincer entre deux rangées de boutons. */
.saisie .cadre { display: flex; flex-direction: column; gap: 5px;
                 background: var(--surface-2);
                 border: 1px solid var(--border-strong); border-radius: 12px;
                 padding: 8px 8px 8px 16px; }
.saisie .cadre.survol { border-color: var(--accent); background: var(--accent-soft); }
.saisie textarea { flex: 1; font: inherit; font-size: 14px; color: var(--text);
                   background: transparent; border: none; outline: none; resize: none;
                   max-height: 144px; padding: 8px 0; line-height: 1.5; }
.saisie textarea::placeholder { color: var(--text-subtle); }
.gestes-saisie { display: flex; align-items: center; gap: 5px; }
.gestes-saisie .pousse { flex: 1; }
.jointes { display: flex; flex-wrap: wrap; gap: 5px; padding: 5px 0 0; }
.jointes .chip { display: inline-flex; align-items: center; gap: 5px; }
.jointes .chip button { font: inherit; background: none; border: none; padding: 0;
                        color: inherit; cursor: pointer; opacity: .62; }
.jointes .chip button:hover { opacity: 1; }
.jointes .chip.lu { color: var(--accent); }
/* Ce qu'on vient faire (#16). Sobre au repos, lisible au survol : c'est un
   choix qu'on fait rarement, il n'a pas à peser autant que le bouton d'envoi.
   Hauteur 34 px, comme les ronds voisins — la barre garde une seule ligne. */
.modele select { font: inherit; font-size: .8rem; height: 34px; max-width: 233px;
                 padding: 0 5px; border-radius: 8px; cursor: pointer;
                 border: 1px solid transparent; background: transparent;
                 color: var(--text-subtle); }
.modele select:hover { background: var(--surface-hover); color: var(--text); }
.modele select:focus { border-color: var(--accent);
                       box-shadow: 0 0 0 3px var(--accent-ring); }

/* Les trois boutons ronds de la barre. 34 px, comme le pas de Fibonacci
   au-dessus du 21 des icônes — assez large pour un pouce sur mobile. */
.rond { width: 34px; height: 34px; border-radius: 50%; flex-shrink: 0;
        border: 1px solid transparent; background: transparent;
        color: var(--text-subtle); display: grid; place-items: center;
        cursor: pointer; }
.rond:hover { background: var(--surface-hover); color: var(--text); }
.rond[aria-pressed="true"] { color: #fff; background: var(--accent);
                             border-color: var(--accent); }
.rond:disabled { opacity: .38; cursor: default; }

/* ── Le témoin du micro  (§4.8) ────────────────────────────────────────────
   « Un témoin visible indique que le micro est ouvert : non négociable. »
   Il est donc rouge, il est écrit, et le point **suit la voix** : `--niveau`
   est reposé à chaque trame par l'écoute. Un point qui bat toujours pareil
   prouve qu'un script tourne ; un point qui répond à ce qu'on dit prouve que
   le micro entend. Sans écoute continue, `--niveau` vaut 1 et le battement
   lent prend la relève — le micro est ouvert, il n'a simplement rien à dire. */
.temoin-micro { display: inline-flex; align-items: center; gap: 8px;
                padding: 3px 10px 3px 8px; border-radius: 999px;
                font-size: 12px; color: var(--accent);
                border: 1px solid var(--accent-ring);
                background: var(--accent-soft); }
.temoin-micro i { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0;
                  background: var(--accent);
                  transform: scale(var(--niveau, 1));
                  transition: transform 55ms linear;
                  animation: micro-bat 2.6s ease-in-out infinite; }
.temoin-micro b { font-weight: 500; }
@keyframes micro-bat { 0%, 100% { opacity: 1; } 50% { opacity: .38; } }
@media (prefers-reduced-motion: reduce) {
  .temoin-micro i { animation: none; transition: none; }
}
/* Sous 610 px la barre est déjà pleine : le mot s'efface, le point reste.
   Ce qui est non négociable, c'est **voir** que le micro est ouvert. */
@media (max-width: 610px) { .temoin-micro b { display: none; }
                            .temoin-micro { padding: 3px 6px; } }
.envoyer { width: 36px; height: 36px; border-radius: 9px; border: none;
           background: var(--accent); color: #fff; display: grid; place-items: center;
           cursor: pointer; flex-shrink: 0; }
.envoyer:disabled { opacity: .45; cursor: default; }
.avis { margin-top: 8px; font-size: 11.5px; color: var(--text-subtle); text-align: center; }

/* Le panneau de 38 %. La silhouette d'attente est celle de la maquette ; le
   visage animé (§4.11, lot J) se pose **par-dessus** elle quand l'entreprise
   en a retenu un — la silhouette ne disparaît pas, elle passe dessous. C'est
   ce qui rend vrai le « jamais d'écran noir » du §4.11 : si aucun clip ne
   charge, c'est elle qu'on voit, sans une ligne de script pour le décider. */
.persona { display: flex; flex-direction: column; background: var(--bg-sunken);
           min-width: 0; min-height: 0; }
.corps-persona { flex: 1; min-height: 0; display: grid;
                 grid-template-rows: minmax(0, 1fr); }
.scene-persona { padding: 21px; display: flex; flex-direction: column; gap: 13px;
                 min-height: 0; }
.cadre-persona { flex: 1; border-radius: 12px; border: 1px solid var(--border);
                 background: var(--surface); position: relative; overflow: hidden;
                 display: grid; place-items: center; }
.silhouette { width: 148px; height: 148px; color: var(--text-subtle); opacity: .45; }

/* Les deux balises du lecteur, superposées au pixel près : c'est l'échange
   d'opacité qui masque la couture entre deux boucles de 3,80 s. La transition
   est courte — 233 ms — parce qu'un fondu long ferait un fantôme là où il n'y
   a qu'un raccord. `object-fit: cover` : les clips n'ont pas tous la même
   définition (472×592 à 556×694 selon la persona), et un cadre qui s'adapte à
   chacun ferait sauter la mise en page au changement de visage. */
.clip-persona { position: absolute; inset: 0; width: 100%; height: 100%;
                object-fit: cover; transition: opacity .233s linear; }
.cadre-habite .silhouette { display: none; }

/* ── Les visages dessinés (§4.11, #61) ───────────────────────────────────
   Denis, le 2026-08-29 : *« HAL 9000 — Pure CSS […] très bon pour un avatar IA
   minimaliste […] tu peux en chercher d'autres de film et les implémenter »*.

   Cinq figures, cinq archétypes de cinéma tracés en quelques cercles. Tout le
   mouvement est ici et rien dans le script : une figure doit pouvoir être
   peinte **sans une ligne de JavaScript**, sans quoi l'écran de choix devrait
   poser cinq lecteurs pour montrer cinq vignettes.

   `container-type: size` et les unités `cqmin` : le même balisage sert le
   cadre de la discussion (haut, étroit) et une vignette de 233 px. Dimensionner
   en pixels demanderait deux jeux de règles ; en pourcentage, une figure
   s'ovaliserait dans un cadre qui n'est pas carré. `cqmin` prend le plus petit
   côté du cadre, donc la figure reste ronde partout et occupe la même part.

   Les cinq états du §4.11 se disent en **rythme et en couleur**, jamais en
   changeant de figure : ce qu'on regarde reste le même objet, et c'est ce qui
   rend l'état lisible. Le repos est lent (5,5 s), la vigilance deux fois plus
   vive, l'alerte pressante (0,89 s) — des durées de Fibonacci, comme les
   proportions. */
.dessin-persona { position: absolute; inset: 0; container-type: size;
                  display: grid; place-items: center; overflow: hidden; }
.dessin-persona > i { display: block; }

/* ① L'Œil — la lentille unique qui respire. */
.dessin-persona[data-dessin="oeil"] {
  background: radial-gradient(circle at 50% 46%, #1c1e24 0%, #0a0b0d 72%); }
.oe-plaque { position: relative; width: 62cqmin; height: 62cqmin; border-radius: 13%;
             background: linear-gradient(160deg, #24262c, #121317);
             box-shadow: inset 0 .3cqmin 0 rgba(255,255,255,.07),
                         0 0 8cqmin rgba(0,0,0,.55); }
.oe-halo, .oe-lentille, .oe-eclat { position: absolute; top: 50%; left: 50%;
                                    border-radius: 50%; }
.oe-halo { width: 58%; height: 58%; transform: translate(-50%,-50%);
           background: radial-gradient(circle, rgba(255,58,48,.5), rgba(255,58,48,0) 68%);
           animation: oe-souffle 5.5s ease-in-out infinite; }
.oe-lentille { width: 34%; height: 34%; transform: translate(-50%,-50%);
               background: radial-gradient(circle at 50% 38%, #ff7266 0%,
                           #e01b0d 44%, #590905 100%);
               box-shadow: 0 0 3cqmin rgba(255,60,45,.7),
                           inset 0 0 1.6cqmin rgba(0,0,0,.6);
               animation: oe-souffle 5.5s ease-in-out infinite; }
.oe-eclat { width: 7%; height: 7%; background: #fff4e2;
            box-shadow: 0 0 1.6cqmin #ffd9a0;
            transform: translate(-92%, -128%); }
@keyframes oe-souffle { 0%, 100% { opacity: .68; }
                        50%      { opacity: 1; } }

/* ② Le Balayage — la barre qui va et vient. */
.dessin-persona[data-dessin="balayage"] {
  background: linear-gradient(#0a0b0d, #15161a); }
.ba-rail { position: relative; width: 76cqmin; height: 13cqmin; border-radius: 999px;
           background: #17181c; overflow: hidden;
           box-shadow: inset 0 0 2cqmin rgba(0,0,0,.9),
                       0 0 3cqmin rgba(255,40,30,.14); }
.ba-curseur { position: absolute; top: 0; bottom: 0; width: 26%;
              background: radial-gradient(ellipse at center, #ff7a6c 0%,
                          rgba(224,27,13,.72) 46%, rgba(224,27,13,0) 76%);
              animation: ba-va-et-vient 3.4s ease-in-out infinite alternate; }
@keyframes ba-va-et-vient { from { left: 0; } to { left: 74%; } }

/* ③ L'Onde — des cercles chauds, sans visage. */
.dessin-persona[data-dessin="onde"] {
  background: radial-gradient(circle at 50% 50%, #1b1117, #0a0709); }
.on-cercle, .on-coeur { position: absolute; top: 50%; left: 50%; border-radius: 50%; }
.on-coeur { width: 21cqmin; height: 21cqmin; transform: translate(-50%,-50%);
            background: radial-gradient(circle at 42% 36%, #ffdcae, #ff8a5b 46%, #d64d6e 100%);
            box-shadow: 0 0 5cqmin rgba(255,138,91,.5);
            animation: on-respire 4.2s ease-in-out infinite; }
.on-cercle { width: 21cqmin; height: 21cqmin; opacity: 0;
             border: .8cqmin solid rgba(255,150,110,.55);
             animation: on-ouvre 4.2s ease-out infinite; }
.on-cercle:nth-of-type(2) { animation-delay: 1.4s; }
.on-cercle:nth-of-type(3) { animation-delay: 2.8s; }
@keyframes on-respire { 0%, 100% { transform: translate(-50%,-50%) scale(.94); }
                        50%      { transform: translate(-50%,-50%) scale(1.06); } }
@keyframes on-ouvre { 0%   { transform: translate(-50%,-50%) scale(1);   opacity: .7; }
                      100% { transform: translate(-50%,-50%) scale(3.4); opacity: 0; } }

/* ④ La Grille — le phosphore vert des consoles d'avant. */
.dessin-persona[data-dessin="grille"] { background: #020603; }
.gr-fond { position: absolute; inset: 8%;
           background-image:
             repeating-linear-gradient(0deg, rgba(60,255,140,.16) 0 1px, transparent 1px 8%),
             repeating-linear-gradient(90deg, rgba(60,255,140,.16) 0 1px, transparent 1px 8%);
           box-shadow: inset 0 0 8cqmin rgba(0,0,0,.85); }
.gr-scan { position: absolute; left: 8%; right: 8%; height: 8%;
           background: linear-gradient(rgba(60,255,140,0), rgba(60,255,140,.2),
                                       rgba(60,255,140,0));
           animation: gr-descend 5.5s linear infinite; }
@keyframes gr-descend { from { top: 2%; } to { top: 90%; } }
.gr-curseur { position: absolute; top: 50%; left: 50%;
              transform: translate(-50%,-50%);
              width: 8cqmin; height: 13cqmin; background: #4dff9a;
              box-shadow: 0 0 3cqmin rgba(77,255,154,.75);
              animation: gr-clignote 1.3s steps(1) infinite; }
@keyframes gr-clignote { 0%, 49.9% { opacity: 1; } 50%, 100% { opacity: 0; } }

/* ⑤ L'Iris — un diaphragme d'objectif, froid et net. */
.dessin-persona[data-dessin="iris"] {
  background: radial-gradient(circle at 50% 50%, #101822, #05080c); }
.ir-lames, .ir-pupille, .ir-reflet { position: absolute; top: 50%; left: 50%;
                                     border-radius: 50%; }
.ir-lames { width: 55cqmin; height: 55cqmin; transform: translate(-50%,-50%);
            background: repeating-conic-gradient(#2c3b4e 0 15deg, #16202b 15deg 30deg);
            box-shadow: inset 0 0 4cqmin rgba(0,0,0,.7),
                        0 0 4cqmin rgba(90,170,255,.14);
            animation: ir-tourne 21s linear infinite; }
.ir-pupille { width: 26cqmin; height: 26cqmin;
              background: radial-gradient(circle at 50% 44%, #0b111a, #000);
              box-shadow: 0 0 0 .8cqmin rgba(120,190,255,.45),
                          0 0 4cqmin rgba(90,170,255,.3);
              animation: ir-diaphragme 6.8s ease-in-out infinite; }
.ir-reflet { width: 8cqmin; height: 8cqmin; background: rgba(205,232,255,.6);
             filter: blur(.8cqmin); transform: translate(-158%, -168%); }
@keyframes ir-tourne { to { transform: translate(-50%,-50%) rotate(360deg); } }
@keyframes ir-diaphragme { 0%, 100% { transform: translate(-50%,-50%) scale(1); }
                           50%      { transform: translate(-50%,-50%) scale(.72); } }

/* ── Les cinq états, dits en rythme et en couleur ─────────────────────────
   Une seule règle par état et par figure : ce qui change est la durée, la
   teinte, et rien de plus. Un état qui changerait la figure ferait croire à
   un autre visage. */
.dessin-persona[data-etat="vigilance"] .oe-halo,
.dessin-persona[data-etat="vigilance"] .oe-lentille { animation-duration: 2.6s; }
.dessin-persona[data-etat="vigilance"] .ba-curseur  { animation-duration: 1.6s; }
.dessin-persona[data-etat="vigilance"] .on-coeur,
.dessin-persona[data-etat="vigilance"] .on-cercle   { animation-duration: 2.1s; }
.dessin-persona[data-etat="vigilance"] .on-cercle:nth-of-type(2) { animation-delay: .7s; }
.dessin-persona[data-etat="vigilance"] .on-cercle:nth-of-type(3) { animation-delay: 1.4s; }
.dessin-persona[data-etat="vigilance"] .gr-curseur  { animation-duration: .55s; }
.dessin-persona[data-etat="vigilance"] .ir-pupille  { animation-duration: 3.4s; }

/* Concentration : le regard se resserre. C'est le seul état qui touche à une
   taille, parce que c'est le geste que fait un œil qui cherche. */
.dessin-persona[data-etat="concentration"] .oe-lentille { transform: translate(-50%,-50%) scale(.79); }
.dessin-persona[data-etat="concentration"] .oe-halo     { opacity: .35; animation-duration: 1.6s; }
.dessin-persona[data-etat="concentration"] .ba-curseur  { animation-duration: 1s; width: 16%; }
.dessin-persona[data-etat="concentration"] .on-coeur    { animation-duration: 1.6s; }
.dessin-persona[data-etat="concentration"] .on-cercle   { animation-duration: 1.6s; }
.dessin-persona[data-etat="concentration"] .gr-curseur  { animation-duration: .34s; }
.dessin-persona[data-etat="concentration"] .ir-pupille  { animation-duration: 2.1s; }
.dessin-persona[data-etat="concentration"] .ir-lames    { animation-duration: 8s; }

/* Encouragement : la même figure, en plus chaud. `hue-rotate` sur le cadre
   entier plutôt que cinq jeux de couleurs à tenir. */
.dessin-persona[data-etat="encouragement"] { filter: hue-rotate(-21deg) saturate(1.13); }

/* Alerte : rien ne change de forme, tout devient pressant. */
.dessin-persona[data-etat="alerte"] { filter: saturate(1.34) brightness(1.08); }
.dessin-persona[data-etat="alerte"] .oe-halo,
.dessin-persona[data-etat="alerte"] .oe-lentille,
.dessin-persona[data-etat="alerte"] .on-coeur,
.dessin-persona[data-etat="alerte"] .on-cercle,
.dessin-persona[data-etat="alerte"] .ir-pupille { animation-duration: .89s; }
.dessin-persona[data-etat="alerte"] .ba-curseur { animation-duration: .89s; }
.dessin-persona[data-etat="alerte"] .gr-curseur { animation-duration: .21s; }
.dessin-persona[data-etat="alerte"] .gr-scan    { animation-duration: 1.3s; }

/* Un visage qui bat en permanence est une gêne pour qui a demandé qu'on
   l'épargne. La figure reste — c'est elle qu'on a choisie —, seul le
   mouvement s'arrête. */
@media (prefers-reduced-motion: reduce) {
  .dessin-persona * { animation: none !important; }
}

/* ── Le choix du visage, écran « Votre IA » (§4.11) ──────────────────────
   Tuile 233 × 377 : le nombre du §4.11, et son φ. L'image garde sa
   proportion d'origine (291 px de haut) et le nom prend la bande du bas
   (86 px) — 291 + 86 = 377. Le cadre est donc doré sans que le visage soit
   rogné, ce qu'un `object-fit: cover` sur toute la tuile aurait fait.

   **`tuile-visage`, et pas `visage`** : `.visage` est pris depuis longtemps
   par la pastille ronde de 32 px qui porte l'initiale de l'IA devant chaque
   réponse (§4.7). Une tuile qui reprend ce nom hérite de sa hauteur — 32 px,
   `overflow: hidden` par-dessus — et rend six visages réduits à un liséré.
   Mesuré dans Chrome le 2026-08-27, invisible à toute recette textuelle : le
   HTML était juste, l'image se décodait, et l'écran ne montrait rien. */
.visages { display: flex; flex-wrap: wrap; gap: 13px; margin-top: 8px; }
.tuile-visage { width: 233px; height: 377px; box-sizing: border-box;
                padding: 0; border-radius: 13px; overflow: hidden;
                border: 2px solid var(--border); background: var(--surface);
                cursor: pointer; display: flex; flex-direction: column;
                text-align: center; transition: border-color .144s, transform .144s; }
.tuile-visage:hover { transform: translateY(-2px); }
.tuile-visage[aria-pressed="true"] { border-color: var(--accent); }
/* L'image prend ce que la bande du nom laisse. Lui fixer 291 px ferait
   déborder la tuile de l'épaisseur des bordures — 381 au lieu de 377, mesuré
   dans Chrome : quatre pixels qui suffisent à ce que la proportion ne soit
   plus celle qu'on annonce. */
.tuile-visage img, .tuile-visage .tuile-vide,
.tuile-visage .tuile-dessin { width: 100%; flex: 1; min-height: 0;
                              object-fit: cover; display: block; }
.tuile-visage .tuile-vide { background: var(--bg-sunken); }
/* Un visage dessiné n'a pas de vignette à servir : on trace la figure elle-même
   en réduction (#61). `position: relative` parce que `.dessin-persona` se pose
   en `inset: 0` — sans elle, la figure irait se caler sur la page. */
.tuile-visage .tuile-dessin { position: relative; }
.tuile-nom { height: 86px; flex-shrink: 0; display: grid; place-items: center;
             font-size: 12.5px; font-weight: 600; color: var(--text); }
.tuile-visage[aria-pressed="true"] .tuile-nom { color: var(--accent); }
.etat { position: absolute; top: 13px; left: 13px; display: inline-flex; align-items: center;
        gap: 6px; font-size: 11.5px; font-weight: 600; padding: 3px 9px; border-radius: 999px;
        background: var(--accent-soft); color: var(--accent); }
.etat .point { background: currentColor; }

/* ── Le panneau de droite : trois volets qui se choisissent (#53, #58) ───────
   *Denis, le 2026-08-25 : « les boutons en haut vont être un vrai outil de
   sélection de l'affichage du volet. »* L'ancien couple partagé/entier disait
   **où** les choses allaient sans jamais dire **lesquelles** : le contexte
   n'existait qu'en bas d'un panneau partagé, et la réflexion nulle part. Trois
   interrupteurs indépendants les remplacent — `data-volets` porte les noms
   retenus, `data-volets-n` leur nombre. `replie` reste à part : ce n'est pas un
   volet de moins, c'est le panneau entier qui s'efface. L'état vit dans le
   navigateur, comme le thème : c'est une préférence de l'œil qui regarde, pas
   du compte qui se connecte.

   Les hauteurs suivent φ : à deux volets, `1fr / 1.618fr` — 38 % pour celui du
   haut, 62 % pour celui du bas, qui porte ce qu'on lit. À trois, la présence
   garde sa part et les deux autres se partagent le reste à égalité : trois
   proportions différentes seraient une élégance que personne ne verrait. */

.volet { display: none; }
.scene[data-volets~="presence"] .volet-presence { display: flex; }
.scene[data-volets~="reflexion"] .volet-reflexion { display: flex; }
.scene[data-volets~="contexte"] .volet-contexte { display: flex; }

.scene[data-volets-n="2"] .corps-persona {
  grid-template-rows: minmax(0, 1fr) minmax(0, 1.618fr); }
.scene[data-volets-n="3"] .corps-persona {
  grid-template-rows: minmax(0, 1fr) minmax(0, 1.618fr) minmax(0, 1.618fr); }
/* À quatre (l'écran des agents), la même règle tient : le premier carré dit
   *qui* c'est en trois lignes, les trois autres portent ce qu'on lit. */
.scene[data-volets-n="4"] .corps-persona {
  grid-template-rows: minmax(0, 1fr) minmax(0, 1.618fr) minmax(0, 1.618fr)
                      minmax(0, 1.618fr); }

/* Dès qu'un volet en accompagne un autre, la silhouette de la maquette
   déborde : elle rétrécit plutôt que d'être rognée, et le rappel de « Votre
   IA » cède la place. */
.scene:not([data-volets-n="1"]) .silhouette { width: 89px; height: 89px; }
.scene:not([data-volets-n="1"]) .scene-persona { padding: 13px; gap: 8px; }
.scene:not([data-volets-n="1"]) [data-persona-detail] { display: none; }

/* Le sélecteur : trois interrupteurs collés, qui se lisent comme un seul
   objet. Allumé, il porte la couleur d'accent — c'est ce qu'on voit. */
.choix-volets { display: flex; gap: 2px; padding: 2px; border-radius: 10px;
                background: var(--surface); border: 1px solid var(--border); }
.choix-volets .theme-btn { width: 27px; height: 27px; border: 0;
                           background: transparent; border-radius: 8px; }
.choix-volets .theme-btn[aria-pressed="true"] { background: var(--accent-soft);
                                                color: var(--accent); }

.contexte, .reflexion { flex-direction: column; min-height: 0; }
/* « Discuter » allume ses volets par `data-volets~=…`, parce qu'on les y
   choisit. « Collègues IA » les montre tous les trois, toujours : il n'a
   personne à écouter, mais il a besoin du même `display: flex` — sans lui,
   `.dedans { flex: 1; overflow-y: auto }` ne s'applique à rien, et le volet
   du dessus déborde sur celui du dessous. Mesuré dans Chrome contre la
   production le 2026-08-27 : 151 px de recouvrement, et un dépliant devenu
   inatteignable à la souris. */
.scene[data-ecran="collegues"] .contexte { display: flex; }
/* Un trait sépare deux volets ; le volet du haut n'a rien au-dessus de quoi
   se séparer. `:first-child` ne saurait pas le dire — il compte les enfants,
   pas ceux qu'on voit. On l'écrit donc à l'endroit : un volet se souligne en
   haut quand un volet est montré au-dessus de lui. */
.scene[data-volets~="presence"] .volet-reflexion,
.scene[data-volets~="presence"] .volet-contexte,
.scene[data-volets~="reflexion"] .volet-contexte {
  border-top: 1px solid var(--border); }

/* La réflexion est du texte machine : chasse fixe, petite, et elle se lit du
   bas — c'est là que la pensée en cours s'écrit. */
.reflexion .dedans { white-space: pre-wrap; overflow-wrap: anywhere;
           font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
           font-size: 11px; line-height: 1.6; color: var(--text-muted); }

/* Le plan qu'un collègue annonce avant d'agir (L24-C). Il se lit **tel qu'il
   a été écrit** — `pre-wrap`, parce que ce sont des étapes numérotées et
   qu'une liste repliée en paragraphe cesse d'être une liste. Le filet à
   gauche reprend la teinte de la puce « plan à valider » : ce qui attend une
   décision porte la même couleur partout. */
.plan-propose { margin-top: 8px; padding: 8px 13px; white-space: pre-wrap;
           border-left: 2px solid var(--warning); border-radius: 0 5px 5px 0;
           background: var(--warning-soft); color: var(--text-muted);
           font-size: 12.5px; line-height: 1.618; }

.contexte .titre-contexte, .reflexion .titre-contexte { flex-shrink: 0; padding: 13px 21px 8px; font-size: 10px;
           font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
           color: var(--text-subtle); }
/* Les onglets d'un volet (L24-F) : ils ne défilent pas avec ce qu'ils
   commandent — `flex-shrink: 0`, comme le titre au-dessus d'eux. Sans cela,
   choisir « Mémoire » demanderait de remonter la liste qu'on vient de lire. */
.contexte .onglets-volet { flex-shrink: 0; margin: 0 21px 8px; }
.contexte .dedans, .reflexion .dedans { flex: 1; min-height: 0; overflow-y: auto;
           padding: 0 21px 21px; display: flex; flex-direction: column; gap: 8px; }
.appui { border: 1px solid var(--border); border-radius: 12px; background: var(--surface);
         padding: 10px 13px; }
.appui b { display: block; font-size: 12.5px; font-weight: 600; overflow-wrap: anywhere; }
.appui .meta { margin-top: 4px; display: flex; flex-wrap: wrap; align-items: center;
               gap: 8px; font-size: 11px; color: var(--text-subtle); }
.appui p { margin-top: 8px; font-size: 11.5px; line-height: 1.55; color: var(--text-muted);
           display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
           overflow: hidden; }

/* Replié : le fil prend tout, et le rail est le seul chemin du retour. */
.scene[data-panneau="replie"] .persona { display: none; }
.rail { display: none; }
.scene[data-panneau="replie"] .rail { display: flex; flex-direction: column;
        align-items: center; gap: 8px; padding-top: 13px; background: var(--bg-sunken);
        border-left: 1px solid var(--border); }
.rail .nom { writing-mode: vertical-rl; font-size: 10px; letter-spacing: .12em;
             text-transform: uppercase; color: var(--text-subtle); }

/* ── Les écrans de gestion ──────────────────────────────────────────────── */

.feuille { flex: 1; overflow-y: auto; padding: 34px; min-height: 0; }
.titre { margin-bottom: 21px; display: flex; align-items: flex-start; gap: 16px; }
.titre h1 { font-size: 21px; }
.titre p { margin-top: 6px; font-size: 13.5px; color: var(--text-muted); }

.cartes { display: grid; grid-template-columns: repeat(auto-fill, minmax(288px, 1fr));
          gap: 13px; }

/* Le filtre d'un inventaire : à gauche, avant les cartes, et large de 233 px —
   assez pour un nom de compétence, trop peu pour se prendre pour une barre de
   recherche générale. Le compte le suit à droite, en petit : c'est lui qui
   dit qu'un filtre est actif quand la saisie ne rend rien. */
.rangee-filtre { display: flex; align-items: center; gap: 13px;
                 margin: 0 0 13px; }
.rangee-filtre input[type=search] {
  font: inherit; font-size: 14px; color: var(--text); background: var(--surface-2);
  border: 1px solid var(--border-strong); border-radius: 9px; padding: 8px 13px;
  width: 233px; max-width: 100%; outline: none;
}
.rangee-filtre input[type=search]:focus {
  border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-ring); }
.carte { border: 1px solid var(--border); border-radius: 12px; background: var(--surface);
         padding: 17px; display: flex; flex-direction: column; gap: 13px; }
/* `[hidden]` vient du navigateur, et `display: flex` la bat : sans cette
   ligne, une carte écartée par le filtre resterait à l'écran. */
.carte[hidden] { display: none; }
.carte h3 { font-size: 14.5px; }
.chapeau { display: flex; align-items: center; gap: 8px; justify-content: space-between; }
.compte { font-size: 21px; font-weight: 600; letter-spacing: -.02em; }
.compte + span { font-size: 12.5px; color: var(--text-muted); margin-left: 6px; }
.pied { display: flex; align-items: center; justify-content: space-between;
        gap: 8px; font-size: 11.5px; color: var(--text-subtle);
        border-top: 1px solid var(--border); padding-top: 10px; }

.fraicheur.a_jour { color: var(--success); }
.fraicheur.en_retard { color: var(--warning); }
.fraicheur.jamais, .fraicheur.desactive,
.fraicheur.manuelle { color: var(--text-subtle); }

/* ── La galerie, et les trouvailles  (L11unvicies) ─────────────────────── */
/* Une vignette est un rectangle d'or couché : 233 × 144 px, le rapport que la
   maison suit partout. La grille se remplit d'elle-même — `auto-fill` plutôt
   qu'un nombre de colonnes, parce que le nombre juste dépend de la fenêtre et
   non du gabarit. */
.galerie { display: grid; grid-template-columns: repeat(auto-fill, minmax(144px, 1fr));
           gap: 13px; }
.vignette { position: relative; border: 1px solid var(--border); border-radius: 12px;
            overflow: hidden; background: var(--surface-2); aspect-ratio: 1.618;
            display: flex; align-items: center; justify-content: center; }
.vignette img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Le nom ne se lit qu'au survol : une bande de texte sous chaque vignette
   ferait une liste de fichiers, alors qu'on est venu voir des images. */
/* `pointer-events: none` : une légende est du décor, et celle-ci recouvre le
   bas de la vignette. Sans cette ligne elle attrape les clics — y compris,
   sur une petite vignette, celui de la croix. Une décoration qui intercepte
   un geste est une décoration qui casse un bouton. */
.vignette figcaption { pointer-events: none;
                       position: absolute; inset: auto 0 0 0; padding: 8px 10px;
                       font-size: 11.5px; color: #fff; background:
                       linear-gradient(transparent, rgba(0,0,0,.72));
                       opacity: 0; transition: opacity .13s;
                       white-space: nowrap; overflow: hidden;
                       text-overflow: ellipsis; }
.vignette:hover figcaption, .vignette:focus-within figcaption { opacity: 1; }
.vignette .croix { position: absolute; top: 6px; right: 6px; opacity: 0; }
.vignette:hover .croix, .vignette:focus-within .croix { opacity: 1; }

/* Artefacts  (L32) — la même grille, l'exact contraire pour la légende.
   Une vignette d'image se regarde : son nom se cache au repos, sans quoi on
   a une liste de fichiers là où l'on venait voir des images. Un artefact,
   lui, **est** son nom : il n'a rien à montrer, et masquer sa légende
   laisserait une grille de rectangles gris qu'il faut survoler un par un
   pour savoir ce qu'ils contiennent. On garde la grille, on retourne la
   règle. */
.vignette.fichier { aspect-ratio: 1.618 / 1.2; flex-direction: column;
                    gap: 6px; padding: 13px 10px; text-align: center; }
.vignette.fichier a { display: flex; flex-direction: column; align-items: center;
                      gap: 8px; text-decoration: none; color: inherit;
                      min-width: 0; max-width: 100%; }
.vignette.fichier strong { font-size: 12.5px; line-height: 1.35;
                           /* Trois lignes puis les points : un nom de 144
                              caractères ne doit pas pousser la carte hors
                              de la grille. */
                           display: -webkit-box; -webkit-line-clamp: 3;
                           -webkit-box-orient: vertical; overflow: hidden;
                           overflow-wrap: anywhere; }
/* L'étiquette du format tient lieu d'illustration — c'est le seul repère
   visuel d'une grille de fichiers, et il se lit d'un coup d'œil. */
.vignette.fichier .etiquette { font-size: 10px; font-weight: 700;
                               letter-spacing: .09em; text-transform: uppercase;
                               padding: 3px 8px; border-radius: 999px;
                               color: #fff; background: var(--accent); }
.vignette.fichier[data-genre="docx"] .etiquette { background: #2b579a; }
.vignette.fichier[data-genre="xlsx"] .etiquette { background: #217346; }
.vignette.fichier[data-genre="pptx"] .etiquette { background: #c43e1c; }
.vignette.fichier[data-genre="csv"]  .etiquette { background: #5b6470; }
/* Toujours lisible, jamais en surimpression : voir plus haut. */
.vignette.fichier figcaption { position: static; opacity: 1; padding: 0;
                               background: none; color: var(--muted);
                               white-space: normal; font-size: 11px; }
/* Ce qui part sous huit jours se signale — un fichier qu'on croyait gardé et
   qui disparaît se prend toujours pour une panne. Huit, parce qu'un mois de
   34 jours laisse le temps de revenir. */
.vignette.fichier .bientot { color: var(--danger); font-weight: 600; }

/* Une trouvaille : le titre du fil, puis ce qui s'y est dit autour du mot. */
.trouvaille { display: block; border: 1px solid var(--border); border-radius: 12px;
              background: var(--surface); padding: 13px 17px; text-decoration: none;
              color: inherit; }
.trouvaille:hover { border-color: var(--border-strong); background: var(--surface-2); }
.trouvaille h3 { font-size: 14.5px; margin-bottom: 8px; }
.trouvaille .extrait { font-size: 12.5px; color: var(--text-muted);
                       padding: 5px 0 5px 10px; border-left: 2px solid var(--border);
                       margin-top: 6px; }
.trouvaille .extrait b { color: var(--text); font-weight: 600; }
.trouvaille .qui { font-size: 10px; letter-spacing: .08em; text-transform: uppercase;
                   color: var(--text-subtle); margin-right: 8px; }
.trouvailles { display: flex; flex-direction: column; gap: 13px; }

.depot { border: 1px dashed var(--border-strong); border-radius: 12px; padding: 21px;
         display: flex; flex-direction: column; gap: 8px; align-items: center;
         justify-content: center; text-align: center; color: var(--text-muted);
         background: var(--surface-2); }
.depot.survol { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.depot input[type=file] { display: none; }

table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
th { text-align: left; font-size: 10px; font-weight: 600; letter-spacing: .08em;
     text-transform: uppercase; color: var(--text-subtle); padding: 8px 10px; }
td { padding: 10px; border-top: 1px solid var(--border); }
td.fin { text-align: right; }

/* ── « Votre IA » : trois gestes, en 62 / 38 ────────────────────────────── */

.reglages { display: grid; grid-template-columns: minmax(0, 1.618fr) minmax(0, 1fr);
            gap: 21px; align-items: start; }
.geste { border: 1px solid var(--border); border-radius: 12px; background: var(--surface);
         padding: 21px; display: flex; flex-direction: column; gap: 10px; }
.geste + .geste, .geste + .carte { margin-top: 13px; }
.rang { width: 21px; height: 21px; border-radius: 50%; background: var(--accent-soft);
        color: var(--accent); font-size: 11.5px; font-weight: 700; display: grid;
        place-items: center; flex-shrink: 0; }
.geste .chapeau { justify-content: flex-start; }
.geste .aide { font-size: 12.5px; color: var(--text-subtle); }

input[type=text], input[type=email], input[type=password], select {
  font: inherit; font-size: 14px; color: var(--text); background: var(--surface-2);
  border: 1px solid var(--border-strong); border-radius: 9px; padding: 9px 13px;
  width: 100%; outline: none;
}
input:focus, select:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-ring); }
label.champ, div.champ { display: flex; flex-direction: column; gap: 6px;
              font-size: 12.5px; color: var(--text-muted); }

/* Une liste d'adresses en puces (point 10 de la recette du 2026-08-30). La
   boîte imite le champ texte qu'elle remplace — même bordure, même rayon,
   même anneau au focus — pour qu'on la reconnaisse comme un champ et non
   comme un tableau. Le curseur y entre en cliquant n'importe où. */
.puces { display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
         background: var(--surface-2); border: 1px solid var(--border-strong);
         border-radius: 9px; padding: 6px 8px; cursor: text; }
.puces:focus-within { border-color: var(--accent);
                      box-shadow: 0 0 0 3px var(--accent-ring); }
.puces input[type=text] { flex: 1 1 144px; min-width: 89px; border: none;
                          background: transparent; padding: 3px 4px; width: auto; }
.puces input[type=text]:focus { box-shadow: none; }
.puce { display: inline-flex; align-items: center; gap: 5px; font-size: 12.5px;
        color: var(--text); background: var(--surface-hover);
        border: 1px solid var(--border); border-radius: 21px;
        padding: 3px 3px 3px 10px; }
/* Une adresse qui ne ressemble pas à une adresse se voit **avant** l'envoi :
   la passerelle la refuserait, mais elle le dirait après l'enregistrement. */
.puce.mauvaise { color: var(--warning); border-color: var(--warning); }
.puce .croix { width: 18px; height: 18px; border: none; border-radius: 50%;
               background: transparent; color: var(--text-subtle);
               font-size: 14px; line-height: 1; cursor: pointer;
               display: grid; place-items: center; }
.puce .croix:hover { background: var(--surface); color: var(--accent); }

/* ── La porte ───────────────────────────────────────────────────────────── */

.porte { display: grid; place-items: center; align-content: center; gap: 13px;
         height: 100vh; grid-template-columns: none; background: var(--bg); }
.porte form { width: 377px; display: flex; flex-direction: column; gap: 13px;
              border: 1px solid var(--border); border-radius: 12px;
              background: var(--surface); padding: 34px; }
.porte .marque { padding: 0 0 8px; }
.erreur { font-size: 12.5px; color: var(--accent); background: var(--accent-soft);
          border: 1px solid rgba(227,28,96,.22); border-radius: 9px; padding: 8px 13px; }
.libre { font-size: 12.5px; color: var(--warning); background: var(--warning-soft);
         border: 1px solid var(--border); border-radius: 9px; padding: 8px 13px; }
/* Ce qui s'est bien passé, dit du même souffle que ce qui s'est mal passé —
   même forme, même place, ton neutre : la personne cherche au même endroit. */
.dit { font-size: 12.5px; color: var(--text-muted); background: var(--surface-hover);
       border: 1px solid var(--border); border-radius: 9px; padding: 8px 13px; }
/* Le geste de secours est une carte à part, plus discrète que la principale :
   on la lit quand la première a échoué, jamais avant. */
.porte form.secours { padding: 21px 34px; gap: 8px; }

/* ── La scène de la porte ───────────────────────────────────────────────── */
/* Demande de Denis, le 2026-08-31 : « une page sympa et épurée. un visage IA
   fait de points très légers à la fibonacci … avec une main robot … sur la
   droite regardant une pendule … tout en points reliés ». La page se coupe
   donc en deux parts d'or — le formulaire tient les 38 %, la scène les 62 %.
   Le dessin est un SVG **commis**, écrit par un script (voir
   `scripts/dessiner-porte-constellation.py`) : la porte ne calcule rien.
   `.duo` est explicite, et non déduit : `.porte` sert aussi au code à usage
   unique, au mot de passe oublié et à la reprise de séance, qui n'ont pas de
   scène et resteraient centrés de travers. */
.porte.duo { grid-template-columns: minmax(377px, 38.2fr) minmax(0, 61.8fr);
            gap: 34px; padding: 0 34px; }
/* Les noms sont préfixés, et ce n'est pas une coquetterie : `.scene` est déjà
   la grille conversation/persona et `.visage` déjà la pastille d'un
   interlocuteur. Le premier jet les a repris tels quels — un `display: grid`
   hérité s'est posé sur un groupe SVG et la tête a **disparu**, sans une
   erreur nulle part. Une classe générique se réserve à ce qui est générique. */
.constellation { width: 100%; max-width: 987px; color: var(--text-muted); }
/* Le rayon des points vient du `<g>` qui les groupe, en **style** et non en
   attribut : `r` est bien une propriété de présentation en SVG2, mais un
   attribut `r` posé sur un groupe n'est pas hérité par ses `<circle>` — ils
   tomberaient tous à zéro, et la scène serait vide sans une erreur nulle
   part. Le style, lui, hérite. C'est ce qui permet aux sept cents points de
   ne porter que leurs deux coordonnées : 38 Kio au lieu de 75. */
.constellation circle { r: inherit; }
/* Très léger, au sens propre : le trait est deux fois plus pâle que le point.
   Ce qu'on doit lire de loin, c'est la silhouette — pas le maillage. */
.constellation .c-fil { fill: none; stroke: currentColor; stroke-width: .55;
                        opacity: .21; }
.constellation .c-pale { opacity: .07; }
.constellation .c-pt { fill: currentColor; opacity: .55; }
.constellation .c-graine { opacity: .38; }
/* Les deux décroissances du nuage. Un halo serré seul découpe la silhouette
   au ciseau ; une poussière lointaine seule fait un brouillard sans sujet.
   Il faut les deux, et à des opacités franchement différentes — sinon l'œil
   les lit comme une seule masse et le relief disparaît. */
.constellation .c-halo { opacity: .34; }
.constellation .c-poussiere { opacity: .16; }
.constellation .c-regard { opacity: .21; }
.constellation .c-vif { fill: var(--accent); opacity: .89; }
/* Le balancement : ±2,1° sur cinq secondes, autour de la pince des doigts.
   `transform-box` est déclaré plutôt que supposé — la valeur par défaut a
   changé de sens entre deux versions de la spécification, et un point de
   pivot faux ferait tourner la pendule autour du coin de l'image. */
.constellation .c-balancier { transform-box: view-box;
                              animation: balance 5s ease-in-out infinite alternate; }
@keyframes balance { from { transform: rotate(-2.1deg); }
                     to   { transform: rotate(2.1deg); } }
/* Une horloge qui bat sans repos est un mouvement de fond, pas une alerte :
   elle s'arrête quand le système dit que le mouvement dérange. */
@media (prefers-reduced-motion: reduce) {
  .constellation .c-balancier { animation: none; }
}
/* Sous 987 px, la porte redevient ce qu'elle est : une porte. La scène n'est
   pas rétrécie mais **retirée** — un nuage de sept cents points sur 380 px de
   large ne se lit plus, il salit. Et c'est un téléphone qui la lirait : on ne
   lui fait pas télécharger 38 Kio de SVG pour les cacher ensuite... mais le
   gabarit étant commis en un bloc, le retirer côté serveur demanderait de
   connaître la largeur avant de répondre. Le compromis est assumé, et le
   poids a été divisé par deux pour cette raison. */
@media (max-width: 986px) {
  .porte.duo { grid-template-columns: minmax(0, 1fr); padding: 0 21px; }
  .porte.duo .constellation { display: none; }
}

.vide { color: var(--text-subtle); font-size: 13.5px; padding: 21px 0; }

/* ── Comptes (L6bis) ────────────────────────────────────────────────────── */
/* Une ligne de formulaire, proportionnée : l'adresse tient la part d'or de la
   largeur (≈ 62 %), les deux champs secondaires se partagent le reste. */
.invitation { display: flex; flex-direction: column; gap: 13px;
              margin-top: 13px; }
.invitation .invitation-qui { display: flex; gap: 13px; align-items: flex-end;
                              flex-wrap: wrap; }
.invitation .champ { margin: 0; }
.invitation fieldset.champ { border: 1px solid var(--border); border-radius: 9px;
                             padding: 10px 13px 13px; }
.invitation fieldset.champ legend { padding: 0 5px; font-size: 12.5px;
                                    color: var(--text-muted); }
.invitation .bouton { margin-bottom: 2px; }
/* Les rôles d'office d'abord, les espaces réservés ensuite : deux natures de
   rôle, une seule liste, et un filet entre les deux plutôt qu'un second bloc. */
.invitation .cases .separation { grid-column: 1 / -1; font-size: 12px;
                                 color: var(--text-subtle); border-top: 1px solid var(--border);
                                 padding-top: 8px; margin-top: 2px; }

/* Les gestes d'une ligne de compte. `white-space: nowrap` sur la cellule
   laissait deux boutons de largeur figée se recouvrir mot sur mot (Denis, le
   2026-08-25) : ce qu'il fallait n'était pas d'empêcher le retour à la ligne,
   c'était de **donner sa largeur à chaque bouton** et de les espacer. Ils se
   rangent à droite, dans l'ordre où l'on hésite : le geste doux d'abord, le
   geste qui met dehors en dernier. Sous 987 px ils passent à la ligne plutôt
   que de pousser le tableau hors de l'écran. */
.gestes { display: flex; flex-wrap: wrap; gap: 5px; justify-content: flex-end; }
.gestes .bouton.nue { white-space: nowrap; }
.gestes .bouton.nue[disabled] { opacity: .38; cursor: default; }
.gestes .bouton.nue[disabled]:hover { background: transparent; color: var(--text-muted); }

/* ── Ingestion (L8, §4.9bis) ──────────────────────────────────────────────
   Les mêmes teintes et le même tiroir que la console du parc : deux écrans
   qui montrent le même circuit ne doivent pas le peindre différemment, sinon
   l'état d'une source se lit deux fois avant d'être cru. */
.chip.vert { background: var(--success-soft); color: var(--success);
             border-color: transparent; }
.chip.rouge { background: var(--accent-soft); color: var(--accent);
              border-color: transparent; }
.chip.jaune { background: var(--warning-soft); color: var(--warning);
              border-color: transparent; }
/* La file de lecture, en tête de « Derniers documents » : elle n'alerte de
   rien — elle dit seulement qu'un travail court (recette du 2026-08-30). */
.chip.attente { background: var(--info-soft); color: var(--info);
                border-color: transparent; }

.actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
td.fin { text-align: right; }
/* Les gestes d'une ligne tiennent **sur une ligne**, et sur la **même** ligne
   de base. `.actions` s'enroule ailleurs — utile là où les libellés sont
   longs —, mais dans la dernière colonne d'un tableau l'enroulement casse la
   grille : deux boutons là où la croix manque, trois ailleurs.
   `align-items: center` ne suffisait pas — la croix est plus courte que ses
   voisins, et un conteneur qui s'enroule aligne chaque rangée séparément
   (relevé du 2026-09-05 : 8 px d'écart, mesurés dans Chrome). */
td.fin { white-space: nowrap; }
td.fin .actions { justify-content: flex-end; flex-wrap: nowrap; }
/* La croix prend la hauteur de ses voisins plutôt que la sienne : c'est un
   geste de la même rangée, pas un bouton d'une autre famille. */
td.fin .actions .croix { align-self: stretch; }
.aide.mauvais { color: var(--accent); }
/* Un réglage qui ouvre plus grand qu'il n'y paraît le dit sous lui-même :
   pas une alarme — rien n'est cassé —, une couleur qu'on remarque. */
.aide.attention { color: var(--warning); }
/* Le même avertissement, porté par un mot **dans** une aide plutôt que par
   l'aide entière : une clé nue en service se signale au milieu d'une phrase
   qui, elle, reste ordinaire. `.aide.attention` ne l'atteindrait pas — deux
   classes sur un élément ne sont pas une classe sur son descendant. */
.aide .attention { color: var(--warning); font-weight: 600; }

/* ── L'activité d'un objet (§4.5quater · L26-C) ──────────────────────────
   Une courbe de veille n'a ni axe ni graduation : elle ne remplace pas la
   valeur écrite à côté d'elle, elle dit seulement si ça bouge. 89 × 34 —
   φ² —, la couleur du texte, et une opacité qui la met derrière le chiffre. */
.courbe { display: block; color: var(--info); opacity: .618; }

/* ── La cadence, montrée du doigt (§4.3) ─────────────────────────────────
   Un rythme se choisit, il ne s'épelle pas. Quatre segments soudés en une
   seule pièce : l'œil voit d'un coup qu'ils s'excluent — ce qu'une liste
   déroulante cache, et ce que quatre boutons séparés ne disent pas. Les
   réglages du rythme choisi apparaissent dessous ; les autres sortent de la
   vue au lieu d'être grisés, un champ grisé se lisant quand même. */
fieldset.cadence { border: 0; margin: 0; padding: 0;
                   display: flex; flex-direction: column; gap: 8px; }
fieldset.cadence legend { padding: 0; font-size: 12.5px; color: var(--text-muted); }
.segments { display: flex; overflow: hidden; background: var(--surface);
            border: 1px solid var(--border); border-radius: 9px; }
.segment { flex: 1; min-width: 0; font: inherit; font-size: 12.5px;
           line-height: 1.3; padding: 8px 5px; border: 0;
           border-left: 1px solid var(--border); background: transparent;
           color: var(--text-muted); cursor: pointer; }
.segment:first-child { border-left: 0; }
.segment:hover { background: var(--surface-hover); color: var(--text); }
.segment[aria-pressed="true"] { background: var(--accent-soft); color: var(--accent); }
/* Une énumération courte dans une carte : ce que l'outil apporte de son
   côté. Retrait de 13 px — la puce doit se voir sans pousser le texte hors
   de la colonne étroite (≈ 38 % de la grille φ). */
/* Un champ à recopier : son nom au-dessus, sa valeur dans la boîte à copier.
   Le libellé anglais est mis en retrait plutôt qu'en note — c'est lui qu'on
   cherche des yeux dans le panneau de l'extension, la traduction ne sert
   qu'à comprendre de quoi il s'agit. */
.champs-outil { display: grid; gap: 8px; margin-bottom: 13px; }
.champs-outil .mot { display: flex; align-items: baseline; gap: 8px;
                     margin: 0 0 3px; font-size: 12.5px; color: var(--text); }
.champs-outil .mot b { font-weight: 600; }
.champs-outil .mot code { font-size: 11.5px; color: var(--text-muted);
                          font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.champs-outil .aide { margin: 5px 0 0; font-size: 12px; }
kbd { display: inline-block; padding: 1px 5px; border: 1px solid var(--border-strong);
      border-bottom-width: 2px; border-radius: 5px; background: var(--surface-2);
      font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
      font-size: 11px; color: var(--text); }
/* Un groupe d'outils du poste : le titre, sa puce d'état, ce qu'il ouvre, et
   les noms techniques en dernier — l'ordre dans lequel on les lit. Éteint, il
   reste **lisible** : un outil fermé qu'on ne peut plus déchiffrer ne dit pas
   « votre entreprise l'a fermé », il dit « je ne sais pas ». */
.groupe-outils { padding: 8px 0; border-top: 1px solid var(--border); }
.groupe-outils:first-child { border-top: 0; padding-top: 0; }
.groupe-outils.eteint { opacity: 0.62; }
.groupe-outils .mot { display: flex; align-items: center; gap: 8px;
                      margin: 0 0 3px; font-size: 12.5px; color: var(--text); }
.groupe-outils .aide { margin: 2px 0 0; }
.groupe-outils code { font-size: 11px; }

.liste-sobre { margin: 5px 0 0; padding-left: 13px; font-size: 12.5px;
               line-height: 1.5; color: var(--text-muted); }
.liste-sobre li { margin: 3px 0; }
.liste-sobre b { color: var(--text); font-weight: 600; }

.reglages-cadence { display: flex; gap: 13px; }
.reglages-cadence label { flex: 1; display: flex; flex-direction: column;
                          gap: 5px; font-size: 12.5px; color: var(--text-muted); }

/* 377 px : la part courte de la grille φ. Plus large, un formulaire invite à
   mettre deux champs par ligne — ici l'ordre des champs est celui du
   catalogue, et il se lit de haut en bas. */
.tiroir { width: 377px; max-width: calc(100vw - 34px); max-height: 89vh;
          overflow-y: auto;
          border: 1px solid var(--border); border-radius: 12px; padding: 21px;
          background: var(--surface); color: var(--text); }
.tiroir::backdrop { background: rgba(0, 0, 0, .55); }

/* Une **boîte**, pas une bande (Denis, le 2026-08-25 : « la modale est
   illisible… il faut une boîte rectangle »). 377 px conviennent à un
   formulaire qui se lit de haut en bas ; une liste de sept rôles empilés y
   devient une colonne de 500 px de haut qu'on parcourt au lieu de la voir
   d'un coup. `large` ouvre à 610 px — le rang de Fibonacci au-dessus, et le
   rapport à la hauteur retombe près de φ — et range les cases en deux
   colonnes. Le `max-height` était déjà là ; sans `overflow-y`, il coupait le
   pied du formulaire au lieu de le laisser défiler. */
.tiroir.large { width: 610px; }
/* Le pied d'une **boîte** n'est pas celui d'une carte : deux boutons écartés
   aux deux bords se cherchent du regard. Ils se groupent à droite, dans
   l'ordre où l'on décide — on renonce avant de confirmer. */
.tiroir .pied { justify-content: flex-end; }
.tiroir.large .coches { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
                        gap: 8px 21px; }
.tiroir form { display: flex; flex-direction: column; gap: 13px; }

/* ── Les pans d'une boîte ────────────────────────────────────────────────
   Point 12 de la recette du 2026-08-30 : quinze champs à la file ne se lisent
   pas — au milieu du formulaire, on ne sait plus si l'on décrit la tâche ou si
   on la borne. Un **pan** groupe les champs d'une même question sous un titre,
   dans un cadre discret. Le fond est celui de la boîte, à peine relevé : un
   pan sépare, il ne s'annonce pas. Le titre tient à 12,5 px, comme les
   mentions — c'est un repère, pas un second niveau de lecture. */
/* Le pan **n'a pas de fond** : les champs, eux, sont peints en `--surface-2`,
   et un pan de la même teinte les y ferait disparaître. Un filet suffit à
   grouper. */
.tiroir .pan { display: flex; flex-direction: column; gap: 13px;
               border: 1px solid var(--border); border-radius: 10px;
               padding: 13px; }
.tiroir .pan-titre { font-size: 12.5px; font-weight: 600; letter-spacing: .02em;
                     color: var(--text-muted); text-transform: uppercase; }
/* Une boîte **sans formulaire** — celle du droit d'accès — n'a pas le `gap`
   du `form` : ses pans se poussent à la marge. On ne pose surtout pas de
   `display` sur le `dialog` lui-même : ce serait défaire le `display: none`
   du navigateur, et la boîte resterait visible fermée. */
.tiroir > .chapeau + .pan, .tiroir > .pan + .pan { margin-top: 13px; }
/* Hors d'un pan — les autres boîtes du produit —, un `fieldset` garde son
   cadre : il y est le seul à grouper quoi que ce soit. */
.tiroir fieldset.champ:not(.cadence) { border: 1px solid var(--border);
                                       border-radius: 9px; padding: 10px 13px 13px; }
/* Dans un pan, ce cadre ferait un second trait à un doigt du premier. Le
   `fieldset` n'y garde que sa légende. Cette règle vient **après** la
   précédente : à égalité de poids, c'est la dernière écrite qui l'emporte. */
.tiroir .pan fieldset.champ { border: 0; margin: 0; padding: 0;
                              display: flex; flex-direction: column; gap: 8px; }
.tiroir .pan fieldset.champ legend { padding: 0; font-size: 12.5px;
                                     color: var(--text-muted); }
.tiroir [data-champs] { display: flex; flex-direction: column; gap: 13px; }
.tiroir textarea { font: inherit; font-size: 14px; color: var(--text);
                   background: var(--surface-2); border: 1px solid var(--border-strong);
                   border-radius: 9px; padding: 9px 13px; width: 100%;
                   outline: none; resize: vertical; }
.tiroir textarea:focus { border-color: var(--accent);
                         box-shadow: 0 0 0 3px var(--accent-ring); }
/* Un choix qu'on fait n'est pas une indication : il se lit à la couleur du
   texte, pas à celle des mentions. Sept rôles en gris à 12,5 px sur fond
   sombre, c'est ce qui rendait la boîte illisible. */
label.coche { display: flex; align-items: center; gap: 8px; font-size: 13px;
              color: var(--text); flex-wrap: wrap; }
label.coche input { width: auto; }
/* Les rôles qui ouvrent une pièce (lot A/4). Décalés sous le régime auquel
   ils appartiennent : sans le retrait, on ne voit pas qu'ils n'ont de sens que
   si « Seulement ces rôles » est choisi. */
.coches { display: flex; flex-direction: column; gap: 8px;
          margin-left: 21px; padding-left: 13px;
          border-left: 1px solid var(--border); }

/* ── Paramètres : le visage, les rôles lus, les séances (lot E · #14·15·18) ─ */
/* La pastille de la barre de gauche fait 30 px ; celle-ci fait 55 — assez pour
   qu'on reconnaisse un visage, pas assez pour qu'elle prenne la carte. Le
   `<img>` se pose **par-dessus** les initiales plutôt qu'à leur place : rien à
   effacer quand il arrive, rien à reconstruire quand on le retire. */
.portrait-rang { display: flex; align-items: center; gap: 16px; }
.portrait { width: 55px; height: 55px; border-radius: 50%; flex-shrink: 0;
            position: relative; overflow: hidden; background: var(--surface-2);
            border: 1px solid var(--border); display: grid; place-items: center;
            font-size: 19px; font-weight: 600; color: var(--text-muted); }
.portrait img { position: absolute; inset: 0; width: 100%; height: 100%;
                object-fit: cover; }
.portrait-gestes { display: flex; flex-wrap: wrap; align-items: center;
                   gap: 8px; min-width: 0; }
.portrait-gestes .aide { flex-basis: 100%; margin: 0; }

/* Les rôles ne sont pas un champ : on les lit, on ne les pose pas. Ils tiennent
   pourtant la place d'un champ pour que la colonne reste alignée. */
.roles-lus { display: flex; flex-wrap: wrap; align-items: center; gap: 5px;
             min-height: 34px; }

/* Un réglage de mémoire tient sur deux lignes : ce qu'on autorise, puis ce que
   cela veut dire. L'explication passe donc sous le libellé, pas à côté. */
label.coche > span { display: block; }
label.coche .aide { display: block; margin-top: 2px; }

/* Une séance : l'appareil à gauche, le geste à droite. Pas un tableau — il n'y
   a qu'une colonne de texte, et une table de deux cellules par ligne se lit
   moins bien qu'une liste sur un écran de 377 px de large. */
.seance { display: flex; align-items: center; gap: 10px; padding: 10px 0;
          border-top: 1px solid var(--border); font-size: 12.5px; }
.seance:first-child { border-top: 0; }
.seance b { display: block; font-size: 13px; font-weight: 520; color: var(--text); }
.seance .quand { color: var(--text-subtle); }
.seance .ou { flex: 1; min-width: 0; }
.seance .ou b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ══════════════════════════════════════════════════════════════════════════
   L'écran qu'on a dans la poche                    (§4.7quater · lot L9bis)
   ══════════════════════════════════════════════════════════════════════════
   **Trois largeurs, pas davantage** — et les deux seuils sont des nombres de
   Fibonacci, pas des nombres ronds hérités d'un appareil de 2014 :

     · téléphone   —  < 610 px
     · tablette    —  610 → 986 px
     · bureau      —  ≥ 987 px

   Un jeu de deux seuils tient de tête et se respecte ; un jeu de sept se
   contourne. Ce qu'ils commandent est écrit une fois pour toutes :
   **sous 987 px la barre devient un tiroir** — elle ne peut pas manger 38 %
   d'un écran de 390 px — et **sous 610 px la mise en page cesse de singer φ**
   plutôt que de le comprimer jusqu'à l'absurde.

   Une seule adresse, une seule application : rien ici n'est un second jeu de
   gabarits. C'est la même page, mesurée autrement.
   ══════════════════════════════════════════════════════════════════════════ */

/* Le clavier virtuel recouvre le tiers bas de l'écran, et `100vh` l'ignore :
   la hauteur reste celle de la fenêtre *sans* clavier, si bien que la zone de
   saisie passe dessous — le défaut le plus visible d'un chat sur téléphone.
   `100dvh` corrige déjà l'essentiel ; `--clavier`, posé par le script depuis
   `visualViewport`, rattrape les navigateurs qui redimensionnent la fenêtre
   sans redimensionner la vue. Les deux déclarations sont voulues : la
   première sert les navigateurs qui ignorent la seconde. */
.app { height: 100vh; height: calc(100dvh - var(--clavier, 0px)); }
.porte { height: 100vh; height: calc(100dvh - var(--clavier, 0px)); }

/* Le voile et le bouton du tiroir n'existent qu'en dessous de 987 px. Ils sont
   dans le HTML de toutes les pages : c'est le CSS qui décide, pas le serveur —
   une page servie différemment selon l'appareil est la porte ouverte à deux
   interfaces qui divergent. */
.voile { display: none; }
.tiroir-bouton { display: none; }

@media (max-width: 986px) {
  .app { grid-template-columns: minmax(0, 1fr); }

  .barre { position: fixed; top: 0; bottom: 0; left: 0; width: 233px; z-index: 55;
           transform: translateX(-100%); transition: transform .21s ease;
           border-right: 1px solid var(--border-strong); }
  .app[data-tiroir="ouvert"] .barre { transform: none;
           box-shadow: 0 0 34px rgba(0, 0, 0, .55); }
  .app[data-tiroir="ouvert"] .voile { display: block; position: fixed; inset: 0;
           z-index: 34; background: rgba(0, 0, 0, .55); border: none; padding: 0; }

  .tiroir-bouton { display: grid; place-items: center; width: 44px; height: 44px;
           margin-left: -10px; border-radius: 9px; border: 1px solid transparent;
           background: transparent; color: var(--text-muted); cursor: pointer;
           flex-shrink: 0; }
  .tiroir-bouton:hover { background: var(--surface-hover); color: var(--text); }

  /* On appuie du doigt, pas d'un curseur : 44 px est le minimum réel, et il
     vaut pour la navigation autant que pour les boutons. Le rembourrage
     inchangé garderait des cibles de 30 px — visibles, mais pas atteignables. */
  .nav a, .fils a { min-height: 44px; }
  .bouton { min-height: 44px; }
  /* Même partage au doigt : le carré de 44 px est un plancher pour une
     icône, et un libellé garde le droit de dépasser. */
  .bouton.nue { min-width: 44px; height: 44px; }
  .theme-btn { width: 44px; height: 44px; }
  .envoyer { width: 44px; height: 44px; }
  .retours button { min-height: 44px; }
  /* Au doigt, un dessin de 28 px n'est pas une cible : le carré repasse à 44. */
  .retours .geste, .gestes-reponse .geste { width: 44px; height: 44px; }
  .entete { padding: 0 13px; gap: 10px; }
}

@media (max-width: 609px) {
  /* φ tient où la largeur le permet, et s'efface franchement où elle ne le
     permet pas : une conversation sur 390 px n'a pas de place pour un panneau
     de 38 %, et le comprimer donnerait deux colonnes illisibles au lieu d'une
     lisible. Le panneau — et son rail — reviennent dès la tablette. */
  .scene, .scene[data-panneau="replie"] { grid-template-columns: minmax(0, 1fr); }
  .scene .persona, .scene .rail,
  .scene[data-panneau="replie"] .rail { display: none; }
  .colonne { border-right: none; }

  .echange { padding: 13px; gap: 13px; }
  .question { max-width: 89%; }
  .reponse { max-width: 100%; gap: 8px; }
  .saisie { padding: 8px 13px 13px; }
  .feuille { padding: 21px 13px; }
  .titre { flex-direction: column; gap: 8px; }
  .reglages { grid-template-columns: minmax(0, 1fr); }
  .cartes { grid-template-columns: minmax(0, 1fr); }
  /* Deux colonnes, pas une : une vignette pleine largeur n'est plus une
     galerie, c'est un diaporama qu'on parcourt au pouce. */
  .galerie { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .invitation { flex-direction: column; align-items: stretch; }
  /* Deux colonnes de cases sur 390 px ne sont plus une boîte, ce sont deux
     bandes : la large redevient étroite, comme les autres. */
  .tiroir.large .coches { grid-template-columns: minmax(0, 1fr); }

  /* Un tableau de huit colonnes sur 390 px ne se lit pas, il se devine. Il
     défile donc **lui-même**, plutôt que d'emporter la page avec lui : c'est
     le défilement horizontal du document qui est proscrit, pas celui d'un
     tableau qu'on a choisi de consulter. */
  .feuille table { display: block; width: 100%; overflow-x: auto;
                   white-space: nowrap; }
  html, body { overflow-x: hidden; }
}

/* ── Le doigt n'a pas de survol, et il est plus large qu'un curseur (#3) ────
   *Denis, recette du 2026-08-26 : « en interface mobile, les boutons micro et
   interactifs ne produisent aucune action, l'impression que les micros ne sont
   pas pris en compte ».* Trois de ces boutons ne produisaient rien parce qu'ils
   n'étaient **pas là** : `opacity: 0` levé par `:hover`, sur un écran où l'on
   ne pose pas de curseur. La croix qui supprime un fil, celle qui supprime une
   image et la légende d'une vignette étaient invisibles et intouchables — un
   geste offert au bureau, retiré en silence au téléphone.

   Les autres étaient là mais trop petits. Le plancher de 44 px posé plus haut
   ne visait que `.bouton`, `.envoyer`, `.theme-btn` et la navigation : le
   micro, la voix et le « + » (`.rond`, 34 px), les segments de cadence et la
   croix d'un fil (26 px) étaient passés au travers. Compter les déclarations
   ne dit pas qu'on a couvert toutes les cibles — c'est l'erreur qu'on a déjà
   faite au lot A, et elle se refait ici pour la même raison.

   `hover: none` vise l'**appareil**, pas la largeur : une tablette de 1024 px
   a le même doigt qu'un téléphone de 390, et la règle en largeur l'oubliait. */
@media (hover: none) {
  .projet .croix,
  .souvenir .croix,
  .fils .fil .croix,
  .vignette .croix,
  .vignette figcaption { opacity: 1; }

  .rond { width: 44px; height: 44px; }
  .modele select { height: 44px; }
  .fils .fil .croix { width: 44px; height: 44px; }
  .vignette .croix { width: 44px; height: 44px; }
  .projet .croix { width: 44px; height: 44px; }
  .projet .nom { min-height: 44px; }
  .souvenir .croix { width: 44px; height: 44px; }
  .croix.plus { width: 44px; height: 44px; }
  .segment { min-height: 44px; }
  .moi-bouton { min-height: 44px; }
  /* La carte de métier porte deux lignes : au clavier elle dépasse déjà 44 px,
     mais rien ne le garantissait. Le plancher l'écrit. */
  .carte-metier { min-height: 44px; }

  /* L'épingle est dessinée à 26 px — la taille d'une icône, et c'est voulu :
     agrandir la punaise elle-même la ferait passer pour une entrée du menu.
     C'est donc sa **zone de touche** qu'on porte à 44 px, par un calque
     invisible centré sur elle. Le doigt gagne 18 px, l'œil ne voit rien
     changer. Le calque ne vit qu'ici : sur un écran à souris, un mort-terrain
     de 44 px dans le coin du menu se paierait sans rien rapporter. */
  .menu-compte .epingle::after { content: ""; position: absolute;
                width: 44px; height: 44px; }
}

/* ── Utilisation : des cadrans, pas des barres  (#16bis) ─────────────────
   Denis, recette du 2026-08-26 : *« ROND, pas une ligne droite comme
   Claude »*. Une barre se lit comme une progression vers une fin ; un cadran
   se lit comme une réserve — ce n'est pas le même sentiment devant le même
   nombre. Les tailles viennent de Fibonacci : 144 pour le grand, 89 pour les
   petits, 34 et 21 pour les espacements. */
.cadran-grand { display: flex; align-items: center; gap: 21px; flex-wrap: wrap; }
.cadran { position: relative; flex-shrink: 0; }
.cadran-svg { display: block; width: 144px; height: 144px; }
.cadran-petit .cadran-svg { width: 89px; height: 89px; }
.cadran-svg .creux { stroke: var(--surface-hover); }
.cadran-svg .plein { stroke: var(--accent); transition: stroke-dasharray .34s ease; }
/* `--warning` et non un rouge inventé : la maison a quatre couleurs d'état,
   et le seuil de 89 % avertit — il n'annonce pas une panne. */
.cadran.haut .cadran-svg .plein { stroke: var(--warning); }
.dedans-cadran { position: absolute; inset: 0; display: grid; place-items: center;
                 align-content: center; text-align: center; }
.dedans-cadran b { font-size: 1.3rem; line-height: 1.1; }
.cadran-petit .dedans-cadran b { font-size: 1rem; }
.dedans-cadran span { font-size: .72rem; color: var(--text-subtle); }
.cadran-mot { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.cadran-mot b { font-size: 1.05rem; }
/* La bulle d'un refus. Ce n'est pas une panne — c'est une réponse, et elle
   dit quoi faire. La couleur de la maison, pas un rouge inventé. */
.bulle.refus { color: var(--accent); background: var(--accent-soft);
               border: 1px solid var(--accent-ring); }

/* Le mot du palier, sous le compte. Deux couleurs de la maison, jamais un
   rouge inventé : `--warning` prévient à 89 %, `--accent` dit ce qui est
   fermé. Largeur bornée à 34 em — au-delà, l'œil perd la ligne. */
.mot-plafond { display: block; margin-top: 8px; max-width: 34em; font-size: .82rem;
               line-height: 1.45; padding: 8px 13px; border-radius: 8px; }
.mot-plafond.avertit { color: var(--warning); background: var(--warning-soft); }
.mot-plafond.ferme { color: var(--accent); background: var(--accent-soft); }
.cadran-nu { display: flex; flex-direction: column; gap: 5px; }
.cadran-nu b { font-size: 2.1rem; line-height: 1; }
.cadran-nu span { color: var(--text-subtle); }
/* Deux cadrans côte à côte tant qu'il y a la place, l'un sous l'autre sinon.
   `minmax(233px, 1fr)` : en dessous, deux colonnes tronqueraient les noms. */
.cadrans { display: grid; gap: 21px; margin-top: 34px;
           grid-template-columns: repeat(auto-fit, minmax(233px, 1fr)); }
.cadran-petit { display: flex; align-items: center; gap: 13px; min-width: 0; }

/* ── D'où viennent les jetons, et l'activité du mois  (recette 2026-08-30) ──
   Des **parts d'un même tout** : une barre, jamais un cadran. Les cadrans du
   haut disent une réserve — ce qu'il reste avant le refus ; ici l'on compare
   quatre origines entre elles, et quatre cadrans côte à côte se comparent mal.
   Même grille auto-ajustée que les cadrans, même respiration de 21 px. */
.origines, .chiffres, .parts { display: grid; gap: 21px; margin-top: 13px;
                       grid-template-columns: repeat(auto-fit, minmax(233px, 1fr)); }
/* Une carte, un nom : depuis L27, les origines se peignent avec la fonction
   partagée des sectorisations, et rendent donc une `.part` comme les autres.
   `.origine` n'était plus émise nulle part — une classe morte se garde mal. */
.part { display: flex; flex-direction: column; gap: 8px; min-width: 0;
        border: 1px solid var(--border); border-radius: 12px; padding: 13px;
        background: var(--surface); }
.part .chapeau b { font-size: 14px; }
.part .aide { font-size: 12px; }
/* Les sectorisations de L27 tiennent dans la même grille que les origines —
   modèles, agents, personnes répondent à la même question et se lisent donc
   du même œil. Une grille plus étroite : un nom de modèle ou de personne est
   court, et 233 px les étalerait sur toute la largeur pour rien. */
.parts { grid-template-columns: repeat(auto-fit, minmax(199px, 1fr)); }
/* Un chiffre se lit d'un coup d'œil : le nombre d'abord, ce qu'il compte
   ensuite, pourquoi il compte en dessous. */
.chiffre { display: flex; flex-direction: column; gap: 3px; min-width: 0;
           border-left: 2px solid var(--border-strong); padding: 3px 0 3px 13px; }
.chiffre .compte { font-size: 27px; line-height: 1.1; }
.chiffre b { font-size: 13px; }
.chiffre .aide { font-size: 12px; }
/* La liste du droit d'accès : des faits, pas des puces décoratives. */
.liste-rgpd { margin: 0; padding-left: 21px; display: flex; flex-direction: column;
              gap: 5px; font-size: 12.5px; color: var(--text-muted);
              line-height: 1.55; }
.liste-rgpd b { color: var(--text); }

/* ── Planification : le carré de droite, en trois  (§4.9sexies · #20) ─────
   Le panneau de « Discuter » sert ici sans être refait : même fond creusé,
   même en-tête, mêmes titres de volet. Ce qui change est qu'aucun des trois
   ne se choisit — Progression, Sorties, Contexte disent chacun une seule
   chose, et en cacher un cacherait un tiers de la tâche. Les hauteurs suivent
   φ : la progression tient en trois lignes, les deux listes se partagent le
   reste. */
.scene[data-ecran="planification"] .corps-persona {
  grid-template-rows: minmax(0, 1fr) minmax(0, 1.618fr) minmax(0, 1.618fr); }
.scene[data-ecran="planification"] .corps-persona > .contexte { display: flex; }
.scene[data-ecran="planification"] .corps-persona > .contexte + .contexte {
  border-top: 1px solid var(--border); }

/* **Une barre, et c'est voulu.** Les cadrans d'« Utilisation » disent une
   réserve ; ici on regarde une chose qui avance vers sa fin, et c'est
   exactement ce qu'une barre raconte — la note du bloc ci-dessus le dit dans
   l'autre sens. Elle n'anime que ce qui bouge vraiment (§4.3) : sur une
   occurrence terminée, elle est pleine et immobile. */
.jauge { height: 5px; border-radius: 999px; background: var(--surface-hover);
         overflow: hidden; }
.jauge i { display: block; height: 100%; background: var(--accent);
           border-radius: 999px; transition: width .34s ease; }
.jauge.finie i { background: var(--success); }
.jauge.perdue i { background: var(--accent); }
.pas { display: flex; flex-direction: column; gap: 8px; }
.pas .mot { display: flex; align-items: baseline; gap: 8px; font-size: 12.5px; }
.pas .mot b { font-size: 13px; }
.pas .mot .aide { margin-left: auto; }

/* Deux champs qui vont ensemble — le dossier et l'espace de sortie — tiennent
   sur une ligne dans la boîte large ; ils reprennent leur colonne dès que la
   place manque. */
.paire { display: grid; gap: 13px;
         grid-template-columns: repeat(auto-fit, minmax(233px, 1fr)); }
.paire > .champ { min-width: 0; }
/* Les compétences se cochent : deux colonnes tant qu'il y a la place, sans le
   retrait de `.coches` — elles ne dépendent d'aucun choix au-dessus d'elles. */
.cases { display: grid; gap: 8px 21px;
         grid-template-columns: repeat(auto-fit, minmax(233px, 1fr)); }

/* ── La documentation, sur sa propre page ──────────────── (lot N · #29) ──
   Denis, le 2026-08-27 : *« on déroule la doc avec, sur le côté gauche, un
   menu. »* La page réemploie tous les jetons de la coquille — elle porte
   `class="app"` pour cela — mais pas sa grille : ici la colonne de gauche est
   un **sommaire**, et la droite se déroule.

   Les largeurs sont Fibonacci et le texte s'arrête où l'œil s'arrête : une
   ligne de 987 px reste confortable, une pleine largeur d'écran de bureau ne
   se lit plus — on perd le début de la ligne suivante. */
.app.doc { grid-template-columns: 233px minmax(0, 1fr); }

.doc-barre { background: var(--bg-sunken); border-right: 1px solid var(--border);
             display: flex; flex-direction: column; min-height: 0;
             overflow-y: auto; }

/* Le seul chemin de retour, et il est en haut : la page s'ouvre dans un autre
   onglet, mais personne ne doit avoir à le deviner pour en sortir. */
.doc-retour { display: flex; align-items: center; gap: 8px; padding: 13px 18px 0;
              font-size: 12.5px; font-weight: 520; color: var(--text-muted); }
.doc-retour:hover { color: var(--text); text-decoration: none; }

.doc-marque { display: flex; align-items: center; gap: 10px; padding: 13px 18px 8px; }

.doc-nav { padding: 8px 8px 21px; display: flex; flex-direction: column; gap: 2px; }
.doc-nav a { display: block; padding: 7px 10px; border-radius: 9px;
             font-size: 13px; font-weight: 520; line-height: 1.35;
             color: var(--text-muted); text-decoration: none; }
.doc-nav a:hover { background: var(--surface-hover); color: var(--text);
                   text-decoration: none; }
.doc-nav a[aria-current] { background: var(--accent-soft); color: var(--accent); }
.doc-nav .groupe:not(:first-child) { margin-top: 13px; }

.doc-corps { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
.doc-entete { display: flex; align-items: center; gap: 8px; height: 55px;
              padding: 0 21px; border-bottom: 1px solid var(--border);
              flex-shrink: 0; }
/* Le bouton du sommaire n'existe qu'en dessous de 987 px : au-dessus, le
   sommaire est déjà à l'écran, et un bouton qui ouvre ce qui est ouvert est
   la définition d'une option sans effet visible (§5.6). */
.doc-entete [data-sommaire] { display: none; }
.doc-feuille { flex: 1; overflow-y: auto; padding: 34px 21px 89px;
               scroll-behavior: smooth; }
.doc-titre { max-width: 987px; margin: 0 auto 34px; }
.doc-titre h1 { font-size: 25px; margin-bottom: 8px; }
.doc-titre p { color: var(--text-muted); font-size: 14px; max-width: 610px; }

/* `scroll-margin-top` pour que l'ancre ne colle pas le titre sous l'en-tête :
   un lien de sommaire qui amène juste **au-dessus** du titre demandé fait
   croire qu'il n'a pas marché. */
.doc-chapitre { max-width: 987px; margin: 0 auto; padding: 34px 0;
                border-top: 1px solid var(--border); scroll-margin-top: 21px; }
.doc-chapitre:first-of-type { border-top: none; padding-top: 0; }
.doc-ou { font-size: 10px; font-weight: 600; letter-spacing: .08em;
          text-transform: uppercase; color: var(--text-subtle); margin-bottom: 5px; }
.doc-chapitre h2 { font-size: 19px; }
.doc-chapitre h3 { font-size: 14.5px; margin: 21px 0 5px; color: var(--text); }
.doc-resume { color: var(--text-muted); font-size: 13.5px; margin-top: 3px; }
/* 62 caractères de mesure — la proportion de φ appliquée à ce qui se lit,
   et la largeur qu'un œil parcourt sans avoir à chercher la ligne suivante. */
.doc-chapitre p, .doc-chapitre li { max-width: 62ch; font-size: 14px;
                                    line-height: 1.62; }
.doc-chapitre p + p { margin-top: 8px; }
.doc-chapitre ul, .doc-chapitre ol { margin: 8px 0 0; padding-left: 21px;
                                     display: flex; flex-direction: column; gap: 5px; }
.doc-chapitre code { font-family: var(--font-mono); font-size: 12.5px;
                     background: var(--surface-2); border: 1px solid var(--border);
                     border-radius: 5px; padding: 1px 5px; }

/* L'aparté. Un trait de couleur à gauche plutôt qu'un cadre complet : il se
   distingue du texte sans découper la page en boîtes. */
.doc-note { max-width: 62ch; margin: 13px 0 0; padding: 10px 13px;
            background: var(--surface); border: 1px solid var(--border);
            border-left: 3px solid var(--accent); border-radius: 9px;
            font-size: 13.5px; line-height: 1.55; color: var(--text-muted); }
.doc-note b { color: var(--text); }

.doc-pied { max-width: 987px; margin: 34px auto 0; padding-top: 21px;
            border-top: 1px solid var(--border); color: var(--text-subtle);
            font-size: 13px; }

@media (max-width: 986px) {
  .app.doc { grid-template-columns: minmax(0, 1fr); }
  .doc-barre { position: fixed; top: 0; bottom: 0; left: 0; width: 233px; z-index: 55;
               transform: translateX(-100%); transition: transform .21s ease;
               border-right: 1px solid var(--border-strong); }
  .app.doc[data-tiroir="ouvert"] .doc-barre { transform: none;
               box-shadow: 0 0 34px rgba(0, 0, 0, .55); }
  .app.doc[data-tiroir="ouvert"] .voile { display: block; position: fixed; inset: 0;
               z-index: 34; background: rgba(0, 0, 0, .55); border: none; padding: 0; }
  .doc-entete [data-sommaire] { display: grid; place-items: center; }
  .doc-nav a { min-height: 44px; }
  .doc-feuille { padding: 21px 13px 55px; }
  .doc-entete { padding: 0 13px; }
}

/* ==========================================================================
   L'écran d'une salle                                       (§4.12 · L12)
   ==========================================================================
   Un écran posé au mur se lit **debout, à trois mètres**. Rien n'y descend
   sous 21 px, rien n'y clignote, et la couleur ne porte aucune information à
   elle seule : ce qui compte est écrit en toutes lettres à côté.

   La page est partagée selon φ — 62 % à ce qui se dit, 38 % à l'en-tête et au
   témoin d'écoute réunis.
   ========================================================================== */
.app.piece { display: grid; place-items: center; align-content: center;
             min-height: 100vh; min-height: 100dvh; padding: 34px; }

.piece-porte { width: 377px; display: flex; flex-direction: column; gap: 13px;
               background: var(--surface); border: 1px solid var(--border);
               border-radius: 13px; padding: 34px; }
.piece-porte h1 { margin: 0; font-size: 19px; }

.app.piece .piece-salle { display: grid; grid-template-rows: auto 1fr auto;
                          gap: 21px; width: min(987px, 100%);
                          height: min(100dvh - 68px, 987px); }

.piece-tete { display: flex; align-items: flex-start; justify-content: space-between;
              gap: 21px; }
.piece-lieu { font-size: 34px; font-weight: 600; letter-spacing: -.01em; }
.piece-nom { color: var(--text-muted); font-size: 15px; margin-top: 3px; }
.piece-etat { color: var(--text-muted); font-size: 15px; white-space: nowrap; }

.piece-fil { overflow-y: auto; display: flex; flex-direction: column; gap: 13px;
             padding-right: 8px; }
.piece-tour { display: flex; flex-direction: column; gap: 3px; }
.piece-qui { font-size: 13px; color: var(--text-subtle);
             text-transform: uppercase; letter-spacing: .08em; }
.piece-dit { margin: 0; font-size: 21px; line-height: 1.55; }
.piece-tour.elle .piece-dit { color: var(--text); }
.piece-tour.moi .piece-dit { color: var(--text-muted); }

.piece-pied { display: flex; align-items: center; gap: 13px;
              border-top: 1px solid var(--border); padding-top: 13px; }
.piece-oreille { display: flex; align-items: center; gap: 8px;
                 font-size: 15px; color: var(--text-muted); white-space: nowrap; }
/* Le témoin. Il **suit la voix** : `--niveau` est réécrit à chaque trame par
   `ecouteContinue`. Un point qui bat toujours pareil ne prouve pas qu'un micro
   est branché, il prouve qu'un script tourne — et le §4.12 demande un témoin
   qui dise quand le micro est ouvert, pas un qui le décore. */
.piece-anneau { width: 13px; height: 13px; border-radius: 50%;
                background: var(--accent);
                transform: scale(var(--niveau, 1));
                transition: transform .055s linear; }
.piece-pied .aide { margin: 0; }

/* La veille. L'écran s'assombrit, le micro reste ouvert : un écran qu'il
   faudrait toucher pour réveiller n'est pas un collègue. */
.piece-salle.dort { opacity: .34; transition: opacity 1.618s ease; }

/* ── Développeur (L12bis, §4.16bis) ───────────────────────────────────────
   Trois blocs à copier, et la copie est le geste principal de l'écran : le
   bouton se pose donc **dans** le cadre du texte, à droite, plutôt qu'en
   dessous — on ne cherche pas un bouton pour un texte qu'on regarde. */
.a-copier { display: flex; align-items: flex-start; gap: 8px;
            border: 1px solid var(--border-strong); border-radius: 9px;
            background: var(--surface-2); padding: 9px 9px 9px 13px; }
.a-copier code, .a-copier pre { flex: 1; min-width: 0; margin: 0;
            font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
            font-size: 12.5px; color: var(--text); }
/* Un secret tient sur une ligne et ne se coupe pas au milieu d'un mot : une
   clé lue de travers se recolle mal. Le bloc de réglage, lui, garde ses
   retours à la ligne — c'est du JSON, et son indentation est ce qui le rend
   relisable dans l'éditeur où on le colle. */
.a-copier code { overflow-wrap: anywhere; line-height: 1.618; }
.a-copier.bloc pre { white-space: pre; overflow-x: auto; line-height: 1.618; }
.a-copier .bouton { flex-shrink: 0; }
.secret { display: flex; flex-direction: column; gap: 8px;
          border: 1px solid var(--warning); border-radius: 9px;
          background: var(--warning-soft); padding: 13px; }
.secret .aide { color: var(--text); margin: 0; }
/* Le champ et son bouton sur une ligne : nommer une clé et la frapper sont un
   seul geste, et deux lignes le feraient paraître deux. */
.rang-champ { display: flex; align-items: flex-end; gap: 8px; }
.rang-champ .bouton { margin-bottom: 2px; flex-shrink: 0; }

/* Les sièges du contrat (§4.16.3bis). Un bandeau qui se lit d'un coup d'œil :
   le compte à gauche, la jauge à droite, dans le rapport de la section d'or —
   ce qu'on lit d'abord occupe les 62 %. La jauge reprend celle du forfait
   plutôt que d'en inventer une seconde : deux façons de dessiner la même idée
   se mettent à diverger dès la première retouche. */
.sieges { display: flex; align-items: center; gap: 21px;
          padding: 13px 21px; margin-bottom: 21px;
          border: 1px solid var(--border); border-radius: 13px;
          background: var(--bg-sunken); }
.sieges .compte { flex: 1.618; min-width: 0; }
.sieges .compte b { font-size: 21px; font-weight: 610; }
.sieges .mesure { flex: 1; min-width: 89px; }
.sieges .aide { margin: 3px 0 0; }
/* Plein n'est pas une panne : c'est un état qu'on veut voir venir. L'accent
   passe au ton d'alerte **avant** le refus, à la dernière place. */
.sieges[data-plein="oui"] { border-color: var(--danger, #b4453c); }
.sieges[data-plein="oui"] .jauge i { background: var(--danger, #b4453c); }

/* ── Collègues : deux menus, un fil, une boîte redessinée  (§4.5quinquies) ──
   Point 23 de la recette du 2026-08-30. Trois besoins, trois blocs. */

/* Le fil d'activité. Une **liste de faits**, pas un tableau : un tour a un
   objectif long et quatre chiffres courts ; en colonnes, l'objectif écrase
   tout le reste ou se coupe. Le filet à gauche donne la ligne du temps sans
   dessiner de frise. */
.fil-activite { display: flex; flex-direction: column; gap: 13px; }
.fil-activite .fait { border-left: 2px solid var(--border); padding-left: 13px; }
.fil-activite .fait.attend { border-left-color: var(--accent); }
.fil-activite .fait b { display: block; font-weight: 600; }
.fil-activite .fait .aide { margin-top: 2px; }

/* La boîte d'embauche. 987 px — le rang de Fibonacci au-dessus de 610 —, et
   le corps se partage en 62 % / 38 % : à gauche ce qu'on écrit, à droite ce
   qu'on choisit. Sous 843 px, une seule colonne : deux colonnes de 300 px
   rendraient les zones de texte inutilisables. */
.tiroir.tres-large { width: 987px; }
.tiroir-colonnes { display: grid; grid-template-columns: 1.618fr 1fr;
                   gap: 21px; align-items: start; }
@media (max-width: 843px) {
  .tiroir.tres-large { width: 610px; }
  .tiroir-colonnes { grid-template-columns: minmax(0, 1fr); }
}

/* Le métier en cartes. Sept segments côte à côte donnaient 87 px chacun :
   le libellé s'y coupait, et le résumé — qui est la seule chose permettant de
   choisir — vivait dans une ligne d'aide sous la barre, donc après le clic.
   Une carte porte les deux ensemble, avant le clic. */
.champ.sans-cadre { border: 0; margin: 0; padding: 0; }
.cartes-metier { display: grid; gap: 8px;
                 grid-template-columns: repeat(auto-fill, minmax(233px, 1fr)); }
.carte-metier { text-align: left; font: inherit; cursor: pointer;
                border: 1px solid var(--border); border-radius: 10px;
                padding: 13px; background: var(--surface-2); color: var(--text);
                display: flex; flex-direction: column; gap: 3px; }
.carte-metier:hover { border-color: var(--accent); }
.carte-metier[aria-pressed="true"] { border-color: var(--accent);
                                     background: var(--accent-soft);
                                     color: var(--accent); }
.carte-metier b { font-weight: 600; }
.carte-metier span { font-size: 12.5px; color: var(--text-muted); }
.carte-metier[aria-pressed="true"] span { color: inherit; opacity: .89; }
.carte-metier:disabled { cursor: default; opacity: .62; }
.carte-metier:disabled:hover { border-color: var(--border); }
.carte-metier:disabled[aria-pressed="true"] { opacity: 1; }

/* ==========================================================================
   Le WebIDE                                        (§4.16quater · lot L26 · #68)
   ==========================================================================
   Denis, le 2026-08-31 : *« le user doit avoir un IDE web comme GitLab par
   exemple pour faire du code »*, et *« sur le côté du WebIDE il faut l'IA
   disponible pour développer, comme si j'avais un Cline dans Codium »*.

   **Trois colonnes, et deux nombres de Fibonacci.** 233 px pour l'arbre, 377
   pour l'IA, et le reste pour l'éditeur : 377/233 = 1,618, les deux flancs
   sont entre eux dans le rapport d'or et le milieu prend ce qu'ils laissent —
   c'est lui qu'on regarde.

   **Tout est préfixé `ide-`,** sans exception. Le 2026-08-31, `.scene` et
   `.visage` — déjà pris par la conversation et l'avatar du chat — ont effacé
   un dessin entier par héritage, sans qu'aucune erreur ne paraisse nulle
   part. Un écran neuf ne réutilise plus un nom générique.
   ========================================================================== */
.ide { display: grid; grid-template-columns: 233px minmax(0, 1fr) 377px;
       grid-template-rows: auto minmax(0, 1fr);
       min-width: 0; min-height: 0; }
.ide > .entete { grid-column: 1 / -1; }

.ide-arbre, .ide-ia { display: flex; flex-direction: column; min-height: 0;
                      min-width: 0; background: var(--bg-sunken); }
.ide-arbre { border-right: 1px solid var(--border); }
.ide-ia { border-left: 1px solid var(--border); }
.ide-editeur { display: flex; flex-direction: column; min-width: 0; min-height: 0; }

.ide-tete { display: flex; align-items: center; gap: 8px; padding: 8px 13px;
            border-bottom: 1px solid var(--border); }
.ide-tete .segments { flex: 1; min-width: 0; }
.ide-depots { flex: 1; min-width: 0; font-size: 12.5px; }
/* Un carré de 21 px pour un « + ». La leçon du 2026-08-29 : `.bouton.nue` est
   dessiné pour une icône et fige sa largeur — un libellé y déborde. Ici il n'y
   a qu'un signe, donc le carré est juste ; on le dit pour que personne n'y
   mette un mot plus tard. */
.ide-icone { width: 21px; height: 21px; padding: 0; flex-shrink: 0;
             font-size: 16px; line-height: 1; }

.ide-fichiers { flex: 1; overflow-y: auto; padding: 8px 0; }
.ide-fichiers .vide { padding: 13px; font-size: 12.5px; color: var(--text-subtle); }
.ide-fichier { display: flex; align-items: center; gap: 6px; width: 100%;
               padding: 5px 13px; border: 0; background: none; cursor: pointer;
               font: inherit; font-size: 12.5px; color: var(--text-muted);
               text-align: left; }
.ide-fichier:hover { background: var(--surface-hover); color: var(--text); }
.ide-fichier[aria-current="true"] { background: var(--accent-soft);
                                    color: var(--text); font-weight: 560; }
/* Le nom se tronque **par la gauche** : d'un `src/routes/api/handlers.py`, ce
   qu'on doit lire est `handlers.py`, pas `src/routes/api/…`. `direction: rtl`
   sur la boîte déplace l'ellipse au début ; `text-align: left` remet le texte
   à sa place, et le `‎` posé par le script empêche l'algorithme
   bidirectionnel de faire voyager la ponctuation. */
.ide-nom { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis;
           white-space: nowrap; direction: rtl; text-align: left; }
.ide-taille { font-size: 11px; color: var(--text-subtle); flex-shrink: 0; }

.ide-pied { padding: 13px; border-top: 1px solid var(--border);
            display: flex; flex-direction: column; gap: 8px; }
.ide-plein { width: 100%; justify-content: center; }

/* La forge rattachée, et le chemin d'achat quand il n'y en a pas (#68).
   Une colonne, parce qu'elle vit dans les 233 px de l'arbre : tout ce qui
   s'aligne en ligne y tiendrait sur deux caractères. */
.ide-distant { display: flex; flex-direction: column; gap: 5px; margin: 0;
               line-height: 1.618; }
.ide-forge { font-family: var(--font-mono); font-size: 11px; color: var(--text);
             overflow-wrap: anywhere; }
.ide-branche { display: inline-block; padding: 0 5px; border-radius: 3px;
               background: var(--surface-hover); color: var(--text-muted); }
.ide-quand { font-size: 10.5px; color: var(--text-subtle); }
/* Le bouton qui n'en a pas l'air. `.bouton.nue` ne convient pas : ses 32 px
   de large sont dessinés pour une icône, et un libellé y déborde — la leçon
   du 2026-08-28. Celui-ci est du texte, et se comporte comme du texte. */
.ide-distant .lien { background: none; border: 0; padding: 0; font: inherit;
                     font-size: 11.5px; color: var(--brand-500); cursor: pointer;
                     text-align: left; text-decoration: underline;
                     text-underline-offset: 2px; }
.ide-distant .lien:hover { color: var(--brand-300); }
/* Le chemin d'achat : proposé, jamais imposé — d'où le fond neutre et non
   une couleur d'alerte. Il manque quelque chose, rien n'est cassé. */
.ide-achat { display: flex; flex-direction: column; gap: 3px; padding: 8px;
             border-radius: 5px; background: var(--bg-sunken);
             border: 1px solid var(--border); font-size: 11px; }
.ide-achat em { color: var(--text-subtle); font-size: 10.5px; }

/* Le formulaire de rattachement : libellé au-dessus du champ, parce qu'une
   modale de 377 px n'a pas la largeur d'une colonne de libellés. */
.ide-forge-form label { display: flex; flex-direction: column; gap: 3px;
                        font-size: 12px; color: var(--text-muted); }
/* La clé privée est le seul champ de ce formulaire qui compte des lignes :
   en monospace, une clé mal collée se voit — les lignes n'ont pas la même
   longueur. En police proportionnelle, tout se ressemble. */
.ide-forge-form textarea { font-family: var(--font-mono); font-size: 11px;
                           resize: vertical; }

/* « Préciser », replié : le recours des forges servies sous un chemin. Replié
   et non absent — un champ qu'on ne voit pas ne coûte rien à qui n'en a pas
   besoin, alors que son absence rend une forge irrattachable. */
.ide-forge-plus { font-size: 12px; color: var(--text-muted);
                  border-top: 1px solid var(--border); padding-top: 8px; }
.ide-forge-plus summary { cursor: pointer; color: var(--text-subtle);
                          font-size: 11px; }
.ide-forge-plus[open] summary { margin-bottom: 8px; }
.ide-forge-plus label + label { margin-top: 5px; }

/* La sortie d'un geste. 55 vh : la fenêtre garde le titre et le bouton en vue
   pendant qu'on lit 233 lignes de journal — un `<pre>` qui pousse le bouton
   « Fermer » hors de l'écran est un piège, pas un compte rendu. */
.ide-sortie { font-family: var(--font-mono); font-size: 11px; line-height: 1.45;
              white-space: pre-wrap; word-break: break-word; margin: 0;
              max-height: 55vh; overflow: auto; padding: 8px 10px;
              background: var(--surface-2);
              border: 1px solid var(--border); border-radius: 5px; }

.ide-onglet { display: flex; align-items: center; gap: 8px; padding: 8px 21px;
              border-bottom: 1px solid var(--border); min-height: 47px; }
.ide-chemin { font-family: var(--font-mono); font-size: 12.5px;
              overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ide-etat { font-size: 11px; color: var(--text-subtle); flex-shrink: 0; }

/* La toile de Monaco. Elle ne porte **aucun** style de contenu : gouttière,
   coloration, curseurs et menus viennent de `editor.main.css`, que l'éditeur
   pose lui-même dans le `<head>` au chargement. Tout ce qu'on lui doit ici,
   c'est une boîte de taille connue — sans `min-height: 0`, une grille CSS
   laisse un enfant déborder au lieu de le contraindre, et l'éditeur se
   mesurerait plus haut que la fenêtre. */
.ide-toile { flex: 1; min-width: 0; min-height: 0; overflow: hidden;
             background: var(--bg-sunken); }

/* Monaco dessine son propre fond ; il n'a pas à trancher avec celui de la
   coquille. Les deux thèmes de la maison lui donnent le leur. */
.ide-toile .monaco-editor,
.ide-toile .monaco-editor .overflow-guard,
.ide-toile .monaco-editor-background,
.ide-toile .monaco-editor .margin { background: var(--bg-sunken); }

/* L'aveu de panne prend la place de l'éditeur, il ne se glisse pas à côté. */
.ide-panne { flex: 1; margin: 0; display: flex; align-items: center;
             justify-content: center; text-align: center; padding: 34px;
             font-size: 12.5px; line-height: 1.618; color: var(--text-muted);
             background: var(--danger-soft); }

.ide-avis { margin: 0; padding: 8px 21px; font-size: 11.5px;
            color: var(--text-muted); background: var(--warning-soft);
            border-top: 1px solid var(--border); }

.ide-fils { flex: 1; overflow-y: auto; padding: 8px 0; }
.ide-fils .vide { padding: 13px; font-size: 12.5px; color: var(--text-subtle); }
.ide-fil { display: block; width: 100%; padding: 8px 13px; border: 0;
           background: none; cursor: pointer; font: inherit; font-size: 12.5px;
           color: var(--text-muted); text-align: left; }
.ide-fil:hover { background: var(--surface-hover); color: var(--text); }
.ide-fil[aria-current="true"] { background: var(--accent-soft); color: var(--text); }
.ide-fil b { display: block; font-weight: 560; overflow: hidden;
             text-overflow: ellipsis; white-space: nowrap; }
.ide-fil span { font-size: 11px; color: var(--text-subtle); }

.ide-echange { flex: 1; overflow-y: auto; padding: 13px;
               display: flex; flex-direction: column; gap: 13px; }
.ide-echange .vide { font-size: 12.5px; color: var(--text-subtle); }
.ide-dit { font-size: 12.5px; line-height: 1.618; white-space: pre-wrap;
           overflow-wrap: anywhere; border-radius: 8px; padding: 8px 11px; }
.ide-dit.moi { background: var(--accent-soft); align-self: flex-end;
               max-width: 89%; }
.ide-dit.elle { background: var(--surface); border: 1px solid var(--border); }
.ide-dit.refus { background: var(--danger-soft); border: 1px solid var(--danger); }
/* Un bloc de code dans la réponse : à chasse fixe, et il défile seul plutôt
   que d'élargir la colonne — 377 px ne s'étirent pas. */
.ide-dit pre { margin: 8px 0 0; padding: 8px; border-radius: 6px;
               background: var(--bg-sunken); overflow-x: auto;
               font-family: var(--font-mono); font-size: 11.5px; }

.ide-saisie { padding: 13px; border-top: 1px solid var(--border);
              display: flex; flex-direction: column; gap: 8px; }
.ide-coche { font-size: 11.5px; }
/* L'avertissement du journal (#66) se lit sous sa case, plus petit qu'elle :
   c'est une conséquence de la case, pas une seconde décision. */
.ide-avis-journal { font-size: 11px; margin: -4px 0 0; line-height: 1.45; }
.ide-ligne { display: flex; gap: 8px; align-items: flex-end; }
.ide-ligne textarea { flex: 1; min-width: 0; resize: none; font-size: 12.5px; }

/* Sous 1597 px, l'écran ne tient plus trois colonnes : c'est l'arbre qui part,
   parce qu'il se remplace par le menu des dépôts et une liste qu'on rouvre,
   tandis que l'éditeur et l'IA sont ce qu'on est venu faire. Sous 987 px,
   l'IA passe dessous : on écrit **ou** on demande, on ne fait plus les deux
   côte à côte sur un téléphone. */
@media (max-width: 1596px) {
  .ide { grid-template-columns: 187px minmax(0, 1fr) 300px; }
}
@media (max-width: 986px) {
  .ide { grid-template-columns: minmax(0, 1fr);
         grid-template-rows: auto auto minmax(0, 1fr) minmax(0, 1fr); }
  .ide-arbre { border-right: 0; border-bottom: 1px solid var(--border); }
  .ide-fichiers { max-height: 144px; }
  /* Le pied de l'arbre est **borné** ici, comme l'est la liste au-dessus. Sur
     un téléphone, la grille donne à l'arbre la hauteur de son contenu : le
     chemin d'achat (#68) y ajoutait un paragraphe, et l'éditeur tombait à
     31 px de haut — mesuré le 2026-09-01. 233 px (Fibonacci) laissent les deux
     boutons et la première ligne du distant sans repousser le code hors écran ;
     le reste se fait défiler. */
  .ide-pied { max-height: 233px; overflow-y: auto; }
  .ide-ia { border-left: 0; border-top: 1px solid var(--border); }
}

/* ── Réunions — nommer les voix  (§4.12bis, L33) ──────────────────────────
   Une ligne par voix inconnue : l'étiquette que la machine a produite, et le
   champ où un humain écrit le nom. La proportion est celle de φ — l'étiquette
   tient 38 %, le nom 62 % : c'est le nom qu'on lit, le numéro n'est là que
   pour dire *lequel*. */
.grille-nom, .grille-noms { display: flex; flex-direction: column; gap: 8px; }
.ligne-nom { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.ligne-nom .etiquette { flex: 0 0 auto; min-width: 89px;
  font-variant-numeric: tabular-nums; color: var(--muted); }
.ligne-nom input { flex: 1 1 144px; min-width: 0; }
/* Un nom déjà donné ne redevient pas un champ : il se lit, tout simplement. */
.ligne-nom strong { flex: 1 1 auto; }
