/* ============================================================================
   Allkin — système visuel « Console »
   ----------------------------------------------------------------------------
   Allkin orchestre des agents : l'interface doit se lire comme un poste de
   pilotage, pas comme une messagerie grand public. D'où trois partis pris :

   1. Neutres réellement neutres (pas de gris teintés violet) + UNE couleur
      signal (cyan). Le violet générique est abandonné.
   2. Toute valeur de taille/espacement vient d'une échelle. Aucune valeur au
      jugé : si un chiffre n'est pas dans les tokens, c'est un bug.
   3. Les messages sont des blocs pleine colonne, pas des bulles alternées :
      un outil, pas un iMessage.
   ========================================================================== */

:root {
  /* Deux rôles typographiques distincts, et un seul endroit où les changer.
     --font-ui  : le châssis (sidebar, en-têtes, modales, tableaux). Pile
                  système moderne — donne SF Pro sur Apple, Segoe UI Variable
                  sur Windows. Aucun téléchargement.
     --font-console : le contenu de la conversation uniquement (messages,
                  saisie, propositions de commande). C'est là que la chasse
                  fixe a du sens : c'est le texte de travail, pas le décor.
     --font-mono : le code proprement dit (aperçus de fichiers, extraits). */
  --font-ui: -apple-system, BlinkMacSystemFont, "Segoe UI Variable Text", "Segoe UI", Inter, "Helvetica Neue", Arial, sans-serif;
  --font-console: ui-monospace, "SF Mono", SFMono-Regular, "JetBrains Mono", "Cascadia Mono", "Roboto Mono", Menlo, Consolas, "Liberation Mono", monospace;
  --font-mono: ui-monospace, "SF Mono", "JetBrains Mono", "Cascadia Code", "Roboto Mono", Consolas, monospace;
  /* --font-read : proposée au choix pour le fil de la conversation seul
     (Paramètres > Apparence). Une proportionnelle à empattements se lit mieux
     sur de longues réponses que la chasse fixe, qui reste le défaut. */
  --font-read: "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua", Georgia, serif;

  /* Échelle typographique (racine 16px) — 7 crans, rien en dehors. */
  --text-2xs: 0.6875rem;  /* 11px — méta, badges */
  --text-xs: 0.75rem;     /* 12px — labels de section */
  --text-sm: 0.8125rem;   /* 13px — texte secondaire */
  --text-md: 0.875rem;    /* 14px — texte courant */
  --text-lg: 0.9375rem;   /* 15px — corps des messages */
  --text-xl: 1.0625rem;   /* 17px — titres de panneau */
  --text-2xl: 1.375rem;   /* 22px — titre du login */

  /* Échelle d'espacement — base 4px. */
  --sp-1: 0.25rem;
  --sp-2: 0.5rem;
  --sp-3: 0.75rem;
  --sp-4: 1rem;
  --sp-5: 1.25rem;
  --sp-6: 1.5rem;
  --sp-8: 2rem;

  --radius: 10px;
  --radius-sm: 7px;
  --radius-lg: 14px;

  /* Largeur de la colonne de lecture du chat. */
  --measure: 70ch;

  /* Clair — papier froid, encre presque noire. */
  --bg: #f6f8f8;
  --bg-alt: #ffffff;
  --bg-raised: #ffffff;
  --sidebar-bg: #eef1f1;
  --border: #e2e6e6;
  --border-strong: #ccd3d2;
  --text: #0f1413;
  --text-dim: #59635f;
  --text-faint: #8a9491;

  --accent: #0d9488;
  --accent-hover: #0f766e;
  --accent-contrast: #ffffff;
  --accent-soft: rgba(13, 148, 136, 0.1);

  /* Remplissage des graphes. Série unique, donc une seule teinte — et un pas
     choisi par thème, jamais l'accent inversé automatiquement : les deux
     valeurs sont validées contre leur propre fond (bande de clarté OKLCH et
     contraste >= 3:1). */
  --chart-fill: #0d9488;

  --bubble-user-bg: var(--bg-raised);
  --bubble-user-text: var(--text);
  --bubble-agent-bg: transparent;

  --risk-low: #15803d;
  --risk-mid: #a16207;
  --risk-high: #be123c;
  /* Bleu de l'attente d'approbation (fil d'activité de l'accueil). */
  --dot-await: #1d4ed8;
  --risk-low-soft: rgba(21, 128, 61, 0.1);
  --risk-mid-soft: rgba(161, 98, 7, 0.1);
  --risk-high-soft: rgba(190, 18, 60, 0.1);

  /* Coloration syntaxique (voir web/highlight.js) — thème « Allkin » par
     défaut, celui des autres jetons de cette feuille. Les thèmes alternatifs
     (data-code-theme, plus bas) redéfinissent ce même jeu de variables. */
  --tok-comment: var(--text-faint);
  --tok-string: #0f7b6c;
  --tok-number: #b45309;
  --tok-keyword: #0d9488;
  --tok-literal: #7c3aed;
  --tok-function: #1d4ed8;
  --tok-type: #be185d;
  --tok-property: #0369a1;
  --tok-tag: #0d9488;
  --tok-attr: #a16207;
  --tok-variable: #be123c;
  --tok-operator: var(--text-dim);
  --tok-punct: var(--text-dim);

  /* Conversations entre agents. Une teinte à part, franchement éloignée du
     turquoise de l'accent : ces blocs ne sont ni l'utilisateur ni l'agent à qui
     il parle, et doivent se reconnaître avant d'être lus. */
  --exchange: #6d5ce0;
  --exchange-soft: rgba(109, 92, 224, 0.08);
  --exchange-border: rgba(109, 92, 224, 0.35);


  --shadow-sm: 0 1px 2px rgba(15, 20, 19, 0.05);
  --shadow-md: 0 16px 40px rgba(15, 20, 19, 0.12);
  --shadow-inset: inset 0 1px 2px rgba(15, 20, 19, 0.05);
  --shadow-btn: 0 1px 1px rgba(13, 148, 136, 0.25), 0 2px 5px rgba(15, 20, 19, 0.1);
  --shadow-btn-hover: 0 1px 1px rgba(13, 148, 136, 0.3), 0 4px 10px rgba(15, 20, 19, 0.14);
}

/* Thème sombre : suit le système par défaut (media query ci-dessous), mais un
   choix explicite dans Paramètres > Apparence (data-theme, voir applyAppTheme
   dans app.js) doit l'emporter dans les deux sens — d'où le :not([data-theme=
   "light"]) qui empêche la media query de reprendre la main sur un choix
   « clair » explicite pendant qu'un système sombre reste actif, et le second
   bloc, hors media query, qui impose le sombre même sur un système clair. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    /* Presque noir neutre : un poste de pilotage, pas un thème « gamer ». */
    --bg: #0c0e0e;
    --bg-alt: #131716;
    --bg-raised: #1a1f1e;
    --sidebar-bg: #090b0b;
    --border: #222827;
    --border-strong: #333b39;
    --text: #e7eceb;
    --text-dim: #97a3a0;
    --text-faint: #6a7674;

    --accent: #2dd4bf;
    --accent-hover: #5eead4;
    --accent-contrast: #04211d;
    --accent-soft: rgba(45, 212, 191, 0.14);

    /* Plus sombre que --accent : à 0,78 de clarté, l'accent du thème sombre
       sort de la bande admise pour une surface pleine et brûle l'œil. */
    --chart-fill: #10a294;

    --bubble-user-bg: var(--bg-raised);
    --bubble-user-text: var(--text);
    --bubble-agent-bg: transparent;

    --risk-low: #4ade80;
    --risk-mid: #fbbf24;
    --risk-high: #fb7185;
    --dot-await: #60a5fa;
    --risk-low-soft: rgba(74, 222, 128, 0.14);
    --risk-mid-soft: rgba(251, 191, 36, 0.14);
    --risk-high-soft: rgba(251, 113, 133, 0.14);

    --tok-comment: var(--text-faint);
    --tok-string: #4ade80;
    --tok-number: #fbbf24;
    --tok-keyword: #2dd4bf;
    --tok-literal: #a78bfa;
    --tok-function: #60a5fa;
    --tok-type: #f472b6;
    --tok-property: #38bdf8;
    --tok-tag: #2dd4bf;
    --tok-attr: #fbbf24;
    --tok-variable: #fb7185;
    --tok-operator: var(--text-dim);
    --tok-punct: var(--text-dim);

    --exchange: #a99bff;
    --exchange-soft: rgba(169, 155, 255, 0.1);
    --exchange-border: rgba(169, 155, 255, 0.3);

    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
    --shadow-md: 0 20px 48px rgba(0, 0, 0, 0.55);
    --shadow-inset: inset 0 1px 3px rgba(0, 0, 0, 0.4);
    --shadow-btn: 0 1px 1px rgba(0, 0, 0, 0.4), 0 2px 6px rgba(45, 212, 191, 0.2);
    --shadow-btn-hover: 0 1px 1px rgba(0, 0, 0, 0.45), 0 4px 12px rgba(45, 212, 191, 0.28);
  }

  :root:not([data-theme="light"]) select {
    color-scheme: dark;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236a7674' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  }
}

/* Choix explicite « Sombre », système clair ou non — mêmes valeurs que
   ci-dessus, dupliquées : une media query ne peut pas s'appliquer hors de sa
   condition système, il n'y a donc pas d'autre façon de les partager. */
:root[data-theme="dark"] {
  --bg: #0c0e0e;
  --bg-alt: #131716;
  --bg-raised: #1a1f1e;
  --sidebar-bg: #090b0b;
  --border: #222827;
  --border-strong: #333b39;
  --text: #e7eceb;
  --text-dim: #97a3a0;
  --text-faint: #6a7674;

  --accent: #2dd4bf;
  --accent-hover: #5eead4;
  --accent-contrast: #04211d;
  --accent-soft: rgba(45, 212, 191, 0.14);

  --chart-fill: #10a294;

  --bubble-user-bg: var(--bg-raised);
  --bubble-user-text: var(--text);
  --bubble-agent-bg: transparent;

  --risk-low: #4ade80;
  --risk-mid: #fbbf24;
  --risk-high: #fb7185;
  --dot-await: #60a5fa;
  --risk-low-soft: rgba(74, 222, 128, 0.14);
  --risk-mid-soft: rgba(251, 191, 36, 0.14);
  --risk-high-soft: rgba(251, 113, 133, 0.14);

  --tok-comment: var(--text-faint);
  --tok-string: #4ade80;
  --tok-number: #fbbf24;
  --tok-keyword: #2dd4bf;
  --tok-literal: #a78bfa;
  --tok-function: #60a5fa;
  --tok-type: #f472b6;
  --tok-property: #38bdf8;
  --tok-tag: #2dd4bf;
  --tok-attr: #fbbf24;
  --tok-variable: #fb7185;
  --tok-operator: var(--text-dim);
  --tok-punct: var(--text-dim);

  --exchange: #a99bff;
  --exchange-soft: rgba(169, 155, 255, 0.1);
  --exchange-border: rgba(169, 155, 255, 0.3);

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
  --shadow-md: 0 20px 48px rgba(0, 0, 0, 0.55);
  --shadow-inset: inset 0 1px 3px rgba(0, 0, 0, 0.4);
  --shadow-btn: 0 1px 1px rgba(0, 0, 0, 0.4), 0 2px 6px rgba(45, 212, 191, 0.2);
  --shadow-btn-hover: 0 1px 1px rgba(0, 0, 0, 0.45), 0 4px 12px rgba(45, 212, 191, 0.28);
}
:root[data-theme="dark"] select {
  color-scheme: dark;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236a7674' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
}

/* ---- Thèmes de coloration syntaxique --------------------------------------
   « Allkin », ci-dessus, suit le thème clair/sombre de l'application. Les
   thèmes suivants sont choisis dans Paramètres (localStorage, indépendant du
   clair/sombre système) et imposent leurs propres couleurs ET leur propre
   fond de zone de code, à l'image de l'éditeur dont ils reprennent le nom.
   Un sélecteur d'attribut l'emporte toujours sur `:root` seul (voir plus
   haut) : ces blocs gagnent sur le thème clair comme sur le sombre, quel que
   soit l'ordre des règles dans ce fichier. */
:root[data-code-theme="github"] {
  --tok-comment: #6e7781;
  --tok-string: #0a3069;
  --tok-number: #0550ae;
  --tok-keyword: #cf222e;
  --tok-literal: #0550ae;
  --tok-function: #8250df;
  --tok-type: #953800;
  --tok-property: #0550ae;
  --tok-tag: #116329;
  --tok-attr: #0550ae;
  --tok-variable: #953800;
  --tok-operator: #24292f;
  --tok-punct: #24292f;
  --code-bg: #ffffff;
  --code-fg: #24292f;
  --code-border: #d0d7de;
}

:root[data-code-theme="monokai"] {
  --tok-comment: #75715e;
  --tok-string: #e6db74;
  --tok-number: #ae81ff;
  --tok-keyword: #f92672;
  --tok-literal: #ae81ff;
  --tok-function: #a6e22e;
  --tok-type: #66d9ef;
  --tok-property: #a6e22e;
  --tok-tag: #f92672;
  --tok-attr: #a6e22e;
  --tok-variable: #fd971f;
  --tok-operator: #f92672;
  --tok-punct: #f8f8f2;
  --code-bg: #272822;
  --code-fg: #f8f8f2;
  --code-border: #3e3d32;
}

:root[data-code-theme="dracula"] {
  --tok-comment: #6272a4;
  --tok-string: #f1fa8c;
  --tok-number: #bd93f9;
  --tok-keyword: #ff79c6;
  --tok-literal: #bd93f9;
  --tok-function: #50fa7b;
  --tok-type: #8be9fd;
  --tok-property: #8be9fd;
  --tok-tag: #ff79c6;
  --tok-attr: #50fa7b;
  --tok-variable: #f8f8f2;
  --tok-operator: #ff79c6;
  --tok-punct: #f8f8f2;
  --code-bg: #282a36;
  --code-fg: #f8f8f2;
  --code-border: #44475a;
}

:root[data-code-theme="solarized"] {
  --tok-comment: #586e75;
  --tok-string: #2aa198;
  --tok-number: #d33682;
  --tok-keyword: #859900;
  --tok-literal: #cb4b16;
  --tok-function: #268bd2;
  --tok-type: #b58900;
  --tok-property: #268bd2;
  --tok-tag: #859900;
  --tok-attr: #b58900;
  --tok-variable: #268bd2;
  --tok-operator: #93a1a1;
  --tok-punct: #93a1a1;
  --code-bg: #002b36;
  --code-fg: #839496;
  --code-border: #073642;
}

/* ---- Thèmes de la fenêtre de conversation ---------------------------------
   Même mécanique et même rangement que les thèmes de code ci-dessus, mais
   appliqués au fil de la conversation seul (Paramètres > Apparence).

   Un thème ne crée pas un jeu de variables « chat » que chaque règle du fil
   irait chercher : il redéfinit les neutres de l'application (--bg, --text,
   --accent…) sur `.chat-messages`. Tout ce qui vit dans le fil — messages,
   cartes de commande, blocs d'échange, notes — suit alors sans qu'aucune de
   leurs règles ne bouge, et le reste de l'application (barre latérale,
   en-têtes, barre de saisie) garde le thème clair/sombre choisi juste
   au-dessus. La conversation devient une surface de lecture posée dans le
   châssis, comme l'est déjà un bloc de code coloré par son propre thème.

   Le défaut (« Allkin ») n'est volontairement aucun bloc : sans attribut, le
   fil hérite du thème de l'application. */

/* Papier — encre sombre sur blanc, quel que soit le thème de l'application. */
:root[data-chat-theme="paper"] .chat-messages {
  --bg: #ffffff;
  --bg-alt: #f6f8fa;
  --bg-raised: #f6f8fa;
  --border: #d0d7de;
  --border-strong: #afb8c1;
  --text: #1f2328;
  --text-dim: #59636e;
  --text-faint: #818b98;
  --accent: #0969da;
  --accent-hover: #0550ae;
  --accent-contrast: #ffffff;
  --accent-soft: rgba(9, 105, 218, 0.1);
  --bubble-user-bg: #f6f8fa;
  --bubble-user-text: #1f2328;
}

/* Sépia — le seul thème pensé pour la lecture longue plutôt que pour le
   contraste : fond chaud, encre brune, aucun blanc pur. */
:root[data-chat-theme="sepia"] .chat-messages {
  --bg: #f4ecd8;
  --bg-alt: #fbf5e6;
  --bg-raised: #fbf5e6;
  --border: #e0d3b4;
  --border-strong: #c8b791;
  --text: #3b3226;
  --text-dim: #6b5d48;
  --text-faint: #93826a;
  --accent: #9a6b3f;
  --accent-hover: #7d5530;
  --accent-contrast: #fdf8ec;
  --accent-soft: rgba(154, 107, 63, 0.12);
  --bubble-user-bg: #ece0c4;
  --bubble-user-text: #3b3226;
}

/* Les quatre suivants reprennent le fond, l'encre et la couleur d'accent des
   thèmes de coloration du code de même nom : choisir la même paire des deux
   côtés donne un fil d'un seul tenant, blocs de code compris. */
:root[data-chat-theme="monokai"] .chat-messages {
  --bg: #272822;
  --bg-alt: #2e2f28;
  --bg-raised: #32332b;
  --border: #3e3d32;
  --border-strong: #55564a;
  --text: #f8f8f2;
  --text-dim: #bcbcae;
  --text-faint: #75715e;
  --accent: #a6e22e;
  --accent-hover: #c3f04a;
  --accent-contrast: #1b1c17;
  --accent-soft: rgba(166, 226, 46, 0.14);
  --bubble-user-bg: #32332b;
  --bubble-user-text: #f8f8f2;
}

:root[data-chat-theme="dracula"] .chat-messages {
  --bg: #282a36;
  --bg-alt: #2f313f;
  --bg-raised: #343746;
  --border: #44475a;
  --border-strong: #565a70;
  --text: #f8f8f2;
  --text-dim: #c3c5da;
  --text-faint: #6272a4;
  --accent: #bd93f9;
  --accent-hover: #d6b6ff;
  --accent-contrast: #21222c;
  --accent-soft: rgba(189, 147, 249, 0.16);
  --bubble-user-bg: #343746;
  --bubble-user-text: #f8f8f2;
}

:root[data-chat-theme="solarized"] .chat-messages {
  --bg: #002b36;
  --bg-alt: #04323d;
  --bg-raised: #073642;
  --border: #0e4a58;
  --border-strong: #175c6b;
  --text: #93a1a1;
  --text-dim: #839496;
  --text-faint: #657b83;
  --accent: #2aa198;
  --accent-hover: #4fc3ba;
  --accent-contrast: #002b36;
  --accent-soft: rgba(42, 161, 152, 0.16);
  --bubble-user-bg: #073642;
  --bubble-user-text: #eee8d5;
}

:root[data-chat-theme="nord"] .chat-messages {
  --bg: #2e3440;
  --bg-alt: #343b4a;
  --bg-raised: #3b4252;
  --border: #434c5e;
  --border-strong: #4c566a;
  --text: #e5e9f0;
  --text-dim: #b9c2d0;
  --text-faint: #7b88a1;
  --accent: #88c0d0;
  --accent-hover: #a3d4e2;
  --accent-contrast: #23292f;
  --accent-soft: rgba(136, 192, 208, 0.16);
  --bubble-user-bg: #3b4252;
  --bubble-user-text: #eceff4;
}

/* Police et corps du fil — mêmes crans que partout ailleurs (échelle en tête
   de fichier) : personnaliser ne veut pas dire sortir du système. Les valeurs
   par défaut sont posées sur `.chat-messages` (plus bas) ; ces blocs ne font
   que les remplacer. Les blocs de code gardent --font-mono : le choix porte
   sur le texte de la conversation, pas sur ce qui doit rester aligné. */
:root[data-chat-font="ui"] .chat-messages { --chat-font: var(--font-ui); }
:root[data-chat-font="serif"] .chat-messages { --chat-font: var(--font-read); }

:root[data-chat-size="xs"] .chat-messages { --chat-size: var(--text-xs); }
:root[data-chat-size="md"] .chat-messages { --chat-size: var(--text-md); }
:root[data-chat-size="lg"] .chat-messages { --chat-size: var(--text-lg); }
:root[data-chat-size="xl"] .chat-messages { --chat-size: var(--text-xl); }

.tok-comment { color: var(--tok-comment); }
.tok-string { color: var(--tok-string); }
.tok-number { color: var(--tok-number); }
.tok-keyword { color: var(--tok-keyword); }
.tok-literal { color: var(--tok-literal); }
.tok-function { color: var(--tok-function); }
.tok-type { color: var(--tok-type); }
.tok-property { color: var(--tok-property); }
.tok-tag { color: var(--tok-tag); }
.tok-attr { color: var(--tok-attr); }
.tok-variable { color: var(--tok-variable); }
.tok-operator { color: var(--tok-operator); }
.tok-punct { color: var(--tok-punct); }

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

* {
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
}
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border-radius: 999px;
  border: 2px solid transparent;
  background-clip: padding-box;
}
*::-webkit-scrollbar-thumb:hover { background: var(--text-faint); }

/* Respecte le réglage système « réduire les animations ». */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

html {
  /* Safari agrandit de lui-même le texte de certains blocs (« text
     autosizing ») : les libellés grossissent, les rangées s'élargissent, et la
     mise en page finit plus large que l'écran — là où Chromium ne bouge pas.
     100% = on garde la taille demandée. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

html, body {
  height: 100%;
  /* Fallback pour navigateurs sans dvh ; corrigé en JS (--app-height) pour
     compenser la barre d'adresse et le clavier virtuel sous iOS Safari. */
  height: 100dvh;
  margin: 0;
  overflow: hidden;
  overscroll-behavior-y: none;
}

body {
  font-family: var(--font-ui);
  font-size: var(--text-md);
  line-height: 1.5;
  background: var(--bg);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
  /* Supprime le zoom au double-tap (et le délai de 300 ms qui va avec) : dans
     une interface faite de listes et de boutons, un double-tap est toujours un
     tap répété, jamais une demande d'agrandissement. */
  touch-action: manipulation;
}

.hidden { display: none !important; }
.dim { color: var(--text-dim); }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-sm); }

/* Hauteur commune à tous les boutons, quelle que soit leur variante, pour que
   deux boutons voisins (ex: Annuler + Enregistrer, + et Envoyer) s'alignent
   toujours — sans ça chaque variante (ghost/secondary/défaut) a son propre
   padding et les rangées de boutons paraissent décalées. */
button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-1);
  min-height: 2.35rem;
  font-family: inherit;
  cursor: pointer;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  padding: 0 var(--sp-4);
  background: var(--accent);
  color: var(--accent-contrast);
  font-size: var(--text-md);
  font-weight: 600;
  line-height: 1;
  touch-action: manipulation;
  box-shadow: var(--shadow-btn);
  transition: background-color 0.12s ease, border-color 0.12s ease, color 0.12s ease,
    opacity 0.12s ease, transform 0.08s ease, box-shadow 0.12s ease;
}
button:hover { background: var(--accent-hover); box-shadow: var(--shadow-btn-hover); transform: translateY(-1px); }
button:active { transform: translateY(0) scale(0.98); box-shadow: var(--shadow-btn); }
button.secondary {
  background: var(--bg-alt);
  border: 1px solid var(--border-strong);
  color: var(--text);
  box-shadow: var(--shadow-sm);
}
button.secondary:hover { background: var(--sidebar-bg); border-color: var(--text-faint); box-shadow: var(--shadow-md); }
/* Action irréversible (suppression définitive d'un agent, de ses conversations
   et de ses fichiers) : rouge plein comme le bouton Refuser d'une commande à
   risque, pour que le doigt/la souris hésite avant de cliquer. */
button.danger {
  background: var(--risk-high);
  color: #fff;
  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15), 0 2px 5px var(--risk-high-soft);
}
button.danger:hover { filter: brightness(1.08); background: var(--risk-high); box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15), 0 2px 5px var(--risk-high-soft); }
button.ghost {
  background: transparent;
  border-color: transparent;
  color: var(--text-dim);
  padding: 0 var(--sp-2);
  box-shadow: none;
}
button.ghost:hover { color: var(--text); background: var(--sidebar-bg); box-shadow: none; transform: none; }
button.ghost:active { transform: scale(0.96); }
/* Boutons icône seuls (☰ ⚙ ⏻ +) : carrés plutôt qu'étirés en largeur.
   Sans effet sur les ghost à texte (ex: "Annuler"), déjà plus larges que ça. */
button.ghost { min-width: 2.35rem; }
button:disabled { opacity: 0.45; cursor: not-allowed; }
button:disabled:hover { background: inherit; transform: none; box-shadow: var(--shadow-btn); }
button:disabled:active { transform: none; }
/* Toujours visible (agents comme pages globales) : c'est l'unique commande
   d'affichage de la sidebar, en recouvrement sur mobile ou en réduction de
   colonne sur desktop — voir applySidebarState() dans app.js. */
.menu-btn { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; }

/* Icônes SVG inline (flat, colorées à même le markup — voir index.html/app.js)
   plutôt que des emoji, dont la taille de rendu varie trop selon l'OS/la
   police système. Taille fixe et cohérente partout où une icône apparaît. */
.icon { width: 18px; height: 18px; flex-shrink: 0; display: block; }
.icon-sm { width: 12px; height: 12px; }
button:disabled .icon { opacity: 0.7; }

/* Champs « compacts remplis » : un fond légèrement teinté (--bg, plus sombre
   que la carte qui le porte) tient lieu de cadre, plutôt qu'une bordure
   marquée + une ombre en creux — plus plat, plus dense, et nettement moins
   haut que l'ancien style (2.25rem au lieu de 2.5rem, sans lui faire perdre
   en confort de saisie). */
input[type="text"], input[type="password"], input[type="number"], input[type="search"], textarea, select {
  width: 100%;
  min-height: 2.25rem;
  font-family: inherit;
  font-size: var(--text-md);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  padding: var(--sp-1) var(--sp-3);
  box-shadow: none;
  transition: border-color 0.12s ease, box-shadow 0.12s ease, background-color 0.12s ease;
}
textarea { min-height: 2.25rem; }
input[type="text"]::placeholder, input[type="password"]::placeholder, input[type="number"]::placeholder,
input[type="search"]::placeholder, textarea::placeholder { color: var(--text-faint); }
input[type="text"]:hover, input[type="password"]:hover, input[type="number"]:hover,
input[type="search"]:hover, textarea:hover, select:hover {
  border-color: var(--border-strong);
}
input[type="text"]:disabled, input[type="password"]:disabled, input[type="number"]:disabled,
input[type="search"]:disabled, textarea:disabled, select:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}
input[type="checkbox"] {
  width: 15px;
  height: 15px;
  margin: 0;
  accent-color: var(--accent);
  flex-shrink: 0;
}
input[type="text"]:focus, input[type="password"]:focus, input[type="number"]:focus,
input[type="search"]:focus, textarea:focus, select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
/* Select : flèche par défaut du système remplacée par un chevron qui suit le
   thème (clair/sombre), pour rester visuellement cohérent avec les autres
   champs plutôt que de laisser transparaître le style natif de l'OS. */
select {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  padding-right: 2.25rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238a9491' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--sp-3) center;
  background-size: 16px;
  cursor: pointer;
}
select:-moz-focusring { color: transparent; text-shadow: 0 0 0 var(--text); }

/* Le panneau qui s'ouvre au clic reste dessiné par le système : aucune règle
   CSS ne le met en page. `color-scheme` est le seul levier qui le fasse
   basculer avec le thème (sans lui, liste blanche sur application sombre — il
   vaut la déclaration sur `select` plutôt que sur `:root`, qui repeindrait
   aussi les ascenseurs déjà habillés plus haut) ; `option` et `optgroup`
   reprennent la palette là où le moteur l'accepte (Chromium et Firefox sous
   Windows/Linux). macOS et les téléphones imposent leur propre panneau et
   ignorent ces règles : c'est `color-scheme` qui y fait tout le travail. */
select { color-scheme: light; }
select option,
select optgroup {
  background-color: var(--bg-alt);
  color: var(--text);
}
select optgroup {
  font-weight: 650;
  color: var(--text-dim);
}
select option:disabled { color: var(--text-faint); }

/* Une liste affichée ouverte (`multiple`, ou `size` > 1) n'a pas de bouton à
   décorer : le chevron se poserait au milieu des options et la hauteur fixe
   n'aurait plus de sens. */
select[multiple],
select[size]:not([size="1"]) {
  height: auto;
  padding-right: var(--sp-3);
  background-image: none;
  cursor: default;
}
select[multiple] option,
select[size]:not([size="1"]) option {
  padding: var(--sp-1) var(--sp-2);
  border-radius: var(--radius-sm);
}

/* Micro-label façon formulaire dense, commun à tous les formulaires de
   l'appli (Paramètres, modales, création d'agent, config de service) : le
   nom du champ est réduit à une étiquette discrète collée dessus, plutôt
   qu'un intitulé de pleine taille qui double l'espace vertical occupé par
   chaque champ — c'est ce qui rendait les formulaires massifs sur mobile.
   Le sélecteur :not(.checkbox-label) épargne les lignes « case à cocher +
   texte », qui restent en taille de lecture normale. La spécificité
   supplémentaire de :not() l'emporte sur les règles .modal/.config-form/
   .create-fields label existantes sans avoir à les toucher. */
.settings-card label:not(.checkbox-label),
.settings-section label:not(.checkbox-label),
.modal label:not(.checkbox-label),
.config-form label:not(.checkbox-label),
.create-fields label:not(.checkbox-label) {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  font-size: var(--text-2xs);
  font-weight: 650;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--text-faint);
}
/* Le texte de l'étiquette hérite de la casse/graisse du label — on les
   annule sur le champ lui-même pour qu'il garde une typo de lecture normale. */
.settings-card label:not(.checkbox-label) input,
.settings-card label:not(.checkbox-label) textarea,
.settings-card label:not(.checkbox-label) select,
.settings-section label:not(.checkbox-label) input,
.settings-section label:not(.checkbox-label) textarea,
.settings-section label:not(.checkbox-label) select,
.modal label:not(.checkbox-label) input,
.modal label:not(.checkbox-label) textarea,
.modal label:not(.checkbox-label) select,
.config-form label:not(.checkbox-label) input,
.config-form label:not(.checkbox-label) textarea,
.config-form label:not(.checkbox-label) select,
.create-fields label:not(.checkbox-label) input,
.create-fields label:not(.checkbox-label) textarea,
.create-fields label:not(.checkbox-label) select {
  font-size: var(--text-md);
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  color: var(--text);
}
/* Une précision entre parenthèses dans un label n'a pas besoin du même
   relief que le nom du champ : elle doit rester lisible en un coup d'œil,
   pas rivaliser avec lui. */
.settings-card label .dim,
.settings-section label .dim,
.modal label .dim,
.config-form label .dim,
.create-fields label .dim {
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  color: var(--text-faint);
}

/* Login */
.screen { width: 100%; height: 100%; }
#login-screen, #setup-screen, #lock-screen, #pin-setup-screen {
  display: flex;
  align-items: center;
  justify-content: center;
  background:
    radial-gradient(480px circle at 20% 15%, var(--accent-soft), transparent 60%),
    radial-gradient(560px circle at 85% 85%, var(--accent-soft), transparent 60%),
    var(--bg);
}
.login-card {
  width: min(340px, 90vw);
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: var(--sp-8);
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
}
/* Plus large que la carte de connexion : la configuration initiale porte
   plusieurs champs (clé API, mot de passe, confirmation), pas un seul. */
.login-card.wide { width: min(420px, 92vw); }
.login-card h1 {
  margin: 0;
  color: var(--text);
  font-size: var(--text-2xl);
  font-weight: 700;
  letter-spacing: -0.02em;
}
.login-card .dim { font-size: var(--text-md); }
.error { color: var(--risk-high); font-size: var(--text-sm); }

.setup-note {
  padding: var(--sp-3);
  background: var(--accent-soft);
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  border-radius: var(--radius-sm);
  color: var(--accent);
  font-size: var(--text-sm);
}
.setup-note code { color: inherit; }
.setup-field { display: flex; flex-direction: column; gap: var(--sp-1); }
.setup-field > span:first-child { font-size: var(--text-sm); font-weight: 600; color: var(--text-dim); }
.setup-hint { font-size: var(--text-xs); }

/* Verrouillage : pavé numérique
   ----------------------------------------------------------------------------
   Le code de déverrouillage se saisit toujours ici, jamais dans un champ
   texte — même geste au téléphone et au bureau. Les touches sont
   dimensionnées pour le pouce (56px, au-dessus de la cible tactile
   recommandée) et le pavé garde sa largeur sur grand écran : un pavé étiré
   ne se vise plus. */
.pin-card { align-items: center; text-align: center; }
.pin-card h1, .pin-card .dim { text-align: center; }

.pin-pad { display: flex; flex-direction: column; align-items: center; gap: var(--sp-6); }

.pin-dots { display: flex; gap: var(--sp-3); }
.pin-dot {
  width: 14px;
  height: 14px;
  border: 1.5px solid var(--border-strong);
  border-radius: 50%;
  transition: background 0.12s ease, border-color 0.12s ease, transform 0.12s ease;
}
.pin-dot.filled {
  background: var(--accent);
  border-color: var(--accent);
  transform: scale(1.1);
}

.pin-keys {
  display: grid;
  grid-template-columns: repeat(3, 66px);
  gap: var(--sp-4);
}
/* Touches carrées avant d'être arrondies : un `border-radius: 50%` sur un
   rectangle donne une ellipse, pas un rond. */
.pin-key {
  width: 66px;
  height: 66px;
  min-height: 0;
  padding: 0;
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: 50%;
  box-shadow: none;
  color: var(--text);
  font-family: var(--font-ui);
  font-size: var(--text-xl);
  font-weight: 500;
  cursor: pointer;
  transition: background 0.1s ease, transform 0.06s ease;
}
.pin-key:hover {
  background: var(--accent-soft);
  border-color: var(--accent);
  box-shadow: none;
  transform: none;
}
/* Retour visuel immédiat : sur mobile, le survol n'existe pas. */
.pin-key:active { transform: scale(0.94); background: var(--accent-soft); box-shadow: none; }
.pin-key-back {
  border-color: transparent;
  background: transparent;
  color: var(--text-dim);
  font-size: var(--text-lg);
}
.pin-key-back:hover { background: transparent; border-color: transparent; color: var(--text); }

/* Code refusé : on le montre, on ne se contente pas de l'écrire. */
.pin-shake { animation: pin-shake 0.32s ease; }
@keyframes pin-shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-7px); }
  40% { transform: translateX(6px); }
  60% { transform: translateX(-4px); }
  80% { transform: translateX(2px); }
}

/* Bouton qui n'en a pas l'air : une porte de secours ne doit pas rivaliser
   avec l'action principale. */
.linklike {
  min-height: 0;
  padding: 0;
  background: none;
  border: none;
  box-shadow: none;
  color: var(--text-dim);
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  font-weight: 400;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}
.linklike:hover {
  background: none;
  box-shadow: none;
  transform: none;
  color: var(--accent);
}
.linklike:active { transform: none; box-shadow: none; }

.checkbox-row {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  color: var(--text-dim);
  font-size: var(--text-sm);
  cursor: pointer;
}
.checkbox-row input { width: auto; margin: 0; accent-color: var(--accent); }

/* Règles de mot de passe / de code, cochées au fil de la frappe. */
.policy-list {
  margin: var(--sp-2) 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  text-align: left;
}
.policy-item {
  position: relative;
  padding-left: 1.15rem;
  color: var(--text-faint);
  font-size: var(--text-xs);
  line-height: 1.45;
}
.policy-item::before {
  content: "○";
  position: absolute;
  left: 0;
}
.policy-ok { color: var(--risk-low); }
.policy-ok::before { content: "✔"; }
.policy-ko { color: var(--risk-high); }
.policy-ko::before { content: "✕"; }

/* Appareils mémorisés (Paramètres > Sécurité) */
.device-list { display: flex; flex-direction: column; gap: var(--sp-2); margin-bottom: var(--sp-3); }
.device-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
}
.device-label { color: var(--text); }
.device-seen { font-size: var(--text-xs); }

/* App shell */
#app-screen.screen:not(.hidden) {
  display: grid;
  grid-template-columns: 276px 1fr;
  height: var(--app-height, 100dvh);
  transition: grid-template-columns 0.15s ease;
}
/* Sidebar réduite (desktop uniquement — le tiroir mobile est une autre
   mécanique, voir .sidebar.open) : la colonne s'efface, son padding fixe ne
   doit pas déborder pendant la transition. */
#app-screen.screen:not(.hidden).sidebar-collapsed { grid-template-columns: 0 1fr; }
#app-screen.sidebar-collapsed .sidebar { padding: 0; overflow: hidden; border-right: none; }
#app-screen.sidebar-collapsed .sidebar-collapse-btn { display: none; }

.sidebar-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(4, 8, 8, 0.55);
  z-index: 15;
  opacity: 0;
  transition: opacity 0.2s ease;
}

.sidebar {
  position: relative;
  background: var(--sidebar-bg);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  padding: var(--sp-3);
  padding-top: calc(var(--sp-3) + env(safe-area-inset-top));
  padding-left: calc(var(--sp-3) + env(safe-area-inset-left));
  padding-bottom: calc(var(--sp-3) + env(safe-area-inset-bottom));
  gap: var(--sp-3);
  overflow-x: hidden;
  /* L'entête, le menu et la recherche restent dans le flux normal (donc
     immobiles) : seule .agent-list défile, en flex:1 plus bas. */
  overflow-y: hidden;
}
@media (max-width: 720px) {
  .sidebar-collapse-btn { display: none; }
}
.sidebar-header {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  padding: var(--sp-1) var(--sp-1) var(--sp-2);
}
/* flex:1 plutôt que justify-content:space-between sur .sidebar-header : ça
   pousse les deux boutons (menu, repli) en groupe à droite, l'un à la suite
   de l'autre, au lieu de les écarter aux deux extrémités. */
.brand { display: flex; align-items: center; justify-content: flex-start; gap: var(--sp-2); flex: 1; min-width: 0; }
.brand-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--text-faint);
  flex-shrink: 0;
  transition: background-color 0.2s ease, box-shadow 0.2s ease;
}
.brand-dot.online {
  background: var(--risk-low);
  box-shadow: 0 0 0 3px var(--risk-low-soft);
}
.sidebar-header h1 {
  margin: 0;
  font-size: var(--text-lg);
  font-weight: 700;
  /* Le nom du produit en petites capitales espacées : signature « console ». */
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--text);
}

/* Menu de la barre latérale : se déploie dans le flux normal, entre le titre
   et la liste des agents (pas de superposition — il pousse simplement la
   liste vers le bas tant qu'il reste ouvert). Même mécanique de fermeture que
   .chat-menu (clic extérieur, Échap), voir app.js. */
.sidebar-menu {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: var(--sp-1);
  background: var(--bg-raised);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
}
.sidebar-menu-item {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  justify-content: flex-start;
  width: 100%;
  min-height: 2.3rem;
  padding: 0 var(--sp-2);
  background: none;
  border: none;
  box-shadow: none;
  color: var(--text);
  font-size: var(--text-sm);
  font-weight: 500;
  text-align: left;
}
.sidebar-menu-item:hover { background: var(--sidebar-bg); box-shadow: none; transform: none; }
.sidebar-menu-item:active { transform: none; }
.sidebar-menu-sep { height: 1px; margin: var(--sp-1) var(--sp-2); background: var(--border); }

/* Recherche + filtre par catégorie, au-dessus de la liste des agents. */
.agent-search {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 0 var(--sp-1);
}
.agent-search-input-wrap { position: relative; flex: 1; min-width: 0; }
.agent-search-input-wrap input[type="search"] { width: 100%; padding-right: 1.9rem; }
/* Chrome/Safari posent leur propre croix d'effacement sur type=search : la
   nôtre la remplace, pour un bouton cliquable identique sur tous les
   navigateurs (et qui double comme reset des filtres, pas juste du texte). */
.agent-search-input-wrap input[type="search"]::-webkit-search-cancel-button { display: none; }
.agent-search-clear {
  position: absolute;
  top: 50%;
  right: 0.3rem;
  transform: translateY(-50%);
  width: 1.4rem;
  height: 1.4rem;
  min-width: 1.4rem;
  min-height: 1.4rem;
  padding: 0;
  border-radius: 50%;
  background: var(--border-strong);
  border: none;
  box-shadow: none;
  color: var(--bg);
}
.agent-search-clear:hover { background: var(--text-faint); transform: translateY(-50%); }
.agent-category-btn { position: relative; flex-shrink: 0; }
/* Pastille discrète sur le bouton catégorie : indique un filtre actif sans
   avoir à ouvrir le menu pour le savoir. */
.agent-category-btn-dot {
  position: absolute;
  top: 0.35rem;
  right: 0.35rem;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
}
.agent-category-menu {
  position: absolute;
  top: 100%;
  right: var(--sp-1);
  z-index: 30;
  min-width: 13rem;
  max-height: 16rem;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 1px;
  margin-top: var(--sp-1);
  padding: var(--sp-1);
  background: var(--bg-raised);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
}
.agent-category-menu-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
  width: 100%;
  min-height: 2.2rem;
  padding: 0 var(--sp-2);
  background: none;
  border: none;
  box-shadow: none;
  color: var(--text);
  font-size: var(--text-sm);
  font-weight: 500;
  text-align: left;
}
.agent-category-menu-item:hover { background: var(--sidebar-bg); box-shadow: none; transform: none; }
.agent-category-menu-item.active { color: var(--accent); font-weight: 650; }
.agent-category-menu-empty { padding: var(--sp-2); font-size: var(--text-sm); color: var(--text-faint); }

.section-label {
  font-size: var(--text-2xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--text-faint);
  padding: 0 var(--sp-1);
}

.agent-list {
  display: flex;
  flex-direction: column;
  gap: 1px;
  /* Seule zone défilante de la sidebar : l'entête, le menu et la recherche
     restent fixes au-dessus (voir overflow-y: hidden sur .sidebar). */
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}
.agent-list-empty { padding: var(--sp-2) var(--sp-1); font-size: var(--text-sm); }

/* .agent-item n'est plus un <button> (il en contient deux : sélection + accès
   fichiers), donc les couleurs de survol/actif vivent sur ce conteneur, et
   .agent-item-main (le bouton de sélection) est réinitialisé à un style neutre. */
.agent-item {
  display: flex;
  align-items: center;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  width: 100%;
  position: relative;
}
.agent-item:hover { background: var(--bg-alt); }
.agent-item.active {
  background: var(--bg-alt);
  border-color: var(--border-strong);
  box-shadow: var(--shadow-sm);
}
/* Filet d'accent sur l'agent actif : repère plus net qu'un simple fond, sans
   ajouter d'élément au markup. */
.agent-item.active::before {
  content: "";
  position: absolute;
  left: -1px;
  top: 20%;
  bottom: 20%;
  width: 2px;
  border-radius: 999px;
  background: var(--accent);
}

.agent-item-main {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: var(--sp-2);
  flex: 1;
  min-width: 0;
  min-height: auto;
  background: none;
  border: none;
  box-shadow: none;
  color: inherit;
  text-align: left;
  font-weight: 400;
  padding: var(--sp-2) var(--sp-1) var(--sp-2) var(--sp-2);
}
.agent-item-main:hover { background: none; box-shadow: none; transform: none; }

/* Fichiers / Historique : actions directes sur la ligne, à la place de
   l'ancienne icône Config (déplacée dans le menu de la conversation — voir
   #chat-menu). Colonne à part de .agent-item-main : deux <button> ne peuvent
   pas s'imbriquer, elle reste donc un sibling, centrée sur toute la hauteur
   de la ligne (avatar + nom + statut). */
.agent-item-actions { display: flex; align-items: center; flex-shrink: 0; }
.agent-item-files-btn, .agent-item-history-btn { flex-shrink: 0; opacity: 0.6; padding: 0 var(--sp-1); }
.agent-item-files-btn:hover, .agent-item-history-btn:hover { opacity: 1; }

.agent-item-avatar {
  width: 24px;
  height: 24px;
  min-width: 24px;
  border-radius: 7px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  font-size: var(--text-2xs);
  font-weight: 700;
  letter-spacing: 0.02em;
  color: #fff;
  background: var(--avatar-color, var(--accent));
}
/* Avatar personnalisé (image importée) : remplace la lettre, même cadre. */
.agent-item-avatar img { width: 100%; height: 100%; object-fit: cover; }
.agent-item-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.agent-item-name-row { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; }
/* Pastille de messages non lus : disque plein, jamais discret — c'est tout
   son rôle. Masquée par .hidden quand le compteur retombe à zéro. */
.agent-item-badge {
  flex-shrink: 0;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--risk-high);
  color: #fff;
  font-size: var(--text-2xs);
  font-weight: 700;
  line-height: 18px;
  text-align: center;
}
.agent-item-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 600;
  font-size: var(--text-md);
  letter-spacing: -0.005em;
}
/* État de l'agent plutôt que sa description : une fois l'agent configuré, on
   ne relit jamais sa description, alors que savoir s'il travaille ou attend
   une validation change la façon dont on lit la liste. Même vocabulaire de
   couleurs et de pastille que le reste de l'interface : un agent qui attend une
   validation doit se reconnaître de la même façon partout.
   Sur sa propre ligne (sous le nom) plutôt qu'accolé à lui : le texte du
   statut ("Attend ta validation"…) a besoin de largeur pour ne pas se
   retrouver tronqué contre le nom. */
.agent-item-status {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  width: 100%;
  font-size: var(--text-2xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-faint);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.agent-item-status-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}
.agent-item-status.status-working { color: var(--accent); }
.agent-item-status.status-working .agent-item-status-dot {
  animation: status-pulse 1.4s ease-in-out infinite;
}
.agent-item-status.status-awaiting_approval { color: var(--risk-mid); }
.agent-item-status.status-awaiting_approval .agent-item-status-dot {
  box-shadow: 0 0 0 3px var(--risk-mid-soft);
}
/* Chat */
.chat { display: flex; flex-direction: column; height: 100%; min-width: 0; min-height: 0; background: var(--bg); }
.chat-header {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  justify-content: space-between;
  padding: var(--sp-3) var(--sp-5);
  padding-top: calc(var(--sp-3) + env(safe-area-inset-top));
  padding-right: calc(var(--sp-5) + env(safe-area-inset-right));
  border-bottom: 1px solid var(--border);
  background: var(--bg-alt);
}
/* ---- Barre d'onglets -----------------------------------------------------
   Occupe la place de l'ancien titre dans l'en-tête : c'est elle qui dit
   maintenant où l'on est. Elle défile horizontalement au lieu de passer à la
   ligne — l'en-tête doit garder sa hauteur quel que soit le nombre d'onglets. */
/* Enveloppe non défilante : porte les ombres de bord, qui doivent rester
   fixées à l'écran pendant que .tab-bar défile en dessous. Un pseudo-élément
   du conteneur qui défile scrolle avec son propre contenu (l'overflow interne
   déplace tout ce qu'il contient, position: absolute ou non) — il faut donc un
   ancêtre séparé, immobile, pour les ombres. */
.tab-bar-wrap {
  position: relative;
  flex: 1;
  min-width: 0;
}
.tab-bar-wrap::before,
.tab-bar-wrap::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: 1.25rem;
  pointer-events: none;
  z-index: 2;
}
.tab-bar-wrap::before { left: 0; background: linear-gradient(to right, var(--bg-alt), transparent); }
.tab-bar-wrap::after { right: 0; background: linear-gradient(to left, var(--bg-alt), transparent); }

.tab-bar {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  overflow-x: auto;
  scrollbar-width: none;
}
.tab-bar::-webkit-scrollbar { display: none; }

/* Aucun de ces éléments n'est du texte à sélectionner : sans ça, l'appui long
   qui ouvre un menu (onglet — voir bindLongPress dans app.js — ou simplement
   le tapoter) déclenche AUSSI la sélection native du navigateur, poignées de
   sélection et bulle Copier/Rechercher comprises, par-dessus le menu.
   Safari iOS est le plus strict des trois : il ignore par endroits la
   propriété non préfixée sur un appui long et exige -webkit-user-select en
   plus de -webkit-touch-callout — les deux sont donc systématiques ici, pas
   seulement la version standard. */
.tab,
.chat-menu-item,
.tab-overview-item,
.sidebar-menu-item,
.agent-category-menu-item,
.settings-nav-item,
.toolbar-toggle,
.toolbar-split-btn {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

.tab {
  display: flex;
  align-items: center;
  flex-shrink: 0;
  max-width: 14rem;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  color: var(--text-dim);
  transition: background-color 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.tab:hover { background: var(--sidebar-bg); color: var(--text); }
.tab.active {
  background: var(--accent-soft);
  border-color: color-mix(in srgb, var(--accent) 35%, transparent);
  color: var(--accent);
}
.tab-main {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  min-width: 0;
  min-height: 2rem;
  padding: 0 var(--sp-3);
  background: none;
  border: none;
  box-shadow: none;
  color: inherit;
  font-size: var(--text-sm);
  font-weight: 600;
}
.tab-main:hover { background: none; box-shadow: none; transform: none; }
.tab-icon { display: flex; flex-shrink: 0; opacity: 0.85; }
.tab-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Onglet dont l'enregistrement est encore en attente : un point suffit, le
   détail est donné par l'indicateur de l'éditeur. */
.tab.dirty .tab-label::after {
  content: " •";
  color: var(--risk-mid);
}
.tab-close {
  min-width: 1.5rem;
  min-height: 1.5rem;
  padding: 0;
  margin-right: var(--sp-1);
  background: none;
  box-shadow: none;
  color: inherit;
  opacity: 0.55;
}
.tab-close:hover { background: none; box-shadow: none; transform: none; opacity: 1; }
/* L'onglet de conversation porte le nom de l'agent : c'est le repère principal
   de l'en-tête, il garde donc le poids que le titre avait avant lui. */
.tab-chat .tab-main { font-weight: 700; }

/* ---- Vue d'ensemble des onglets -------------------------------------------
   Bouton fixe en tête de barre (hors de la zone qui défile) : ouvre la liste
   des onglets ouverts + fermés récemment, avec recherche — voir
   renderTabOverviewMenu() dans app.js. */
.tab-overview-wrap { position: relative; flex-shrink: 0; }
.tab-overview-menu {
  position: absolute;
  top: calc(100% + var(--sp-2));
  left: 0;
  z-index: 30;
  width: 18rem;
  max-height: min(24rem, calc(var(--app-height, 100dvh) - 6rem));
  display: flex;
  flex-direction: column;
  background: var(--bg-raised);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  overflow: hidden;
}
/* Fixe en tête du menu : la recherche reste joignable même en ayant défilé
   dans une longue liste d'onglets. */
.tab-overview-search {
  flex-shrink: 0;
  margin: var(--sp-2);
  width: auto;
}
.tab-overview-list { flex: 1; min-height: 0; overflow-y: auto; padding: 0 var(--sp-2) var(--sp-2); display: flex; flex-direction: column; gap: 1px; }
.tab-overview-empty { padding: var(--sp-2); font-size: var(--text-sm); color: var(--text-faint); }
.tab-overview-label {
  padding: var(--sp-2) var(--sp-2) var(--sp-1);
  font-size: var(--text-2xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-faint);
}
.tab-overview-item {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  width: 100%;
  min-height: 2.2rem;
  padding: 0 var(--sp-2);
  background: none;
  border: none;
  box-shadow: none;
  border-radius: var(--radius-sm);
  color: var(--text);
  font-size: var(--text-sm);
  font-weight: 500;
  text-align: left;
}
.tab-overview-item:hover { background: var(--sidebar-bg); box-shadow: none; transform: none; }
.tab-overview-item-icon { display: flex; flex-shrink: 0; opacity: 0.85; }
.tab-overview-item-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Onglet fermé récemment : mêmes rangs que les ouverts, mais atténué — la
   couleur seule dit "à rouvrir" sans avoir besoin d'une icône dédiée. */
.tab-overview-item.is-closed { color: var(--text-faint); font-style: italic; }
.tab-overview-item.is-closed:hover { color: var(--text-dim); }
.tab-overview-item.is-closed .tab-overview-item-icon { opacity: 0.5; }

/* ---- Menu contextuel d'un onglet -------------------------------------
   Position: fixed, coordonnées posées en JS au clic (openTabContextMenu) :
   contrairement aux autres menus de l'en-tête, son origine change avec
   l'onglet visé, pas avec un bouton toujours au même endroit. */
.tab-context-menu {
  position: fixed;
  z-index: 40;
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 15rem;
  padding: var(--sp-2);
  background: var(--bg-raised);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
}
/* Ancre du menu de la conversation, qui se déploie sous son bouton. */
.chat-header-actions {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

.chat-menu {
  position: absolute;
  top: calc(100% + var(--sp-2));
  right: 0;
  z-index: 30;
  min-width: 15rem;
  padding: var(--sp-2);
  background: var(--bg-raised);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
}
/* Groupe d'options propre à une vue (conversation vs explorateur de fichiers)
   — un seul visible à la fois, voir switchChatMenuGroup() dans app.js. Porte
   maintenant le flex-column que .chat-menu appliquait directement à ses
   items. */
.chat-menu-group { display: flex; flex-direction: column; gap: 1px; }
.chat-menu-item {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  justify-content: flex-start;
  width: 100%;
  min-height: 2.2rem;
  padding: 0 var(--sp-2);
  background: none;
  border: none;
  box-shadow: none;
  color: var(--text);
  font-size: var(--text-sm);
  font-weight: 500;
  text-align: left;
}
.chat-menu-item:hover { background: var(--sidebar-bg); box-shadow: none; transform: none; }
.chat-menu-sep { height: 1px; margin: var(--sp-1) 0; background: var(--border); }
.chat-menu-label {
  padding: var(--sp-1) var(--sp-2) 0;
  font-size: var(--text-2xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-faint);
}
/* Le switch devient une ligne de menu comme les autres : même hauteur, même
   retrait, libellé à gauche et interrupteur poussé à droite. */
.chat-menu .auto-approve-toggle {
  flex-direction: row-reverse;
  justify-content: space-between;
  min-height: 2.2rem;
  padding: 0 var(--sp-2);
  border-radius: var(--radius-sm);
}
.chat-menu .auto-approve-toggle:hover { background: var(--sidebar-bg); }
.chat-menu .auto-approve-text { font-size: var(--text-sm); font-weight: 500; text-transform: none; letter-spacing: 0; color: var(--text); }
.chat-menu .auto-approve-toggle input:checked ~ .auto-approve-text { color: var(--accent); }

/* Interrupteur « auto-approuver ». Remplace la longue case à cocher qui
   écrasait la hiérarchie de l'en-tête : la case native reste dans le DOM
   (le JS lit .checked) mais est masquée au profit d'un switch dessiné. */
.auto-approve-toggle {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--text-2xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--text-faint);
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
  transition: color 0.12s ease;
}
.auto-approve-toggle:hover { color: var(--text-dim); }
.auto-approve-toggle input[type="checkbox"] {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
  pointer-events: none;
}
.switch {
  width: 30px;
  height: 18px;
  border-radius: 999px;
  background: var(--border-strong);
  position: relative;
  flex-shrink: 0;
  transition: background-color 0.16s ease;
}
.switch::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: #fff;
  box-shadow: var(--shadow-sm);
  transition: transform 0.16s ease;
}
.auto-approve-toggle input:checked ~ .switch { background: var(--accent); }
.auto-approve-toggle input:checked ~ .switch::after { transform: translateX(12px); }
.auto-approve-toggle input:checked ~ .auto-approve-text { color: var(--accent); }
.auto-approve-toggle input:focus-visible ~ .switch {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Bandeau d'options de la conversation : entre l'en-tête (onglets) et le fil.
   Volontairement hors du fil qui défile (flex-shrink: 0, comme .chat-header)
   — les réglages restent joignables quelle que soit la position de lecture,
   et rien ici ne fait grandir la page au-delà de la fenêtre. */
.chat-toolbar {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-5);
  border-bottom: 1px solid var(--border);
  background: var(--bg-alt);
  overflow-x: auto;
  scrollbar-width: none;
}
.chat-toolbar::-webkit-scrollbar { display: none; }
.toolbar-spacer { flex: 1; min-width: var(--sp-2); }

/* Réglages sous forme de bouton-pigeonnier (bascule visible par sa propre
   couleur, pas par un interrupteur séparé) — même langage visuel que l'onglet
   actif de la barre d'onglets (.tab.active), pour que "actif" se lise pareil
   partout dans l'en-tête. La case à cocher reste dans le DOM (le JS lit
   .checked) mais s'efface au profit du style du label. */
.toolbar-toggle {
  display: flex;
  align-items: center;
  flex-shrink: 0;
  gap: var(--sp-2);
  min-height: 2.1rem;
  padding: 0 var(--sp-3);
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  color: var(--text-dim);
  font-size: var(--text-sm);
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  transition: background-color 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.toolbar-toggle input[type="checkbox"] {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
  pointer-events: none;
}
.toolbar-toggle:hover { background: var(--sidebar-bg); color: var(--text); }
.toolbar-toggle:has(input:checked) {
  background: var(--accent-soft);
  border-color: color-mix(in srgb, var(--accent) 35%, transparent);
  color: var(--accent);
}
.toolbar-toggle:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Auto-approuver exécute les commandes sans validation : une fois actif, ce
   réglage doit se voir comme un risque, pas comme une simple préférence — on
   l'écarte donc du bleu générique des autres bascules de la barre. */
#auto-approve-label:has(input:checked) {
  background: var(--risk-high-soft);
  border-color: color-mix(in srgb, var(--risk-high) 35%, transparent);
  color: var(--risk-high);
}

/* Double bouton [Session | +] : deux actions liées (reprendre / repartir à
   neuf) sous un seul repère visuel plutôt que deux boutons épars. */
.toolbar-split {
  display: flex;
  flex-shrink: 0;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--bg-alt);
}
.toolbar-split-btn {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 2.1rem;
  min-width: 0;
  padding: 0 var(--sp-3);
  background: none;
  border: none;
  border-radius: 0;
  box-shadow: none;
  color: var(--text-dim);
  font-size: var(--text-sm);
  font-weight: 600;
  white-space: nowrap;
}
.toolbar-split-btn:hover { background: var(--sidebar-bg); color: var(--text); box-shadow: none; transform: none; }
.toolbar-split-add {
  padding: 0 var(--sp-3);
  border-left: 1px solid var(--border-strong);
  color: #4ade80;
}

.chat-messages-wrap {
  position: relative;
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
/* Fondu entre les messages et la barre de saisie : les derniers messages
   s'estompent sous la zone de saisie au lieu d'être coupés net par un trait.
   Volontairement très transparent — la conversation doit rester lisible
   derrière. Le pseudo-élément vit sur le wrapper (pas sur .chat-messages, qui
   scrolle) pour rester fixe pendant le défilement. */
.chat-messages-wrap::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 7rem;
  background: linear-gradient(
    to bottom,
    transparent,
    color-mix(in srgb, var(--bg) 12%, transparent) 55%,
    color-mix(in srgb, var(--bg) 30%, transparent) 80%,
    color-mix(in srgb, var(--bg) 55%, transparent)
  );
  pointer-events: none;
  z-index: 1;
}

.scroll-bottom-btn {
  position: absolute;
  left: 50%;
  bottom: var(--sp-4);
  transform: translateX(-50%);
  z-index: 2;
  width: 2.1rem;
  height: 2.1rem;
  min-width: 2.1rem;
  min-height: 2.1rem;
  border-radius: 50%;
  padding: 0;
  background: var(--bg-alt);
  color: var(--text-dim);
  border: 1px solid var(--border-strong);
  box-shadow: var(--shadow-md);
  animation: scroll-btn-in 0.16s ease;
  transition: background-color 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}
@keyframes scroll-btn-in {
  from { opacity: 0; transform: translateX(-50%) translateY(6px); }
  to { opacity: 1; transform: translateX(-50%) translateY(0); }
}
.scroll-bottom-btn:hover {
  background: var(--sidebar-bg);
  color: var(--text);
  border-color: var(--text-faint);
  box-shadow: var(--shadow-md);
  transform: translateX(-50%);
}
.scroll-bottom-btn:active { transform: translateX(-50%) scale(0.94); }
.scroll-bottom-btn.hidden { display: none; }

/* Frontière du « terminal » : tout ce qui est à l'intérieur de la conversation
   (messages, notes, propositions de commande) est en chasse fixe. Le reste de
   l'application garde la typo d'interface — voir --font-ui / --font-console. */
.chat-messages {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  /* Explicite plutôt que délégué à l'appariement overflow-x/y par défaut : un
     contenu qui déborderait horizontalement (mot très long sans espace,
     export d'un futur bloc mal contenu) doit être coupé, jamais rendre tout le
     fil défilable dans les deux sens — ça n'arrive qu'à cause d'un bug
     ailleurs, jamais par un besoin réel. */
  overflow-x: hidden;
  padding: var(--sp-6) var(--sp-5) var(--sp-8);
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);
  /* Valeurs par défaut des trois réglages de Paramètres > Apparence ; les
     blocs :root[data-chat-*] (voir « Thèmes de la fenêtre de conversation »)
     ne font que remplacer celles-ci. Le fond est repeint ici plutôt que laissé
     au <body> pour qu'un thème de fil couvre toute la zone de défilement. */
  --chat-font: var(--font-console);
  --chat-size: var(--text-sm);
  font-family: var(--chat-font);
  background: var(--bg);
  color: var(--text);
}
/* Colonne de lecture unique : messages, notes système et cartes de commande
   partagent le même axe, au lieu d'alterner gauche/droite. */
.chat-messages > * {
  width: 100%;
  max-width: var(--measure);
  margin-inline: auto;
}
/* Préférence de lecture (menu de la conversation) : la colonne resserrée
   reste le défaut, plus facile à suivre en chasse fixe, mais certains textes
   (tableaux, longues commandes) profitent de toute la largeur. */
.chat-messages.full-width > * { max-width: none; }
.chat-messages:empty {
  align-items: center;
  justify-content: center;
}
.chat-messages:empty::before {
  content: "Aucun message pour l'instant — écris quelque chose ci-dessous.";
  color: var(--text-faint);
  font-size: var(--text-md);
}
/* Chargement d'une conversation (ouverture d'un onglet, reprise d'une
   session) : le conteneur n'est alors plus vraiment ":empty" — il porte
   l'indicateur ci-dessous — d'où une classe dédiée plutôt que de compter sur
   le sélecteur :empty pour le centrage. */
.chat-messages.is-loading { align-items: center; justify-content: center; }
.chat-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-3);
}
.chat-loading-spinner {
  width: 28px;
  height: 28px;
  color: var(--accent);
  transform-box: border-box;
  transform-origin: 50% 50%;
  animation: thinking-spin 0.9s linear infinite;
}
.chat-loading-text {
  font-size: var(--text-sm);
  color: var(--accent);
  animation: thinking-glow 1.6s ease-in-out infinite;
}

/* Un message = un bloc pleine colonne (ligne de méta + corps), pas une bulle
   alternée. C'est ce qui distingue un outil de travail d'une messagerie. */
.msg { display: flex; flex-direction: column; gap: var(--sp-2); animation: msg-in 0.18s ease; }
@keyframes msg-in {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: translateY(0); }
}
.msg-meta { display: flex; align-items: center; gap: var(--sp-2); }
.msg-author {
  font-size: var(--text-2xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--text-dim);
}
.msg.agent .msg-author { color: var(--accent); }
.msg-time {
  font-size: var(--text-2xs);
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}
.msg-body {
  /* Un cran sous l'ancienne taille : à corps égal une chasse fixe paraît plus
     grosse et plus dense qu'une proportionnelle. C'est la valeur par défaut de
     --chat-size, que Paramètres > Apparence peut déplacer sur l'échelle. */
  font-size: var(--chat-size, var(--text-sm));
  line-height: 1.7;
  word-wrap: break-word;
  color: var(--text);
}
/* Le message d'agent est du HTML issu du Markdown : ses sauts de ligne sont
   déjà des balises, pre-wrap ajouterait des blancs en double. Le message
   utilisateur reste du texte brut et doit conserver sa mise en forme. */
.msg.user .msg-body { white-space: pre-wrap; }
/* L'agent parle « à nu » (pas de cadre) ; l'utilisateur est encadré. La
   distinction passe par la méta et le fond, plus par le côté de l'écran. */
.msg.user .msg-body {
  background: var(--bubble-user-bg);
  color: var(--bubble-user-text);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--sp-3) var(--sp-4);
  box-shadow: var(--shadow-sm);
}
.msg.agent .msg-body { background: var(--bubble-agent-bg); }

/* ---- Conversation entre agents ----
   Ni « Toi » ni l'agent de la conversation : deux tiers qui se parlent. Le bloc
   sort donc de la grammaire des messages — encadré, en retrait, replié sur son
   en-tête — pour être consultable sans couper la lecture du fil.

   Masqué par défaut : c'est la classe `show-exchanges` du conteneur qui le
   révèle. Les blocs sont insérés dans tous les cas, ce qui permet à la case du
   menu de faire apparaître aussi ce qui a déjà eu lieu.

   Sans marge propre : `.chat-messages > *` centre déjà chaque bloc sur la même
   colonne que les messages (souvent à largeur pleine, marges nulles — voir
   plus haut). Une marge ici s'ajouterait par-dessus au lieu de la remplacer,
   poussant le bloc au-delà du bord droit de la colonne (et rendant tout le fil
   défilable horizontalement rien que pour ce débord). Le « retrait » reste
   lisible par la bordure et le fond teintés, pas par un décalage. */
.msg.agent-exchange { display: none; }
.chat-messages.show-exchanges .msg.agent-exchange {
  display: block;
  border: 1px solid var(--exchange-border);
  border-left: 3px solid var(--exchange);
  border-radius: var(--radius);
  background: var(--exchange-soft);
  padding: var(--sp-2) var(--sp-3);
}
.exchange-head {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
  cursor: pointer;
  list-style: none;
  font-size: var(--text-2xs);
}
.exchange-head::-webkit-details-marker { display: none; }
/* Chevron maison : le marqueur natif ne se colore pas de façon fiable. */
.exchange-head::before {
  content: "▸";
  color: var(--exchange);
  transition: transform 0.15s ease;
}
.msg.agent-exchange[open] .exchange-head::before { transform: rotate(90deg); }
.exchange-route {
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--exchange);
  /* Deux noms d'agent : sans ça un nom long empêche la ligne de rétrécir
     (min-width auto par défaut sur un enfant flex) et déborde au lieu de
     casser proprement. */
  min-width: 0;
  overflow-wrap: anywhere;
}
.exchange-note { color: var(--text-faint); }
.msg.agent-exchange.failed .exchange-route { color: var(--risk-high); }
.msg.agent-exchange.failed { border-left-color: var(--risk-high); }
.exchange-head .msg-time { margin-left: auto; }
.exchange-body {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  margin-top: var(--sp-3);
  padding-top: var(--sp-3);
  border-top: 1px dashed var(--exchange-border);
}
.exchange-label {
  display: block;
  font-size: var(--text-2xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--text-dim);
  margin-bottom: var(--sp-1);
}
.exchange-text { font-size: var(--text-sm); line-height: 1.65; color: var(--text); overflow-wrap: anywhere; }
/* La demande est du texte brut (le message tel qu'il a été transmis) ; la
   réponse est du Markdown déjà converti en balises. */
.exchange-request .exchange-text { white-space: pre-wrap; color: var(--text-dim); }

/* ---- Commande exécutée sans validation humaine ----
   Même famille que la conversation entre agents juste au-dessus : bloc en
   retrait, replié par défaut, montré par le même réglage « Afficher tous ».
   Couleur ambre (celle du risque moyen) plutôt que le violet des échanges :
   ce n'est pas un tiers qui parle, c'est une commande qui a tourné sans
   qu'on ait eu à cliquer — mérite un signal, pas la couleur d'un dialogue. */
.msg.command-record { display: none; }
.chat-messages.show-exchanges .msg.command-record {
  display: block;
  border: 1px solid color-mix(in srgb, var(--risk-mid) 35%, transparent);
  border-left: 3px solid var(--risk-mid);
  border-radius: var(--radius);
  background: var(--risk-mid-soft);
  padding: var(--sp-2) var(--sp-3);
}
.command-record-title {
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--risk-mid);
  min-width: 0;
}
.command-record-output {
  margin: 0;
  padding: var(--sp-2) var(--sp-3);
  background: var(--sidebar-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  overflow-x: auto;
  color: var(--text-dim);
}
.command-record-exit { font-size: var(--text-2xs); color: var(--text-faint); }
/* Chevron et couleur du titre assortis à l'ambre du bloc plutôt qu'au violet
   des échanges, que .exchange-head porte par défaut. */
.msg.command-record .exchange-head::before { color: var(--risk-mid); }
.msg.command-record[open] .exchange-head::before { transform: rotate(90deg); }

.proposal-card {
  border-radius: var(--radius);
  border: 1px solid var(--border-strong);
  background: var(--bg-alt);
  padding: var(--sp-4);
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  box-shadow: var(--shadow-sm);
}
/* Non scopées à .proposal-card : réutilisées telles quelles par le bloc
   « commande approuvée automatiquement » (voir .command-record plus bas) —
   même commande, même badge de risque, qu'elle ait attendu un clic ou non. */
.command {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  background: var(--sidebar-bg);
  border: 1px solid var(--border);
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--radius-sm);
  overflow-x: auto;
}
.risk {
  font-weight: 600;
  font-size: var(--text-2xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  width: fit-content;
  padding: var(--sp-1) var(--sp-2);
  border-radius: 999px;
}
.risk.faible { color: var(--risk-low); background: var(--risk-low-soft); }
.risk.moyen { color: var(--risk-mid); background: var(--risk-mid-soft); }
.risk.élevé { color: var(--risk-high); background: var(--risk-high-soft); }
.proposal-actions { display: flex; gap: var(--sp-2); }
.proposal-actions button.deny {
  background: var(--risk-high);
  color: #fff;
  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15), 0 2px 5px var(--risk-high-soft);
}
.proposal-actions button.deny:hover { filter: brightness(1.08); background: var(--risk-high); }
.proposal-card.resolved { opacity: 0.5; }

/* ---- Formulaire demandé par un agent (outil ask_form) ----
   Même famille visuelle que la carte de validation juste au-dessus : c'est le
   second cas où le fil attend un geste de l'utilisateur, et non l'inverse.
   Teinté à l'accent plutôt qu'au risque : remplir un formulaire n'engage
   rien sur la machine. */
.form-card {
  border-radius: var(--radius);
  border: 1px solid var(--border-strong);
  border-left: 3px solid var(--accent);
  background: var(--bg-alt);
  padding: var(--sp-4);
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  box-shadow: var(--shadow-sm);
}
.form-card-head {
  font-weight: 700;
  font-size: var(--text-2xs);
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--accent);
}
.form-card-desc { margin: 0; font-size: var(--text-sm); line-height: 1.6; color: var(--text-dim); }
.form-field { display: flex; flex-direction: column; gap: var(--sp-1); }
.form-field-label { font-size: var(--text-xs); font-weight: 600; color: var(--text-dim); }
.form-required { color: var(--risk-high); margin-left: 2px; }
.form-field-help { font-size: var(--text-2xs); color: var(--text-faint); }
/* Une seule géométrie pour tous les contrôles — même hauteur, même largeur,
   même rembourrage — quel que soit leur type : dans une carte du fil, des
   champs qui ne s'alignent pas se lisent comme des éléments sans rapport.
   La hauteur est fixée (et non seulement `min-height`) parce que les champs
   date et les listes déroulantes se dimensionnent autrement d'eux-mêmes. */
.form-field input[type="text"],
.form-field input[type="date"],
.form-field textarea,
.form-field select {
  box-sizing: border-box;
  width: 100%;
  height: 2.25rem;
  min-height: 2.25rem;
  font: inherit;
  font-size: var(--text-sm);
  line-height: 1.4;
  color: var(--text);
  /* `background-color` et non le raccourci `background` : celui-ci
     réinitialiserait la répétition et la position de l'image de fond, et le
     chevron de la liste déroulante se retrouverait carrelé sur tout le champ. */
  background-color: var(--bg);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  padding: 0 var(--sp-3);
}
/* La bordure ci-dessus est posée plus tard dans la feuille que la règle de
   focus globale, à spécificité égale : sans ce rappel, le champ actif perdrait
   son liseré d'accent et ne garderait que le halo. */
.form-field input:focus,
.form-field textarea:focus,
.form-field select:focus { border-color: var(--accent); }
/* Le champ date d'iOS/Safari ignore la hauteur tant qu'il garde son apparence
   native, et se centre mal verticalement. */
.form-field input[type="date"] { appearance: none; -webkit-appearance: none; }
/* Le chevron dessiné par la règle `select` globale occupe cette réserve. */
.form-field select { padding-right: 2.25rem; }
.form-field textarea {
  height: auto;
  min-height: 5rem;
  resize: vertical;
  padding: var(--sp-2) var(--sp-3);
}
/* Case à cocher dessinée en interrupteur : contrôle et libellé sur une même
   ligne, l'aide dessous. La case native reste dans le DOM (c'est elle que lit
   le JS) mais devient invisible, le rendu passe par le pion `.switch`. */
.form-field.type-checkbox {
  flex-direction: row;
  align-items: center;
  gap: var(--sp-2);
  flex-wrap: wrap;
  cursor: pointer;
  min-height: 2.25rem;
}
.form-field.type-checkbox input[type="checkbox"] {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
  pointer-events: none;
}
.form-field.type-checkbox .form-field-label { font-size: var(--text-sm); color: var(--text); }
.form-field.type-checkbox input:checked ~ .switch { background: var(--accent); }
.form-field.type-checkbox input:checked ~ .switch::after { transform: translateX(12px); }
.form-field.type-checkbox input:focus-visible ~ .switch {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.form-field.type-checkbox input:disabled ~ .switch { opacity: 0.55; }
.form-field.type-checkbox .form-field-help { flex-basis: 100%; }
.form-actions { display: flex; gap: var(--sp-2); }
.form-actions button.cancel { background: transparent; color: var(--text-dim); border: 1px solid var(--border-strong); box-shadow: none; }
.form-actions button.cancel:hover { background: var(--sidebar-bg); }
.form-error { font-size: var(--text-xs); color: var(--risk-high); }
.form-note { font-size: var(--text-2xs); color: var(--text-faint); }
.form-card.resolved { opacity: 0.65; border-left-color: var(--border-strong); }
/* Répondu ou expiré : plus rien n'est cliquable, l'interrupteur ne doit plus
   se présenter comme tel. */
.form-card.resolved .form-field.type-checkbox { cursor: default; }

/* Formulaire déjà répondu, rejoué depuis l'historique. Contrairement aux
   échanges entre agents et aux commandes auto-approuvées, il reste visible
   sans réglage : c'est l'utilisateur lui-même qui a saisi ces valeurs, elles
   font partie de la conversation au même titre qu'un message. */
.msg.form-record {
  display: block;
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius);
  background: var(--bg-alt);
  padding: var(--sp-2) var(--sp-3);
}
.form-record-title {
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--accent);
  min-width: 0;
  overflow-wrap: anywhere;
}
.msg.form-record .exchange-head::before { color: var(--accent); }
.msg.form-record[open] .exchange-head::before { transform: rotate(90deg); }
.form-record-values {
  margin: 0;
  display: grid;
  grid-template-columns: minmax(6rem, auto) 1fr;
  gap: var(--sp-1) var(--sp-3);
  font-size: var(--text-sm);
}
.form-record-values dt { color: var(--text-dim); font-size: var(--text-xs); align-self: center; }
.form-record-values dd { margin: 0; color: var(--text); overflow-wrap: anywhere; white-space: pre-wrap; }

.chat-input-bar {
  background: transparent;
  position: relative;
  z-index: 2;
  padding: var(--sp-2) var(--sp-5) var(--sp-3);
  padding-bottom: calc(var(--sp-3) + env(safe-area-inset-bottom));
  padding-left: calc(var(--sp-5) + env(safe-area-inset-left));
  padding-right: calc(var(--sp-5) + env(safe-area-inset-right));
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  flex-shrink: 0;
}
/* La barre suit la même colonne de lecture que les messages. */
.chat-input-bar > * {
  width: 100%;
  max-width: var(--measure);
  margin-inline: auto;
}
.chat-input-row {
  display: flex;
  align-items: flex-end;
  gap: var(--sp-1);
  background: var(--bg-alt);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  padding: var(--sp-1);
  box-shadow: var(--shadow-sm);
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
.chat-input-row:focus-within {
  border-color: var(--accent);
  box-shadow: var(--shadow-sm), 0 0 0 3px var(--accent-soft);
}
.chat-input-row textarea {
  flex: 1;
  resize: none;
  max-height: 8rem;
  /* On écrit dans le même « terminal » qu'on lit : même police, même corps
     que le corps des messages. */
  font-family: var(--font-console);
  font-size: var(--text-sm);
  background: transparent;
  border: none;
  box-shadow: none;
  border-radius: 0;
  padding: var(--sp-2) var(--sp-1);
}
.chat-input-row textarea:focus {
  outline: none;
  border: none;
  box-shadow: none;
}

/* Barre d'entrée façon « pilule » : bouton + rond à gauche, champ transparent
   au milieu, envoi rond à droite. */
.pill-icon-btn {
  width: 2.35rem;
  height: 2.35rem;
  min-width: 2.35rem;
  min-height: 2.35rem;
  border-radius: 50%;
  padding: 0;
  flex-shrink: 0;
}
#attach-btn.pill-icon-btn { background: transparent; color: var(--text-dim); box-shadow: none; }
#attach-btn.pill-icon-btn:hover { background: var(--sidebar-bg); color: var(--text); }
#send-btn.pill-icon-btn { background: var(--accent); color: var(--accent-contrast); border-color: transparent; }
#send-btn.pill-icon-btn:disabled { background: var(--border-strong); color: var(--text-faint); box-shadow: none; }
#stop-btn.pill-icon-btn { background: var(--accent); color: var(--accent-contrast); border-color: transparent; }
#stop-btn.pill-icon-btn:hover { filter: brightness(0.92); }

.attachment-list { display: flex; flex-wrap: wrap; gap: var(--sp-1); }
.attachment-chip {
  font-size: var(--text-2xs);
  background: var(--bg-alt);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  padding: var(--sp-1) var(--sp-2);
  display: flex;
  align-items: center;
  gap: var(--sp-1);
}
.attachment-chip button {
  min-height: auto;
  min-width: auto;
  padding: 0;
  background: none;
  box-shadow: none;
  color: var(--text-dim);
  font-size: var(--text-sm);
}
.attachment-chip button:hover { background: none; box-shadow: none; transform: none; color: var(--risk-high); }
.attachment-chip button:active { transform: scale(0.9); }

.modal-backdrop {
  position: fixed; inset: 0;
  background: rgba(4, 8, 8, 0.6);
  display: flex; align-items: center; justify-content: center;
  z-index: 10;
  padding: var(--sp-4);
  padding-top: calc(var(--sp-4) + env(safe-area-inset-top));
  padding-bottom: calc(var(--sp-4) + env(safe-area-inset-bottom));
  overflow-y: auto;
}
.modal {
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--sp-6);
  width: min(380px, 90vw);
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  box-shadow: var(--shadow-md);
}
.modal h2 {
  margin: 0 0 var(--sp-1);
  font-size: var(--text-xl);
  font-weight: 700;
  letter-spacing: -0.015em;
}
.modal label { display: flex; flex-direction: column; gap: var(--sp-1); font-size: var(--text-sm); color: var(--text-dim); }
.modal label.checkbox-label { flex-direction: row; align-items: center; gap: var(--sp-2); color: var(--text); }
.modal-actions { display: flex; justify-content: flex-end; gap: var(--sp-2); }
.modal-actions-split { justify-content: space-between; align-items: center; }
.modal-wide { width: min(580px, 90vw); max-height: 90vh; overflow-y: auto; }
.modal textarea.mono { font-family: var(--font-mono); font-size: var(--text-sm); resize: vertical; }

.thinking {
  color: var(--text-faint);
  font-size: var(--text-sm);
}

/* Indicateur « réponse en cours ». Deux signaux complémentaires : une icône
   qui tourne (mouvement franc, visible du coin de l'œil) et un halo qui
   balaie le texte (montre que l'attente est vivante, pas figée). */
.thinking-indicator {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

.thinking-spinner {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  color: var(--accent);
  /* transform-box/origin explicites : sur un élément SVG, WebKit peut sinon
     prendre le système de coordonnées SVG comme référence et faire tourner
     l'icône autour d'un coin au lieu de son centre. */
  transform-box: border-box;
  transform-origin: 50% 50%;
  animation: thinking-spin 0.9s linear infinite;
}
@keyframes thinking-spin { to { transform: rotate(360deg); } }

/* Halo pulsant : opacité + text-shadow, et surtout PAS background-clip: text.
   La version précédente peignait un dégradé animé à l'intérieur des lettres —
   élégant, mais WebKit (donc Safari/iOS) ne réinvalide pas le rendu quand
   seul background-position change : le texte restait figé sur iPhone alors
   que Chromium l'animait correctement. opacity et text-shadow sont repeints
   de façon fiable par tous les moteurs. */
.thinking-text {
  color: var(--accent);
  animation: thinking-glow 1.6s ease-in-out infinite;
}
@keyframes thinking-glow {
  0%, 100% {
    opacity: 0.5;
    text-shadow: 0 0 4px color-mix(in srgb, var(--accent) 20%, transparent);
  }
  50% {
    opacity: 1;
    text-shadow: 0 0 10px color-mix(in srgb, var(--accent) 55%, transparent);
  }
}

/* Exception assumée à la règle globale « réduire les animations ».
   Cet indicateur est le SEUL signal qu'un tour est en cours : le figer ne
   supprime pas une décoration, ça supprime l'information. Le réglage système
   vise les mouvements amples (parallaxe, glissements plein écran), pas les
   témoins d'activité — on ralentit donc au lieu de couper. */
@media (prefers-reduced-motion: reduce) {
  .thinking-spinner { animation: thinking-spin 2.4s linear infinite !important; }
  .thinking-text { animation: thinking-glow 3.2s ease-in-out infinite !important; }
  .chat-loading-spinner { animation: thinking-spin 2.4s linear infinite !important; }
  .chat-loading-text { animation: thinking-glow 3.2s ease-in-out infinite !important; }
}

/* Navigateur de fichiers data/ — remplace chat-messages/chat-input-bar dans
   .chat (pas une modale), prend donc toute la hauteur/largeur disponible. */
.data-view {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: var(--sp-5);
  padding-bottom: calc(var(--sp-5) + env(safe-area-inset-bottom));
  padding-left: calc(var(--sp-5) + env(safe-area-inset-left));
  padding-right: calc(var(--sp-5) + env(safe-area-inset-right));
}
.data-view-header {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  flex-wrap: wrap;
  padding-bottom: var(--sp-3);
  border-bottom: 1px solid var(--border);
}

.data-breadcrumb {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 1px;
  font-size: var(--text-sm);
}
.data-crumb {
  background: none;
  border: none;
  box-shadow: none;
  min-height: auto;
  min-width: auto;
  padding: var(--sp-1) var(--sp-1);
  color: var(--text-dim);
  font-weight: 500;
  border-radius: var(--radius-sm);
}
.data-crumb:last-of-type { color: var(--text); font-weight: 600; }
.data-crumb:hover { background: var(--sidebar-bg); box-shadow: none; transform: none; }
.data-crumb-sep { color: var(--text-faint); }

/* Sélecteur de dossier de destination (Copier/déplacer) : liste de dossiers
   cliquables, même hauteur/interligne que les autres listes de l'app. */
.data-move-list {
  display: flex;
  flex-direction: column;
  gap: 1px;
  max-height: 16rem;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--sp-1);
}
.data-move-dir-row {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  width: 100%;
  min-height: 2.4rem;
  padding: 0 var(--sp-2);
  background: none;
  border: none;
  box-shadow: none;
  color: var(--text);
  font-size: var(--text-sm);
  text-align: left;
}
.data-move-dir-row:hover { background: var(--bg-alt); box-shadow: none; transform: none; }

/* Barre de tri réservée au mobile : sur grand écran, ce sont les en-têtes de
   colonnes qui trient (mêmes data-sort-key, même code). */
.data-sort-bar { display: none; }

.data-table-wrap {
  flex: 1;
  min-height: 0;
  overflow: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-alt);
}
.data-table { width: 100%; border-collapse: collapse; table-layout: fixed; font-size: var(--text-md); }
.data-table thead th {
  position: sticky;
  top: 0;
  background: var(--sidebar-bg);
  color: var(--text-faint);
  text-align: left;
  font-weight: 600;
  font-size: var(--text-2xs);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  padding: var(--sp-2) var(--sp-3);
  border-bottom: 1px solid var(--border);
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}
.data-table thead th:hover { color: var(--text); }
.data-table th[data-sort-key="name"] { width: 38%; }
.data-table th[data-sort-key="size"] { width: 12%; }
.data-table th[data-sort-key="createdAt"] { width: 18%; }
.data-table th[data-sort-key="modifiedAt"] { width: 18%; }
.data-table th.data-table-actions-col { width: 14%; }
.data-table thead th.data-table-actions-col { cursor: default; }
.data-table thead th.data-table-actions-col:hover { color: var(--text-faint); }
.data-table thead th.sorted-asc::after { content: " ▲"; color: var(--accent); }
.data-table thead th.sorted-desc::after { content: " ▼"; color: var(--accent); }
.data-row { cursor: pointer; transition: background-color 0.1s ease; }
.data-row:hover { background: var(--sidebar-bg); }
.data-row td { padding: var(--sp-2) var(--sp-3); border-bottom: 1px solid var(--border); vertical-align: middle; }
.data-row:last-child td { border-bottom: none; }
.data-row-name { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; }
.data-row-icon { flex-shrink: 0; }
.data-row-name-text {
  min-width: 0;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.data-row-size, .data-row-created, .data-row-modified {
  color: var(--text-faint);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  font-size: var(--text-sm);
}
.data-row-actions { white-space: nowrap; text-align: right; }
.data-row-actions button {
  background: none;
  box-shadow: none;
  min-width: 2rem;
  min-height: 1.8rem;
  padding: 0 var(--sp-1);
  color: var(--text-faint);
}
.data-row-actions button:hover { background: var(--bg); box-shadow: none; transform: none; }
.data-row-delete:hover { color: var(--risk-high); background: var(--risk-high-soft) !important; }
.data-table-empty { text-align: center; color: var(--text-faint); padding: var(--sp-8) var(--sp-4) !important; cursor: default; }

/* Colonne de sélection : masquée hors mode « Sélectionner » (voir le switch
   du menu ⋮ sur l'explorateur), qui bascule .select-mode sur #data-table. Les
   actions par ligne (télécharger/supprimer) s'effacent en retour, pour ne pas
   mélanger action ponctuelle et action groupée pendant la sélection. */
.data-table-select-col, .data-row-select { width: 2.25rem; display: none; }
.data-table.select-mode .data-table-select-col,
.data-table.select-mode .data-row-select { display: table-cell; }
.data-table.select-mode .data-table-actions-col,
.data-table.select-mode .data-row-actions { display: none; }
.data-row-checkbox { width: 1.1rem; height: 1.1rem; cursor: pointer; }

/* ---- Onglet fichier : visionneuse et éditeur ----------------------------
   Même mécanique de panneau plein que .data-view. L'éditeur et l'aperçu
   occupent tout l'espace restant sous l'en-tête : éditer un document, c'est
   le voir en entier, pas dans une boîte. */
.file-view {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding: var(--sp-5);
  padding-bottom: calc(var(--sp-4) + env(safe-area-inset-bottom));
  padding-left: calc(var(--sp-5) + env(safe-area-inset-left));
  padding-right: calc(var(--sp-5) + env(safe-area-inset-right));
  gap: var(--sp-3);
}
/* Plein écran : recouvre tout, sidebar comprise — c'est le but, écrire sans
   rien autour. Un simple changement de position, rien d'autre : l'en-tête et
   le corps gardent la mise en page qu'ils ont déjà. */
.file-view.is-fullscreen {
  position: fixed;
  inset: 0;
  z-index: 40;
  background: var(--bg);
  padding: calc(var(--sp-4) + env(safe-area-inset-top)) calc(var(--sp-5) + env(safe-area-inset-right))
    calc(var(--sp-4) + env(safe-area-inset-bottom)) calc(var(--sp-5) + env(safe-area-inset-left));
}
/* Une seule ligne, aussi basse que possible : le nom du fichier vit dans son
   onglet, l'en-tête n'a plus à porter que les outils. */
.file-view-header {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 2.1rem;
  padding-bottom: var(--sp-2);
  border-bottom: 1px solid var(--border);
}
/* Les boutons flottants de la zone de lecture sont hors du flux : le corps du
   fichier doit donc être un repère de positionnement. */
.file-view-body { position: relative; }
.file-float-actions {
  position: absolute;
  top: var(--sp-2);
  right: var(--sp-2);
  z-index: 2;
  display: flex;
  gap: 2px;
  padding: 2px;
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-sm);
}
.file-float-btn {
  min-width: 1.9rem;
  min-height: 1.9rem;
  padding: 0;
  color: var(--text-dim);
  background: transparent;
  border: none;
  box-shadow: none;
}
.file-float-btn:hover { background: var(--accent-soft); color: var(--accent); box-shadow: none; transform: none; }
.file-float-btn.copied { color: var(--risk-low); }

.file-mode-btn { min-height: 2.1rem; padding: 0 var(--sp-4); }

/* Chemin complet, affiché uniquement en aperçu (voir applyFileMode) : en
   édition l'en-tête n'a que le texte à défendre. Comme la barre de balises,
   il prend l'espace disponible pour pousser l'état d'enregistrement à droite. */
.file-path {
  display: flex;
  align-items: center;
  flex: 1;
  min-width: 0;
  gap: var(--sp-1);
}
.file-path-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-dim);
}

.file-fullscreen-btn { flex-shrink: 0; }

/* L'enregistrement étant automatique, cet indicateur est la seule preuve que
   le travail est en sécurité : il reste visible en permanence. */
.file-save-state {
  flex-shrink: 0;
  font-size: var(--text-2xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-faint);
  white-space: nowrap;
}
.file-save-state.saved { color: var(--accent); }
.file-save-state.dirty { color: var(--risk-mid); }
.file-save-state.failed { color: var(--risk-high); }

.file-view-body { flex: 1; min-height: 0; display: flex; flex-direction: column; }

.file-preview {
  flex: 1;
  min-height: 0;
  overflow: auto;
  padding: var(--sp-5) var(--sp-6);
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-size: var(--text-lg);
  line-height: 1.65;
  color: var(--text);
}
/* Le premier et le dernier bloc ne doivent pas ajouter de blanc au cadre. */
.file-preview > :first-child { margin-top: 0; }
.file-preview > :last-child { margin-bottom: 0; }
/* markdown.js décale les titres de deux crans (« # » rend un h3) pour qu'un
   message n'écrase pas les titres de l'interface. Ici le fichier EST la page :
   on lui rend une échelle de document. */
.file-preview h3.md-h { font-size: var(--text-2xl); margin-top: 0.9em; }
.file-preview h4.md-h { font-size: var(--text-xl); }
.file-preview h5.md-h { font-size: var(--text-lg); }

/* ---- Éditeur markdown ----------------------------------------------------
   Modèle « live preview » : une seule représentation du texte, la source
   habillée par decorateMarkdown (markdown.js), éditable telle quelle. Il n'y a
   donc rien à basculer au clic, et rien qui se remette en page.

   Tout ce qui suit ne fait qu'habiller ce texte. Règle de la section : aucun
   cadre, aucun fond, aucun contour de focus. L'édition doit être invisible —
   le seul signe qu'on écrit quelque part, c'est le curseur, et les marqueurs
   qui reparaissent autour de lui. */
.file-live {
  flex: 1;
  min-height: 0;
  overflow: auto;
  padding: var(--sp-5) var(--sp-6);
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-size: var(--text-lg);
  line-height: 1.65;
  color: var(--text);
  overflow-wrap: break-word;
}

.live-block {
  /* La source contient de vrais retours à la ligne : ils doivent compter. */
  white-space: pre-wrap;
  outline: none;
  cursor: text;
}
.live-block:focus,
.live-block:focus-visible { outline: none; }

/* Typographie par nature de bloc. Elle est la même que le curseur soit dedans
   ou non : c'est ce qui fait qu'entrer dans un bloc ne déplace rien. */
.live-block[data-kind="heading"] { font-weight: 700; line-height: 1.3; margin: 1.4em 0 0.5em; }
.live-block[data-kind="heading"]:first-child { margin-top: 0; }
.live-block[data-kind="heading"][data-level="1"] { font-size: var(--text-2xl); }
.live-block[data-kind="heading"][data-level="2"] { font-size: var(--text-xl); }
.live-block[data-kind="heading"][data-level="3"] { font-size: var(--text-lg); }
.live-block[data-kind="heading"][data-level="4"] { font-size: var(--text-md); }
.live-block[data-kind="heading"][data-level="5"],
.live-block[data-kind="heading"][data-level="6"] { font-size: var(--text-md); color: var(--text-dim); }
.live-block[data-kind="paragraph"] { margin: 0.75em 0; }
.live-block[data-kind="paragraph"]:first-child { margin-top: 0; }
.live-block[data-kind="list"] { margin: 0.6em 0; }
.live-block[data-kind="quote"] {
  margin: 0.8em 0;
  padding-left: var(--sp-3);
  border-left: 2px solid var(--accent);
  color: var(--text-dim);
}
.live-block[data-kind="code"],
.live-block[data-kind="table"] {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  line-height: 1.55;
}
.live-block[data-kind="code"] {
  margin: 0.8em 0;
  padding: var(--sp-3);
  background: var(--sidebar-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.live-block[data-kind="hr"] { color: var(--text-faint); }

/* ---- Syntaxe ----
   Deux familles, décidées dans markdown.js : ce qui se masque tant que le
   curseur est ailleurs (md-syn-hide), et ce qui reste toujours lisible mais
   estompé (md-syn-keep). Masquer les puces ou les barres d'un tableau
   décalerait l'indentation à chaque clic — c'est précisément ce qu'on évite. */
.md-syn { color: var(--text-faint); font-weight: 400; font-style: normal; }
.md-syn-hide { display: none; }
/* Révélation : le fragment qui porte le curseur, et lui seul. */
.live-block.on > .md-syn-hide,
.live-block .md-i.on > .md-syn-hide { display: inline; }

/* ---- Listes ----
   Chaque ligne est une boîte : la puce vit dans une gouttière à gauche et le
   texte s'aligne au même retrait, y compris quand il déborde sur plusieurs
   lignes (retrait pendant). Le marqueur de la source (« - », « 2. ») est
   conservé dans le texte — il le faut, c'est lui qu'on enregistre — mais sorti
   du flux et rendu transparent. Il reprend sa couleur quand le curseur entre
   dans la ligne, pendant que la puce s'efface : les deux partagent la même
   gouttière, donc rien ne bouge. */
.md-li {
  --li-indent: 0em;
  --li-gutter: 1.5em;
  display: block;
  position: relative;
  margin: 0;
  padding-left: calc(var(--li-indent) + var(--li-gutter));
}
.md-li[data-level="1"] { --li-indent: 1.5em; }
.md-li[data-level="2"] { --li-indent: 3em; }
.md-li[data-level="3"] { --li-indent: 4.5em; }
.md-li[data-level="4"] { --li-indent: 6em; }
.md-li[data-level="5"] { --li-indent: 7.5em; }

.md-li::before {
  content: attr(data-marker);
  position: absolute;
  left: var(--li-indent);
  width: var(--li-gutter);
  color: var(--text-faint);
  /* Les chiffres d'une liste numérotée s'alignent sur le point. */
  text-align: left;
}
/* Hors du flux et invisible : sa présence ne déplace jamais le texte. */
.md-li-mark {
  position: absolute;
  left: var(--li-indent);
  color: transparent;
  white-space: pre;
  pointer-events: none;
}
.md-li.on > .md-li-mark { color: var(--text-faint); pointer-events: auto; }
.md-li.on::before { content: ""; }
/* Présents dans le texte, jamais à l'écran : le retrait est celui du CSS. */
.md-li-indent { display: none; }

/* Mise en forme du contenu. Le gras est réellement gras — c'est possible ici,
   n'ayant plus de calque à aligner sur une zone de saisie. */
.md-i-strong > .md-c { font-weight: 700; }
.md-i-em > .md-c { font-style: italic; }
.md-i-strong-em > .md-c { font-weight: 700; font-style: italic; }
.md-i-del > .md-c { text-decoration: line-through; }
.md-link { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }

/* Éditeur de texte brut. La coloration syntaxique est un calque : voir
   web/highlight.js pour l'invariant qui la rend possible (même texte, même
   métrique, seules les couleurs diffèrent d'un calque à l'autre). Sans
   langage reconnu, le calque reste masqué et la zone de saisie garde son
   apparence pleine — c'est le seul cas où elle est directement lisible. */
.file-editor-wrap { position: relative; flex: 1; min-height: 0; }

.file-editor-highlight,
.file-editor {
  position: absolute;
  inset: 0;
  margin: 0;
  width: 100%;
  height: 100%;
  padding: var(--sp-4) var(--sp-5);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-family: var(--font-mono);
  font-size: var(--text-md);
  line-height: 1.7;
  tab-size: 2;
  white-space: pre-wrap;
  word-wrap: break-word;
}

.file-editor-highlight {
  display: none;
  overflow: hidden;
  pointer-events: none;
  color: var(--code-fg, var(--text));
  background: var(--code-bg, var(--bg-alt));
}
.file-editor-wrap.has-highlight .file-editor-highlight { display: block; }
.file-editor-wrap.has-highlight:focus-within .file-editor-highlight { border-color: var(--accent); }

.file-editor {
  resize: none;
  overflow: auto;
  color: var(--text);
  background: var(--bg-alt);
}
.file-editor:focus { border-color: var(--accent); outline: none; }
.file-editor-wrap.has-highlight .file-editor {
  color: transparent;
  background: transparent;
  border-color: transparent;
  caret-color: var(--text);
}
.file-editor-wrap.has-highlight .file-editor:focus { border-color: transparent; }

.file-media {
  flex: 1;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: auto;
  background: var(--sidebar-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.file-media-image { max-width: 100%; max-height: 100%; object-fit: contain; }
.file-media-frame { width: 100%; height: 100%; border: none; background: #fff; }

/* ---- Barre d'outils markdown -------------------------------------------
   Visible uniquement en mode édition. Boutons compacts et de largeur libre :
   ce sont des balises, pas des actions à lire. */
/* Logée dans l'en-tête, à gauche. Elle défile horizontalement plutôt que de
   passer à la ligne : un en-tête dont la hauteur change selon la largeur de la
   fenêtre ferait sauter la zone de lecture. */
.md-toolbar {
  display: flex;
  align-items: center;
  flex: 1;
  min-width: 0;
  gap: var(--sp-1);
  overflow-x: auto;
  scrollbar-width: none;
}
.md-toolbar::-webkit-scrollbar { display: none; }
.md-toolbar > * { flex-shrink: 0; }
/* Sans la barre (fichier sans rendu), les actions restent calées à droite. */
.file-save-state { margin-left: auto; }
.md-toolbar:not(.hidden) ~ .file-save-state,
.file-path:not(.hidden) ~ .file-save-state { margin-left: 0; }
/* Sans cadre au repos : quatorze rectangles alignés faisaient une grille de
   boutons, là où il ne s'agit que d'un menu. Le contour n'apparaît qu'au
   survol, sur celui qu'on vise. */
.md-tool {
  min-width: 1.9rem;
  min-height: 1.9rem;
  padding: 0 var(--sp-2);
  background: transparent;
  border: 1px solid transparent;
  box-shadow: none;
  color: var(--text-dim);
  font-size: var(--text-sm);
  font-weight: 600;
}
.md-tool:hover { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); box-shadow: none; transform: none; }
.md-tool-bold { font-weight: 800; }
.md-tool-italic { font-style: italic; font-family: Georgia, "Times New Roman", serif; }
.md-tool-strike { text-decoration: line-through; }
.md-tool-code { font-family: var(--font-mono); font-weight: 500; }
/* Sépare les familles d'outils (titres / style / blocs). */
.md-toolbar-sep {
  width: 1px;
  align-self: stretch;
  margin: 2px var(--sp-1);
  background: var(--border-strong);
}

/* ---- Onglet Agent : la configuration en pleine page ---------------------
   Anciennement une modale. En onglet, elle peut respirer : le CLAUDE.md prend
   toute la hauteur restante au lieu d'une zone de texte de 14 lignes. */
.config-view {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: var(--sp-5);
  padding-bottom: calc(var(--sp-5) + env(safe-area-inset-bottom));
  padding-left: calc(var(--sp-5) + env(safe-area-inset-left));
  padding-right: calc(var(--sp-5) + env(safe-area-inset-right));
}
.config-form {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  max-width: 100rem;
  margin: 0 auto;
  min-height: 100%;
}
.config-title-row { display: flex; align-items: center; gap: var(--sp-2); }
.config-title {
  margin: 0;
  font-size: var(--text-xl);
  font-weight: 700;
  letter-spacing: -0.015em;
}
.config-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-4); }
.config-form label { display: flex; flex-direction: column; gap: var(--sp-2); font-size: var(--text-sm); color: var(--text-dim); }
.config-form .checkbox-label { flex-direction: row; align-items: center; gap: var(--sp-2); }
.config-field-block { display: flex; flex-direction: column; gap: var(--sp-2); margin-top: var(--sp-4); }
.config-field-label { font-size: var(--text-sm); color: var(--text-dim); }

/* Sélecteur d'avatar : aperçu rond + actions, réutilisé identique en création
   (create-view) et en configuration (config-view). */
.avatar-picker { display: flex; align-items: center; gap: var(--sp-3); }
.avatar-picker-preview {
  width: 3rem;
  height: 3rem;
  min-width: 3rem;
  border-radius: 50%;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--text-lg);
  font-weight: 700;
  color: #fff;
  background: var(--avatar-color, var(--accent));
}
.avatar-picker-preview img { width: 100%; height: 100%; object-fit: cover; }

/* ---- Mise en page à deux colonnes -----------------------------------------
   En dessous de 960px, tout s'empile (le CLAUDE.md en dernier). Au-delà, le
   CLAUDE.md profite de la largeur libérée par la sidebar au lieu de laisser
   un formulaire étroit flotter au milieu de l'écran, et reste visible sans
   défiler pendant qu'on ajuste les droits à côté. */
.config-layout { display: flex; flex-direction: column; gap: var(--sp-4); flex: 1; min-height: 0; }
.config-col-main { display: flex; flex-direction: column; gap: var(--sp-4); }
.config-col-prompt { display: flex; flex-direction: column; }
.config-card-prompt { flex: 1; min-height: 24rem; }
@media (min-width: 960px) {
  .config-layout { flex-direction: row; align-items: flex-start; }
  .config-col-main { flex: 0 0 min(28rem, 42%); }
  .config-col-prompt { flex: 1; min-width: 0; position: sticky; top: 0; align-self: stretch; }
  .config-card-prompt { height: 100%; }
}

/* ---- Droits de l'agent ----
   Les choix eux-mêmes empruntent .create-choice (cartes sélectionnables,
   réutilisées depuis l'assistant de création d'agent) plutôt qu'un style
   propre à cette page. */
.rights-field { display: flex; flex-direction: column; gap: var(--sp-2); }

/* ---- Tâches planifiées ---- */
.cron-intro { margin: 0 0 var(--sp-4); font-size: var(--text-sm); line-height: 1.6; max-width: var(--measure); }
.cron-card {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  padding: var(--sp-4);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-alt);
}
.cron-card-head { display: flex; align-items: baseline; gap: var(--sp-3); flex-wrap: wrap; }
.cron-card-label { font-weight: 650; color: var(--text); }
.cron-card-schedule {
  font-size: var(--text-2xs);
  padding: 2px var(--sp-2);
  border-radius: var(--radius-sm);
  background: var(--accent-soft);
  color: var(--accent);
}
.cron-card-command {
  font-size: var(--text-sm);
  color: var(--text-dim);
  word-break: break-all;
}
.cron-card-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  font-size: var(--text-sm);
}
.cron-card-actions { display: flex; gap: var(--sp-2); }
.rights-field-label {
  font-size: var(--text-2xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--text-dim);
}
/* Un droit verrouillé (agent système) se distingue au premier coup d'œil,
   sans qu'il faille lire l'infobulle du champ désactivé. */
.create-choice:has(input:disabled) { opacity: 0.6; cursor: not-allowed; }
.create-choice:has(input:disabled):hover { border-color: var(--border); }
.rights-help {
  display: block;
  font-size: var(--text-sm);
  line-height: 1.55;
  color: var(--text-dim);
  margin-top: 2px;
}
.builtin-notice {
  margin: 0;
  padding: var(--sp-3);
  border-radius: var(--radius-sm);
  background: var(--accent-soft);
  border: 1px solid var(--border);
  font-size: var(--text-sm);
  line-height: 1.55;
  color: var(--text-dim);
}
/* Le prompt système est le vrai contenu de cette page : il occupe la hauteur
   que le reste de sa carte lui laisse, sans jamais descendre sous 14 lignes. */
.config-claude-md {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  flex: 1;
  min-height: 0;
}
.config-claude-md textarea {
  flex: 1;
  min-height: 14rem;
  resize: vertical;
  line-height: 1.7;
}
/* Barre normale en fin de formulaire : en sticky, elle recouvrait les
   derniers champs sur mobile plutôt que de rester discrètement en pied. */
.config-actions {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--border);
}
/* Pousse le bouton d'enregistrement à droite, l'indicateur juste avant lui. */
.config-save-state {
  margin-left: auto;
  font-size: var(--text-2xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-faint);
}
.config-save-state.saved { color: var(--accent); }

/* ---- Actions d'un message (copier, marquer) -----------------------------
   Groupées à droite de la ligne de méta — donc en haut à droite du message,
   jamais dans le corps. Pleinement visibles par défaut : une icône qu'il faut
   deviner sous 0,45 d'opacité n'est pas un bouton, c'est un indice. */
.msg-actions { display: flex; align-items: center; gap: 2px; margin-left: auto; flex-shrink: 0; }
.msg-action-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.9rem;
  min-height: 1.9rem;
  padding: 0;
  background: none;
  border: none;
  box-shadow: none;
  color: var(--text-dim);
}
/* La taille réelle vient d'ici, pas de la classe portée par le SVG (icon-sm
   ailleurs dans l'appli) : les icônes de la conversation doivent rester
   nettement plus grandes que dans une barre d'outils secondaire. */
.msg-action-btn svg { width: 18px; height: 18px; }
.msg-action-btn:hover { background: var(--accent-soft); color: var(--accent); box-shadow: none; transform: none; }
.msg-action-btn:active { transform: scale(0.92); }
.msg-action-btn.copied { color: var(--risk-low); }
.msg-action-btn.speaking { color: var(--accent); }
.msg-flag.flagged { color: var(--risk-mid); }
.msg-flag.flagged svg { fill: var(--risk-mid); }
.msg-flag:hover { background: var(--risk-mid-soft); color: var(--risk-mid); }

/* ---- Page Historique — même mécanique de panneau plein que data-view ---- */
.history-view {
  position: relative;
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: var(--sp-5);
  padding-bottom: calc(var(--sp-5) + env(safe-area-inset-bottom));
  padding-left: calc(var(--sp-5) + env(safe-area-inset-left));
  padding-right: calc(var(--sp-5) + env(safe-area-inset-right));
  min-width: 0;
}

/* Barre de sélection/recherche : fixe, ne défile jamais avec .history-body
   (même esprit que .chat-toolbar). */
.history-toolbar {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-2);
  padding-bottom: var(--sp-3);
  border-bottom: 1px solid var(--border);
}
.history-session-select { flex: 1 1 16rem; min-width: 0; }
.history-search-input { flex: 1 1 14rem; min-width: 0; }

/* Repère bas/haut du fil, sur le modèle de .scroll-bottom-btn mais ancrés à
   droite (le fil de la page Historique n'est pas centré comme le chat) et
   empilés — top au-dessus de bottom. */
.history-scroll-btn {
  position: absolute;
  right: calc(var(--sp-5) + env(safe-area-inset-right));
  z-index: 2;
  width: 2.1rem;
  height: 2.1rem;
  min-width: 2.1rem;
  min-height: 2.1rem;
  border-radius: 50%;
  padding: 0;
  background: var(--bg-alt);
  color: var(--text-dim);
  border: 1px solid var(--border-strong);
  box-shadow: var(--shadow-md);
  animation: scroll-btn-in-y 0.16s ease;
  transition: background-color 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}
.history-scroll-btn.bottom { bottom: calc(var(--sp-5) + env(safe-area-inset-bottom)); }
.history-scroll-btn.top { bottom: calc(var(--sp-5) + env(safe-area-inset-bottom) + 2.6rem); }
@keyframes scroll-btn-in-y {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: translateY(0); }
}
.history-scroll-btn:hover { background: var(--sidebar-bg); color: var(--text); border-color: var(--text-faint); }
.history-scroll-btn:active { transform: scale(0.94); }
.history-scroll-btn.hidden { display: none; }
.history-body {
  flex: 1;
  min-height: 0;
  min-width: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);
}

/* Le sommaire précède l'historique : c'est le point d'entrée rapide. */
.history-summary {
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--bg-alt);
  padding: var(--sp-4);
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  min-width: 0;
}
.history-summary-title {
  font-size: var(--text-2xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-faint);
}
.history-summary-empty { margin: 0; font-size: var(--text-sm); }
.history-summary-list { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.history-summary-item {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: var(--sp-2);
  width: 100%;
  min-width: 0;
  min-height: auto;
  padding: var(--sp-2);
  background: none;
  border: none;
  box-shadow: none;
  border-radius: var(--radius-sm);
  color: var(--text);
  font-weight: 400;
  font-size: var(--text-sm);
  text-align: left;
}
.history-summary-item:hover { background: var(--sidebar-bg); box-shadow: none; transform: none; }
.history-summary-item svg { color: var(--risk-mid); fill: var(--risk-mid); flex-shrink: 0; }
.history-summary-text {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.history-summary-date {
  flex-shrink: 0;
  font-size: var(--text-2xs);
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}

.history-sessions { display: flex; flex-direction: column; gap: var(--sp-6); min-width: 0; }
.history-session { display: flex; flex-direction: column; gap: var(--sp-3); min-width: 0; }
.history-session-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
  padding-bottom: var(--sp-2);
  border-bottom: 1px solid var(--border);
  min-width: 0;
}
.history-session-title {
  font-weight: 600;
  font-size: var(--text-md);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.history-session-date {
  flex-shrink: 0;
  font-size: var(--text-2xs);
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}
.history-session-meta { display: flex; align-items: center; gap: var(--sp-2); flex-shrink: 0; }
.history-session-resume-btn { font-size: var(--text-2xs); padding: var(--sp-1) var(--sp-2); }

.history-entry {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  padding: var(--sp-2) var(--sp-3);
  border-left: 2px solid var(--border);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  min-width: 0;
}
.history-entry.agent { border-left-color: var(--accent); }
.history-entry.user { border-left-color: var(--border-strong); background: var(--bg-alt); }
.history-entry.delegation { border-left-color: var(--risk-mid); }
.history-entry.flagged { background: var(--risk-mid-soft); border-left-color: var(--risk-mid); }
.history-entry-meta {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--text-2xs);
  color: var(--text-faint);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.history-entry-meta > span:first-child { flex: 1; min-width: 0; }
/* Plus discret que dans le fil de chat : la page Historique en affiche
   beaucoup à la suite, la pastille ne doit pas dominer la ligne de méta. */
.history-entry-flag { min-width: 1.5rem; min-height: 1.5rem; flex-shrink: 0; }
.history-entry-flag svg { width: 14px; height: 14px; }
.history-entry-body {
  font-family: var(--font-console);
  font-size: var(--text-sm);
  line-height: 1.65;
  min-width: 0;
  overflow-wrap: anywhere;
}
.history-entry.user .history-entry-body { white-space: pre-wrap; }
.history-delegation-request {
  color: var(--text-dim);
  padding-bottom: var(--sp-2);
  border-bottom: 1px dashed var(--border);
}

/* Surlignage après un saut depuis le sommaire : sans repère visuel, on ne
   sait pas où le défilement a atterri. */
.history-hit { animation: history-hit 1.6s ease-out; }
@keyframes history-hit {
  0%, 40% { background: var(--accent-soft); }
  100% { background: transparent; }
}

/* Page d'accueil — même mécanique de vue plein panneau que data-view.
   Point d'entrée de l'app, et onglet comme les autres : un tableau de bord en
   widgets (état du parc, consommation, activité, machine…) plutôt qu'une
   conversation choisie arbitrairement. */
.overview-view {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  padding: var(--sp-6) var(--sp-5);
  padding-bottom: calc(var(--sp-6) + env(safe-area-inset-bottom));
  padding-left: calc(var(--sp-5) + env(safe-area-inset-left));
  padding-right: calc(var(--sp-5) + env(safe-area-inset-right));
  overflow-y: auto;
}
/* ---- En-tête et chiffres clés ---- */

.home-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-3);
  flex-wrap: wrap;
}
.home-head-text { display: flex; flex-direction: column; gap: 2px; }
.home-title { margin: 0; font-size: var(--text-2xl); font-weight: 700; letter-spacing: -0.02em; }
.home-subtitle { margin: 0; font-size: var(--text-sm); }
.home-head-actions { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.home-head-btn { flex-shrink: 0; }

/* Quatre nombres en ligne, hors grille : ce qu'on vient vérifier en arrivant
   se lit avant tout widget, sans avoir à choisir dans quelle carte regarder. */
.home-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(150px, 100%), 1fr));
  gap: var(--sp-3);
}
.home-stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  padding: var(--sp-3) var(--sp-4);
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.home-stat-label {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--text-2xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--text-faint);
}
.home-stat-value {
  font-size: 1.5rem;
  font-weight: 650;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
.home-stat-hint { font-size: var(--text-xs); color: var(--text-dim); }
/* Carte cliquable (coût du jour → onglet Consommation) : c'est un <button>,
   il ne doit rien garder du style des boutons d'action, seulement signaler
   qu'on peut appuyer dessus. */
.home-stat-btn {
  align-items: flex-start;
  text-align: left;
  color: inherit;
  box-shadow: none;
  cursor: pointer;
}
.home-stat-btn:hover {
  background: var(--bg-alt);
  border-color: var(--accent);
  box-shadow: none;
  transform: none;
}
/* « 2/3 » : les agents au repos comptent moins que ceux qui travaillent — la
   taille le dit avant la lecture. */
.home-stat-rest { font-size: 0.95rem; font-weight: 550; color: var(--text-faint); }

/* ---- Grille de widgets ---- */

.home-grid {
  display: grid;
  /* min(300px, 100%) et non 300px : sur un conteneur plus étroit, une piste
     minmax fixe déborde au lieu de se réduire. */
  grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr));
  gap: var(--sp-3);
  align-items: start;
}
/* Deux colonnes seulement là où il y en a au moins deux : sur une grille
   d'une seule piste, `span 2` créerait une colonne fantôme. */
@media (min-width: 900px) {
  .widget-wide { grid-column: span 2; }
}

.widget {
  display: flex;
  flex-direction: column;
  min-width: 0;
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}
.widget-head {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--border);
}
.widget-icon { display: flex; flex-shrink: 0; }
.widget-title {
  margin: 0;
  font-size: var(--text-md);
  font-weight: 650;
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Pousse ce qui suit (lien, compteur) à droite du titre. */
.widget-sub,
.widget-link { margin-left: auto; }
.widget-sub { font-size: var(--text-xs); color: var(--text-faint); white-space: nowrap; }
.widget-link {
  padding: 2px 0;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--accent);
  background: none;
  border: none;
  box-shadow: none;
  white-space: nowrap;
}
.widget-link:hover { color: var(--accent-hover); background: none; box-shadow: none; transform: none; }

/* Action d'un widget réduite à son icône (édition des favoris) : même place
   que .widget-link, mais une cible tactile carrée plutôt qu'un mot. */
.widget-icon-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  margin-left: auto;
  width: 32px;
  height: 32px;
  min-height: 0; /* sinon la hauteur de bouton par défaut l'emporte */
  padding: 0;
  color: var(--text-dim);
  background: none;
  border: none;
  border-radius: var(--radius-sm);
  box-shadow: none;
}
.widget-icon-btn:hover { color: var(--accent); background: var(--bg); box-shadow: none; transform: none; }

/* Pastille « démo » : dit franchement qu'un widget affiche encore des valeurs
   fictives, le temps de figer le visuel avant de brancher les données. */
.widget-badge {
  flex-shrink: 0;
  padding: 1px 6px;
  font-size: var(--text-2xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-faint);
  background: var(--sidebar-bg);
  border: 1px solid var(--border);
  border-radius: 999px;
}

.widget-body {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  min-width: 0;
  padding: var(--sp-4);
}
.widget-caption { margin: 0; font-size: var(--text-xs); color: var(--text-faint); }
.widget-empty { margin: 0; font-size: var(--text-sm); color: var(--text-faint); font-style: italic; }

@media (prefers-reduced-motion: reduce) {
  /* Même raisonnement que l'indicateur de réponse : ce point porte
     l'information « en cours », le figer la supprimerait. */
  .agent-item-status.status-working .agent-item-status-dot {
    animation: status-pulse 3s ease-in-out infinite !important;
  }
}

/* La marque est devenue un bouton (retour à l'accueil) : on la ramène à
   l'apparence d'un titre. */
button.brand {
  padding: 0;
  min-height: auto;
  background: none;
  border: none;
  box-shadow: none;
  color: inherit;
}
button.brand:hover { background: none; box-shadow: none; transform: none; }
button.brand:hover h1 { color: var(--accent); }
button.brand:active { transform: scale(0.97); }

/* Page Services externes — même mécanique de vue plein panneau que data-view
   (voir .data-view / .data-view-header, réutilisés tels quels pour l'en-tête). */
.services-view {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: var(--sp-5);
  padding-bottom: calc(var(--sp-5) + env(safe-area-inset-bottom));
  padding-left: calc(var(--sp-5) + env(safe-area-inset-left));
  padding-right: calc(var(--sp-5) + env(safe-area-inset-right));
  overflow-y: auto;
}
.services-view-title { flex: 1; margin: 0; font-size: var(--text-xl); font-weight: 700; letter-spacing: -0.015em; }

.services-list {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}
.service-card {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-alt);
  box-shadow: var(--shadow-sm);
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
.service-card:hover { border-color: var(--border-strong); box-shadow: var(--shadow-md); }
.service-card-icon {
  width: 32px;
  height: 32px;
  min-width: 32px;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--text-md);
  font-weight: 700;
  color: #fff;
  background: var(--avatar-color, var(--accent));
}
.service-card-text { display: flex; flex-direction: column; gap: 0; min-width: 0; flex: 1; }
.service-card-label { font-weight: 600; font-size: var(--text-md); letter-spacing: -0.005em; }
.service-card-provider { font-size: var(--text-2xs); color: var(--text-faint); }

.service-status-badge {
  font-size: var(--text-2xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: var(--sp-1) var(--sp-2);
  border-radius: 999px;
  white-space: nowrap;
}
.service-status-badge.status-connected { color: var(--risk-low); background: var(--risk-low-soft); }
.service-status-badge.status-pending { color: var(--risk-mid); background: var(--risk-mid-soft); }
.service-status-badge.status-error { color: var(--risk-high); background: var(--risk-high-soft); }

.service-card-actions { display: flex; align-items: center; gap: var(--sp-1); flex-shrink: 0; }

.session-list { display: flex; flex-direction: column; gap: var(--sp-2); max-height: 50vh; overflow-y: auto; }
.session-row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-alt);
}
.session-row-text { display: flex; flex-direction: column; gap: 0; min-width: 0; flex: 1; }
.session-row-title {
  font-weight: 600;
  font-size: var(--text-sm);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.session-row-date { font-size: var(--text-2xs); color: var(--text-faint); }
.session-row-badge {
  font-size: var(--text-2xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: var(--sp-1) var(--sp-2);
  border-radius: 999px;
  white-space: nowrap;
  flex-shrink: 0;
}
.session-row-badge.status-active { color: var(--risk-low); background: var(--risk-low-soft); }
.session-row-badge.status-closed { color: var(--text-dim); background: var(--sidebar-bg); }
.session-row .session-resume-btn { flex-shrink: 0; }

.service-field-group {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  padding: var(--sp-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--sidebar-bg);
}
.service-field-group .service-field-group { border-style: dashed; }
.scope-checkbox-list { display: flex; flex-direction: column; gap: var(--sp-1); }
.scope-checkbox-list .checkbox-label { font-size: var(--text-sm); }
.service-oauth-note { font-size: var(--text-2xs); margin: 0; }
.service-oauth-note code { font-family: var(--font-mono); background: var(--bg); padding: 0 var(--sp-1); border-radius: 4px; }

.service-guide-intro { margin: 0; font-size: var(--text-sm); }
.service-guide-steps {
  margin: 0;
  padding-left: var(--sp-5);
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  font-size: var(--text-sm);
  color: var(--text-dim);
}
.service-guide-steps a { color: var(--accent); }
.service-guide-steps strong { color: var(--text); font-weight: 600; }

.service-redirect-uri-box {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  padding-bottom: var(--sp-2);
  border-bottom: 1px dashed var(--border-strong);
}
.service-copy-row { display: flex; align-items: center; gap: var(--sp-1); }
.service-copy-row code {
  flex: 1;
  min-width: 0;
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--sp-2) var(--sp-2);
  overflow-x: auto;
  white-space: nowrap;
}

/* ---------------------------------------------------------------------------
   Markdown rendu dans les messages d'agent (voir markdown.js).
   Les marges vivent entre les blocs (`+`) plutôt qu'en haut/bas de chacun :
   un message ne commence ni ne finit ainsi jamais par un blanc parasite.
   ------------------------------------------------------------------------- */
.msg-body > :first-child { margin-top: 0; }
.msg-body > :last-child { margin-bottom: 0; }

/* Un mot insécable — jeton, hachage, chemin sans espace — est plus large que
   l'écran d'un téléphone à lui tout seul. Sans coupure il pousse son bloc au-delà
   du cadre : le texte sort de la carte et la fin devient illisible. On le coupe
   donc, partout où du markdown est rendu (messages, aperçu de fichier,
   historique). `break-word` ne coupe que faute de mieux : les mots normaux
   gardent leur intégrité. */
.msg-body,
.file-preview,
.history-entry-body { overflow-wrap: break-word; }

.md-p { margin: 0; }
.md-p + .md-p { margin-top: 0.75em; }

.md-h {
  margin: 1.4em 0 0.5em;
  font-weight: 700;
  line-height: 1.3;
  color: var(--text);
}
h3.md-h { font-size: var(--text-xl); }
h4.md-h { font-size: var(--text-lg); }
/* Pas de capitales ici : « ### » est le niveau que les agents emploient le
   plus, le passer en majuscules donnait des titres criards en pleine reponse. */
h5.md-h { font-size: var(--text-md); }
h6.md-h { font-size: var(--text-md); color: var(--text-dim); }

.md-list { margin: 0.6em 0; padding-left: 1.6em; }
.md-list li { margin: 0.25em 0; }
.md-list .md-list { margin: 0.25em 0; }
.md-list li::marker { color: var(--text-faint); }

.md-code-inline {
  font-family: var(--font-mono);
  font-size: 0.92em;
  background: var(--accent-soft);
  color: var(--text);
  border-radius: 4px;
  padding: 0.1em 0.35em;
  /* Un identifiant long ne doit pas élargir la colonne. */
  overflow-wrap: anywhere;
}

.md-code {
  margin: 0.8em 0;
  padding: var(--sp-3);
  background: var(--sidebar-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  line-height: 1.55;
}
/* Le defilement horizontal vit sur <code>, pas sur <pre> : ainsi l'etiquette
   de langue reste fixe pendant qu'on fait defiler une ligne longue. */
.md-code code {
  display: block;
  overflow-x: auto;
  font-family: var(--font-mono);
  white-space: pre;
  color: var(--text);
}
/* En flux, pas en position absolue : au-dessus du code elle le recouvrait
   des que la ligne etait longue (constate sur iPhone). */
.md-code-lang {
  display: block;
  margin-bottom: var(--sp-1);
  text-align: right;
  font-size: var(--text-2xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-faint);
  user-select: none;
}

.md-quote {
  margin: 0.8em 0;
  padding-left: var(--sp-3);
  border-left: 2px solid var(--accent);
  color: var(--text-dim);
}

.md-hr {
  margin: 1.2em 0;
  border: none;
  border-top: 1px solid var(--border);
}

/* Un tableau large scrolle dans son propre cadre au lieu d'élargir la page. */
.md-table-wrap {
  margin: 0.8em 0;
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.md-table { width: 100%; border-collapse: collapse; font-size: var(--text-sm); }
.md-table th, .md-table td {
  padding: var(--sp-2) var(--sp-3);
  text-align: left;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
.md-table th {
  background: var(--sidebar-bg);
  font-weight: 700;
  font-size: var(--text-2xs);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--text-faint);
}
.md-table tr:last-child td { border-bottom: none; }

.msg-body a { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; overflow-wrap: anywhere; }
.msg-body a:hover { color: var(--accent-hover); }
.msg-body strong { font-weight: 700; color: var(--text); }
.msg-body del { color: var(--text-faint); }

@media (max-width: 720px) {
  #app-screen.screen:not(.hidden) { grid-template-columns: 1fr; }

  /* Le menu latéral devient un tiroir plein-hauteur au-dessus du chat,
     plutôt que d'être simplement masqué : sur iPhone il faut pouvoir
     changer d'agent/session sans revenir à un écran plus large. */
  .sidebar {
    position: fixed;
    top: 0;
    left: 0;
    height: var(--app-height, 100dvh);
    width: min(85vw, 320px);
    z-index: 20;
    transform: translateX(-100%);
    transition: transform 0.25s ease;
    box-shadow: 2px 0 16px rgba(0, 0, 0, 0.25);
  }
  .sidebar.open { transform: translateX(0); }

  .sidebar-backdrop.visible { display: block; opacity: 1; }

  /* Cibles tactiles ≥ 44px (repère Apple HIG) pour les boutons icône. */
  button.ghost {
    min-width: 44px;
    min-height: 44px;
    padding: var(--sp-2);
  }
  #send-btn, #stop-btn, #attach-btn, .agent-item-main { min-height: 44px; }

  /* Empêche le zoom automatique de Safari iOS à la mise au point d'un champ
     (déclenché dès que font-size < 16px). Sans ça, toucher un champ recadre
     brutalement la page, et la remettre d'aplomb est à la charge de
     l'utilisateur. */
  /* Les :not() ne filtrent presque rien — ils portent la spécificité qu'il
     faut pour battre les règles par type (input[type="text"]…) sans avoir à
     toutes les réénumérer ici. */
  input:not([type="checkbox"]):not([type="radio"]), textarea, select {
    font-size: 16px;
  }
  /* Les champs habillés par une règle de classe restent plus spécifiques que
     « textarea » : ils doivent être repris un par un, sous peine de laisser
     iOS zoomer là précisément. */
  .chat-input-row textarea,
  .file-editor-highlight,
  .file-editor,
  .modal textarea.mono,
  .settings-card input,
  .create-fields input,
  .form-field input,
  .form-field textarea,
  .form-field select { font-size: 16px; }

  .chat-header { gap: var(--sp-2); padding-left: var(--sp-3); padding-right: calc(var(--sp-3) + env(safe-area-inset-right)); }
  .chat-messages { padding: var(--sp-5) var(--sp-4) var(--sp-6); gap: var(--sp-5); }
  .chat-input-bar { padding-left: calc(var(--sp-3) + env(safe-area-inset-left)); padding-right: calc(var(--sp-3) + env(safe-area-inset-right)); }

  /* ---- Bandeau d'options de la conversation -----------------------------
     Icônes seules : cinq libellés ne tiennent pas sur un téléphone, et les
     faire passer à la ligne ferait grandir le bandeau au lieu de rester une
     bande fixe. Le titre (attribut title) garde le libellé au doigt/à la
     souris qui s'attarde. */
  .chat-toolbar { padding-left: var(--sp-3); padding-right: calc(var(--sp-3) + env(safe-area-inset-right)); }
  .toolbar-toggle span, .toolbar-split-btn span { display: none; }
  .toolbar-toggle { padding: 0 var(--sp-2); min-width: 2.1rem; justify-content: center; }
  .toolbar-split-btn { padding: 0 var(--sp-2); }

  .modal { width: min(92vw, 360px); margin: auto; }
  .modal-wide {
    width: min(92vw, 580px);
    max-height: calc(var(--app-height, 100dvh) - 2rem);
    margin: auto;
  }

  /* ---- Navigateur de fichiers ------------------------------------------
     Cinq colonnes ne tiennent pas sur un téléphone : en fixed layout la
     colonne « Nom » tombait sous 130px pendant que les dates débordaient.
     Sous 720px on abandonne donc la mise en page tabulaire (display: block)
     et chaque ligne devient une carte à deux niveaux :

        [icone] nom-du-fichier.pdf          [telecharger] [supprimer]
                2,4 Mo · 07/08/26 09:00

     Les règles ci-dessous visent « .data-row td.X » et non « .td.X » : le
     display: block appliqué en bloc aux cellules est plus spécifique qu'un
     simple sélecteur de classe et l'emporterait sinon. */
  .data-view { padding: var(--sp-3); padding-bottom: calc(var(--sp-3) + env(safe-area-inset-bottom)); }
  .data-view-header { gap: var(--sp-2); padding-bottom: var(--sp-2); }

  /* ---- Onglets ----------------------------------------------------------
     La règle tactile de 44px vaut pour les boutons isolés ; appliquée à la
     croix d'un onglet elle doublerait la hauteur de l'en-tête. On la ramène
     ici à une cible atteignable sans faire enfler la barre. */
  .tab { max-width: 11rem; }
  .tab-main { min-height: 38px; }
  .tab-close.ghost { min-width: 30px; min-height: 30px; padding: 0; }

  /* ---- Onglet fichier et onglet Agent ---------------------------------- */
  .file-view, .config-view {
    padding: var(--sp-3);
    padding-bottom: calc(var(--sp-3) + env(safe-area-inset-bottom));
  }
  /* L'en-tête tient sur une ligne, ici aussi : la barre de balises défile, et
     l'état d'enregistrement s'efface plutôt que de voler de la place aux
     outils — l'information reste dans l'onglet, qui signale les modifications
     en attente. */
  .file-view-header { gap: var(--sp-1); }
  .file-save-state { display: none; }
  .file-preview { padding: var(--sp-4); font-size: var(--text-md); }
  /* Corps de police : voir la règle anti-zoom en tête de cette section. Les
     deux calques doivent garder le même remplissage, sous peine de désaligner
     le texte tapé de sa coloration. */
  .file-editor-highlight,
  .file-editor { padding: var(--sp-3); }
  .md-tool { min-height: 36px; min-width: 2.4rem; }
  .config-grid { grid-template-columns: 1fr; }
  /* Trois éléments sur une ligne de 390px : l'indicateur passe au-dessus des
     boutons plutôt que de les comprimer jusqu'à couper leur libellé. */
  .config-actions { flex-wrap: wrap; }
  .config-save-state { order: -1; width: 100%; margin-left: 0; }
  .config-actions button { flex: 1; white-space: nowrap; }

  /* Un chemin profond doit défiler horizontalement, pas empiler cinq lignes. */
  .data-breadcrumb {
    flex-wrap: nowrap;
    overflow-x: auto;
    width: 100%;
    padding-bottom: var(--sp-1);
  }
  .data-crumb { white-space: nowrap; }

  .data-sort-bar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--sp-1);
  }
  .data-sort-label {
    font-size: var(--text-2xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--text-faint);
    margin-right: var(--sp-1);
  }
  .data-sort-btn {
    min-height: 44px;
    padding: 0 var(--sp-3);
    font-size: var(--text-sm);
    font-weight: 500;
    border-radius: 999px;
    background: var(--bg-alt);
    border: 1px solid var(--border-strong);
    color: var(--text-dim);
    box-shadow: none;
  }
  .data-sort-btn:hover { background: var(--bg-alt); color: var(--text); box-shadow: none; transform: none; }
  .data-sort-btn.sorted-asc, .data-sort-btn.sorted-desc {
    background: var(--accent-soft);
    border-color: var(--accent);
    color: var(--accent);
  }
  .data-sort-btn.sorted-asc::after { content: " ▲"; font-size: 0.7em; }
  .data-sort-btn.sorted-desc::after { content: " ▼"; font-size: 0.7em; }

  /* Le cadre et le fond passent sur chaque carte, plus sur le conteneur. */
  .data-table-wrap {
    border: none;
    background: transparent;
    overflow-x: hidden;
  }
  .data-table thead { display: none; }
  .data-table, .data-table tbody, .data-table tr, .data-table td { display: block; }
  .data-table { font-size: var(--text-md); }

  /* « .data-table tr.data-row » et non « .data-row » : le display: block
     ci-dessus vise « .data-table tr », plus spécifique qu'une simple classe. */
  .data-table tr.data-row {
    display: grid;
    grid-template-columns: auto 1fr auto;
    grid-template-areas:
      "name name     actions"
      "size modified actions";
    align-items: center;
    column-gap: var(--sp-2);
    row-gap: 2px;
    min-height: 56px;
    padding: var(--sp-2) var(--sp-3);
    margin-bottom: var(--sp-2);
    background: var(--bg-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-sm);
  }
  .data-row:hover { background: var(--bg-alt); }
  .data-row td { padding: 0; border-bottom: none; }

  .data-row td.data-row-name { display: flex; grid-area: name; min-width: 0; }
  /* Un nom long s'enroule au lieu d'être tronqué : sur mobile la fin du nom
     (l'extension, le numéro de version) porte souvent l'information utile. */
  .data-row-name-text {
    white-space: normal;
    overflow: visible;
    overflow-wrap: anywhere;
    font-weight: 500;
  }
  .data-row td.data-row-size { grid-area: size; justify-self: start; }
  .data-row td.data-row-modified { grid-area: modified; justify-self: start; }
  /* Une seule date sur mobile : « modifié le » est plus parlant que « créé le ».
     La date complète reste accessible en appui long (attribut title). */
  .data-row td.data-row-created { display: none; }
  .data-row-modified::before {
    content: "·";
    margin-right: 0.45em;
    color: var(--text-faint);
  }

  .data-row td.data-row-actions {
    grid-area: actions;
    display: flex;
    align-items: center;
    gap: var(--sp-1);
  }
  .data-row-actions button { min-width: 44px; min-height: 44px; }

  /* Mode sélection : une colonne de plus dans la grille de la carte, pas
     seulement dans le tableau desktop. */
  .data-table.select-mode tr.data-row {
    grid-template-columns: auto auto 1fr auto;
    grid-template-areas:
      "select name name     actions"
      "select size modified actions";
  }
  .data-table.select-mode .data-row td.data-row-select {
    grid-area: select;
    display: flex;
    align-items: center;
  }
  .data-row-checkbox { width: 1.4rem; height: 1.4rem; }

  .data-table-empty { padding: var(--sp-6) var(--sp-4) !important; }

  /* Accueil : une colonne, et un peu moins d'air autour. */
  .overview-view { padding: var(--sp-4) var(--sp-3); padding-bottom: calc(var(--sp-4) + env(safe-area-inset-bottom)); }
  .home-grid { grid-template-columns: 1fr; }

  /* En-tête : les trois actions se réduisent à leurs icônes. À trois libellés
     côte à côte, la ligne passait à la ligne et l'en-tête perdait son
     alignement ; le titre reste porté par title/aria-label (voir index.html). */
  .home-head { flex-wrap: nowrap; align-items: center; gap: var(--sp-2); }
  .home-head-text { min-width: 0; }
  .home-head-actions { flex-wrap: nowrap; flex-shrink: 0; }
  .home-head-btn-label { display: none; }
  .home-head-btn {
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
    padding: 0;
  }
  .explorer-view { padding: var(--sp-4) var(--sp-3); padding-bottom: calc(var(--sp-4) + env(safe-area-inset-bottom)); }
  .explorer-col-date { display: none; }
  .home-stats { grid-template-columns: repeat(2, 1fr); }
  .home-title { font-size: var(--text-xl); }
}

/* Coût d'un tour, sous la réponse. Volontairement effacé : c'est une note de
   marge, elle ne doit jamais concurrencer le texte de l'agent. Alignée sur la
   même colonne de lecture que les messages. */
.msg-usage {
  /* Enfant du bloc `.msg` : largeur, alignement et écartement viennent de lui
     (`flex-direction: column` + `gap`), il n'y a rien à recaler ici. */
  font-size: var(--text-2xs);
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
  /* Le détail par modèle est dans l'infobulle : le curseur doit le dire. */
  cursor: help;
}

/* ---- Consommation ------------------------------------------------------- */

/* Fil d'activité : une pastille d'état, une phrase, une heure. */
.home-feed, .home-rows { display: flex; flex-direction: column; gap: var(--sp-2); margin: 0; padding: 0; list-style: none; }
/* Chaque ligne mène à la conversation de l'agent : c'est un <button>, dont on
   annule ici tout ce qui le ferait ressembler à un bouton (voir renderHomeFeed). */
.home-feed-item {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
  font-size: var(--text-sm);
  min-width: 0;
  /* Marge négative égale à la retombée horizontale : le survol déborde dans
     la marge de la carte plutôt que de rétrécir le texte. */
  padding: 4px var(--sp-2);
  margin: 0 calc(-1 * var(--sp-2));
  width: calc(100% + 2 * var(--sp-2));
  text-align: left;
  font-weight: 400;
  line-height: 1.45;
  color: inherit;
  background: none;
  border: none;
  border-radius: var(--radius-sm);
  box-shadow: none;
}
.home-feed-item:hover { background: var(--bg); box-shadow: none; transform: none; }
.home-feed-dot {
  flex-shrink: 0;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--text-faint);
  transform: translateY(-1px);
}
.home-feed-dot.status-ok { background: var(--risk-low); }
.home-feed-dot.status-warn { background: var(--risk-mid); }
.home-feed-dot.status-err { background: var(--risk-high); }
/* Attente d'approbation : ni un succès ni une alerte — une demande. D'où une
   quatrième teinte, hors de l'échelle vert/orange/rouge des risques. */
.home-feed-dot.status-await { background: var(--dot-await); }
.home-feed-text { flex: 1; min-width: 0; color: var(--text-dim); }
.home-feed-text strong { color: var(--text); font-weight: 600; }
.home-feed-text code { font-family: var(--font-mono); font-size: 0.95em; }
.home-feed-time { flex-shrink: 0; font-size: var(--text-xs); color: var(--text-faint); white-space: nowrap; }

/* Lignes « libellé à gauche, valeur à droite » — tâches planifiées, services. */
.home-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  font-size: var(--text-sm);
  min-width: 0;
}
.home-row + .home-row { padding-top: var(--sp-2); border-top: 1px solid var(--border); }
.home-row-main {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.home-row-meta { flex-shrink: 0; font-size: var(--text-xs); color: var(--text-faint); font-variant-numeric: tabular-nums; }

/* Jauges de la machine : libellé, barre, valeur — alignés en colonnes pour
   que les trois lignes se lisent d'un seul balayage vertical. */
.home-meter {
  display: grid;
  grid-template-columns: 5.5rem 1fr 2.6rem;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--text-sm);
}
.home-meter-label { color: var(--text-dim); }
.home-meter-track { height: 6px; background: var(--sidebar-bg); border: 1px solid var(--border); border-radius: 999px; overflow: hidden; }
.home-meter-fill { display: block; width: var(--v); height: 100%; background: var(--chart-fill); }
.home-meter-fill.is-warn { background: var(--risk-mid); }
.home-meter-value { text-align: right; font-size: var(--text-xs); color: var(--text-dim); font-variant-numeric: tabular-nums; }

.home-big { margin: 0; font-size: var(--text-xl); font-weight: 650; letter-spacing: -0.015em; }
.home-tags { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.home-tag {
  padding: 2px 8px;
  font-size: var(--text-xs);
  color: var(--text-dim);
  background: var(--sidebar-bg);
  border: 1px solid var(--border);
  border-radius: 999px;
}
.home-tag.status-ok { color: var(--risk-low); background: var(--risk-low-soft); border-color: transparent; }

/* Favoris : des cibles de même poids, deux par ligne. */
.home-shortcuts { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--sp-2); }
/* Un favori porte le nom d'un onglet (donc d'un agent, parfois long) : il se
   coupe plutôt que d'élargir sa colonne. */
.home-shortcut-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.home-shortcut:disabled { opacity: 0.5; }
.home-shortcut:disabled:hover { border-color: var(--border); }

/* Édition des favoris (modale) : nom modifiable, puis trois actions. */
.fav-edit-list { display: flex; flex-direction: column; gap: var(--sp-2); }
.fav-edit-row { display: flex; align-items: center; gap: var(--sp-2); }
.fav-edit-icon { display: flex; flex-shrink: 0; }
.fav-edit-name { flex: 1; min-width: 0; }
.fav-edit-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  min-height: 0;
  padding: 0;
}
.home-shortcut {
  display: flex;
  align-items: center;
  /* Les noms de favoris n'ont pas tous la même longueur : alignés à gauche,
     ils forment une colonne lisible plutôt que des étiquettes flottantes. */
  justify-content: flex-start;
  gap: var(--sp-2);
  min-height: 40px;
  padding: var(--sp-2) var(--sp-3);
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text);
  text-align: left;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: none;
}
.home-shortcut:hover { background: var(--bg); border-color: var(--accent); box-shadow: none; transform: none; }

/* ---- Explorateur de ~/.allkin ----
   Réutilise la mécanique du navigateur de fichiers d'un agent (fil d'Ariane,
   tableau) sans ses colonnes d'actions : il n'y en a aucune, tout y est en
   lecture seule. */

.explorer-view {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: var(--sp-5);
  padding-bottom: calc(var(--sp-5) + env(safe-area-inset-bottom));
  overflow: hidden;
}
.explorer-readonly {
  padding: 2px 8px;
  font-size: var(--text-2xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-faint);
  background: var(--sidebar-bg);
  border: 1px solid var(--border);
  border-radius: 999px;
}
.explorer-table-wrap {
  flex: 1;
  min-height: 0;
  overflow: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-alt);
}
.explorer-table thead th { cursor: default; }
.explorer-table thead th:hover { color: var(--text-faint); }
.explorer-col-size { width: 6.5rem; }
.explorer-col-date { width: 9rem; }
.explorer-row td.explorer-col-size,
.explorer-row td.explorer-col-date {
  font-size: var(--text-sm);
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.explorer-name {
  display: flex;
  align-items: center;
  /* Explicite : la règle générale des boutons centre son contenu, ce qui
     enverrait chaque nom de fichier au milieu de la colonne. */
  justify-content: flex-start;
  gap: var(--sp-2);
  width: 100%;
  min-width: 0;
  padding: 0;
  min-height: auto;
  font-size: var(--text-md);
  font-weight: 500;
  color: var(--text);
  text-align: left;
  background: none;
  border: none;
  box-shadow: none;
}
.explorer-name:hover { background: none; box-shadow: none; transform: none; color: var(--accent); }
.explorer-icon { display: flex; flex-shrink: 0; }
.explorer-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Écran « Bienvenue » (nom de l'utilisateur) : même carte que la connexion. */
.name-note { margin: 0; font-size: var(--text-xs); text-align: center; }

/* Page « aucun onglet ouvert » : volontairement nue, un seul geste possible. */
.empty-view {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-3);
}
.empty-view-text { margin: 0; font-size: var(--text-md); }

/* Vue plein panneau — même mécanique que data-view / history-view. */
.usage-view {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: var(--sp-5);
  padding-bottom: calc(var(--sp-5) + env(safe-area-inset-bottom));
  padding-left: calc(var(--sp-5) + env(safe-area-inset-left));
  padding-right: calc(var(--sp-5) + env(safe-area-inset-right));
  overflow-y: auto;
}
.usage-body { display: flex; flex-direction: column; gap: var(--sp-6); }
.usage-section { display: flex; flex-direction: column; gap: var(--sp-2); }
.usage-section-title { margin: 0; font-size: var(--text-md); font-weight: 650; color: var(--text); }
.usage-section-sub { margin: 0; font-size: var(--text-sm); }

.usage-stats { display: flex; flex-wrap: wrap; gap: var(--sp-3); }
.usage-stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 110px;
  padding: var(--sp-3) var(--sp-4);
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.usage-stat-value { font-size: var(--text-xl); font-weight: 650; color: var(--text); font-variant-numeric: tabular-nums; }
.usage-stat-label { font-size: var(--text-2xs); text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-faint); }

.usage-table-wrap { overflow-x: auto; }
.usage-table { width: 100%; border-collapse: collapse; font-size: var(--text-sm); }
.usage-table th,
.usage-table td { padding: var(--sp-2) var(--sp-3); text-align: left; white-space: nowrap; }
.usage-table th {
  font-size: var(--text-2xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-faint);
  border-bottom: 1px solid var(--border);
}
.usage-table td { border-bottom: 1px solid var(--border); color: var(--text-dim); }
.usage-table tr:last-child td { border-bottom: none; }
/* Chiffres alignés à droite et en chasse tabulaire : c'est ce qui rend une
   colonne de nombres comparable d'un coup d'œil. */
.usage-num { text-align: right !important; font-variant-numeric: tabular-nums; }
.usage-cost { color: var(--text); font-weight: 600; }

/* Graphe journalier. Série unique : ni légende ni axe des ordonnées — le
   sous-titre porte le maximum, le survol porte la valeur exacte. */
.usage-chart {
  display: flex;
  align-items: flex-end;
  /* La gouttière est ce qui sépare deux journées : sans elle, une série de
     barres pleines se lit comme un seul bloc. */
  gap: 2px;
  height: 96px;
  padding-bottom: var(--sp-1);
  border-bottom: 1px solid var(--border-strong);
}
.usage-chart-col { flex: 1; min-width: 0; height: 100%; display: flex; align-items: flex-end; }
.usage-chart-bar {
  width: 100%;
  background: var(--chart-fill);
  /* Seul le sommet est arrondi : c'est l'extrémité qui porte la donnée, le
     pied reste franc sur la ligne de base. */
  border-radius: 3px 3px 0 0;
}
.usage-chart-col:hover .usage-chart-bar { background: var(--accent-hover); }
.usage-chart-axis { display: flex; justify-content: space-between; font-size: var(--text-2xs); }

.usage-agent-list { display: flex; flex-direction: column; gap: var(--sp-1); }
.usage-agent { border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--bg-raised); }
.usage-agent-head {
  display: flex;
  align-items: baseline;
  gap: var(--sp-3);
  width: 100%;
  padding: var(--sp-3) var(--sp-4);
  text-align: left;
  background: none;
  border: none;
  box-shadow: none;
}
.usage-agent-head:hover { background: var(--sidebar-bg); transform: none; box-shadow: none; }
.usage-agent-name { font-weight: 600; color: var(--text); }
.usage-agent-meta { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--text-xs); color: var(--text-faint); }
.usage-agent-cost { font-variant-numeric: tabular-nums; font-weight: 600; color: var(--text); }
.usage-agent-detail { padding: 0 var(--sp-4) var(--sp-3); display: flex; flex-direction: column; gap: var(--sp-4); }
.usage-subsection { display: flex; flex-direction: column; gap: var(--sp-2); }
.usage-subtitle {
  margin: 0;
  font-size: var(--text-2xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-faint);
}
.usage-line-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--sp-1); }
.usage-line-list li {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-3);
  font-size: var(--text-sm);
  color: var(--text-dim);
}
.usage-line-value { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* 720px : le même seuil que isMobileLayout() côté JS — deux valeurs
   différentes feraient basculer la mise en page et le comportement à des
   largeurs distinctes. */
@media (max-width: 720px) {
  .usage-view { padding: var(--sp-4) var(--sp-3); padding-bottom: calc(var(--sp-4) + env(safe-area-inset-bottom)); }
  .usage-stat { flex: 1; min-width: calc(50% - var(--sp-3)); }
  /* Cible tactile : l'en-tête d'agent est le seul point d'entrée du détail. */
  .usage-agent-head { min-height: 48px; }
}

/* ---------------------------------------------------------------------------
   Aide, Sauvegarde — deux pages globales bâties sur la même mécanique que
   Consommation : panneau plein, défilement propre, en-tête à bouton de
   retour. Paramètres suit sa propre règle juste après : c'est la seule à
   s'ouvrir en plein écran, sidebar comprise.
   ------------------------------------------------------------------------- */
.help-view,
.backup-view {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: var(--sp-5);
  padding-bottom: calc(var(--sp-5) + env(safe-area-inset-bottom));
  padding-left: calc(var(--sp-5) + env(safe-area-inset-left));
  padding-right: calc(var(--sp-5) + env(safe-area-inset-right));
  overflow-y: auto;
}

/* ---- Paramètres : plein écran, sidebar comprise --------------------------
   Les réglages de l'installation ne sont pas un onglet parmi d'autres, ni une
   page qui partage l'écran avec la barre latérale : ils recouvrent tout,
   comme le fait déjà le mode plein écran de l'éditeur de fichier. Fermeture
   par le bouton dédié en haut à gauche (ou Échap, comme le reste de l'app). */
.settings-view {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  background: var(--bg);
  padding: calc(var(--sp-5) + env(safe-area-inset-top)) calc(var(--sp-5) + env(safe-area-inset-right))
    calc(var(--sp-5) + env(safe-area-inset-bottom)) calc(var(--sp-5) + env(safe-area-inset-left));
  overflow-y: auto;
}
.settings-close-btn {
  flex-shrink: 0;
  padding: var(--sp-2);
  min-width: 2.25rem;
  min-height: 2.25rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* ---- Aide : de la documentation, donc une colonne de lecture bornée ------ */
.doc-body {
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);
  max-width: 60ch;
}
.doc-intro {
  margin: 0;
  font-size: var(--text-lg);
  line-height: 1.6;
  color: var(--text);
}
.doc-section { display: flex; flex-direction: column; gap: var(--sp-2); }
.doc-section h3 {
  margin: 0;
  font-size: var(--text-md);
  font-weight: 650;
  color: var(--text);
}
.doc-section p { margin: 0; font-size: var(--text-md); line-height: 1.65; color: var(--text-dim); }
.doc-section ul {
  margin: 0;
  padding-left: var(--sp-5);
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  font-size: var(--text-md);
  line-height: 1.6;
  color: var(--text-dim);
}
.doc-section strong { color: var(--text); font-weight: 600; }
.doc-section code {
  font-family: var(--font-mono);
  font-size: 0.9em;
  padding: 0.1em 0.35em;
  background: var(--sidebar-bg);
  border-radius: 4px;
}
.doc-pre {
  margin: 0;
  padding: var(--sp-3);
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow-x: auto;
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  line-height: 1.6;
  color: var(--text-dim);
}
.doc-pre code { background: none; padding: 0; }

/* ---- Sauvegarde : des cartes empilées ------------------------------------ */
.backup-body {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  max-width: 46rem;
}
.settings-card,
.backup-card {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: var(--sp-4);
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.settings-card-title {
  margin: 0;
  font-size: var(--text-md);
  font-weight: 650;
  color: var(--text);
}
/* Zone dangereuse (suppression définitive d'un agent) : même carte, bordure
   teintée pour qu'elle se distingue avant même de lire son contenu. */
.settings-card-danger {
  border-color: color-mix(in srgb, var(--risk-high) 35%, transparent);
  background: var(--risk-high-soft);
}
.settings-card-danger .settings-card-title { color: var(--risk-high); }

/* ---- Webhook d'upload (carte de configuration d'un agent) ---------------
   L'adresse est longue et se copie d'un bloc : champ en lecture seule à
   police fixe (on y repère une coquille), bouton Copier collé à côté, et
   passage à la ligne sous les petits écrans plutôt qu'un champ rétréci. */
.webhook-url-row { display: flex; gap: var(--sp-2); flex-wrap: wrap; }
.webhook-url-row input {
  flex: 1 1 20rem;
  min-width: 0;
  font-family: var(--font-mono);
  font-size: var(--text-sm);
}
.webhook-actions { display: flex; gap: var(--sp-2); flex-wrap: wrap; }

/* ---- Paramètres : sections à gauche, contenu à droite (façon Chrome) ----
   .settings-view reste l'unique élément défilant (voir VIEW_SCROLLERS.settings
   dans app.js, qui mémorise sa position) : la nav ne prend donc pas sa propre
   zone de défilement, elle se contente de rester en vue via position:sticky. */
.settings-layout {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-5);
}
.settings-nav {
  flex-shrink: 0;
  width: 14rem;
  position: sticky;
  top: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.settings-nav-item {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  min-height: 2.4rem;
  padding: 0 var(--sp-3);
  background: none;
  border: none;
  border-radius: var(--radius-sm);
  box-shadow: none;
  color: var(--text-dim);
  font-size: var(--text-sm);
  font-weight: 500;
  text-align: left;
}
.settings-nav-item .icon { opacity: 0.75; flex-shrink: 0; }
.settings-nav-item:hover { background: var(--bg-alt); box-shadow: none; transform: none; color: var(--text); }
.settings-nav-item.active {
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 650;
}
.settings-nav-item.active .icon { opacity: 1; }

.settings-panels {
  flex: 1;
  min-width: 0;
  padding-bottom: var(--sp-5);
}
.settings-section {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  max-width: 40rem;
}
.settings-section-title {
  margin: 0 0 var(--sp-1);
  font-size: var(--text-lg);
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text);
}
.settings-section form { display: flex; flex-direction: column; gap: var(--sp-3); }
/* Une note explique le champ qui la précède : elle lui reste collée, et ne
   prend pas l'écart complet de la carte. */
.settings-note { margin: calc(-1 * var(--sp-2)) 0 0; font-size: var(--text-sm); line-height: 1.55; }
.settings-note code { font-family: var(--font-mono); font-size: 0.9em; }

/* Aperçu des réglages de la conversation (Paramètres > Apparence). Il porte
   la classe `.chat-messages` — ce n'est pas une imitation du fil, c'en est un,
   avec deux messages figés : thème, police et corps s'y appliquent par les
   mêmes règles que dans la vraie conversation, qui est de toute façon cachée
   par la page des paramètres au moment du choix. Les quelques propriétés
   ci-dessous ne font que le sortir de la mise en page plein écran. */
.chat-preview {
  flex: none;
  overflow: hidden;
  padding: var(--sp-4);
  gap: var(--sp-4);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  /* Décor, pas un fil : ni sélection, ni clic, ni tabulation à l'intérieur. */
  pointer-events: none;
  user-select: none;
}
.chat-preview > * { max-width: none; }
.settings-actions { display: flex; align-items: center; gap: var(--sp-3); justify-content: flex-end; }
.settings-facts {
  margin: 0;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--sp-2) var(--sp-4);
  font-size: var(--text-sm);
}
.settings-facts dt { color: var(--text-faint); }
.settings-facts dd { margin: 0; color: var(--text-dim); overflow-wrap: anywhere; }

/* ---- Catégories d'agents (page Paramètres) ------------------------------ */
.category-manage-list { display: flex; flex-direction: column; gap: var(--sp-1); }
.category-manage-empty { font-size: var(--text-sm); color: var(--text-faint); }
.category-manage-item {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-1);
  border-bottom: 1px solid var(--border);
}
.category-manage-item:last-child { border-bottom: none; }
input[type="text"].category-manage-name {
  flex: 1;
  min-width: 0;
  border-color: transparent;
  background: none;
  box-shadow: none;
  padding: var(--sp-1) var(--sp-2);
  font-size: var(--text-sm);
}
input[type="text"].category-manage-name:hover,
input[type="text"].category-manage-name:focus {
  border-color: var(--border);
  background: var(--bg);
}
.category-manage-delete { flex-shrink: 0; }
.category-add-form { display: flex; gap: var(--sp-2); }
.category-add-form input { flex: 1; min-width: 0; }

/* ---- Sauvegarde : ce que contient l'archive ----------------------------- */
.backup-sections { display: flex; flex-direction: column; }
.backup-section {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-3) 0;
  border-bottom: 1px solid var(--border);
  font-size: var(--text-sm);
}
.backup-section:last-child { border-bottom: none; }
/* Les sections optionnelles sont des <label> : tout le bloc doit être cliquable. */
label.backup-section { cursor: pointer; }
.backup-section input[type="checkbox"] { margin-top: 2px; flex-shrink: 0; }
/* Le crochet des sections toujours incluses occupe la place de la case, pour
   que les libellés restent alignés d'une ligne à l'autre. */
.backup-section-always {
  width: 1rem;
  flex-shrink: 0;
  text-align: center;
  color: var(--accent);
  font-weight: 700;
}
.backup-section-text { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.backup-section-label { color: var(--text); font-weight: 600; }
.backup-section-detail { font-size: var(--text-2xs); line-height: 1.5; }
.backup-section-size { white-space: nowrap; font-variant-numeric: tabular-nums; font-size: var(--text-2xs); }
.backup-section-secrets { border-top: 1px solid var(--border); }
.backup-warning {
  margin: 0;
  padding: var(--sp-3);
  border-radius: var(--radius-sm);
  background: var(--risk-high-soft);
  color: var(--risk-high);
  font-size: var(--text-sm);
  line-height: 1.55;
}
.backup-actions { display: flex; align-items: center; gap: var(--sp-3); justify-content: flex-end; }
.backup-total { margin-right: auto; font-size: var(--text-sm); font-variant-numeric: tabular-nums; }
.backup-steps {
  margin: 0;
  padding-left: var(--sp-5);
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  font-size: var(--text-sm);
  line-height: 1.6;
  color: var(--text-dim);
}
.backup-steps code,
.backup-card code {
  font-family: var(--font-mono);
  font-size: 0.9em;
  padding: 0.1em 0.35em;
  background: var(--sidebar-bg);
  border-radius: 4px;
}

/* ---- Système : charge machine, processus, consommation d'Allkin -------- */
.system-view {
  height: 100%;
  display: flex;
  flex-direction: column;
  padding: var(--sp-4) var(--sp-6);
  overflow-y: auto;
}
.system-body { flex: 1; min-height: 0; display: flex; flex-direction: column; }

/* Petite barre d'onglets internes à la page — distincte de #tab-bar, qui
   porte les onglets ouverts d'un agent (conversation/fichiers/config). */
.sys-tabs {
  display: flex;
  gap: var(--sp-1);
  border-bottom: 1px solid var(--border);
  margin: var(--sp-4) 0;
}
.sys-tab-btn {
  background: transparent;
  border: none;
  border-radius: 0;
  box-shadow: none;
  color: var(--text-faint);
  font-weight: 600;
  padding: var(--sp-2) var(--sp-3);
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}
.sys-tab-btn:hover { background: transparent; color: var(--text); box-shadow: none; transform: none; }
.sys-tab-btn.active { color: var(--accent); border-bottom-color: var(--accent); }

.sys-panel.hidden { display: none; }

.sys-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--sp-4);
}
.sys-card-value {
  font-size: var(--text-2xl);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

/* Jauge de remplissage horizontale : le duplicata de valeur (--sys-gauge-fill,
   posé en JS via style.width) est plus simple à maintenir qu'un <progress>
   habillé, dont le rendu natif varie trop selon le navigateur. */
.sys-gauge {
  height: 8px;
  border-radius: 999px;
  background: var(--bg);
  border: 1px solid var(--border);
  overflow: hidden;
}
.sys-gauge-fill {
  height: 100%;
  border-radius: 999px;
  background: var(--accent);
  transition: width 0.3s ease, background-color 0.3s ease;
}
.sys-gauge-fill.sys-gauge-high { background: var(--risk-high); }

/* Bande d'historique : réutilise .usage-chart telle quelle (barres en flux
   DOM, voir plus haut) mais en plus bas — un simple repère de tendance, pas
   le graphique principal de la carte. */
.sys-history { height: 40px; }

.sys-processes-toolbar { display: flex; gap: var(--sp-2); margin-bottom: var(--sp-3); }
.sys-processes-toolbar input[type="search"] { flex: 1; }

/* Tableau des process (OS et Allkin) : classe dédiée plutôt que .data-table —
   la règle mobile de l'explorateur de fichiers (@media max-width:720px plus
   haut) cible .data-table sans être scopée par vue, elle s'appliquerait à
   tort ici avec des colonnes qui ne correspondent pas. */
.proc-table-wrap {
  flex: 1;
  min-height: 0;
  overflow: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-alt);
}
.proc-table { width: 100%; border-collapse: collapse; font-size: var(--text-md); }
.proc-table thead th {
  position: sticky;
  top: 0;
  background: var(--sidebar-bg);
  color: var(--text-faint);
  text-align: left;
  font-weight: 600;
  font-size: var(--text-2xs);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  padding: var(--sp-2) var(--sp-3);
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
.proc-table thead th[data-sort-key] { cursor: pointer; user-select: none; }
.proc-table thead th[data-sort-key]:hover { color: var(--text); }
.proc-table thead th.sorted-asc::after { content: " ▲"; color: var(--accent); }
.proc-table thead th.sorted-desc::after { content: " ▼"; color: var(--accent); }
.proc-row td {
  padding: var(--sp-2) var(--sp-3);
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
  white-space: nowrap;
}
.proc-row:last-child td { border-bottom: none; }
.proc-row:hover { background: var(--sidebar-bg); }
.proc-cell-name { font-weight: 500; max-width: 22rem; overflow: hidden; text-overflow: ellipsis; }
.proc-badge {
  display: inline-block;
  min-width: 3.5em;
  font-variant-numeric: tabular-nums;
  font-size: var(--text-sm);
}
.proc-status { text-transform: capitalize; }
.proc-status-working { color: var(--accent); }
.proc-status-awaiting_approval { color: var(--risk-mid); }
.proc-table-empty { text-align: center; color: var(--text-faint); padding: var(--sp-8) var(--sp-4) !important; }

@media (max-width: 720px) {
  .system-view { padding: var(--sp-3); padding-bottom: calc(var(--sp-3) + env(safe-area-inset-bottom)); }
  .sys-cards { grid-template-columns: 1fr; }
  .sys-processes-toolbar { flex-wrap: wrap; }

  /* Même bascule tableau -> cartes que .data-row (voir plus haut), mais sur
     une classe dédiée (.proc-row) : les colonnes n'ont pas le même sens ici,
     et .data-table est une règle globale non scopée par vue. */
  .proc-table-wrap { border: none; background: transparent; overflow-x: hidden; }
  .proc-table thead { display: none; }
  .proc-table, .proc-table tbody, .proc-table tr, .proc-table td { display: block; }

  .proc-table tr.proc-row {
    display: grid;
    grid-template-columns: 1fr auto auto;
    grid-template-areas:
      "name   metric-a metric-b"
      "meta-a meta-b   meta-c";
    align-items: center;
    column-gap: var(--sp-2);
    row-gap: 2px;
    min-height: 56px;
    padding: var(--sp-2) var(--sp-3);
    margin-bottom: var(--sp-2);
    background: var(--bg-alt);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-sm);
    white-space: normal;
  }
  .proc-row:hover { background: var(--bg-alt); }
  .proc-row td { padding: 0; border-bottom: none; white-space: normal; }

  .proc-row td.proc-cell-name { grid-area: name; min-width: 0; overflow-wrap: anywhere; max-width: none; }
  .proc-row td.proc-cell-metric-a { grid-area: metric-a; justify-self: end; }
  .proc-row td.proc-cell-metric-b { grid-area: metric-b; justify-self: end; }
  .proc-row td.proc-cell-meta-a { grid-area: meta-a; font-size: var(--text-2xs); color: var(--text-faint); }
  .proc-row td.proc-cell-meta-b { grid-area: meta-b; font-size: var(--text-2xs); color: var(--text-faint); }
  .proc-row td.proc-cell-meta-c { grid-area: meta-c; font-size: var(--text-2xs); color: var(--text-faint); justify-self: end; }
  .proc-row td.proc-cell-meta-b::before { content: "· "; }
  .proc-row td.proc-cell-meta-c::before { content: "· "; }

  .proc-table-empty { padding: var(--sp-6) var(--sp-4) !important; }
}

/* ---------------------------------------------------------------------------
   Fiche d'installation — le mode d'emploi du « Sur l'écran d'accueil » d'iOS.
   Une notice, pas un formulaire : on soigne la lisibilité des trois gestes.
   ------------------------------------------------------------------------- */
.install-sheet { width: min(28rem, 92vw); gap: var(--sp-4); }
.install-sheet-head { display: flex; align-items: center; gap: var(--sp-3); }
.install-sheet-icon { border-radius: var(--radius); flex-shrink: 0; }
.install-sheet-titles { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.install-sheet-titles h2 { margin: 0; font-size: var(--text-xl); font-weight: 700; letter-spacing: -0.015em; }
.install-sheet-titles p { margin: 0; font-size: var(--text-sm); line-height: 1.5; }
.install-steps {
  margin: 0;
  padding-left: var(--sp-5);
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  font-size: var(--text-md);
  line-height: 1.6;
  color: var(--text-dim);
}
.install-steps li::marker { color: var(--accent); font-weight: 700; }
.install-steps strong { color: var(--text); font-weight: 600; }
/* L'icône de partage d'iOS, dessinée à même la phrase : c'est elle qu'on
   cherche des yeux dans la barre de Safari, pas le mot « Partager ». */
.install-share {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  padding: 1px var(--sp-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg);
  color: var(--accent);
  font-size: var(--text-sm);
  font-weight: 600;
  white-space: nowrap;
}
.install-note { margin: 0; font-size: var(--text-sm); line-height: 1.55; }

/* ---------------------------------------------------------------------------
   Création d'agent — un assistant pleine page.
   Trois zones fixes : en-tête (titre + fil des étapes), corps défilant (une
   seule étape visible), pied d'actions. Seul le corps défile : les repères
   « où j'en suis » et « comment j'en sors » ne bougent jamais.
   ------------------------------------------------------------------------- */
.create-view {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding: var(--sp-5);
  padding-bottom: calc(var(--sp-4) + env(safe-area-inset-bottom));
  padding-left: calc(var(--sp-5) + env(safe-area-inset-left));
  padding-right: calc(var(--sp-5) + env(safe-area-inset-right));
  gap: var(--sp-4);
}
.create-header { display: flex; flex-direction: column; gap: var(--sp-4); flex-shrink: 0; }
.create-header-top { display: flex; align-items: flex-start; gap: var(--sp-3); }
.create-titles { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.create-title { margin: 0; font-size: var(--text-xl); font-weight: 700; letter-spacing: -0.015em; }
.create-subtitle { margin: 0; font-size: var(--text-sm); }

/* ---- Fil des étapes ------------------------------------------------------
   Une étape franchie reste cliquable (on revient corriger sans tout refaire),
   les suivantes sont inertes : elles seraient franchies sans validation. */
.create-stepper {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-1) var(--sp-2);
  margin: 0;
  padding: 0;
  list-style: none;
}
.create-stepper-item { display: flex; align-items: center; gap: var(--sp-2); }
.create-stepper-item + .create-stepper-item::before {
  content: "";
  width: var(--sp-5);
  height: 1px;
  background: var(--border-strong);
}
.create-stepper-btn {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-1) var(--sp-2);
  background: transparent;
  border: 1px solid transparent;
  box-shadow: none;
  color: var(--text-faint);
  font-size: var(--text-sm);
  font-weight: 600;
}
.create-stepper-btn:hover:not(:disabled) {
  background: var(--bg-alt);
  border-color: var(--border);
  color: var(--text);
  box-shadow: none;
  transform: none;
}
.create-stepper-btn:disabled { opacity: 1; cursor: default; }
.create-stepper-num {
  display: grid;
  place-items: center;
  width: 1.55rem;
  height: 1.55rem;
  flex-shrink: 0;
  border-radius: 999px;
  border: 1px solid var(--border-strong);
  font-size: var(--text-2xs);
  font-variant-numeric: tabular-nums;
}
.create-stepper-item.current .create-stepper-btn { color: var(--text); }
.create-stepper-item.current .create-stepper-num {
  background: var(--accent);
  border-color: transparent;
  color: var(--accent-contrast);
}
.create-stepper-item.done .create-stepper-btn { color: var(--text-dim); }
.create-stepper-item.done .create-stepper-num {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
}

/* ---- Corps : une étape à la fois, dans une colonne de lecture bornée ----- */
.create-body { flex: 1; min-height: 0; overflow-y: auto; }
.create-step { display: flex; flex-direction: column; gap: var(--sp-3); max-width: 58rem; }
.create-step-title { margin: 0; font-size: var(--text-lg); font-weight: 650; letter-spacing: -0.01em; }
.create-step-help {
  margin: 0;
  max-width: 62ch;
  font-size: var(--text-md);
  line-height: 1.6;
  color: var(--text-dim);
}
.create-step-help code {
  font-family: var(--font-mono);
  font-size: 0.9em;
  padding: 0.1em 0.35em;
  background: var(--sidebar-bg);
  border-radius: 4px;
}

/* ---- Étape 1 : les modèles de métier ------------------------------------ */
.create-template-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: var(--sp-3);
  margin-top: var(--sp-1);
}
.create-template-card {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-4);
  text-align: left;
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: none;
  color: var(--text);
  font: inherit;
}
.create-template-card:hover {
  border-color: var(--border-strong);
  box-shadow: var(--shadow-sm);
  transform: none;
}
.create-template-card.selected {
  border-color: var(--accent);
  background: var(--accent-soft);
}
/* La base vierge n'est pas un métier : le trait discontinu la sort de la
   grille des modèles sans avoir à la déplacer ailleurs. */
.create-template-blank { border-style: dashed; }
.create-template-icon { font-size: 1.35rem; line-height: 1.4; flex-shrink: 0; }
.create-template-text { display: flex; flex-direction: column; gap: var(--sp-1); min-width: 0; }
.create-template-label { font-weight: 650; font-size: var(--text-md); }
.create-template-desc { font-size: var(--text-sm); line-height: 1.5; color: var(--text-dim); }
.create-template-highlights { display: flex; flex-wrap: wrap; gap: var(--sp-1); margin-top: var(--sp-1); }
.create-template-chip {
  padding: 2px var(--sp-2);
  border-radius: 999px;
  background: var(--sidebar-bg);
  color: var(--text-faint);
  font-size: var(--text-2xs);
  white-space: nowrap;
}

/* ---- Étape 2 : identité -------------------------------------------------- */
.create-fields {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  max-width: 34rem;
  margin-top: var(--sp-1);
}
.create-fields label { display: flex; flex-direction: column; gap: var(--sp-2); font-size: var(--text-sm); color: var(--text-dim); }

/* ---- Étape 3 : services et niveau administrateur ------------------------ */
.create-field-block {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  padding: var(--sp-4);
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  max-width: 44rem;
}
.create-field-label { font-size: var(--text-md); font-weight: 650; color: var(--text); }
.create-field-help { margin: 0; font-size: var(--text-sm); line-height: 1.55; }
.create-field-empty { margin: 0; font-size: var(--text-sm); line-height: 1.55; }
.create-service-list { display: flex; flex-direction: column; gap: var(--sp-1); margin-top: var(--sp-1); }
.create-service-list .checkbox-label {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  cursor: pointer;
}
.create-service-list .checkbox-label:hover { background: var(--sidebar-bg); }
.create-service-text { display: flex; align-items: baseline; gap: var(--sp-2); min-width: 0; }
.create-service-status { font-size: var(--text-2xs); }

.create-choice-list,
.create-checkbox-list { display: flex; flex-direction: column; gap: var(--sp-2); margin-top: var(--sp-1); }
.create-choice {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.create-choice:hover { border-color: var(--border-strong); }
.create-choice:has(input:checked) { border-color: var(--accent); background: var(--accent-soft); }
.create-choice input[type="radio"],
.create-choice input[type="checkbox"] { margin-top: 3px; flex-shrink: 0; }
.create-choice-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.create-choice-title { font-size: var(--text-md); font-weight: 650; color: var(--text); }
.create-choice-tag {
  margin-left: var(--sp-1);
  padding: 1px var(--sp-2);
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: var(--text-2xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.create-choice-desc { font-size: var(--text-sm); line-height: 1.55; color: var(--text-dim); }

/* ---- Étape 4 : récapitulatif -------------------------------------------- */
.create-recap {
  margin: var(--sp-1) 0 0;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--sp-3) var(--sp-5);
  padding: var(--sp-4);
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-size: var(--text-sm);
  max-width: 44rem;
}
.create-recap dt { color: var(--text-faint); white-space: nowrap; }
.create-recap dd { margin: 0; color: var(--text); overflow-wrap: anywhere; }

/* ---- Pied : annuler à gauche, progression à droite ---------------------- */
.create-footer {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex-shrink: 0;
  padding-top: var(--sp-3);
  border-top: 1px solid var(--border);
}
.create-footer-gap { flex: 1; }
#create-error { flex-shrink: 0; margin: 0; }

@media (max-width: 720px) {
  .help-view,
  .settings-view,
  .backup-view {
    padding: var(--sp-4) var(--sp-3);
    padding-bottom: calc(var(--sp-4) + env(safe-area-inset-bottom));
  }
  /* Plus de place pour deux colonnes : la nav passe en rangée horizontale
     défilante au-dessus du panneau, comme la barre d'onglets. */
  .settings-layout { flex-direction: column; gap: var(--sp-3); }
  .settings-nav {
    position: static;
    width: 100%;
    flex-direction: row;
    gap: var(--sp-1);
    overflow-x: auto;
  }
  .settings-nav-item { flex-shrink: 0; }
  .create-view { padding: var(--sp-4) var(--sp-3); padding-bottom: calc(var(--sp-3) + env(safe-area-inset-bottom)); }
  /* Le fil des étapes garde ses pastilles, mais perd libellés et traits de
     liaison : à cette largeur ils poussent la barre sur deux lignes. */
  .create-stepper { gap: var(--sp-2); }
  .create-stepper-item + .create-stepper-item::before { width: var(--sp-3); }
  .create-stepper-item:not(.current) .create-stepper-label { display: none; }
  .create-template-grid { grid-template-columns: 1fr; }
  .create-recap { grid-template-columns: 1fr; gap: var(--sp-1); }
  .create-recap dd { margin-bottom: var(--sp-2); }
  /* Cibles tactiles : ce sont les seuls boutons de la page. */
  .create-footer button { min-height: 2.75rem; }
  .create-service-list .checkbox-label { min-height: 2.75rem; }
  /* Cible tactile : ce sont les seules entrées du menu de la barre latérale. */
  .sidebar-menu-item { min-height: 2.75rem; }
  /* Le poids de la section passe à la ligne plutôt que d'écraser le libellé. */
  .backup-section { flex-wrap: wrap; }
  .backup-section-size { width: 100%; padding-left: calc(1rem + var(--sp-3)); }
}

/* ---------------------------------------------------------------------------
   Impression — sert l'export PDF de la conversation (menu de la conversation).
   Le document imprimé est construit à part dans #print-doc : imprimer la vue
   de travail donnerait la conversation tronquée à sa zone de défilement, avec
   la sidebar et le composeur par-dessus. Ici, l'application entière disparaît
   et seul ce document reste, en noir sur blanc.
   ------------------------------------------------------------------------- */
#print-doc { display: none; }

@media print {
  #login-screen, #app-screen { display: none !important; }
  /* Le fond de l'application (et sa variante sombre) n'a rien à faire ici. */
  body { background: #fff; }

  #print-doc {
    display: block;
    max-width: 100%;
    color: #000;
    background: #fff;
    font-family: var(--font-ui);
    font-size: 11pt;
    line-height: 1.5;
  }

  .print-title { margin: 0 0 0.2cm; font-size: 18pt; }
  .print-subtitle { margin: 0 0 0.8cm; color: #444; font-size: 10pt; }

  /* Un message ne doit pas être coupé entre deux pages si l'on peut l'éviter. */
  .print-msg {
    break-inside: avoid;
    margin-bottom: 0.6cm;
    padding-top: 0.3cm;
    border-top: 1px solid #ccc;
  }
  .print-msg-meta { margin: 0 0 0.15cm; font-weight: 700; font-size: 10pt; }
  /* Repli texte brut (messages de l'utilisateur) : les retours à la ligne
     saisis sont significatifs. Le markdown rendu, lui, porte ses propres blocs. */
  .print-msg-plain { white-space: pre-wrap; }
  /* Le markdown rendu garde ses balises : on neutralise seulement les couleurs
     d'interface, illisibles ou coûteuses à l'impression. */
  .print-msg-body .md-code, .print-msg-body .md-code-inline {
    background: #f2f2f2;
    color: #000;
    border-color: #ccc;
  }
  .print-msg-body a { color: #000; text-decoration: underline; }
  .print-msg-body .md-table { border-collapse: collapse; }
  .print-msg-body .md-table th, .print-msg-body .md-table td { border: 1px solid #ccc; padding: 2pt 4pt; }

  @page { margin: 1.6cm; }
}
