/* ══════════════════════════════════════════════════════════════════════════
   AEL STUDIO — LE SOCLE VISUEL DES TROIS OUTILS
   Prompt Engine IA (index.html) · Prompt Engine Tube (tube.html) ·
   Atelier (site/vitrine.html)
   ══════════════════════════════════════════════════════════════════════════
   Victor, 26/07/2026 : « J'en ai marre d'avoir un icône sur un onglet et pas
   d'icône sur l'onglet suivant, d'avoir des décalages de pixels entre 2 pages
   alors que les tailles sont équivalentes. »

   ⚠️⚠️ CE FICHIER EST LE SEUL ENDROIT OÙ S'ÉCRIT UNE VALEUR VISUELLE.
   `tube.css` et `atelier.css` ne portent QUE des couleurs et des composants
   qui n'existent nulle part ailleurs. Un composant présent dans deux outils a
   UNE définition, ici. Vérifiable : `node server/tools/check-uniformite.js`.

   ── 1 · ÉCARTS ──────────────────────────────────────────────────────────
   `--sb-gap: 12px` — entre deux blocs d'une colonne latérale, à gauche comme
   à droite, dans les trois outils. Les tuiles d'une liste (`.mode-list`) :
   12 px aussi. Un écart plus grand se demande par `.sb-gap24` (24 px), jamais
   par une marge improvisée.
   ⚠️ Un enfant direct de colonne ne décide JAMAIS de son écart : ses marges
   verticales sont remises à zéro. Trois classes seulement ont le droit d'y
   déroger : `.sb-gap24`, `.sb-push` (colle en bas), `.sb-sep` (le filet).
   ⚠️ AUCUN `style="margin-…"` EN LIGNE DANS UNE COLONNE : un style en ligne
   l'emporte sur la remise à zéro et rouvre le problème à chaque fois.

   ── 2 · TYPOGRAPHIE ─────────────────────────────────────────────────────
   Deux polices, deux rôles, aucune troisième :
     · `--font-ui`      Inter — TOUTE l'interface.
     · `--font-display` DM Serif Display — les seuls TITRES (marque, titre de
                        modale, nom d'une fiche, grand chiffre d'un tableau).
   Une taille par rôle, et on réutilise le rôle plutôt que le nombre :
     10px  = un titre de section en capitales (`.sidebar-label`,
             `.hist-fs-label`, `.sb-fold-title`) — interlettrage .25em.
     11px  = une mention secondaire, un état, une aide.
     12px  = un bouton, un onglet, une entrée de menu.
     13px  = un champ de saisie, une ligne de liste.
     14px  = le corps de texte (`body`).
   ⚠️ Les polices sont les MÊMES dans les deux thèmes (règle du 15/07) : un
   thème ne change QUE des couleurs. Jamais la typo, jamais les arrondis
   (`--r`), jamais les tailles — sinon la forme dit quel produit est ouvert.

   ── 3 · ICÔNES ──────────────────────────────────────────────────────────
   La règle est par FAMILLE, et elle est absolue à l'intérieur d'une famille :
   soit toutes les entrées en portent une, soit aucune.
     · Onglet (`.nav-tab`)                        → TOUJOURS une icône.
     · Entrée de filtre (`.hist-filter-btn`,
       `.hist-date-btn`)                          → TOUJOURS, « tout » compris.
       ⚠️ L'entrée « tout » d'une liste porte `◎` — c'est elle qu'on oublie,
       et une seule entrée sans icône décale tout le bloc à la lecture.
     · Bouton d'une colonne (`.svc-refresh-btn`,
       `.trash-btn`, `.quick-link`, `.cal-create`) → TOUJOURS.
     · Titre de carte pliable (`.sb-fold-title`,
       `.fs-cat-head`, `.cal-fold-head`)          → JAMAIS. Il porte déjà la
       flèche ▶, qui est son signe ; une icône en plus ferait deux glyphes
       pour une seule ligne de 10 px en capitales.
   ⚠️ Un compte affiché à côté d'un libellé est une PASTILLE `.hist-fcount`,
   jamais un « (3) » entre parenthèses.

   ── 4 · CE QUI A LE DROIT DE DIFFÉRER ENTRE LES TROIS OUTILS ────────────
   La COULEUR, et rien d'autre. Le contenu des listes, oui (deux métiers) —
   leur forme, non.
   ══════════════════════════════════════════════════════════════════════════ */
* { box-sizing: border-box; margin: 0; padding: 0; }
:root {
  --bg:#0b0b0b; --surface:#111; --surface2:#181818; --surface3:#1f1f1f;
  --border:#252525; --border2:#303030;
  --cream:#f0ece2; --cream2:#b8b0a0; --cream3:#666058;
  --gold:#c9a96e; --gold2:#9c7c44; --gold3:#5a4520;
  --sfw:#5a9e72; --sfw-dim:#0f2018;
  --vid:#5a7abf; --vid-dim:#0f1828;
  --nsfw:#bf5a5a; --nsfw-dim:#280f0f;
  --warn:#d4884a; --warn-dim:#261408;
  --modified:#7a6abf; --modified-dim:#1a1828;
  --r:8px;
  /* ══ L'ÉCART D'UNE COLONNE LATÉRALE — UNE SEULE VALEUR (Victor, 26/07) ══
     12 px entre deux blocs d'un encadré, à gauche comme à droite, dans les
     TROIS outils. Changer ce nombre ici les change tous.
     ⚠️ Les colonnes ne partageaient PAS le même conteneur, et c'est ce qui a
     fait échouer les passes précédentes : `.sidebar` était à .6rem (9,6 px),
     `#sidebar-right-global` à 1rem (16), `.hist-filter-scroll` (Bibliothèque
     et Historique) à 4 px et `.cal-side-body` (Calendrier) à 1rem. Quatre
     écarts pour une seule idée. Ils lisent tous cette variable maintenant.
     ⚠️ Un enfant DIRECT de colonne ne décide pas de son écart : ses marges
     verticales sont remises à zéro plus bas. Pour un écart plus grand, il y a
     `.sb-gap24` — pas un `margin` improvisé. */
  --sb-gap:12px;
  /* Largeur de la colonne de gauche des panneaux API. Une seule valeur pour
     Tickets, LinkFORGE, InstaBOT et FanBOT : sans ça la colonne saute de
     300 à 360 px d'une tuile à l'autre (Marcel, 17/07). */
  --api-col:340px;
  --test-col:400px;   /* colonne des zones de test bots + preview LinkFORGE (telephone 360px + marges) */
  /* Polices — surchargées par le thème shadow */
  --font-ui:'Inter','Segoe UI',system-ui,sans-serif;
  --font-display:'DM Serif Display',Georgia,serif;
  /* Lampe qui suit le curseur. `--lamp-teinte` est un triplet RVB SANS
     rgb() autour : il est injecté dans rgba() avec des opacités variables. */
  --mx:50vw; --my:30vh; --lamp:190px; --lamp-teinte:181,123,255;
}

/* ══════════════════════════════════════════════════════════
   THÈME SHADOW — "AEL Studio · Build fantôme"
   Uniquement un override de variables : aucune règle de layout
   n'est touchée, donc aucun risque pour la structure.
   ══════════════════════════════════════════════════════════ */
[data-theme="shadow"] {
  --bg:#08060F; --surface:#120F1D; --surface2:#1A1528; --surface3:#221B34;
  --border:#2A2140; --border2:#3A2E58;
  --cream:#E8E4F0; --cream2:#A79EC2; --cream3:#6E6690;
  --gold:#B57BFF; --gold2:#8B5CD6; --gold3:#2B1C4A;
  --sfw:#3BD9A4; --sfw-dim:#0A2A22;
  --vid:#4DA6FF; --vid-dim:#0A1E33;
  --nsfw:#FF3B5C; --nsfw-dim:#2E0A14;
  --warn:#F0A93B; --warn-dim:#2E1E08;
  --modified:#B57BFF; --modified-dim:#1E1235;
  --r:3px;
  /* Police alignée sur le thème classique (Marcel, 15/07) : le thème
     ne change plus que les couleurs, jamais la typo ni les tailles. */
  --font-ui:'Inter','Segoe UI',system-ui,sans-serif;
  --font-display:'DM Serif Display',Georgia,serif;
}
/* Grille de scan de document en fond */
[data-theme="shadow"] body::before {
  content:""; position:fixed; inset:0; pointer-events:none; z-index:0; opacity:.45;
  background-image:linear-gradient(var(--border) 1px,transparent 1px),linear-gradient(90deg,var(--border) 1px,transparent 1px);
  background-size:64px 64px;
  mask-image:radial-gradient(ellipse 80% 60% at 50% 0%,#000,transparent 75%);
  -webkit-mask-image:radial-gradient(ellipse 80% 60% at 50% 0%,#000,transparent 75%);
}
/* Les overrides de typo/taille du thème shadow ont été retirés (15/07) :
   brand-name, sidebar-label et mbtn-title gardent la police et la taille
   du thème classique. Ne pas les réintroduire. */
[data-theme="shadow"] .mode-btn.sel-sfw,
[data-theme="shadow"] .mode-btn.sel-nsfw,
[data-theme="shadow"] .mode-btn.sel-vid,
[data-theme="shadow"] .mode-btn.sel-warn,
[data-theme="shadow"] .mode-btn.sel-gold { box-shadow:inset 0 0 22px rgba(181,123,255,.10); }
[data-theme="shadow"] .gen-btn:not(:disabled):hover { box-shadow:0 0 22px rgba(181,123,255,.35); }

/* ══ LAMPE — suit le curseur, activable ══
   Présente dans LES DEUX produits (Victor, 22/07). La couleur ne vit plus
   ici : chaque thème pose `--lamp-teinte` (violet en Shadow, rouge en Tube),
   donc le halo est toujours celui du produit ouvert. Une seule règle à tenir
   au lieu d'une par thème. */
#uv-lamp { display:none; }
[data-lamp="on"] #uv-lamp {
  display:block; position:fixed; inset:0; z-index:850; pointer-events:none;
  background:radial-gradient(circle var(--lamp) at var(--mx) var(--my),
    rgba(var(--lamp-teinte), .16) 0%, rgba(var(--lamp-teinte), .06) 45%, transparent 72%);
  mix-blend-mode:screen;
}
/* Coquille d'app : hauteur FIXE, jamais de scroll de page.
   `min-height:100vh` laissait la page grandir au-delà de l'écran → sur
   un écran plus court, la sidebar droite débordait et se faisait couper
   (Marcel, 15/07). Avec `height` + `overflow:hidden`, ce sont les zones
   internes (.sidebar, .sidebar-right, .views-zone) qui scrollent. */
html { overflow:hidden; }
body { background:var(--bg); color:var(--cream); font-family:var(--font-ui); font-size:14px; line-height:1.6; height:100vh; overflow:hidden; display:flex; flex-direction:column; }

/* TOPBAR */
.topbar { background:var(--surface); border-bottom:1px solid var(--border); padding:0 1.5rem; height:56px; display:flex; align-items:center; justify-content:space-between; position:sticky; top:0; z-index:200; gap:1rem; position:relative; }
/* Groupe de boutons de DROITE de la barre du haut (bascule de produit, bulle
   profil…). ⚠️ C'était un `style="display:flex;align-items:center;gap:8px;
   margin-left:auto"` recopié en ligne dans index.html, tube.html et
   vitrine.html — donc trois écritures d'une même valeur, et l'Atelier avait
   fini par dériver. Une classe, trois lecteurs (Victor, 26/07). */
.topbar-actions { display:flex; align-items:center; gap:8px; margin-left:auto; }
/* Bulle centrale (sélecteur d'IA / de chaîne). Centrée en absolu sur grand
   écran ; repasse EN FLUX quand la place manque (voir media queries) pour ne
   plus chevaucher la marque à gauche ni les boutons à droite. */
.header-center { position:absolute; left:50%; transform:translateX(-50%); }
.brand { display:flex; align-items:center; gap:8px; flex-shrink:0; }
/* AEL Studio = l'application. */
.brand-name {
  font-family:var(--font-display); font-size:17px; color:var(--gold);
  font-weight:700; letter-spacing:.06em;
}
.brand-sep { color:var(--cream3); font-size:10px; }
.brand-sub { font-size:11px; color:var(--cream3); letter-spacing:.2em; text-transform:uppercase; font-weight:300; }

/* ACCÈS RAPIDE HEADER */
.nav-quick-link {
  font-size:11px; color:var(--cream3); text-decoration:none;
  padding:4px 10px; border:1px solid var(--border); border-radius:6px;
  background:var(--surface2); transition:all .15s; white-space:nowrap;
  display:flex; align-items:center;
}
.nav-quick-link:hover { color:var(--gold); border-color:var(--gold2); background:var(--gold3); }

/* DROPDOWN */
.pd-wrap { position:relative; }
/* Largeur doublée (Victor, 20/07) : min-width sur le trigger, le nom occupe
   l'espace, la flèche reste collée à droite. Le menu suit la même largeur. */
.pd-trigger { display:flex; align-items:center; gap:8px; background:var(--surface2); border:1px solid var(--border2); border-radius:20px; padding:6px 18px; cursor:pointer; font-family:inherit; color:var(--cream); font-size:13px; font-weight:500; transition:all .15s; white-space:nowrap; min-width:300px; }
/* Le nom occupe toute la largeur restante et reste centré entre la pastille
   et la flèche. Deux sélecteurs et non un : Prompt Engine IA nomme l'élément
   `header-pd-name`, Tube `tb-chan-name` — sans la seconde ligne, le nom de la
   chaîne était collé à gauche dans la bulle (Victor, 22/07). */
.pd-trigger #header-pd-name,
.pd-trigger #tb-chan-name { flex:1; text-align:center; }
.pd-trigger:hover,.pd-trigger.open { border-color:var(--gold); color:var(--gold); }
.pd-dot { width:7px; height:7px; border-radius:50%; background:var(--gold); flex-shrink:0; transition:background .2s; }
.pd-arrow { font-size:10px; color:var(--cream3); transition:transform .2s; }
.pd-trigger.open .pd-arrow { transform:rotate(180deg); }
.pd-menu { display:none; position:absolute; top:calc(100% + 8px); left:50%; transform:translateX(-50%); background:var(--surface); border:1px solid var(--border2); border-radius:var(--r); min-width:400px; z-index:300; overflow:hidden; box-shadow:0 8px 24px rgba(0,0,0,.5); }
.pd-menu.open { display:block; }
.pd-label { padding:.6rem 1rem .4rem; font-size:10px; letter-spacing:.2em; text-transform:uppercase; color:var(--cream3); font-weight:500; border-bottom:1px solid var(--border); }
.pd-settings-btn {
  background:none; border:none; cursor:pointer; font-size:12px;
  color:var(--cream3); padding:2px 5px; border-radius:4px;
  transition:all .12s; flex-shrink:0; font-family:inherit;
}
.pd-settings-btn:hover { color:var(--gold); background:var(--gold3); }
.pd-item { display:flex; align-items:center; gap:8px; padding:.625rem 1rem; font-size:13px; color:var(--cream2); cursor:pointer; transition:background .12s; border:none; background:none; width:100%; font-family:inherit; text-align:left; }
.pd-item:hover { background:var(--surface2); color:var(--cream); }
.pd-item.active { color:var(--gold); }
.pd-item-dot { width:6px; height:6px; border-radius:50%; background:var(--cream3); flex-shrink:0; }
.pd-item.active .pd-item-dot { background:var(--gold); }
/* Ligne d'IA avec stylo d'édition à droite */
.pd-item-row { display:flex; align-items:center; gap:8px; padding:.5rem .625rem .5rem 1rem; font-size:13px; color:var(--cream2); transition:background .12s; }
/* Le nom et son étiquette sont empilés : l'étiquette passe SOUS le nom
   (Marcel, 15/07) — à droite elle poussait la ligne en largeur. */
.pd-item-main { align-items:flex-start; }
.pd-item-names { display:flex; flex-direction:column; align-items:flex-start; gap:2px; min-width:0; }
.pd-item-dot { margin-top:5px; }
.pd-item-row:hover { background:var(--surface2); }
.pd-item-row.active { color:var(--gold); }
.pd-item-main { flex:1; display:flex; align-items:center; gap:8px; background:none; border:none; color:inherit; font-family:inherit; font-size:inherit; cursor:pointer; text-align:left; padding:0; }
.pd-item-row.active .pd-item-dot { background:var(--gold); }
.pd-item-edit { background:none; border:1px solid transparent; color:var(--cream3); cursor:pointer; font-size:13px; line-height:1; padding:4px 7px; border-radius:5px; transition:all .12s; flex-shrink:0; }
.pd-item-edit:hover { color:var(--gold); background:var(--gold3); border-color:var(--gold2); }
.pd-divider { border:none; border-top:1px solid var(--border); }
.pd-add { display:flex; align-items:center; gap:8px; padding:.625rem 1rem; font-size:12px; color:var(--cream3); cursor:pointer; transition:background .12s; border:none; background:none; width:100%; font-family:inherit; }
.pd-add:hover { background:var(--surface2); color:var(--gold); }

/* Variante sidebar du dropdown */
.pd-sidebar { width:100%; }
.pd-sidebar-trigger { width:100%; border-radius:var(--r) !important; padding:.875rem .875rem .875rem 1rem !important; font-size:13px !important; border-left:3px solid var(--gold2) !important; background:var(--surface2) !important; border-color:var(--border) !important; color:var(--cream) !important; }
.pd-sidebar-trigger:hover { background:var(--surface3) !important; border-color:var(--border2) !important; border-left-color:var(--gold) !important; }
.pd-sidebar-trigger.open { border-left-color:var(--gold) !important; border-color:var(--gold2) !important; background:var(--gold3) !important; color:var(--gold) !important; }
.pd-sidebar-trigger.open .pd-arrow { transform:rotate(180deg); color:var(--gold); }
.pd-menu-sidebar { left:0; transform:none; width:100%; top:calc(100% + 4px); }

/* KEY */
.key-zone { display:flex; align-items:center; gap:6px; flex-shrink:0; }
.key-label { font-size:11px; color:var(--cream3); }
.key-input { background:var(--bg); border:1px solid var(--border2); border-radius:6px; padding:5px 10px; font-size:11px; color:var(--cream2); font-family:monospace; width:155px; outline:none; transition:border-color .15s; }
.key-input:focus { border-color:var(--gold2); }
.key-save { background:transparent; border:1px solid var(--border2); border-radius:6px; padding:5px 10px; font-size:11px; color:var(--cream3); cursor:pointer; font-family:inherit; transition:all .15s; }
.key-save:hover { border-color:var(--gold2); color:var(--gold); }

/* NAV */
.nav-tabs { background:var(--surface); border-bottom:1px solid var(--border); padding:0 1.5rem; display:flex; }
.nav-tab { padding:.75rem 1.125rem; font-size:12px; color:var(--cream3); cursor:pointer; border-bottom:2px solid transparent; transition:all .15s; background:none; border-top:none; border-left:none; border-right:none; font-family:inherit; display:flex; align-items:center; gap:6px; }
.nav-tab:hover { color:var(--cream2); }
.nav-tab.active { color:var(--gold); border-bottom-color:var(--gold); }
.nav-count { background:var(--surface3); color:var(--cream3); font-size:10px; padding:1px 6px; border-radius:10px; }
.nav-tab.active .nav-count { background:var(--gold3); color:var(--gold); }

/* LAYOUT */
.app { flex:1; display:flex; min-height:0; }
.views-zone { flex:1; display:flex; flex-direction:column; min-height:0; min-width:0; }
.view { display:none; flex:1; flex-direction:column; min-height:0; }
.view.active { display:flex; }
.gen-layout { display:flex; flex:1; min-height:0; position:relative; }

/* SIDEBAR GAUCHE */
/* ══ UN SEUL ÉCART, DES DEUX CÔTÉS (Victor, 25/07 (4)) ══
   « Les espaces entre les éléments dans les encadrés gauche et droit ne sont
   pas identiques partout, à gauche c'est moins espacé qu'à droite, applique ce
   qu'il y a à gauche. »
   Les deux colonnes étaient bien à `gap:1rem`, mais les cartes de GAUCHE
   (`.fs-cat`) portent en plus leur propre `margin-bottom:.6rem` — c'est cette
   valeur-là qu'on voyait à gauche, et c'est donc elle qui devient la règle.
   ⚠️ La même valeur est reprise dans `site/vitrine.html` (l'Atelier est une
   page autonome, cf. règle d'uniformité en tête de CLAUDE.md) : si on la
   change ici, il faut la changer là-bas AUSSI. */
.sidebar {
  width:260px; flex-shrink:0; border-right:1px solid var(--border);
  padding:1.25rem; display:flex; flex-direction:column; gap:var(--sb-gap);
  background:var(--surface); overflow-y:auto; overflow-x:hidden;
}
.sidebar-label { font-size:10px; letter-spacing:.25em; text-transform:uppercase; color:var(--cream3); font-weight:500; }

/* SIDEBAR DROITE */
.sidebar-right {
  width:260px; flex-shrink:0; border-left:1px solid var(--border);
  padding:1.25rem; display:flex; flex-direction:column; gap:var(--sb-gap);
  background:var(--surface); overflow-y:auto; overflow-x:hidden;
}
/* Écart entre tuiles d'une même liste : 12 px (Victor, 26/07 — était à 6).
   ⚠️ C'est un `gap` pur, et il doit le rester : `.mode-btn` ne porte aucune
   marge verticale, donc cette valeur est la SEULE à changer pour resserrer ou
   aérer. Ne jamais ajouter de `margin` sur une tuile — elle s'ajouterait au
   gap au lieu de fusionner (l'écart de 25 px de l'encadré droit, 25/07 (6)). */
.mode-list { display:flex; flex-direction:column; gap:12px; }
/* Toutes les tuiles (Type de contenu, Général, Messagerie, Outils) partagent
   exactement les mêmes dimensions : hauteur fixe + description sur 2 lignes. */
.mode-btn { background:var(--surface2); border:1px solid var(--border); border-radius:var(--r); padding:.875rem .875rem .875rem 1rem; cursor:pointer; transition:all .15s; text-align:left; color:var(--cream); font-family:inherit; border-left:3px solid transparent; min-height:76px; display:flex; flex-direction:column; justify-content:center; }
.mode-btn:hover { background:var(--surface3); border-color:var(--border2); }
.mode-btn.sel-sfw  { border-left-color:var(--sfw);  background:var(--sfw-dim);  border-color:var(--sfw); }
.mode-btn.sel-vid  { border-left-color:var(--vid);  background:var(--vid-dim);  border-color:var(--vid); }
.mode-btn.sel-nsfw { border-left-color:var(--nsfw); background:var(--nsfw-dim); border-color:var(--nsfw); }
.mode-btn.sel-warn { border-left-color:var(--warn); background:var(--warn-dim); border-color:var(--warn); }
.mode-btn.sel-gold { border-left-color:var(--gold); background:var(--gold3); border-color:var(--gold2); }
.mbtn-header { display:flex; align-items:center; gap:7px; margin-bottom:3px; }
.mbtn-icon { font-size:14px; }
.mbtn-title { font-size:13px; font-weight:500; }
.mbtn-desc { font-size:11px; color:var(--cream3); line-height:1.4; padding-left:21px; min-height:31px; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
/* min-height:31px = la place de 2 lignes. Elle sert dans l'onglet API où les
   descriptions des tuiles font 1 ou 2 lignes selon le libellé : sans elle, les
   tuiles n'auraient pas toutes la même hauteur.
   Dans le Générateur au contraire, les 3 descriptions tiennent sur 1 ligne et la
   réserve se voyait comme un blanc mort avant « Modèle : … » → neutralisée plus bas. */
/* Pied de tuile : le modèle d'images utilisé pour ce type de contenu.
   Remplace le bloc « Modèles utilisés » qui vivait dans la colonne droite
   (Marcel, 16/07) — l'info est lue là où elle sert, pas à l'autre bout.
   Volontairement dépouillé : une ligne de plus sous la description, même
   graisse, même couleur. Ni filet, ni mise en avant à la sélection. */
.mbtn-model { font-size:11px; color:var(--cream3); line-height:1.4; padding-left:21px; }
/* Générateur uniquement : on supprime la réserve de 2e ligne pour coller la ligne
   « Modèle : … » sous la description. Les 3 tuiles gardent la même hauteur entre elles. */
#view-generator .mbtn-desc { min-height:0; }
.mbtn-badge { display:inline-block; margin-top:6px; margin-left:21px; font-size:10px; padding:1px 7px; border-radius:4px; font-weight:500; letter-spacing:.05em; text-transform:uppercase; }
.badge-sfw  { background:var(--sfw-dim);  color:var(--sfw);  border:1px solid var(--sfw); }
.badge-vid  { background:var(--vid-dim);  color:var(--vid);  border:1px solid var(--vid); }
.badge-nsfw { background:var(--nsfw-dim); color:var(--nsfw); border:1px solid var(--nsfw); }

/* TOOLTIP D'AIDE — badge ? avec bulle au survol */
/* Les bulles d'aide « ? » ont été retirées → voir la doc « Glossaire · Termes techniques » */

/* GEN BTN */
/* Bloc d'action du Générateur : étiquette de cible + Générer + Instruction
   prioritaire. ⚠️ Le `margin-top:auto` a été retiré d'ici et remplacé par la
   classe `.sb-push` dans index.html : posé ici, il était écrasé par la remise
   à zéro des marges (`.sidebar > *`, plus bas dans ce fichier) et le bloc ne
   descendait plus en bas de colonne. Écart interne 12 px (Victor, 26/07). */
.gen-btn-wrap { display:flex; flex-direction:column; gap:var(--sb-gap); }
.gen-btn { width:100%; background:var(--gold3); border:1px solid var(--gold2); border-radius:var(--r); padding:1.25rem 1rem; font-size:15px; font-weight:600; color:var(--gold); cursor:pointer; font-family:inherit; transition:all .15s; display:flex; align-items:center; justify-content:flex-start; gap:8px; letter-spacing:.02em; }
.gen-btn:hover { background:var(--gold2); color:#000; }
.gen-btn:disabled { opacity:.4; cursor:not-allowed; }
.test-btn { width:100%; background:transparent; border:1px dashed var(--border2); border-radius:var(--r); padding:.5rem; font-size:11px; color:var(--cream3); cursor:pointer; font-family:inherit; transition:all .15s; }
.test-btn:hover { border-color:var(--warn); color:var(--warn); }
.val-alert { background:var(--warn-dim); border:1px solid var(--warn); border-radius:var(--r); padding:.625rem .875rem; font-size:11px; color:var(--warn); display:none; margin-top:4px; line-height:1.5; }
.val-alert.show { display:block; }

/* OVERRIDE (déroulant) */
.override-wrap { border:1px solid var(--border2); border-radius:var(--r); overflow:hidden; background:var(--surface2); }
.override-toggle {
  width:100%; display:flex; align-items:center; justify-content:space-between;
  background:transparent; border:none; padding:.7rem .875rem;
  font-size:11px; font-weight:500; color:var(--vid); font-family:inherit;
  cursor:pointer; text-align:left; transition:background .15s;
}
.override-toggle:hover { background:var(--surface3); }
.override-body { padding:.75rem .875rem; border-top:1px solid var(--border2); display:flex; flex-direction:column; gap:.5rem; }
.override-block-title { font-size:10px; letter-spacing:.18em; text-transform:uppercase; color:var(--vid); font-weight:500; }
.override-block textarea,
.override-body textarea {
  width:100%; background:var(--bg); border:1px solid var(--border);
  border-radius:6px; outline:none; font-size:12px; color:var(--cream);
  font-family:inherit; line-height:1.65; resize:vertical; padding:.5rem .625rem;
  min-height:120px; transition:border-color .15s; box-sizing:border-box;
}
.override-body textarea:focus { border-color:var(--vid); }
.override-body textarea::placeholder { color:var(--cream3); font-style:italic; }
.override-row { display:flex; align-items:center; gap:8px; }
.override-row-label { font-size:11px; color:var(--cream3); white-space:nowrap; }
.override-qty-select {
  background:var(--surface); border:1px solid var(--border2);
  border-radius:6px; padding:4px 8px; font-size:12px;
  color:var(--cream); font-family:inherit; outline:none; cursor:pointer;
  transition:border-color .15s; width:52px; text-align:center;
}
.override-qty-select:focus { border-color:var(--gold); }
.override-qty-hint { font-size:11px; color:var(--cream3); flex:1; }
.override-gen-btn {
  width:100%; background:var(--gold3); border:1px solid var(--gold2);
  border-radius:6px; padding:.55rem; font-size:12px; font-weight:500;
  color:var(--gold); cursor:pointer; font-family:inherit; transition:all .15s;
}
.override-gen-btn:hover { background:var(--gold2); color:#000; }
.override-gen-btn:disabled { opacity:.4; cursor:not-allowed; }
.override-gen-btn:disabled:hover { background:var(--gold3); color:var(--gold); border-color:var(--gold2); }
.override-clear-btn {
  background:none; border:none; cursor:pointer; font-size:10px;
  color:var(--cream3); font-family:inherit; transition:color .15s; padding:0;
}
.override-clear-btn:hover { color:var(--nsfw); }

/* OUTPUT */
.main-output { flex:1; overflow:hidden; min-width:0; display:flex; flex-direction:column; }
#gen-standard-view { padding:1.5rem; flex:1; overflow-y:auto; height:100%; display:flex; flex-direction:column; }
#gen-lora-view { flex:1; display:flex; flex-direction:column; min-height:0; overflow:hidden; height:100%; }
.notice { background:var(--warn-dim); border-left:3px solid var(--warn); border-radius:0 var(--r) var(--r) 0; padding:.7rem 1rem; font-size:11.5px; color:var(--cream2); margin-bottom:1.25rem; }
.notice strong { color:var(--warn); }
.notice { flex-shrink:0; }
.state-idle { display:flex; flex:1; flex-direction:column; align-items:center; justify-content:center; min-height:300px; gap:10px; text-align:center; color:var(--cream3); }
.idle-glyph { font-family:var(--font-display); font-size:42px; color:var(--border2); }
.idle-text { font-size:12px; line-height:1.7; }
.state-loading { display:none; flex:1; align-items:center; justify-content:center; min-height:300px; gap:10px; }
.dots { display:flex; gap:5px; }
.dot { width:5px; height:5px; border-radius:50%; background:var(--gold2); animation:pulse 1.2s ease-in-out infinite; }
.dot:nth-child(2){animation-delay:.2s}.dot:nth-child(3){animation-delay:.4s}
@keyframes pulse{0%,80%,100%{opacity:.2;transform:scale(.7)}40%{opacity:1;transform:scale(1)}}
.loading-text { font-size:12px; color:var(--cream3); }
.state-results { display:none; }
.results-header { display:flex; align-items:center; justify-content:space-between; margin-bottom:1rem; }
.results-meta { font-size:11px; color:var(--cream3); }
.results-meta strong { color:var(--gold); font-weight:500; }
.results-actions { display:flex; gap:6px; }
.act-btn { background:transparent; border:1px solid var(--border2); border-radius:6px; padding:5px 12px; font-size:11px; color:var(--cream3); cursor:pointer; font-family:inherit; transition:all .15s; }
.act-btn:hover { border-color:var(--gold2); color:var(--gold); }
.act-btn.primary { background:var(--gold3); border-color:var(--gold2); color:var(--gold); font-weight:500; }
.act-btn.primary:hover { background:var(--gold2); color:#000; }

/* PROMPT BLOCKS */
.prompt-block { background:var(--surface); border:1px solid var(--border); border-left:3px solid var(--border2); border-radius:0 var(--r) var(--r) 0; margin-bottom:10px; overflow:hidden; transition:border-left-color .15s; }
.prompt-block:hover { border-left-color:var(--gold2); }
.pb-number { font-size:10px; letter-spacing:.2em; color:var(--cream3); text-transform:uppercase; padding:.6rem 1rem 0; font-weight:500; }
.pb-histoire { padding:.5rem 1rem .75rem; font-size:12.5px; color:var(--cream2); line-height:1.7; font-style:italic; }
.pb-divider { border:none; border-top:1px solid var(--border); margin:0 1rem; }
.pb-prompt-label { padding:.6rem 1rem .25rem; font-size:10px; letter-spacing:.15em; color:var(--gold2); text-transform:uppercase; font-weight:500; }
.pb-prompt-text { padding:0 1rem .875rem; font-size:12px; color:var(--cream); line-height:1.75; font-family:'Courier New',monospace; word-break:break-word; white-space:pre-wrap; }
.pb-footer { background:var(--surface2); padding:.5rem 1rem; display:flex; justify-content:flex-end; gap:6px; border-top:1px solid var(--border); }
.copy-btn { background:transparent; border:1px solid var(--border2); border-radius:5px; padding:3px 10px; font-size:10px; color:var(--cream3); cursor:pointer; font-family:inherit; transition:all .15s; }
.copy-btn:hover { color:var(--gold); border-color:var(--gold2); }
.params-box { background:var(--bg); border:1px solid var(--border); border-left:3px solid var(--gold2); border-radius:0 var(--r) var(--r) 0; padding:1rem 1.125rem; margin-top:4px; }
.params-title { font-size:10px; letter-spacing:.2em; text-transform:uppercase; color:var(--gold2); font-weight:500; margin-bottom:.6rem; }
.params-content { font-size:12px; color:var(--cream2); line-height:1.8; white-space:pre-wrap; font-family:'Courier New',monospace; }

/* HISTORY */
/* Colonne flex : l'en-tête garde sa hauteur, #history-list prend le reste.
   Sans ça, .history-empty (hauteur fixe) se collait sous l'en-tête au lieu
   d'être centré, contrairement à l'état vide du Générateur (Marcel, 16/07). */
/* ══ UN SEUL RETRAIT POUR LA ZONE CENTRALE (Victor, 26/07) ══
   « Aligne le titre "Historique" à "Bibliothèque" … en te basant sur la
   bibliothèque. »
   ⚠️ LES RETRAITS S'ADDITIONNAIENT. La Bibliothèque n'a pas de zone à elle :
   son `#lib-root` vit DANS un `.history-view`. Elle cumulait donc le retrait
   de la zone (1.5rem 2rem) et le sien (1.25rem 1.4rem 2rem) — soit 44 px du
   haut et 54,4 px de la gauche, contre 24 et 32 pour l'Historique. D'où deux
   titres qui ne commencent pas au même endroit alors qu'ils ont la même
   police et la même taille.
   La valeur retenue est celle de la Bibliothèque (la somme d'avant), posée
   UNE fois sur la zone. `#lib-root` perd la sienne juste en dessous.
   ⚠️ Vaut aussi pour les trois `.history-view` de Tube : même zone, même
   retrait, sans quoi le décalage se serait déplacé au lieu de disparaître. */
.history-view { padding:2.75rem 3.4rem 3.5rem; flex:1; overflow-y:auto; display:flex; flex-direction:column; }
#history-list { flex:1; min-height:0; display:flex; flex-direction:column; }
/* ⚠️ MÊME EN-TÊTE QUE LA BIBLIOTHÈQUE (`.lib-head`, 26/07) : `flex-start` et
   non `center`, et le même `margin-bottom:14px`. Avec `center`, le titre se
   recentrait sur la hauteur du bouton — il descendait donc dès que le bouton
   était plus haut que lui, et ne tombait jamais à la même hauteur que celui
   de la Bibliothèque, dont l'en-tête porte deux lignes. */
.history-header { display:flex; align-items:flex-start; justify-content:space-between; gap:1rem; margin-bottom:14px; flex-wrap:wrap; }
.history-title { font-family:var(--font-display); font-size:16px; color:var(--gold); }
.history-filters { display:flex; gap:6px; margin-bottom:1.25rem; flex-wrap:wrap; }
.filter-btn { background:transparent; border:1px solid var(--border2); border-radius:16px; padding:3px 12px; font-size:11px; color:var(--cream3); cursor:pointer; font-family:inherit; transition:all .15s; }
.filter-btn:hover { border-color:var(--gold2); color:var(--gold); }
.filter-btn.active { background:var(--gold3); border-color:var(--gold2); color:var(--gold); }
/* ⚠️ GÉOMÉTRIE DE `.lib-addbtn`, valeur par valeur (26/07) : 12.5px/500,
   padding 8px 14px, rayon 8px. Les deux boutons occupent la même place dans
   le même en-tête ; à 11px et 5px 12px, celui-ci était visiblement plus petit
   d'un onglet à l'autre.
   ⚠️ LA COULEUR, ELLE, RESTE DIFFÉRENTE, ET C'EST VOULU : « ＋ Ajouter des
   images » est l'action principale (or plein), « Tout effacer » est
   destructeur — il reste discret et ne vire au rouge qu'au survol. La règle
   d'uniformité porte sur la FORME ; le rôle d'un bouton a le droit de se
   lire dans sa couleur. */
.history-clear { background:transparent; border:1px solid var(--border2); border-radius:8px; padding:8px 14px; font-size:12.5px; font-weight:500; color:var(--cream3); cursor:pointer; font-family:inherit; transition:all .15s; }
.history-clear:hover { border-color:var(--nsfw); color:var(--nsfw); }
/* ══════════════════════════════════════════════════════════
   TOUS LES MESSAGES CENTRAUX AU MÊME ENDROIT (Victor, 25/07 (4))
   ------------------------------------------------------------
   « Fais un tour complet de tous ces messages qui apparaissent au centre de
   l'écran et mets-les tous, que ce soit sur le prompt engine IA ou le prompt
   engine TUBE, aux mêmes coordonnées, c'est-à-dire centré au milieu de leur
   fenêtre. »

   ⚠️ LA CAUSE ÉTAIT UN DÉCALAGE EN `vh`, RECOPIÉ DIFFÉREMMENT PARTOUT :
   `.msg-empty` était à `margin:15vh auto 0`, `.history-empty` à
   `padding-top:15vh`, `.dsh-empty` à `padding:13vh`, `.lib-empty` à
   `margin-top:11vh`. Quatre valeurs choisies à l'œil, à quatre moments
   différents — d'où quatre hauteurs à l'écran. Et un décalage en `vh` se
   calcule sur la HAUTEUR DE LA FENÊTRE, pas sur celle du panneau : le même
   message tombait donc à un endroit différent selon la taille de l'écran, et
   selon qu'il y avait ou non un bandeau au-dessus.

   ⚠️ LA RÈGLE, désormais unique : on occupe toute la hauteur du panneau
   (`min-height:100%`) et on centre en flex. Aucun `vh` nulle part. C'est ce que
   faisait déjà `.tb-empty` côté Tube depuis le 24/07 — les autres s'alignent
   dessus au lieu que chacun invente le sien.
   ⚠️ Ne jamais réintroduire un décalage fixe ici : centrer et décaler sont
   deux intentions contradictoires, et c'est le décalage qui a produit le
   désordre.
   ══════════════════════════════════════════════════════════ */
.msg-empty, .history-empty, .dsh-empty, .lib-empty, .tb-empty {
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  min-height:100%; margin:0 auto; text-align:center;
}
.history-empty { flex:1; padding:2rem 1.5rem; gap:10px; color:var(--cream3); font-size:12px; }
.history-empty-icon { font-size:30px; opacity:.3; }

/* Section épinglés */
.pinned-section { margin-bottom:1.25rem; }
.pinned-label { font-size:10px; letter-spacing:.2em; text-transform:uppercase; color:var(--vid); font-weight:500; margin-bottom:8px; display:flex; align-items:center; gap:6px; }
.pinned-count { background:var(--vid-dim); color:var(--vid); border:1px solid var(--vid); border-radius:10px; font-size:10px; padding:1px 7px; }
.pinned-divider { border:none; border-top:1px solid var(--border2); margin:1.25rem 0; }

.history-item { background:var(--surface); border:1px solid var(--border); border-radius:var(--r); margin-bottom:10px; overflow:hidden; }
.history-item.is-fav { border-color:var(--gold2); }
.history-item.is-pinned { border-color:var(--vid); border-left:3px solid var(--vid); }
.hi-header { padding:.75rem 1rem; display:flex; align-items:center; justify-content:space-between; border-bottom:1px solid var(--border); background:var(--surface2); cursor:pointer; gap:8px; transition:background .12s; }
.hi-header:hover { background:var(--surface3); }
.history-item { transition:border-color .12s; }
.history-item:hover { border-color:var(--border2); }
.history-item.is-fav:hover { border-color:var(--gold); }
.history-item.is-pinned:hover { border-color:var(--vid); }
.hi-left { display:flex; align-items:center; gap:8px; flex-wrap:wrap; flex:1; }
.hi-badge { font-size:10px; padding:2px 8px; border-radius:4px; font-weight:500; text-transform:uppercase; letter-spacing:.05em; }
.hi-date { font-size:11px; color:var(--cream3); }
/* Badge « IA propriétaire » affiché seulement en vue Profil maître (Victor 20/07). */
.hi-persona-badge { font-size:10px; padding:2px 8px; border-radius:9px; font-weight:600; background:var(--surface3); border:1px solid var(--border2); color:var(--cream); white-space:nowrap; max-width:160px; overflow:hidden; text-overflow:ellipsis; }
.hi-right { display:flex; align-items:center; gap:6px; flex-shrink:0; }
.fav-btn { background:var(--surface3); border:1px solid var(--border2); border-radius:5px; cursor:pointer; font-size:13px; padding:4px 8px; color:var(--cream3); transition:all .15s; }
.fav-btn:hover { color:var(--gold); border-color:var(--gold2); background:var(--gold3); }
.fav-btn.is-fav { color:var(--gold); border-color:var(--gold2); background:var(--gold3); }
.pin-btn { background:var(--surface3); border:1px solid var(--border2); border-radius:5px; cursor:pointer; font-size:12px; padding:4px 7px; color:var(--cream3); transition:all .15s; }
.pin-btn:hover { color:var(--vid); border-color:var(--vid); background:var(--vid-dim); }
.pin-btn.is-pinned { color:var(--vid); border-color:var(--vid); background:var(--vid-dim); }
.hi-toggle { font-size:10px; color:var(--cream3); transition:transform .2s; }
.hi-toggle.open { transform:rotate(180deg); }
.hi-body { display:none; }
.hi-body.open { display:block; }
.hi-note-zone { padding:.75rem 1rem; border-bottom:1px solid var(--border); background:var(--bg); display:flex; align-items:flex-start; gap:8px; }
.hi-note-icon { font-size:12px; color:var(--cream3); margin-top:2px; flex-shrink:0; }
.hi-note-input { flex:1; background:transparent; border:none; outline:none; font-size:12px; color:var(--cream2); font-family:inherit; line-height:1.5; resize:none; min-height:32px; }
.hi-note-input::placeholder { color:var(--cream3); }
.hi-note-save { background:var(--sfw-dim); border:1px solid var(--sfw); border-radius:5px; padding:5px 12px; font-size:11px; color:var(--sfw); cursor:pointer; font-family:inherit; transition:all .15s; flex-shrink:0; font-weight:500; }
.hi-note-save:hover { background:var(--sfw); color:#000; }
.hi-text-zone { padding:1rem; }
.hi-text { font-size:12px; color:var(--cream2); line-height:1.7; white-space:pre-wrap; }
/* Historique déplié : un bloc distinct par prompt (Victor, 20/07). */
.hi-prompts { display:flex; flex-direction:column; gap:.6rem; }
.hi-prompt-block { border:1px solid var(--border2); border-radius:8px; overflow:hidden; background:var(--surface); border-left:3px solid var(--gold2); }
.hi-prompt-block.hi-prompt-params { border-left-color:var(--nsfw); }
.hi-prompt-head { display:flex; align-items:center; justify-content:space-between; gap:8px; padding:.4rem .7rem; background:var(--surface2); border-bottom:1px solid var(--border); }
.hi-prompt-num { font-size:10px; letter-spacing:.12em; text-transform:uppercase; font-weight:600; color:var(--gold); }
.hi-prompt-block.hi-prompt-params .hi-prompt-num { color:var(--nsfw); }
.hi-prompt-copy { background:none; border:1px solid var(--border2); border-radius:5px; padding:3px 9px; font-size:10.5px; color:var(--cream3); cursor:pointer; font-family:inherit; transition:all .15s; }
.hi-prompt-copy:hover { border-color:var(--gold2); color:var(--gold); background:var(--gold3); }
.hi-prompt-block .hi-text { padding:.7rem; }
.hi-actions { display:flex; justify-content:flex-end; margin-top:.75rem; }
.hi-copy-btn { background:var(--gold3); border:1px solid var(--gold2); border-radius:6px; padding:6px 16px; font-size:12px; font-weight:500; color:var(--gold); cursor:pointer; font-family:inherit; transition:all .15s; }
.hi-copy-btn:hover { background:var(--gold2); color:#000; }

/* ── DONNÉES ── */
.donnees-view { display:flex; flex:1; min-height:0; }

/* Sidebar des onglets Données */
.donnees-tabs { width:220px; flex-shrink:0; border-right:1px solid var(--border); background:var(--surface); padding:1rem 0; display:flex; flex-direction:column; }
.donnees-tabs-label { font-size:10px; letter-spacing:.2em; text-transform:uppercase; color:var(--cream3); font-weight:500; padding:.5rem 1.25rem 1rem; }
.d-tab { display:flex; align-items:center; justify-content:space-between; padding:.75rem 1.25rem; font-size:12px; color:var(--cream3); cursor:pointer; transition:all .15s; border-left:3px solid transparent; width:100%; background:none; border-top:none; border-bottom:none; border-right:none; font-family:inherit; text-align:left; gap:8px; }
.d-tab:hover { color:var(--cream2); background:var(--surface2); }
.d-tab.active { color:var(--gold); border-left-color:var(--gold); background:var(--surface2); }
.d-tab-name { flex:1; }
.d-tab-dot { width:6px; height:6px; border-radius:50%; background:var(--modified); display:none; flex-shrink:0; }
.d-tab.has-changes .d-tab-dot { display:block; }

/* Contenu principal Données */
.donnees-content { flex:1; overflow-y:auto; padding:1.5rem 2rem; display:flex; flex-direction:column; gap:0; }

/* Header Données */
.donnees-header { display:flex; align-items:center; justify-content:space-between; margin-bottom:1.25rem; flex-wrap:wrap; gap:8px; }
.donnees-title { font-family:var(--font-display); font-size:16px; color:var(--gold); }
.donnees-actions { display:flex; align-items:center; gap:8px; }

/* Barre « base du serveur » (mode serveur + admin uniquement).
   Volontairement discrète et à part des actions de l'IA courante : elle
   n'agit pas sur une IA mais sur TOUTE la base. */
.srv-backup-bar { display:flex; align-items:center; gap:8px; flex-wrap:wrap;
  padding:.5rem .9rem; border-bottom:1px solid var(--border); background:var(--surface2); }
.srv-backup-lbl { font-size:9px; letter-spacing:.18em; text-transform:uppercase; color:var(--gold2); }
.srv-backup-btn { background:transparent; border:1px solid var(--border2); border-radius:6px;
  padding:5px 11px; font-size:11px; color:var(--cream3); cursor:pointer; font-family:inherit; transition:all .15s; }
.srv-backup-btn:hover { border-color:var(--gold2); color:var(--gold); }
.srv-backup-btn.danger:hover { border-color:var(--nsfw); color:var(--nsfw); }
.srv-backup-btn:disabled { opacity:.45; cursor:progress; }
.srv-backup-last { margin-left:auto; font-size:10px; color:var(--cream3); font-style:italic; }

.export-btn { background:transparent; border:1px solid var(--border2); border-radius:6px; padding:6px 12px; font-size:11px; color:var(--cream3); cursor:pointer; font-family:inherit; transition:all .15s; }
.export-btn:hover { border-color:var(--vid); color:var(--vid); }
.import-btn { background:transparent; border:1px solid var(--border2); border-radius:6px; padding:6px 12px; font-size:11px; color:var(--cream3); cursor:pointer; font-family:inherit; transition:all .15s; }
.import-btn:hover { border-color:var(--sfw); color:var(--sfw); }

/* CANCEL BTN */
.cancel-btn { background:transparent; border:1px solid var(--border2); border-radius:6px; padding:6px 14px; font-size:12px; color:var(--cream3); cursor:pointer; font-family:inherit; transition:all .15s; display:none; }
.cancel-btn:hover { border-color:var(--nsfw); color:var(--nsfw); }
.cancel-btn.show { display:block; }

/* SAVE BTN — grisé par défaut */
.save-btn { background:var(--surface3); border:1px solid var(--border2); border-radius:6px; padding:6px 16px; font-size:12px; font-weight:500; color:var(--cream3); cursor:not-allowed; font-family:inherit; transition:all .2s; opacity:.45; }
.save-btn.dirty { background:var(--gold3); border-color:var(--gold2); color:var(--gold); cursor:pointer; opacity:1; }
.save-btn.dirty:hover { background:var(--gold2); color:#000; }

/* BLOC PROMPT — un seul bloc par onglet */
.prompt-editor-wrap { flex:1; display:flex; flex-direction:column; gap:8px; }

.prompt-editor-header {
  display:flex; align-items:center; justify-content:space-between;
  padding:.75rem 1rem;
  background:var(--surface2);
  border:1px solid var(--border);
  border-bottom:none;
  border-radius:var(--r) var(--r) 0 0;
}

.peh-left { display:flex; align-items:center; gap:10px; }
.peh-label { font-size:10px; letter-spacing:.2em; text-transform:uppercase; color:var(--gold2); font-weight:500; }
.peh-badge { font-size:10px; padding:2px 8px; border-radius:4px; font-weight:500; }

.peh-modified-badge {
  display:none;
  font-size:10px;
  background:var(--modified-dim);
  color:var(--modified);
  border:1px solid var(--modified);
  border-radius:10px;
  padding:1px 8px;
  font-weight:500;
}
.peh-modified-badge.show { display:inline-block; }

.peh-hint { font-size:11px; color:var(--cream3); }

.prompt-editor-body {
  border:1px solid var(--border);
  border-radius:0 0 var(--r) var(--r);
  overflow:hidden;
  transition:border-color .2s;
}
.prompt-editor-body.is-modified { border-color:var(--modified); }

.prompt-textarea {
  width:100%;
  background:var(--surface);
  border:none;
  outline:none;
  font-size:12.5px;
  color:var(--cream);
  font-family:'Courier New',monospace;
  line-height:1.8;
  padding:1.25rem;
  resize:none;
  min-height:420px;
  display:block;
}
.prompt-textarea:focus { background:var(--surface2); }
.prompt-textarea::placeholder { color:var(--cream3); }

.prompt-editor-footer {
  background:var(--surface2);
  border:1px solid var(--border);
  border-top:none;
  padding:.625rem 1rem;
  display:flex;
  align-items:center;
  justify-content:space-between;
  border-radius:0 0 var(--r) var(--r);
}
.pef-chars { font-size:11px; color:var(--cream3); }
.pef-hint { font-size:11px; color:var(--cream3); font-style:italic; }

/* INFO BOX sous le prompt */
.donnees-info-box {
  background:var(--surface);
  border:1px solid var(--border);
  border-left:3px solid var(--gold2);
  border-radius:0 var(--r) var(--r) 0;
  padding:.875rem 1rem;
  margin-top:1rem;
}
.dib-title { font-size:10px; letter-spacing:.2em; text-transform:uppercase; color:var(--gold2); font-weight:500; margin-bottom:.5rem; }
.dib-text { font-size:12px; color:var(--cream2); line-height:1.7; }
.dib-text code { background:var(--surface3); color:var(--gold); font-family:'Courier New',monospace; font-size:11px; padding:1px 5px; border-radius:3px; }

/* MODALS */
.modal-overlay { display:none; position:fixed; inset:0; background:rgba(0,0,0,.78); z-index:500; align-items:center; justify-content:center; padding:2rem; }
.modal-overlay.open { display:flex; animation:ovIn .18s ease; }
.modal-overlay.closing { animation:ovOut .16s ease forwards; }
.modal-overlay.open > .modal { animation:modalIn .26s cubic-bezier(.2,.9,.3,1.25); }
.modal-overlay.closing > .modal { animation:modalOut .16s ease forwards; }
@keyframes ovIn   { from { opacity:0 } to { opacity:1 } }
@keyframes ovOut  { from { opacity:1 } to { opacity:0 } }
@keyframes modalIn  { from { opacity:0; transform:translateY(14px) scale(.97); } to { opacity:1; transform:none; } }
@keyframes modalOut { from { opacity:1; transform:none; } to { opacity:0; transform:translateY(8px) scale(.98); } }
@media (prefers-reduced-motion: reduce) {
  .modal-overlay.open, .modal-overlay.closing,
  .modal-overlay.open > .modal, .modal-overlay.closing > .modal { animation:none; }
}

/* ── Structure header / body / footer (modals compte, partage, mails…) ── */
.modal-header {
  display:flex; align-items:flex-start; justify-content:space-between; gap:1rem;
  margin-bottom:1.25rem; padding-bottom:.875rem; border-bottom:1px solid var(--border);
}
.modal-header .modal-title { margin-bottom:0; }
.modal-close {
  background:none; border:1px solid transparent; color:var(--cream3);
  font-size:15px; line-height:1; cursor:pointer; font-family:inherit;
  width:28px; height:28px; border-radius:6px; flex-shrink:0;
  display:flex; align-items:center; justify-content:center; transition:all .15s;
  margin:-2px -4px 0 0;
}
.modal-close:hover { background:var(--surface2); border-color:var(--border2); color:var(--cream); }
/* Croix de fermeture UNIVERSELLE (23/07). Posée par openModal() sur toute
   modale qui n'a pas déjà son ✕ d'en-tête : plus besoin de scroller jusqu'au
   pied pour trouver un « Annuler ». Ancrée en haut à droite de la modale, au
   repos au-dessus du contenu. */
.modal-overlay > .modal { position:relative; }
.modal-x {
  position:absolute; top:.85rem; right:.85rem; z-index:6;
  background:var(--surface); border:1px solid var(--border); color:var(--cream3);
  font-size:15px; line-height:1; cursor:pointer; font-family:inherit;
  width:30px; height:30px; border-radius:8px;
  display:flex; align-items:center; justify-content:center; transition:all .15s;
}
.modal-x:hover { background:var(--surface2); border-color:var(--border2); color:var(--cream); }
.modal-body { display:block; }
.modal-footer {
  display:flex; gap:8px; justify-content:flex-end; align-items:center;
  margin-top:1.5rem; padding-top:1.125rem; border-top:1px solid var(--border);
}
.modal { background:var(--surface); border:1px solid var(--border2); border-radius:12px; padding:2rem; width:100%; max-width:520px; max-height:90vh; overflow-y:auto; }
/* Modale à en-tête/pied FIXES : seule la zone centrale défile. Sans ça, sur une
   petite fenêtre (zoom réduit), toute la modale défilait d'un bloc et les
   boutons du pied de la corbeille passaient hors écran (fix 19/07). */
.modal.modal-scroll { display:flex; flex-direction:column; overflow:hidden; }
.modal.modal-scroll > .modal-header,
.modal.modal-scroll > .modal-footer { flex-shrink:0; }
.modal.modal-scroll > .modal-body { flex:1 1 auto; min-height:0; overflow-y:auto; margin:0 -.5rem; padding:0 .5rem; }
.modal-title { font-family:var(--font-display); font-size:18px; color:var(--gold); margin-bottom:.25rem; }
.modal-subtitle { font-size:12px; color:var(--cream3); margin-bottom:1.5rem; line-height:1.6; }
.modal-info { background:var(--gold3); border:1px solid var(--gold2); border-radius:var(--r); padding:.75rem 1rem; font-size:12px; color:var(--gold); line-height:1.6; margin-bottom:1.25rem; }
.modal-warn { background:var(--warn-dim); border:1px solid var(--warn); border-radius:var(--r); padding:.75rem 1rem; font-size:12px; color:var(--warn); line-height:1.6; margin-bottom:1.25rem; }
.modal-section { margin-bottom:1.125rem; }
.modal-label { font-size:10px; letter-spacing:.2em; text-transform:uppercase; color:var(--cream3); font-weight:500; margin-bottom:.4rem; display:block; }
.modal-input { width:100%; background:var(--bg); border:1px solid var(--border2); border-radius:var(--r); padding:.6rem .875rem; font-size:13px; color:var(--cream); font-family:inherit; outline:none; transition:border-color .15s; }
.modal-input:focus { border-color:var(--gold2); }
.modal-textarea { width:100%; background:var(--bg); border:1px solid var(--border2); border-radius:var(--r); padding:.6rem .875rem; font-size:12px; color:var(--cream2); font-family:inherit; outline:none; transition:border-color .15s; resize:vertical; min-height:70px; line-height:1.6; }
.modal-textarea:focus { border-color:var(--gold2); color:var(--cream); }
/* La phrase d'aide SOUS une case (Victor, 26/07 : « on peut ne pas comprendre
   très rapidement ce qu'on est censé remplir »). Elle vit dans le SOCLE et non
   dans l'Atelier : dès qu'un formulaire des prompt engines aura besoin de la
   même chose, il la trouvera ici — c'est ce que la règle d'uniformité demande.
   ⚠️ 11 px = la taille « mention » du barème (main.css, en tête). */
.modal-aide { display:block; font-size:11px; color:var(--cream3); line-height:1.5; margin:.35rem 0 .1rem; }
.modal-actions { display:flex; gap:8px; justify-content:flex-end; margin-top:1.5rem; padding-top:1.25rem; border-top:1px solid var(--border); }
.modal-cancel { background:transparent; border:1px solid var(--border2); border-radius:6px; padding:7px 16px; font-size:12px; color:var(--cream3); cursor:pointer; font-family:inherit; transition:all .15s; }
.modal-cancel:hover { color:var(--cream2); }
/* Variante « action irréversible » (suppression). Elle reste DISCRÈTE au repos
   et ne devient rouge franc qu'au survol : un rouge permanent dans une barre
   d'actions se lit comme une alerte et finit par ne plus être vu.
   Écrite ici et pas dans l'Atelier — c'est le même bouton pour les trois. */
.modal-cancel.danger { color:var(--nsfw); border-color:var(--border2); }
.modal-cancel.danger:hover { border-color:var(--nsfw); color:var(--nsfw); background:var(--nsfw-dim); }
.modal-submit { background:var(--gold3); border:1px solid var(--gold2); border-radius:6px; padding:7px 20px; font-size:12px; font-weight:500; color:var(--gold); cursor:pointer; font-family:inherit; transition:all .15s; }
.modal-submit:hover:not(:disabled) { background:var(--gold2); color:#000; }
/* Un bouton d'action principale se DÉSACTIVE pendant une génération (l'Atelier
   le fait pour « ✨ Générer le profil », qui dure des minutes sur ce serveur).
   Sans cette règle il gardait son survol doré et paraissait cliquable.
   Posée dans le socle et pas côté Atelier : c'est le même bouton partout. */
.modal-submit:disabled { opacity:.5; cursor:default; }
.modal-danger { background:var(--nsfw-dim); border:1px solid var(--nsfw); border-radius:6px; padding:7px 20px; font-size:12px; font-weight:500; color:var(--nsfw); cursor:pointer; font-family:inherit; transition:all .15s; }
.modal-danger:hover { background:var(--nsfw); color:#fff; }

/* EXPORT */
.export-check-list { display:flex; flex-direction:column; gap:8px; margin-bottom:1rem; }
.export-check { display:flex; align-items:center; gap:10px; padding:.625rem .875rem; background:var(--surface2); border:1px solid var(--border); border-radius:var(--r); }
.export-check input { accent-color:var(--gold); width:14px; height:14px; cursor:pointer; }
.export-check label { font-size:13px; color:var(--cream2); cursor:pointer; flex:1; }

/* TOAST */
/* ══════════════════════════════════════════════════════════
   NOTIFICATIONS — pile unique, bas-centre (Marcel, 15/07).
   Toutes les notifs de l'app passent par ici : erreur, succès,
   info, avertissement. La nouvelle entre PAR LE BAS et pousse
   les précédentes vers le haut ; au-delà de 3, la plus ancienne
   (tout en haut) sort. `flex-direction:column-reverse` fait le
   travail : le DOM reste en ordre chronologique.
   ══════════════════════════════════════════════════════════ */
.toast-stack {
  /* Remontée au-dessus du footer (≈38px), qu'elle chevauchait avant. */
  position:fixed; bottom:4.25rem; left:50%; transform:translateX(-50%);
  display:flex; flex-direction:column-reverse; align-items:center; gap:8px;
  z-index:1200; pointer-events:none; max-width:min(560px, 90vw);
}
.toast {
  position:relative; overflow:hidden;
  display:flex; align-items:center; gap:9px;
  background:var(--gold); color:#000; font-size:12px; font-weight:500;
  padding:10px 20px 12px; border-radius:6px; box-shadow:0 8px 28px rgba(0,0,0,.5);
  opacity:0; transform:translateY(14px) scale(.96);
  transition:opacity .2s ease, transform .24s cubic-bezier(.2,.9,.3,1.3), margin .24s ease;
  pointer-events:none; max-width:100%;
}
/* Barre de décompte : se vide de gauche à droite pendant la durée de vie.
   Lancée seulement à l'ajout de .show, sinon l'animation démarrerait
   avant que la notif ne soit visible. */
.toast::after {
  content:""; position:absolute; left:0; bottom:0; height:2px; width:100%;
  background:rgba(0,0,0,.4); transform:scaleX(0); transform-origin:left;
}
.toast.show::after { animation:toastCountdown linear forwards; animation-duration:var(--toast-life,2800ms); }
.toast.leaving::after { animation:none; }
@keyframes toastCountdown { from { transform:scaleX(1); } to { transform:scaleX(0); } }
.toast.show { opacity:1; transform:translateY(0) scale(1); }
/* Sortie par le haut : la plus vieille s'efface en montant. */
.toast.leaving { opacity:0; transform:translateY(-10px) scale(.96); margin-bottom:-42px; }
.toast-ico { font-size:13px; flex-shrink:0; line-height:1; }
.toast-msg { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.toast.t-success { background:var(--sfw);  color:#04120a; }
.toast.t-error   { background:var(--nsfw); color:#fff; }
.toast.t-error::after, .toast.t-info::after { background:rgba(255,255,255,.45); }
.toast.t-warn    { background:var(--warn); color:#160c02; }
.toast.t-info    { background:var(--vid);  color:#04101c; }
#file-import { display:none; }

/* ONGLETS MODAL ADD */
/* ⚠️ PLUS DE `flex:1` (Victor, 26/07 : « les boutons Décrire et Se faire guider
   prennent toute la largeur, ce n'est pas beau »).
   Un onglet était étiré pour remplir sa rangée : à cinq onglets ça passait
   inaperçu, à DEUX chacun prenait la moitié de la fenêtre. Un onglet se
   dimensionne sur son libellé — c'est ce que font déjà `.nav-tab` (la barre
   principale) et `.auth-tab`. La rangée `.add-tabs` les aligne à gauche.
   ⚠️ Corrigé ICI et pas dans l'Atelier : la même rangée sert aux onglets de la
   file OpenArt et à ceux des Agents, qui avaient le défaut inverse — trois
   onglets étirés sur toute la largeur d'une modale. */
.add-tab {
  background:var(--surface2); border:1px solid var(--border);
  border-radius:6px; padding:.45rem .85rem; font-size:11px;
  color:var(--cream3); cursor:pointer; font-family:inherit; transition:all .15s;
  white-space:nowrap;
}
.add-tab:hover { color:var(--cream); border-color:var(--border2); }
.add-tab.active { background:var(--gold3); border-color:var(--gold2); color:var(--gold); font-weight:500; }
/* La RANGÉE qui porte les onglets. Elle était écrite en style en ligne à
   chaque usage (`display:flex;gap:4px;margin-bottom:.75rem;flex-wrap:wrap`) —
   donc autant d'écritures que de fenêtres à onglets, dans les trois outils.
   Une classe (Victor, 26/07). */
.add-tabs { display:flex; gap:4px; margin-bottom:.75rem; flex-wrap:wrap; }

/* FOOTER
   ⚠️ Il porte désormais les MESURES DU SERVEUR (Victor, 25/07) en plus de la
   mention légale. Passé en flex : la mention à gauche, les pastilles à droite,
   et sur écran étroit tout se replie au centre plutôt que de déborder.
   Le code couleur (vert / orange / rouge) est défini plus bas — il est commun
   aux deux produits, comme tout le reste de la forme. */
.app-footer {
  display:flex; align-items:center; justify-content:space-between; gap:1rem;
  flex-wrap:wrap;
  text-align:center; font-size:10px; color:var(--cream3);
  padding:.625rem 1rem; border-top:1px solid var(--border);
  background:var(--surface); letter-spacing:.05em; flex-shrink:0;
}
.app-footer-legal { flex:1 1 auto; text-align:left; }
@media (max-width:640px){
  .app-footer { justify-content:center; }
  .app-footer-legal { flex-basis:100%; text-align:center; }
}

/* Les pastilles de mesure. `tabular-nums` : sans ça, « 9 % » puis « 10 % »
   décalent tout le bloc à chaque rafraîchissement, et le pied de page tremble
   en permanence. */
.perf-stats { display:flex; align-items:center; gap:.85rem; flex-wrap:wrap; justify-content:center; }
.perf-item {
  display:inline-flex; align-items:center; gap:5px;
  font-size:10px; letter-spacing:.05em; color:var(--cream3);
  font-variant-numeric:tabular-nums;
}
.perf-item b { color:var(--cream2); font-weight:600; }
.perf-dot { width:7px; height:7px; border-radius:50%; background:var(--cream3); flex-shrink:0; }
/* Le POINT porte la couleur, pas le texte : un pied de page où trois libellés
   passent au rouge vif se lit comme une alerte permanente. Le point suffit à
   attirer l'œil, et le texte reste lisible. */
.perf-vert   .perf-dot { background:var(--sfw); }
.perf-orange .perf-dot { background:var(--warn); }
.perf-rouge  .perf-dot { background:var(--nsfw); }
.perf-rouge  b { color:var(--nsfw); }
.perf-na     .perf-dot { background:var(--border2); }
.perf-na     { opacity:.65; }

/* ── BOUTON PARAMÈTRES sidebar ── */
.params-btn {
  width:100%; background:var(--gold3); border:1px solid var(--gold2);
  border-radius:var(--r); padding:.7rem 1rem; font-size:12px; font-weight:500;
  color:var(--gold); cursor:pointer; font-family:inherit; transition:all .15s;
  text-align:left; display:flex; align-items:center; justify-content:flex-start; gap:8px;
}
.params-btn:hover { background:var(--gold2); color:#000; }

/* ── QUICK LINKS sidebar droite ── */
.quick-link {
  font-size:11px; color:var(--cream2); text-decoration:none;
  display:flex; align-items:center; gap:5px; padding:5px 8px;
  background:var(--surface3); border-radius:5px; transition:all .15s;
}
.quick-link:hover { color:var(--sfw); background:var(--sfw-dim); }

/* ── ONGLET API — tuiles ── */
.api-tile-grid { display:grid; grid-template-columns:1fr 1fr; gap:10px; }
.api-tile {
  background:var(--surface); border:1px solid var(--border);
  border-radius:var(--r); padding:1.25rem 1rem;
  cursor:pointer; transition:all .18s; text-align:left;
  display:flex; flex-direction:column; gap:6px;
  border-left:3px solid transparent;
  position:relative; overflow:hidden;
}
.api-tile:hover { border-color:var(--border2); transform:translateY(-1px); box-shadow:0 4px 16px rgba(0,0,0,.3); }
.api-tile.tile-sfw   { border-left-color:var(--sfw); }
.api-tile.tile-sfw:hover   { background:var(--sfw-dim); border-color:var(--sfw); }
.api-tile.tile-nsfw  { border-left-color:var(--nsfw); }
.api-tile.tile-nsfw:hover  { background:var(--nsfw-dim); border-color:var(--nsfw); }
.api-tile.tile-vid   { border-left-color:var(--vid); }
.api-tile.tile-vid:hover   { background:var(--vid-dim); border-color:var(--vid); }
.api-tile.tile-gold  { border-left-color:var(--gold); }
.api-tile.tile-gold:hover  { background:var(--gold3); border-color:var(--gold); }
.api-tile-icon { font-size:22px; margin-bottom:2px; }
.api-tile-title { font-size:13px; font-weight:500; color:var(--cream); }
.api-tile-desc { font-size:11px; color:var(--cream3); line-height:1.5; }
.api-tile-badge {
  display:inline-block; margin-top:4px; font-size:10px; padding:1px 7px;
  border-radius:4px; font-weight:500; letter-spacing:.05em; text-transform:uppercase;
}
.api-tile-status {
  position:absolute; top:.6rem; right:.75rem;
  font-size:9px; padding:2px 7px; border-radius:10px; font-weight:500;
  letter-spacing:.05em; text-transform:uppercase;
}
.status-ok   { background:var(--sfw-dim); color:var(--sfw); border:1px solid var(--sfw); }
.status-wait { background:var(--warn-dim); color:var(--warn); border:1px solid var(--warn); }
.status-soon { background:var(--vid-dim);  color:var(--vid);  border:1px solid var(--vid); }

/* Panel API détail */
.api-panel { display:none; flex:1; min-height:0; flex-direction:column; }
.api-panel.active { display:flex; }

/* ── DOCUMENTATION · GROS POPUP ── */
.doc-overlay { padding:2.5rem; z-index:700; }
.doc-modal {
  background:var(--surface); border:1px solid var(--border2); border-radius:12px;
  width:100%; max-width:1240px; height:100%; max-height:88vh;
  display:flex; flex-direction:column; overflow:hidden;
  box-shadow:0 24px 70px rgba(0,0,0,.65);
}
.doc-overlay.open > .doc-modal { animation:docIn .3s cubic-bezier(.2,.9,.3,1.2); }
.doc-overlay.closing > .doc-modal { animation:docOut .18s ease forwards; }
@keyframes docIn  { from { opacity:0; transform:translateY(22px) scale(.96); } to { opacity:1; transform:none; } }
@keyframes docOut { from { opacity:1; transform:none; } to { opacity:0; transform:translateY(10px) scale(.985); } }
@media (prefers-reduced-motion: reduce) {
  .doc-overlay.open > .doc-modal, .doc-overlay.closing > .doc-modal { animation:none; }
}
.doc-modal-header {
  display:flex; align-items:center; justify-content:space-between; gap:1rem;
  padding:1.125rem 1.5rem; border-bottom:1px solid var(--border);
  background:var(--surface2); flex-shrink:0;
}
.doc-modal-title { font-family:var(--font-display); font-size:17px; color:var(--gold); }
.doc-modal-sub { font-size:11px; color:var(--cream3); margin-top:2px; }
.doc-modal-actions { display:flex; align-items:center; gap:8px; }
.doc-dl-btn {
  background:var(--gold3); border:1px solid var(--gold2); border-radius:6px;
  padding:6px 14px; font-size:11.5px; font-weight:500; color:var(--gold);
  cursor:pointer; font-family:inherit; transition:all .15s; white-space:nowrap;
}
.doc-dl-btn:hover { background:var(--gold2); color:#000; }
.doc-modal-body { display:flex; flex:1; min-height:0; }
.doc-modal-body .doc-sidebar { border-right:1px solid var(--border); background:var(--surface2); }
.doc-modal-body .doc-content { flex:1; min-width:0; overflow-y:auto; }

/* ── DOCUMENTATION ── */
.doc-sidebar {
  width:260px; flex-shrink:0; border-right:1px solid var(--border);
  background:var(--surface); display:flex; flex-direction:column; overflow-y:auto;
}
.doc-sidebar-label { font-size:10px; letter-spacing:.25em; text-transform:uppercase; color:var(--cream3); font-weight:500; padding:1.25rem 1.25rem .5rem; }
.doc-tab {
  display:flex; align-items:center; gap:10px; padding:.75rem 1.25rem;
  font-size:12px; color:var(--cream3); cursor:pointer; transition:all .15s;
  border-left:3px solid transparent; width:100%; background:none;
  border-top:none; border-bottom:none; border-right:none; font-family:inherit; text-align:left;
}
.doc-tab:hover { color:var(--cream2); background:var(--surface2); }
.doc-tab.active { color:var(--gold); border-left-color:var(--gold); background:var(--surface2); }
.doc-tab-icon { font-size:14px; flex-shrink:0; }
.doc-content { flex:1; overflow-y:auto; padding:2rem; max-width:860px; }
.doc-h1 { font-family:var(--font-display); font-size:22px; color:var(--gold); margin-bottom:.5rem; }
.doc-h2 { font-size:13px; letter-spacing:.15em; text-transform:uppercase; color:var(--gold2); font-weight:500; margin:1.75rem 0 .75rem; }
.doc-h3 { font-size:12px; font-weight:600; color:var(--cream); margin:.75rem 0 .25rem; }
.doc-p { font-size:12.5px; color:var(--cream2); line-height:1.8; margin-bottom:.75rem; }
/* `.doc-ul` était utilisée par src/features/docs.js sans jamais avoir été
   stylée : les listes de la documentation prenaient les valeurs par défaut du
   navigateur (retrait nul, texte à 14px). Réglée ici, elle sert aux DEUX
   produits — c'est le point de la mise en commun (Victor, 22/07). */
.doc-ul { margin:0 0 .875rem 1.2rem; }
.doc-ul li { font-size:12.5px; color:var(--cream2); line-height:1.8; margin-bottom:.35rem; }
.doc-ul li strong { color:var(--cream); }
.doc-block {
  background:var(--surface); border:1px solid var(--border);
  border-left:3px solid var(--gold2); border-radius:0 var(--r) var(--r) 0;
  padding:.875rem 1rem; margin-bottom:.75rem;
}
/* Bloc copiable : le bouton se pose dans le coin du bloc de code.
   position:relative sur l'enveloppe, sinon le bouton se calerait sur la page. */
.doc-copy-wrap { position:relative; }
.doc-copy { position:absolute; top:8px; right:8px; z-index:2; background:var(--surface); border:1px solid var(--border2); color:var(--cream3); border-radius:var(--r); padding:.25rem .5rem; font-size:10px; cursor:pointer; font-family:inherit; transition:all .15s; }
.doc-copy:hover { border-color:var(--gold); color:var(--gold); }
.doc-copy-wrap .doc-code { padding-right:4.5rem; white-space:pre-wrap; }
.doc-code {
  background:var(--surface2); border:1px solid var(--border2); border-radius:var(--r);
  padding:.875rem 1rem; font-family:'Courier New',monospace; font-size:11.5px;
  color:var(--cream); line-height:1.75; white-space:pre-wrap; margin-bottom:.75rem;
  overflow-x:auto;
}
.doc-table { width:100%; border-collapse:collapse; margin-bottom:1rem; font-size:12px; }
.doc-table th { background:var(--surface2); color:var(--gold2); font-weight:500; font-size:10px; letter-spacing:.15em; text-transform:uppercase; padding:.5rem .875rem; border-bottom:2px solid var(--border2); text-align:left; }
.doc-table td { padding:.5rem .875rem; border-bottom:1px solid var(--border); color:var(--cream2); vertical-align:top; }
.doc-table tr:last-child td { border-bottom:none; }
.doc-warn { background:var(--warn-dim); border:1px solid var(--warn); border-radius:var(--r); padding:.75rem 1rem; font-size:12px; color:var(--warn); line-height:1.6; margin-bottom:.75rem; }
.doc-tip  { background:var(--sfw-dim);  border:1px solid var(--sfw);  border-radius:var(--r); padding:.75rem 1rem; font-size:12px; color:var(--sfw);  line-height:1.6; margin-bottom:.75rem; }
/* ── Un lien SORTANT dans la documentation (27/07) ──
   ⚠️ Il se distingue du texte en gras, sinon on ne sait pas qu'il se clique :
   la doc emploie déjà `<strong>` partout, et un lien qui lui ressemble n'est
   pas un lien. Couleur de l'accent + soulignement discret.
   ⚠️ `word-break` : une URL longue dans une cellule de tableau étroite
   élargirait la colonne et casserait la mise en page du tableau entier. */
.doc-lien { color:var(--gold); text-decoration:underline; text-underline-offset:2px;
            text-decoration-color:var(--gold2); word-break:break-word; }
.doc-lien:hover { color:var(--cream); text-decoration-color:var(--cream); }
/* .doc-note existait dans le CSS du téléchargement mais pas ici : même
   piège que les .modal-header de la v48 (classe utilisée, jamais stylée). */
.doc-note { background:var(--vid-dim);  border:1px solid var(--vid);  border-radius:var(--r); padding:.75rem 1rem; font-size:12px; color:var(--vid);  line-height:1.6; margin-bottom:.75rem; }
.doc-badge { display:inline-block; font-size:10px; padding:1px 7px; border-radius:4px; font-weight:500; text-transform:uppercase; letter-spacing:.05em; }
.doc-badge-sfw  { background:var(--sfw-dim); color:var(--sfw); border:1px solid var(--sfw); }
.doc-badge-nsfw { background:var(--nsfw-dim); color:var(--nsfw); border:1px solid var(--nsfw); }
.doc-badge-gold { background:var(--gold3); color:var(--gold); border:1px solid var(--gold2); }

/* ══ POLISH DES 3 ÉCRANS — Calendrier · API/Outils · Documentation (18/07) ══
   Confort et lisibilite SANS toucher au rendu au repos deja valide :
   focus clavier visible, profondeur au survol, bandeau d'orientation en encart. */

/* 1 · Focus clavier coherent (or, comme le reste de l'app). :focus-visible
   ne s'affiche qu'au clavier -> le clic souris ne change rien. Outline seul :
   ni le fond ni les coins de l'element ne bougent. */
#view-calendar :is(button,select,input,[tabindex]):focus-visible,
#view-api :is(button,select,input,textarea,[tabindex]):focus-visible,
.doc-overlay :is(a,button,select,input,textarea,[tabindex]):focus-visible {
  outline:2px solid var(--gold); outline-offset:2px;
}

/* 2 · Profondeur tactile au survol (le repos reste plat, comme valide). */
#view-api .mode-btn:hover { box-shadow:0 6px 16px -6px rgba(0,0,0,.55); }
.cal-create:hover        { box-shadow:0 6px 16px -6px rgba(0,0,0,.55); }

/* 3 · Onglets Doc : on pre-affiche l'accent or au survol (sauf l'actif, qui
   garde son or plein) -> on voit « ce que je vais ouvrir ». */
.doc-tab:not(.active):hover { border-left-color:var(--gold3); }

/* 4 · Bandeau d'orientation API en vrai encart : barre d'accent or a gauche,
   pour qu'il se lise comme un conseil « par ou commencer » et non un pave.
   Qualifie par #view-api pour depasser la regle de base (l.871, meme
   specificite mais plus tardive) — y compris dans la colonne large LinkFORGE. */
#view-api .api-intro { border-left-width:3px; border-left-color:var(--gold); padding-left:1rem; }

/* ── MESSAGERIE ── */
.msg-tab {
  padding:.6rem 1rem; font-size:12px; color:var(--cream3); cursor:pointer;
  border-bottom:2px solid transparent; transition:all .15s; background:none;
  border-top:none; border-left:none; border-right:none; font-family:inherit;
}
.msg-tab:hover { color:var(--cream2); }
.msg-tab.active { color:var(--gold); border-bottom-color:var(--gold); }
.msg-panel { display:none; flex:1; overflow-y:auto; }
.msg-panel.active { display:flex; align-items:center; justify-content:center; }
.msg-empty { padding:2rem; max-width:420px; }   /* centrage : bloc partagé ci-dessus */
.msg-empty-icon { font-size:40px; margin-bottom:1rem; opacity:.3; }
.msg-empty-title { font-family:var(--font-display); font-size:16px; color:var(--gold); margin-bottom:.5rem; }
.msg-empty-text { font-size:12px; color:var(--cream3); line-height:1.7; margin-bottom:1rem; }
/* Panels qui prennent toute la place */
#apipanel-linkforge, #apipanel-instabot, #apipanel-fanbot { flex:1; min-height:0; }

/* ══ RÈGLE DE CENTRAGE DES PANELS API ══
   Un panel API a souvent une colonne-liste à gauche (tickets: 300px,
   LinkFORGE: 340px). Sans compensation, le contenu se centre sur la
   sous-fenêtre restante et paraît décalé à droite.
   Le padding-right = largeur de la colonne recentre le contenu sur TOUTE
   la zone centrale, donc au centre de l'écran.
   `margin:auto` sur l'enfant = centrage vertical ET horizontal, sans
   rogner le haut quand le contenu dépasse (contrairement à align-items). */
.api-center-zone {
  flex:1; min-width:0; overflow-y:auto; display:flex;
  padding: var(--api-pad, 0px);
  /* Compense la colonne-liste pour recentrer sur l'écran entier */
  padding-right: calc(var(--api-pad, 0px) + var(--api-list-w, 0px));
}
.api-center-zone > * { margin:auto; }
.msg-badge { display:inline-block; font-size:10px; padding:3px 12px; border-radius:10px; font-weight:500;
  background:var(--vid-dim); color:var(--vid); border:1px solid var(--vid); }
.msg-badge-wait { background:var(--warn-dim); color:var(--warn); border-color:var(--warn); }

/* ── PILE DE NOTIFICATIONS ── */
#notif-stack {
  position:fixed; bottom:4.5rem; left:50%; transform:translateX(-50%);
  display:flex; flex-direction:column; gap:6px; align-items:center;
  z-index:800; pointer-events:none;
}
.notif-item {
  pointer-events:auto;
  background:var(--surface); border:1px solid var(--gold2);
  border-radius:10px; padding:.6rem 1.25rem;
  font-size:12px; color:var(--gold); font-weight:500;
  box-shadow:0 4px 20px rgba(0,0,0,.5);
  display:flex; align-items:center; gap:8px; white-space:nowrap;
  opacity:0; transform:translateY(10px);
  transition:opacity .25s, transform .25s;
}
.notif-item.visible { opacity:1; transform:translateY(0); }
.notif-item.done { border-color:var(--sfw); color:var(--sfw); }
.notif-item.error { border-color:var(--nsfw); color:var(--nsfw); }
.notif-icon { font-size:13px; }
.notif-bar-wrap { position:absolute; bottom:0; left:0; right:0; height:3px; background:rgba(255,255,255,.1); border-radius:0 0 10px 10px; overflow:hidden; }
.notif-bar { height:100%; background:currentColor; opacity:.5; width:100%; }
.notif-item { position:relative; overflow:hidden; }
.notif-item .notif-text { flex:1; }
/* Chronomètre de génération. Le moteur tourne sur CPU (≈ 5 tokens/s) : sans
   temps qui défile, une attente de plusieurs minutes passe pour une panne. */
.notif-chrono { font-family:'IBM Plex Mono',monospace; font-size:10.5px; opacity:.65; margin-left:2px; }

/* ── LINKFORGE ÉDITEUR ── */
/* Chaque panneau API porte le même cadre : un en-tête qui le nomme, un corps
   qui défile, un pied d'action. C'était la forme de Tickets, elle est devenue
   la règle (Marcel, 17/07). */
.api-col { width:var(--api-col);flex-shrink:0;border-right:1px solid var(--border);display:flex;flex-direction:column;min-height:0; }
/* Sur les bots, c'est la CONFIG qui mérite la place, pas le chat : la zone de
   test ne sert qu'à vérifier un réglage (Marcel, 17/07). */
.api-col-wide { width:auto;flex:1;min-width:0; }

/* ══ MISE EN PAGE DES CONFIGS · InstaBOT · FanBOT · LinkFORGE ══
   (Marcel, 17/07 : « le contenu est nickel, la mise en forme est à chier »)

   AVANT : une grille CSS. Une grille aligne ses lignes sur la section la plus
   HAUTE de la ligne — un bloc de 3 champs à côté d'un bloc de 12 laissait donc
   400 px de vide sous le petit. D'où les trous.

   MAINTENANT : des COLONNES CSS (column-width). Le navigateur remplit une
   colonne de haut en bas, puis passe à la suivante : plus aucun trou intérieur,
   et il s'adapte tout seul à la largeur dispo. `break-inside:avoid` interdit de
   couper une section en deux.

   Et chaque section devient une CARTE : un en-tête coloré, un cadre, un fond.
   On voit où commence et où finit un réglage, au lieu d'une soupe de libellés. */
/* Le corps large ne fait plus QUE défiler verticalement. Le multi-colonnes est
   descendu dans .api-col-cols : posé sur un élément à hauteur AUTO, il équilibre
   ses colonnes vers le BAS au lieu de paginer vers la droite. Sans ça, sur une
   fenêtre courte, les colonnes débordaient à droite et créaient un scroll
   horizontal (Marcel : « pas de scroll droite/gauche, uniquement haut/bas »). */
.api-col-wide .api-col-body {
  display:block;
  padding:1rem;
  overflow-x:hidden;
}
/* ⚠️⚠️ QUATRE COLONNES, DEMANDÉES EXPLICITEMENT (Victor, 27/07 : « tu mets
   4 colonnes et tu laisses les infos dans chaque colonne »).
   `column-width:288px` laissait le navigateur DÉCIDER du nombre de colonnes
   d'après la place — deux ou trois selon la fenêtre. `column-count:4` fixe le
   nombre et laisse la largeur s'adapter : c'est l'inverse, et c'est ce qui
   donne une mise en page prévisible d'un écran à l'autre.
   ⚠️ Un minimum de largeur reste nécessaire : en dessous, quatre colonnes
   deviennent quatre bandes illisibles. On retombe donc à trois puis à deux. */
.api-col-cols {
  column-count:4; column-gap:1rem;
}
@media (max-width:1500px){ .api-col-cols { column-count:3; } }
@media (max-width:1150px){ .api-col-cols { column-count:2; } }
@media (max-width:820px){  .api-col-cols { column-count:1; } }
.api-col-cols > div {
  break-inside:avoid; -webkit-column-break-inside:avoid; page-break-inside:avoid;
  display:flow-root;                 /* pas d'effondrement de marges dans la carte */
  background:var(--surface2); border:1px solid var(--border);
  border-radius:var(--r); margin:0 0 1rem; padding:0 .875rem .875rem;
  overflow:hidden;
}
/* Le libellé de section devient l'en-tête de la carte : il ressort de son
   padding pour toucher les bords. */
.api-col-cols > div > .lf-section-label {
  margin:0 -.875rem .875rem; padding:.5rem .875rem;
  background:var(--surface3); border-bottom:1px solid var(--border);
  font-size:10px; letter-spacing:.16em;
}
/* Rien ne flotte en bas d'une carte : le dernier champ colle au cadre. */
.api-col-cols > div > :last-child { margin-bottom:0; }
.api-col-cols > div > .bot-hint:last-child { margin-top:.5rem; }
.api-col-head { padding:.75rem 1rem;border-bottom:1px solid var(--border);background:var(--surface2);flex-shrink:0;display:flex;align-items:center;gap:10px;min-height:63px; }
.api-col-title { font-size:11px;color:var(--cream3);text-transform:uppercase;letter-spacing:.2em;font-weight:500; }
/* En-tête d'outil enrichi : pastille + nom + plateforme, pour situer l'outil
   d'un coup d'œil au lieu d'un simple libellé perdu en haut (Claria, 18/07). */
.api-col-ico { width:30px;height:30px;flex-shrink:0;display:flex;align-items:center;justify-content:center;font-size:15px;background:var(--surface3);border:1px solid var(--border2);border-radius:9px; }
.api-col-titles { display:flex;flex-direction:column;gap:1px;min-width:0; }
.api-col-head .api-col-title { font-size:13px;color:var(--gold);letter-spacing:.08em;text-transform:none;font-family:var(--font-display);font-weight:700; }
.api-col-sub { font-size:10px;color:var(--cream3);letter-spacing:.04em; }
.api-col-head-actions { margin-left:auto;display:flex;align-items:center;gap:8px; }

/* Bandeau d'accueil de l'outil : dit ce qu'il fait ET par où commencer.
   column-span:all → il barre toute la largeur, au-dessus des cartes en colonnes.
   Sélecteur qualifié pour l'emporter sur le style « carte » des sections. */
.api-col-cols > .api-intro,
.api-intro { -webkit-column-span:all;column-span:all;background:var(--gold3);border:1px solid var(--gold2);border-radius:var(--r);padding:.7rem .85rem;margin:0 0 1rem;overflow:visible; }
.api-intro-desc { font-size:12px;color:var(--cream);line-height:1.5;margin:0; }
.api-intro-start { font-size:11px;color:var(--gold);line-height:1.5;margin:.4rem 0 0;font-weight:500; }
.api-col-body { flex:1;min-height:0;overflow-y:auto;padding:1.5rem;display:flex;flex-direction:column;gap:1.25rem; }
.api-col-pad { overflow-y:auto;padding:1.5rem;gap:1.25rem; }

/* Pied d'action — gris tant qu'il n'y a rien à écrire, et dès qu'il devient
   cliquable il prend exactement la couleur du bouton Générer (Marcel, 17/07) :
   même or, même survol qui bascule en noir. */
.api-col-save { flex-shrink:0;display:flex;align-items:center;justify-content:center;gap:8px;background:var(--surface2);border:none;border-top:1px solid var(--border);padding:.75rem 1rem;font-size:12px;font-weight:600;color:var(--cream3);cursor:pointer;font-family:inherit;transition:all .15s;width:100%;letter-spacing:.02em; }
.api-col-save:not(:disabled) { background:var(--gold3);border-top-color:var(--gold2);color:var(--gold); }
.api-col-save:not(:disabled):hover { background:var(--gold2);color:#000; }
.api-col-save:disabled { opacity:.4;cursor:not-allowed; }

/* Profil maître · sélecteur d'IA de l'onglet API + barre « modèle » des bots (Victor 20/07). */
/* ⚠️ `.api-master-pick` / `.api-master-hint` ont été supprimées le 22/07 avec
   le sélecteur déroulant « La centrale · IA » de l'encadré de gauche : l'IA
   se choisit désormais dans le popup commun (`.pf-list`) et s'affiche dans le
   bandeau `.gen-target-banner`, comme au Générateur. */
.bot-presetbar { display:flex; align-items:center; gap:10px; flex-wrap:wrap; padding:.6rem 1rem; border-top:1px solid var(--border); background:var(--surface2); }
.bot-preset-btn { background:var(--surface3); border:1px solid var(--border2); border-radius:6px; padding:6px 12px; font-size:11.5px; font-weight:600; color:var(--cream2); cursor:pointer; font-family:inherit; transition:all .15s; }
.bot-preset-btn:hover { border-color:var(--gold2); color:var(--gold); background:var(--gold3); }
.bot-preset-apply { display:flex; align-items:center; gap:6px; font-size:11px; color:var(--cream3); margin-left:auto; }
.bot-preset-apply .lf-input { width:auto; min-width:130px; }

.lf-section-label { font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--gold2);font-weight:500;margin-bottom:.6rem; }
.lf-field { display:flex;flex-direction:column;gap:3px;margin-bottom:.5rem; }
.lf-label { font-size:10px;color:var(--cream3); }
.lf-input { background:var(--surface2);border:1px solid var(--border2);border-radius:6px;padding:.45rem .75rem;font-size:12px;color:var(--cream);font-family:inherit;outline:none;transition:border-color .15s;width:100%;box-sizing:border-box; }
.lf-input:focus { border-color:var(--gold2); }
/* Une valeur IMPOSÉE, à la place du réglage : mêmes dimensions que `.lf-input`
   pour que la colonne ne saute pas d'une ligne à l'autre, mais sans cadre de
   saisie — on ne fait pas semblant d'offrir un choix qui n'existe pas
   (langue de FanBOT, bots.js, 26/07). */
.bot-fixe { padding:.45rem 0; font-size:12px; color:var(--cream2); }
.lf-color { width:100%;height:34px;border:1px solid var(--border2);border-radius:6px;cursor:pointer;background:none;padding:2px; }
.lf-link-row { display:flex;gap:6px;align-items:center; }
/* ⚠️⚠️ LA CIBLE EST LA CARTE, PLUS LA RANGÉE (27/07). La règle visait
   `.lf-link-row input` — donc uniquement les champs posés dans une rangée. En
   étiquetant chaque champ (Icône · Texte affiché · Adresse · Animation), ils
   sont sortis de ces rangées et sont retombés sur l'apparence par défaut du
   navigateur : **fond blanc** sur un thème sombre. Le sélecteur porte
   maintenant sur la CARTE, qui les contient tous, quelle que soit la façon dont
   ils y sont disposés.
   ⚠️ `[type=file]` est exclu : il est masqué dans un `<label>` (le bouton
   « 🖼 Image ») et n'a aucune apparence à recevoir. */
.lf-link-card input:not([type=file]),
.lf-link-card select {
  background:var(--surface2);border:1px solid var(--border2);border-radius:6px;
  padding:.4rem .6rem;font-size:11px;color:var(--cream);font-family:inherit;
  outline:none;flex:1;min-width:0;
}
.lf-link-card input:not([type=file]):focus,
.lf-link-card select:focus { border-color:var(--gold2); }
.lf-link-del { background:none;border:none;color:var(--cream3);cursor:pointer;font-size:14px;padding:2px 5px;border-radius:4px;transition:all .12s;flex-shrink:0; }
.lf-link-del:hover { color:var(--nsfw);background:var(--nsfw-dim); }
.lf-check { display:flex;align-items:center;gap:7px;font-size:11px;color:var(--cream2);cursor:pointer;margin-bottom:.5rem;user-select:none; }
.lf-check input { accent-color:var(--gold2);cursor:pointer;width:14px;height:14px;flex-shrink:0; }
.lf-link-card { background:var(--surface2);border:1px solid var(--border);border-radius:var(--r);padding:.5rem; }
.lf-link-opts { display:flex;gap:5px;align-items:center;margin-top:5px; }
.lf-opt-btn { background:var(--surface3);border:1px solid var(--border2);border-radius:4px;color:var(--cream3);font-size:10px;padding:3px 8px;cursor:pointer;font-family:inherit;transition:all .12s;white-space:nowrap; }
.lf-opt-btn:hover { border-color:var(--gold2);color:var(--gold); }
.lf-opt-btn.on { background:var(--gold3);border-color:var(--gold2);color:var(--gold);font-weight:600; }
.lf-opt-btn.on-nsfw { background:var(--nsfw-dim);border-color:var(--nsfw);color:var(--nsfw);font-weight:600; }
.lf-pos-off { opacity:.28; cursor:not-allowed; }
.lf-pos-off:hover { background:var(--surface2) !important; border-color:var(--border2) !important; color:var(--cream3) !important; }
.lf-hero-drop { position:relative;border:1px dashed var(--border2);border-radius:var(--r);padding:1rem;text-align:center;color:var(--cream3);cursor:pointer;transition:all .15s;background:var(--surface2); }
.lf-hero-drop:hover, .lf-hero-drop.dragover { border-color:var(--gold2);color:var(--gold);background:var(--gold3); }
.lf-hero-drop input { position:absolute;inset:0;opacity:0;cursor:pointer;width:100%;height:100%; }
/* ══ CALENDRIER ══
   (refonte du 17/07 · 9 points demandés par Marcel)

   CHARPENTE
     colonne gauche fixe (--api-col)  : mini-calendrier · ＋ Créer · Agenda · Fériés
     colonne droite (flex)            : barre · grille (mois) ou rail horaire (jour/semaine) ou cartes (liste)
     le formulaire                    : dans un popup, plus dans la colonne

   OUTIL DE PLANIFICATION : il n'envoie rien. Les champs sont ceux que
   réclament les deux API, pour n'avoir plus qu'à brancher l'envoi. */
.cal-wrap { display:flex;flex:1;min-height:0; }
.cal-main { flex:1;min-width:0;display:flex;flex-direction:column; }
.cal-bar { display:flex;align-items:center;gap:.75rem;padding:.875rem 1.25rem;border-bottom:1px solid var(--border);background:var(--surface2);flex-shrink:0; }
.cal-nav { display:flex;align-items:center;gap:4px; }
.cal-nav-btn { background:var(--surface3);border:1px solid var(--border2);border-radius:6px;color:var(--cream2);font-size:13px;width:28px;height:28px;cursor:pointer;font-family:inherit;transition:all .12s;line-height:1;flex-shrink:0; }
.cal-nav-btn:hover { border-color:var(--gold2);color:var(--gold); }
.cal-label { font-family:var(--font-display);font-size:15px;color:var(--gold);min-width:205px;text-transform:capitalize;white-space:nowrap;flex-shrink:0; }
.cal-today-btn { background:var(--surface3);border:1px solid var(--border2);border-radius:6px;color:var(--cream3);font-size:10px;padding:4px 10px;cursor:pointer;font-family:inherit;transition:all .12s;white-space:nowrap; }
.cal-today-btn:hover { border-color:var(--gold2);color:var(--gold); }

/* Le filtre d'affichage est un menu déroulant, à droite de « Fanvue », sur la
   ligne de la légende (Marcel, 17/07). C'étaient 4 boutons. */
.cal-view-sel { background:var(--surface3);border:1px solid var(--border2);border-radius:6px;color:var(--cream2);font-size:11px;padding:4px 8px;cursor:pointer;font-family:inherit;outline:none;transition:all .12s; }
.cal-view-sel:hover, .cal-view-sel:focus { border-color:var(--gold2);color:var(--gold); }

.cal-legend { display:flex;gap:11px;align-items:center;margin-left:auto;font-size:10px;color:var(--cream3);flex-shrink:0; }
.cal-legend span { display:flex;align-items:center;gap:5px; }
.cal-dot { width:8px;height:8px;border-radius:50%;flex-shrink:0; }
.cal-dot-instagram { background:var(--sfw); }
.cal-dot-fanvue    { background:var(--nsfw); }

/* ── Colonne gauche ── */
.cal-side { width:260px;flex-shrink:0;border-right:1px solid var(--border);display:flex;flex-direction:column;background:var(--surface);min-height:0; }
.cal-side-body { flex:1;min-height:0;overflow-y:auto;padding:1.25rem;display:flex;flex-direction:column;gap:var(--sb-gap); }

/* ＋ Créer — ouvre le popup. Même or que le bouton Générer. */
.cal-create { width:100%;background:var(--gold3);border:1px solid var(--gold2);border-radius:var(--r);padding:.65rem;font-size:12px;font-weight:600;color:var(--gold);cursor:pointer;font-family:inherit;transition:all .15s;flex-shrink:0;letter-spacing:.02em; }
.cal-create:hover { background:var(--gold2);color:#000; }
.cal-create-sub { display:block;font-size:9.5px;font-weight:400;opacity:.75;margin-top:2px;letter-spacing:0; }
.cal-create-tip { font-size:10px;color:var(--cream3);line-height:1.4;padding:0 .15rem;flex-shrink:0; }

/* Blocs repliables de la colonne (Agenda, Fériés) */
/* ══ TOUT CE QUI SE PLIE A LA MÊME TÊTE (Victor, 25/07) ══
   Référence unique : la tuile de l'encadré droit (.sb-fold, plus bas).
   `.cal-fold` (encadré gauche du Calendrier) s'en écartait sur quatre points
   — fond de l'en-tête en surface3, titre doré, interlettrage .16em, flèche
   poussée à droite — et deux blocs pliables voisins ne se ressemblaient donc
   pas. On aligne : mêmes couleurs, même interlettrage, flèche À GAUCHE.
   ⚠️ Ne pas redonner un style propre à un pliable : c'est la forme qui dit
   « ceci se plie », et elle doit être la même partout dans les deux produits. */
.cal-fold { border:1px solid var(--border);border-radius:var(--r);background:var(--surface2);overflow:hidden;flex-shrink:0; }
.cal-fold-head { width:100%;display:flex;align-items:center;gap:7px;background:none;border:none;border-bottom:1px solid transparent;padding:.75rem .5rem .75rem 1rem;font-size:10px;letter-spacing:.25em;text-transform:uppercase;color:var(--cream3);font-weight:500;cursor:pointer;font-family:inherit;text-align:left;transition:background .12s; }
.cal-fold-head:hover { background:var(--surface3); }
/* Même chevron que le repli du chat Claude (#claude-collapse-btn) :
   glyphe ▶ au repos, pivoté à 90° une fois déplié. */
/* Panneau GAUCHE → flèche à DROITE de la tuile (Marcel) : le libellé reste à
   gauche, la flèche est poussée au bout par margin-left:auto. */
/* Flèche à GAUCHE, comme .sb-fold-arrow. Elle était poussée à droite
   (margin-left:auto) : deux pliables côte à côte ouvraient dans le même sens
   avec la flèche à deux endroits différents. */
.cal-fold-arrow { display:inline-block;transition:transform .15s;font-size:10px;order:1;color:var(--cream3);flex-shrink:0; }
.cal-fold.open .cal-fold-head { border-bottom-color:var(--border); }
.cal-fold.open .cal-fold-arrow { transform:rotate(90deg); }
.cal-fold:not(.open) .cal-fold-body { display:none; }
.cal-fold-body { padding:.75rem; }

/* Agenda : une ligne par PROPRIÉTAIRE de calendrier, pas par IA. */
.cal-ag { display:flex;align-items:center;gap:8px;padding:.35rem .25rem;font-size:11.5px;color:var(--cream2);cursor:pointer;border-radius:4px;user-select:none; }
.cal-ag:hover { background:var(--surface3); }
.cal-ag input { accent-color:var(--gold2);cursor:pointer;width:14px;height:14px;flex-shrink:0; }
.cal-ag-dot { width:9px;height:9px;border-radius:50%;flex-shrink:0;border:1.5px solid currentColor; }
.cal-ag-dot.full { background:currentColor; }
.cal-ag-name { flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap; }
.cal-ag-role { font-size:9px;color:var(--cream3);flex-shrink:0; }
.cal-share-lab { font-size:10px;color:var(--cream3);margin:.6rem 0 .3rem;padding-top:.6rem;border-top:1px solid var(--border); }
.cal-share-row { display:flex;gap:5px; }
.cal-share-row input { flex:1;min-width:0;background:var(--surface);border:1px solid var(--border2);border-radius:5px;padding:.35rem .5rem;font-size:10.5px;color:var(--cream);font-family:inherit;outline:none; }
.cal-share-row input:focus { border-color:var(--gold2); }
.cal-share-go { background:var(--gold3);border:1px solid var(--gold2);border-radius:5px;color:var(--gold);font-size:10px;padding:0 9px;cursor:pointer;font-family:inherit;flex-shrink:0;transition:all .12s; }
.cal-share-go:hover { background:var(--gold2);color:#000; }
.cal-share-chip { display:flex;align-items:center;gap:6px;font-size:10px;color:var(--cream2);background:var(--surface);border:1px solid var(--border2);border-radius:4px;padding:2px 4px 2px 7px;margin-top:4px; }
.cal-share-chip span { flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis; }
.cal-share-x { background:none;border:none;color:var(--cream3);cursor:pointer;font-size:11px;padding:1px 4px;border-radius:3px;font-family:inherit; }
.cal-share-x:hover { color:var(--nsfw);background:var(--nsfw-dim); }

/* Mini-calendrier — {mois} {année} {‹ ›} au-dessus */
.cal-mini { flex-shrink:0; }
.cal-mini-bar { display:flex;align-items:center;gap:4px;margin-bottom:.5rem; }
.cal-mini-lab { flex:1;font-size:11.5px;color:var(--gold);text-transform:capitalize;font-weight:600;letter-spacing:.02em; }
.cal-mini-btn { background:var(--surface2);border:1px solid var(--border2);border-radius:4px;color:var(--cream3);font-size:11px;width:20px;height:20px;line-height:1;cursor:pointer;font-family:inherit;transition:all .12s;flex-shrink:0;padding:0; }
.cal-mini-btn:hover { border-color:var(--gold2);color:var(--gold); }
.cal-mini-grid { display:grid;grid-template-columns:repeat(7,1fr);gap:1px; }
.cal-mini-dow { font-size:8px;text-align:center;color:var(--cream3);text-transform:uppercase;letter-spacing:.05em;padding-bottom:3px; }
.cal-mini-d { position:relative;aspect-ratio:1;display:flex;align-items:center;justify-content:center;font-size:10px;color:var(--cream2);border-radius:4px;cursor:pointer;border:1px solid transparent;font-variant-numeric:tabular-nums; }
.cal-mini-d:hover { background:var(--surface3); }
.cal-mini-d.out { opacity:.3; }
.cal-mini-d.today { color:var(--gold);font-weight:700;border-color:var(--gold2); }
.cal-mini-d.armed { background:var(--gold);color:#000;font-weight:700; }
.cal-mini-d.fer { color:var(--vid); }
.cal-mini-d.has::after { content:"";position:absolute;bottom:2px;left:50%;transform:translateX(-50%);width:3px;height:3px;border-radius:50%;background:var(--gold); }
.cal-mini-d.armed.has::after { background:#000; }

/* Fériés — le sélecteur de pays + les prochaines dates */
.cal-fer-next { display:flex;align-items:baseline;gap:6px;font-size:10px;color:var(--cream2);padding:2px 0; }
.cal-fer-when { color:var(--cream3);font-variant-numeric:tabular-nums;flex-shrink:0;min-width:52px; }
.cal-fer-n { flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap; }

/* Calendriers nationaux téléchargeables (chantier 21/07) — bouton d'ajout,
   barre de progression du téléchargement, liste des pays de la modale. */
.cal-fer-add { flex:1;background:var(--surface3);border:1px solid var(--border);color:var(--cream2);
  border-radius:var(--r);padding:.34rem .5rem;font-family:var(--font-ui);font-size:10px;cursor:pointer;
  transition:border-color .15s, color .15s; }
.cal-fer-add:hover { border-color:var(--gold2);color:var(--gold); }
.hol-progress { margin-top:.5rem; }
.hol-prog-label { font-size:10px;color:var(--cream3);margin-bottom:3px;display:flex;justify-content:space-between;gap:6px; }
.hol-prog-pct { color:var(--gold);font-variant-numeric:tabular-nums; }
.hol-prog-rail { height:4px;background:var(--surface3);border-radius:2px;overflow:hidden; }
.hol-prog-fill { height:100%;background:var(--gold);transition:width .25s ease; }
.hol-row { display:flex;align-items:center;gap:8px;width:100%;text-align:left;background:var(--surface2);
  border:1px solid var(--border);border-radius:var(--r);padding:.45rem .6rem;color:var(--cream2);
  font-family:var(--font-ui);font-size:12px;cursor:pointer;transition:border-color .15s; }
.hol-row:hover { border-color:var(--gold2); }
.hol-row.has { border-left:2px solid var(--sfw); }
.hol-row-flag { font-size:15px;flex-shrink:0; }
.hol-row-name { flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap; }
.hol-row-tag { font-size:9px;letter-spacing:.1em;text-transform:uppercase;color:var(--cream3);flex-shrink:0; }
.hol-row.has .hol-row-tag { color:var(--sfw); }

/* ── Grille du mois ── */
/* 8 colonnes : une colonne « n° de semaine » étroite + les 7 jours (24/07). */
.cal-head { display:grid;grid-template-columns:2.1rem repeat(7,1fr);border-bottom:1px solid var(--border);flex-shrink:0; }
.cal-head div { padding:.4rem;text-align:center;font-size:9px;letter-spacing:.18em;text-transform:uppercase;color:var(--cream3); }
.cal-head-week { color:var(--cream4,var(--cream3));opacity:.75; }
.cal-grid { flex:1;display:grid;grid-template-columns:2.1rem repeat(7,1fr);grid-auto-rows:1fr;overflow-y:auto;min-height:0; }
/* Cellule n° de semaine : discrète, cliquable (→ Préparer la semaine). */
.cal-week { display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:600;
  color:var(--cream3);background:var(--surface2);border-right:1px solid var(--border);border-bottom:1px solid var(--border);
  cursor:pointer;transition:all .12s; }
.cal-week:hover { color:var(--gold);background:var(--gold3); }
/* Sélecteur de semaine (« Préparer la semaine ») — IA et Tube (24/07). */
.weekpick-sub { font-size:12px;color:var(--cream3);margin:.25rem 0 1rem; }
.weekpick-list { display:flex;flex-direction:column;gap:6px;max-height:60vh;overflow-y:auto; }
.weekpick-item { display:flex;align-items:center;gap:12px;width:100%;text-align:left;
  background:var(--surface2);border:1px solid var(--border2);border-radius:var(--r);
  color:var(--cream);padding:.7rem .9rem;cursor:pointer;font-family:inherit;font-size:13px;transition:all .12s; }
.weekpick-item:hover { border-color:var(--gold);background:var(--gold3);color:var(--gold); }
.weekpick-num { font-weight:700;color:var(--gold);min-width:2.4rem; }
.weekpick-plage { color:var(--cream2); }
.weekpick-item:hover .weekpick-plage { color:var(--gold); }
/* Bulle « Action en cours » (encadré droit, IA + Tube — 24/07). */
.act-empty { font-size:11px;color:var(--cream3);text-align:center;padding:.4rem 0; }
.act-item { border:1px solid var(--border2);border-radius:var(--r);padding:.55rem .65rem;margin-bottom:.5rem;background:var(--surface); }
.act-item:last-child { margin-bottom:0; }
.act-item.act-encours { border-left:3px solid var(--gold); }
.act-item.act-pause   { border-left:3px solid var(--warn); }
.act-item.act-fini    { border-left:3px solid var(--ok, #5cba7d);opacity:.85; }
.act-item.act-stoppe, .act-item.act-echec { opacity:.7; }
.act-h { display:flex;align-items:center;justify-content:space-between;gap:8px; }
.act-titre { font-size:12px;font-weight:600;color:var(--cream); }
.act-etat { font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;flex-shrink:0; }
.act-detail { font-size:11px;color:var(--cream2);margin-top:3px; }
.act-meta { font-size:10px;color:var(--cream3);margin-top:4px; }
.act-ctrl { display:flex;flex-wrap:wrap;gap:5px;margin-top:6px; }
.act-btn-mini { background:var(--surface2);border:1px solid var(--border2);border-radius:6px;color:var(--cream2);
  font-size:10.5px;padding:3px 8px;cursor:pointer;font-family:inherit;transition:all .12s; }
.act-btn-mini:hover { border-color:var(--gold);color:var(--gold); }
.act-btn-mini.danger:hover { border-color:var(--danger, #e06b6b);color:var(--danger, #e06b6b); }
.cal-cell { border-right:1px solid var(--border);border-bottom:1px solid var(--border);padding:4px;min-height:96px;display:flex;flex-direction:column;gap:3px;cursor:pointer;transition:background .12s;overflow:hidden; }
.cal-cell:hover { background:var(--surface2); }
.cal-cell:nth-child(7n) { border-right:none; }
.cal-cell.out { opacity:.32; }
.cal-cell.today { background:var(--gold3); }
.cal-cell.today:hover { background:var(--gold2); }
/* Le jour repris dans le formulaire — c'est le seul retour visuel qui dit
   « ton clic a servi », puisqu'il ne crée plus rien. */
.cal-cell.armed { box-shadow:inset 0 0 0 2px var(--gold); }
.cal-daynum { font-size:10px;color:var(--cream3);padding:1px 3px;flex-shrink:0;display:flex;justify-content:space-between;align-items:center;gap:4px; }
.cal-cell.today .cal-daynum { color:var(--gold);font-weight:700; }
/* Jour férié / date à contenu — un mot dans la case, jamais plus */
.cal-fer-tag { font-size:8px;letter-spacing:.02em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--vid);opacity:.9; }
.cal-fer-tag.k-date { color:var(--cream3);font-style:italic; }
/* Toutes les entrées du jour restent atteignables : la pile défile plutôt que
   de masquer les dernières derrière un « +N » sur lequel il n'y a rien à cliquer. */
.cal-chips { flex:1;min-height:0;overflow-y:auto;display:flex;flex-direction:column;gap:3px; }
.cal-chips::-webkit-scrollbar { width:4px; }
.cal-chips::-webkit-scrollbar-thumb { background:var(--border2);border-radius:2px; }

.cal-chip { display:flex;align-items:center;gap:5px;border-radius:4px;padding:2px 5px;font-size:9px;cursor:pointer;background:var(--surface2);border:1px solid var(--border2);border-left:3px solid var(--border2);color:var(--cream2);transition:all .12s;white-space:nowrap;overflow:hidden;flex-shrink:0; }
.cal-chip:hover { border-color:var(--gold2); }
.cal-chip.sel { border-color:var(--gold);background:var(--gold3);color:var(--gold); }
.cal-chip-instagram { border-left-color:var(--sfw); }
.cal-chip-fanvue    { border-left-color:var(--nsfw); }
.cal-chip-txt { overflow:hidden;text-overflow:ellipsis; }
/* Initiales de l'IA sur un chip (multi-IA · Victor 20/07). */
.cal-chip-ia { flex-shrink:0;font-size:8px;font-weight:700;line-height:1;padding:1px 3px;border:1px solid var(--border2);border-radius:3px;letter-spacing:.02em; }
.cal-chip-h { flex-shrink:0;opacity:.75;font-variant-numeric:tabular-nums; }
.cal-chip.st-poste  { opacity:.5; }
.cal-chip.st-erreur { border-color:var(--nsfw);color:var(--nsfw); }
/* Marque d'une entrée qui n'est pas à moi : c'est la seule façon de savoir
   de qui on regarde le planning quand deux agendas sont cochés. */
.cal-chip.guest { border-style:dashed; }

/* ══ RAIL HORAIRE · vues Jour et Semaine (Marcel, 17/07 · §2.1) ══
   Les posts sont calés sur leur date ET leur heure, pas listés en vrac dans
   la journée. Position = (h*60+m)/1440 de la hauteur.
   --calh = hauteur d'une heure. Toute la géométrie en dépend. */
.cal-time { flex:1;display:flex;flex-direction:column;min-height:0;--calh:48px;--calrail:56px; }
.cal-time-head { display:grid;grid-template-columns:var(--calrail) repeat(var(--cols),1fr);border-bottom:1px solid var(--border);background:var(--surface2);flex-shrink:0; }
.cal-th { padding:.4rem 4px;text-align:center;border-left:1px solid var(--border);min-width:0; }
.cal-th-dow { font-size:9px;letter-spacing:.14em;text-transform:uppercase;color:var(--cream3); }
.cal-th-num { font-size:15px;color:var(--cream2);font-family:var(--font-display);line-height:1.3; }
.cal-th.today .cal-th-num { color:var(--gold); }
.cal-th.today .cal-th-dow { color:var(--gold2); }
.cal-th-fer { font-size:8px;color:var(--vid);overflow:hidden;text-overflow:ellipsis;white-space:nowrap; }
.cal-time-scroll { flex:1;min-height:0;overflow-y:auto; }
.cal-time-grid { display:grid;grid-template-columns:var(--calrail) repeat(var(--cols),1fr);position:relative;height:calc(var(--calh) * 24); }
.cal-rail { position:relative;border-right:1px solid var(--border); }
.cal-rail-h { position:absolute;right:6px;font-size:9px;color:var(--cream3);transform:translateY(-50%);font-variant-numeric:tabular-nums; }
.cal-col { position:relative;border-left:1px solid var(--border);min-width:0;cursor:pointer;
  background-image:repeating-linear-gradient(to bottom, var(--border) 0 1px, transparent 1px var(--calh)); }
.cal-col:hover { background-color:var(--surface2); }
.cal-col.today { background-color:var(--gold3); }
.cal-col.wend { background-color:rgba(0,0,0,.14); }
/* Une entrée posée à son heure. Hauteur fixe : un post n'a pas de durée. */
.cal-ev { position:absolute;left:3px;right:3px;min-height:20px;border-radius:4px;padding:2px 5px;font-size:9px;cursor:pointer;background:var(--surface2);border:1px solid var(--border2);border-left:3px solid var(--border2);color:var(--cream2);transition:all .12s;overflow:hidden;display:flex;flex-direction:column;gap:1px;z-index:2; }
.cal-ev:hover { border-color:var(--gold2);z-index:4; }
.cal-ev.sel { border-color:var(--gold);background:var(--gold3);color:var(--gold); }
.cal-ev-instagram { border-left-color:var(--sfw); }
.cal-ev-fanvue    { border-left-color:var(--nsfw); }
.cal-ev.st-poste  { opacity:.55; }
.cal-ev.st-erreur { border-color:var(--nsfw);color:var(--nsfw); }
.cal-ev.guest { border-style:dashed; }
.cal-ev-h { font-weight:600;font-variant-numeric:tabular-nums;flex-shrink:0; }
.cal-ev-t { overflow:hidden;text-overflow:ellipsis;white-space:nowrap; }
/* Trait de l'heure courante */
.cal-now { position:absolute;left:var(--calrail);right:0;height:0;border-top:1px solid var(--nsfw);z-index:5;pointer-events:none; }
.cal-now::before { content:"";position:absolute;left:-4px;top:-4px;width:8px;height:8px;border-radius:50%;background:var(--nsfw); }
.cal-now-lab { position:absolute;right:100%;top:-6px;margin-right:6px;font-size:8.5px;color:var(--nsfw);font-weight:700;background:var(--bg);padding:0 3px;font-variant-numeric:tabular-nums; }

/* Vue Liste — des cartes, pas des cases */
.cal-cards { flex:1;overflow-y:auto;padding:1.25rem;display:flex;flex-direction:column;gap:8px;min-height:0; }
.cal-card { background:var(--surface2);border:1px solid var(--border);border-left:3px solid var(--border2);border-radius:var(--r);padding:.7rem .85rem;cursor:pointer;transition:all .12s;display:flex;gap:10px;align-items:flex-start; }
.cal-card:hover { border-color:var(--gold2);background:var(--surface3); }
.cal-card.sel { border-color:var(--gold);background:var(--gold3); }
.cal-card-instagram { border-left-color:var(--sfw); }
.cal-card-fanvue    { border-left-color:var(--nsfw); }
.cal-card-thumb { width:52px;height:52px;border-radius:5px;object-fit:cover;flex-shrink:0;border:1px solid var(--border2); }
.cal-card-noimg { width:52px;height:52px;border-radius:5px;flex-shrink:0;border:1px dashed var(--border2);display:flex;align-items:center;justify-content:center;font-size:16px;opacity:.35; }
.cal-card-body { flex:1;min-width:0; }
.cal-card-top { display:flex;align-items:center;gap:8px;margin-bottom:3px;flex-wrap:wrap; }
.cal-card-when { font-size:11px;color:var(--cream);font-weight:500;font-variant-numeric:tabular-nums; }
.cal-card-meta { font-size:10px;color:var(--cream3); }
.cal-card-cap { font-size:11px;color:var(--cream2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap; }
.cal-who { font-size:9px;color:var(--cream3);border:1px solid var(--border2);border-radius:8px;padding:0 6px;white-space:nowrap; }

/* Étiquette de statut — posée par le système, jamais par la main */
.cal-tag { font-size:9px;border-radius:9px;padding:1px 8px;border:1px solid;white-space:nowrap;flex-shrink:0; }
/* Proposition d'agent non validee : visible, mais visuellement distincte
   d'une vraie programmation (bordure pointillee, teinte neutre). */
.cal-tag-brouillon { background:var(--surface3);border-color:var(--border2);color:var(--cream2);border-style:dashed; }
.cal-tag-attente { background:var(--gold3);border-color:var(--gold2);color:var(--gold); }
.cal-tag-poste   { background:var(--sfw-dim);border-color:var(--sfw);color:var(--sfw); }
.cal-tag-erreur  { background:var(--nsfw-dim);border-color:var(--nsfw);color:var(--nsfw); }

/* ── Le formulaire · popup au milieu de l'écran (Marcel, 17/07 · §2.5) ── */
.cal-modal { max-width:600px;padding:0;display:flex;flex-direction:column;max-height:88vh; }
.cal-detail-head { padding:.9rem 1.25rem;border-bottom:1px solid var(--border);background:var(--surface2);flex-shrink:0;display:flex;align-items:center;gap:10px;border-radius:12px 12px 0 0; }
/* Pastille 📅 devant le titre : le popup se reconnaît d'un coup d'œil. */
.cal-detail-head::before { content:"📅";width:30px;height:30px;flex-shrink:0;display:flex;align-items:center;justify-content:center;font-size:15px;background:var(--gold3);border:1px solid var(--gold2);border-radius:9px; }
.cal-detail-title { font-size:13px;color:var(--gold);letter-spacing:.06em;font-weight:700;font-family:var(--font-display);flex:1;min-width:0; }
#cal-edit-actions { margin-left:auto; }
.cal-detail-body { flex:1;overflow-y:auto;padding:1.25rem;display:flex;flex-direction:column;gap:1rem;min-height:0;background:var(--bg); }
/* Chaque section devient une carte, comme les panneaux Outils : on voit où
   commence et où finit un réglage au lieu d'une pile de libellés (Claria, 18/07). */
.cal-detail-body > div { background:var(--surface2);border:1px solid var(--border);border-radius:var(--r);padding:0 .9rem .9rem;display:flow-root; }
.cal-detail-body > div > .lf-section-label { margin:0 -.9rem .8rem;padding:.5rem .9rem;background:var(--surface3);border-bottom:1px solid var(--border);border-radius:var(--r) var(--r) 0 0;font-size:10px;letter-spacing:.16em; }
.cal-detail-body > div > :last-child { margin-bottom:0; }
/* La zone d'alerte garde son propre style : pas de cadre « carte » autour. */
.cal-detail-body > #cal-warn-zone { background:none;border:none;padding:0; }
.cal-detail-body > #cal-warn-zone:empty { display:none; }
.cal-detail-foot { border-top:1px solid var(--border);padding:.875rem 1.25rem;flex-shrink:0;background:var(--surface);border-radius:0 0 12px 12px; }
.cal-del { background:var(--nsfw-dim);border:1px solid var(--nsfw);border-radius:4px;color:var(--nsfw);font-size:10px;padding:3px 9px;cursor:pointer;font-family:inherit;transition:all .12s; }
.cal-del:hover { background:var(--nsfw);color:#fff; }
.cal-cancel { background:var(--surface3);border:1px solid var(--border2);border-radius:4px;color:var(--cream3);font-size:10px;padding:3px 9px;cursor:pointer;font-family:inherit;transition:all .12s; }
.cal-cancel:hover { border-color:var(--gold2);color:var(--gold); }
.cal-go { width:100%;background:var(--gold3);border:1px solid var(--gold2);border-radius:var(--r);padding:.7rem;font-size:12px;font-weight:500;color:var(--gold);cursor:pointer;font-family:inherit;transition:all .15s; }
.cal-go:hover:not(:disabled) { background:var(--gold2);color:#000; }
.cal-go:disabled { opacity:.35;cursor:not-allowed; }
.cal-foot-why { font-size:10px;color:var(--cream3);text-align:center;margin-top:.45rem;line-height:1.5; }
.cal-count { font-size:9px;color:var(--cream3);text-align:right;margin-top:2px; }
.cal-count.over { color:var(--nsfw);font-weight:600; }
/* Crédits OpenArt (credits.js) — carte de la vue Données */
.oa-card { background:var(--surface2);border:1px solid var(--border);border-radius:var(--r);
  padding:.75rem .9rem;margin:0 0 1rem; }
.oa-card:empty { display:none; }
.oa-head { display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap;margin-bottom:.7rem; }
.oa-title { font-size:12.5px;color:var(--gold); }
.oa-stats { display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:8px; }
.oa-stat { background:var(--surface);border:1px solid var(--border);border-radius:6px;padding:.5rem .6rem; }
.oa-stat.ko { border-color:var(--nsfw); }
.oa-stat-val { font-size:17px;color:var(--cream);font-variant-numeric:tabular-nums; }
.oa-stat.ko .oa-stat-val { color:var(--nsfw); }
.oa-stat-lbl { font-size:9px;letter-spacing:.12em;text-transform:uppercase;color:var(--gold2);margin-top:2px; }
.oa-stat-sub { font-size:9.5px;color:var(--cream3);margin-top:2px; }
.oa-alerte { margin-top:.6rem;font-size:11px;color:var(--nsfw);background:var(--nsfw-dim);
  border:1px solid var(--nsfw);border-radius:6px;padding:.45rem .6rem; }
.oa-ventil { display:flex;flex-wrap:wrap;gap:6px;margin-top:.6rem; }
.oa-ventil-item { font-size:10.5px;color:var(--cream3);background:var(--surface3);border-radius:20px;padding:2px 9px; }
.oa-row { display:flex;align-items:center;justify-content:space-between;gap:8px;font-size:11px;
  color:var(--cream2);background:var(--surface);border:1px solid var(--border);border-radius:5px;padding:.35rem .55rem; }

/* Campagnes (campaign.js) — période + contexte partagé */
.cm-chips { margin-top:.45rem; }
.cm-chip { display:inline-block;font-size:10.5px;border:1px solid;border-radius:20px;padding:2px 10px;margin-right:5px; }
.cm-line { display:flex;align-items:center;gap:7px;padding:.3rem .1rem;cursor:pointer;border-radius:4px; }
.cm-line:hover { background:var(--surface2); }
.cm-dot { width:8px;height:8px;border-radius:50%;flex-shrink:0; }
.cm-line-nom { font-size:11.5px;color:var(--cream2);flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap; }
.cm-line-when { font-size:9.5px;color:var(--cream3);flex-shrink:0; }
.cm-sep { font-size:9px;letter-spacing:.12em;text-transform:uppercase;color:var(--gold2);margin:.5rem 0 .2rem; }
.cm-row { display:flex;align-items:flex-start;gap:8px;background:var(--surface);border:1px solid var(--border);
  border-radius:6px;padding:.5rem .6rem; }
.cm-row.editing { border-color:var(--gold2); }
.cm-row .cm-dot { margin-top:5px; }
.cm-row-main { flex:1;min-width:0;display:flex;flex-direction:column; }
.cm-row-nom { font-size:12.5px;color:var(--cream); }
.cm-row-sub { font-size:10px;color:var(--cream3);margin-top:1px; }
.cm-row-ctx { font-size:10.5px;color:var(--cream2);margin-top:3px;line-height:1.5; }
.cm-ias { display:flex;flex-wrap:wrap;gap:8px;background:var(--surface);border:1px solid var(--border);
  border-radius:6px;padding:.45rem .6rem; }
.cm-ia { display:flex;align-items:center;gap:5px;font-size:11px;color:var(--cream2);cursor:pointer; }
.cm-ia input { accent-color:var(--gold);cursor:pointer; }

/* Mémoire des fans (fanmem.js) — fiches minimales, purge annoncée */
.fm-retention { display:flex;flex-wrap:wrap;align-items:center;gap:8px;background:var(--surface2);
  border:1px solid var(--border);border-radius:var(--r);padding:.6rem .7rem; }
.fm-row { position:relative;background:var(--surface);border:1px solid var(--border);
  border-radius:6px;padding:.5rem .6rem;padding-right:74px; }
.fm-row.editing { border-color:var(--gold2); }
.fm-row-main { display:flex;flex-wrap:wrap;align-items:center;gap:6px; }
.fm-alias { font-size:12.5px;color:var(--cream); }
.fm-tag { font-size:9px;letter-spacing:.06em;text-transform:uppercase;color:var(--cream3);
  background:var(--surface3);border-radius:8px;padding:1px 7px; }
.fm-row-sub { font-size:10.5px;color:var(--cream3);margin-top:3px;line-height:1.5; }
.fm-row-purge { color:var(--cream3);opacity:.75;font-size:9.5px; }
.fm-row-acts { position:absolute;top:6px;right:6px;display:flex;gap:2px; }

/* A/B de légendes (abtest.js) — deux angles côte à côte, un seul retenu */
.ab-grid { display:grid;grid-template-columns:1fr 1fr;gap:10px; }
@media (max-width:640px){ .ab-grid { grid-template-columns:1fr; } }
.ab-card { display:flex;flex-direction:column;gap:8px;background:var(--surface2);
  border:1px solid var(--border);border-radius:var(--r);padding:.7rem .75rem; }
.ab-card:hover { border-color:var(--gold2); }
.ab-card-head { display:flex;align-items:center;gap:7px;font-size:10px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--gold2); }
.ab-badge { display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;
  border-radius:5px;background:var(--gold3);border:1px solid var(--gold2);color:var(--gold);
  font-size:11px;letter-spacing:0; }
.ab-card-text { flex:1;font-size:12px;line-height:1.6;color:var(--cream2);white-space:pre-wrap; }
.ab-pick { width:100%;background:var(--gold3);border:1px solid var(--gold2);border-radius:6px;
  color:var(--gold);font-size:11px;padding:6px;cursor:pointer;font-family:inherit;transition:all .12s; }
.ab-pick:hover { background:var(--gold2);color:#000; }
.ab-wait { padding:1.6rem;text-align:center;font-size:12px;color:var(--cream3); }
.ab-foot-note { font-size:10px;color:var(--cream3); }
.ab-trace { position:relative;margin-top:.45rem;background:var(--surface2);border:1px solid var(--border2);
  border-radius:6px;padding:.45rem .6rem;font-size:10.5px;color:var(--cream2);line-height:1.5; }
.ab-trace-sub { font-size:9.5px;color:var(--cream3);margin-top:2px; }
.ab-trace-x { position:absolute;top:5px;right:6px;background:none;border:none;color:var(--cream3);
  font-size:11px;cursor:pointer;font-family:inherit;padding:0 3px; }
.ab-trace-x:hover { color:var(--nsfw); }

/* Checklist de conformité (compliance.js) — remplace les anciennes alertes
   « cal-warn ». Vert = vérifié · rouge = bloquant · orange = conseil.
   SEUL « créneau déjà passé » est rouge parmi les conseils : c'est le seul qui
   dit « ça ne partira jamais » (Marcel, 17/07 · §2.9). */
.cp-check { border:1px solid var(--border2);border-radius:var(--r);padding:.55rem .65rem;background:var(--surface2); }
.cp-check.has-bloq { border-color:var(--nsfw); }
.cp-check-head { display:flex;align-items:center;justify-content:space-between;gap:8px;
  font-size:9px;letter-spacing:.14em;text-transform:uppercase;color:var(--gold2);margin-bottom:6px; }
.cp-check-score { letter-spacing:.05em;color:var(--cream3); }
.cp-check-score.ok { color:var(--sfw); }
.cp-check-score.ko { color:var(--nsfw); }
.cp-line { display:flex;align-items:flex-start;gap:7px;font-size:10.5px;line-height:1.5;padding:2px 0;color:var(--cream3); }
.cp-ico { flex-shrink:0;width:12px;text-align:center; }
.cp-line.ok   { color:var(--cream3); } .cp-line.ok .cp-ico { color:var(--sfw); }
.cp-line.warn { color:var(--warn); }   .cp-line.warn .cp-ico { color:var(--warn); }
.cp-line.red,
.cp-line.ko   { color:var(--nsfw); }   .cp-line.ko .cp-ico, .cp-line.red .cp-ico { color:var(--nsfw); }
.cp-check-foot { font-size:9.5px;color:var(--cream3);margin-top:6px;padding-top:5px;border-top:1px solid var(--border);line-height:1.5; }
.cp-check-foot.ko { color:var(--nsfw); }
.cal-thumb { width:100%;border-radius:6px;border:1px solid var(--border2);display:block; }

/* Pack Fanvue — plusieurs images, une seule sert d'appât */
.cal-pack { display:grid;grid-template-columns:repeat(3,1fr);gap:6px; }
.cal-pack-item { position:relative;border-radius:5px;overflow:hidden;border:2px solid var(--border2);cursor:pointer;aspect-ratio:1;transition:all .12s; }
.cal-pack-item:hover { border-color:var(--gold2); }
.cal-pack-item img { width:100%;height:100%;object-fit:cover;display:block; }
.cal-pack-item.is-preview { border-color:var(--gold); }
.cal-pack-badge { position:absolute;left:0;right:0;bottom:0;background:var(--gold);color:#000;font-size:8px;text-align:center;padding:1px 0;font-weight:700;letter-spacing:.05em; }
.cal-pack-del { position:absolute;top:2px;right:2px;background:rgba(0,0,0,.65);border:none;border-radius:3px;color:#fff;font-size:9px;width:16px;height:16px;cursor:pointer;line-height:1;padding:0; }
.cal-pack-del:hover { background:var(--nsfw); }
.cal-pack-add { border:1px dashed var(--border2);border-radius:5px;aspect-ratio:1;display:flex;align-items:center;justify-content:center;font-size:18px;color:var(--cream3);cursor:pointer;position:relative;transition:all .12s;background:var(--surface2); }
.cal-pack-add:hover, .cal-pack-add.dragover { border-color:var(--gold2);color:var(--gold);background:var(--gold3); }
.cal-pack-add input { position:absolute;inset:0;opacity:0;cursor:pointer;width:100%;height:100%; }

/* L'appât, tel que le voit un non-abonné */
.cal-blur { position:relative;border-radius:6px;overflow:hidden;border:1px solid var(--border2);margin-top:.5rem; }
.cal-blur img { width:100%;display:block;filter:blur(16px);transform:scale(1.1); }
.cal-blur-lock { position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;background:rgba(0,0,0,.35);color:#fff;font-size:12px;font-weight:600;text-shadow:0 1px 4px rgba(0,0,0,.8); }
.cal-blur-sub { font-size:10px;font-weight:400;opacity:.85; }
.lf-range { width:100%;accent-color:var(--gold2);cursor:pointer; }
.lf-export-btn { width:100%;background:var(--surface3);border:1px solid var(--border2);border-radius:var(--r);padding:.6rem;font-size:11px;color:var(--cream2);cursor:pointer;font-family:inherit;transition:all .15s; }
.lf-export-btn:hover { border-color:var(--gold2);color:var(--gold);background:var(--gold3); }

/* ══ PANNEAUX BOT · InstaBOT et FanBOT ══
   Même charpente que LinkFORGE : réglages à gauche, résultat à droite.
   Les deux panneaux partagent ces classes — leur contenu est fabriqué par
   botPanelHTML(bot), une seule fois, pour ne pas dupliquer 250 lignes. */

.bot-test { width:var(--test-col);flex-shrink:0;display:flex;flex-direction:column;background:var(--bg);min-height:0; }
/* Prévisualisation LinkFORGE — exactement le gabarit de la zone de test des
   bots (même en-tête, même colonne à droite d'une config large), mais la
   prévisualisation prend la place du tchat et se voit EN GRAND (Marcel, 17/07).
   Le téléphone remplit sa colonne au lieu de flotter dans un vide. */
.lf-preview-col { width:var(--test-col);flex-shrink:0;display:flex;flex-direction:column;background:var(--bg);min-height:0; }
.lf-preview-zone { flex:1;min-height:0;overflow-y:auto;padding:1.25rem;display:flex; }
.lf-phone { width:100%;max-width:360px;border-radius:40px;border:3px solid var(--border2);overflow:hidden;box-shadow:0 8px 40px rgba(0,0,0,.6);background:#000;flex-shrink:0;margin:auto; }
.bot-area { background:var(--surface2);border:1px solid var(--border2);border-radius:6px;padding:.5rem .7rem;font-size:11px;color:var(--cream);font-family:inherit;outline:none;width:100%;box-sizing:border-box;resize:vertical;line-height:1.6; }
.bot-area:focus { border-color:var(--gold2); }
.bot-hint { font-size:10px;color:var(--cream3);line-height:1.55;margin-top:.4rem; }

/* Interrupteur maître — trois états exclusifs, chacun sa couleur */
.bot-seg { display:flex;gap:6px; }
.bot-seg-btn { flex:1;background:var(--surface2);border:1px solid var(--border2);border-radius:6px;padding:.5rem .2rem;font-size:11px;color:var(--cream3);cursor:pointer;font-family:inherit;transition:all .12s;text-align:center; }
.bot-seg-btn:hover { border-color:var(--gold2);color:var(--gold); }
.bot-seg-btn.on-on     { background:var(--sfw-dim);border-color:var(--sfw);color:var(--sfw);font-weight:600; }
.bot-seg-btn.on-silent { background:var(--warn-dim);border-color:var(--warn);color:var(--warn);font-weight:600; }
.bot-seg-btn.on-off    { background:var(--surface3);border-color:var(--cream3);color:var(--cream2);font-weight:600; }

/* Listes éditables : situations à ticket + limites infranchissables */
.bot-line-row { display:flex;gap:6px;align-items:flex-start;margin-bottom:6px; }
.bot-line-row .bot-area { min-height:34px; }
.bot-add { width:100%;background:var(--surface2);border:1px dashed var(--border2);border-radius:6px;color:var(--cream3);font-size:11px;padding:.45rem;cursor:pointer;font-family:inherit;transition:all .12s;margin-top:.25rem; }
.bot-add:hover { border-color:var(--gold2);color:var(--gold);background:var(--gold3); }

/* Zone de test */
/* min-height alignée sur .api-col-head (63px) pour que le bas de « Prévisualisation »
   / « Zone de test » tombe au même niveau que « LinkFORGE » / « InstaBOT » / « FanBOT »
   (en-tête gauche à 2 lignes + icône, plus haut). Victor, 20/07. */
.bot-test-head { padding:.875rem 1rem;border-bottom:1px solid var(--border);background:var(--surface2);flex-shrink:0;display:flex;align-items:center;justify-content:space-between;gap:1rem;min-height:63px; }
.bot-test-title { font-size:11px;color:var(--cream3);text-transform:uppercase;letter-spacing:.2em;font-weight:500; }
/* Reset de la conversation de test — discret, à droite du titre. */
.bot-reset { background:transparent;border:1px solid var(--border2);border-radius:6px;padding:4px 9px;font-size:10.5px;color:var(--cream3);cursor:pointer;font-family:inherit;transition:all .15s;white-space:nowrap; }
.bot-reset:hover { border-color:var(--gold2);color:var(--gold); }
.bot-test-safe { font-size:10px;color:var(--warn);background:var(--warn-dim);border:1px solid var(--warn);border-radius:10px;padding:2px 10px;white-space:nowrap; }
.bot-test-body { flex:1;overflow-y:auto;min-height:0; }
/* Le fil de discussion est centré dans la zone, comme l'était le détail d'un
   ticket : même largeur max, même centrage. Le message d'accueil hérite donc
   exactement des mêmes coordonnées. */
.bot-chat { padding:1rem;display:flex;flex-direction:column;gap:10px;width:100%;margin:auto; }
.bot-test-foot { border-top:1px solid var(--border);padding:.875rem 1rem;flex-shrink:0;background:var(--surface); }
.bot-bubble { max-width:88%;border-radius:12px;padding:.6rem .85rem;font-size:12px;line-height:1.65;white-space:pre-wrap;word-break:break-word; }
.bot-bubble-fan { align-self:flex-start;background:var(--surface2);border:1px solid var(--border2);color:var(--cream2);border-bottom-left-radius:3px; }
.bot-bubble-bot { align-self:flex-end;background:var(--gold3);border:1px solid var(--gold2);color:var(--cream);border-bottom-right-radius:3px; }
.bot-bubble-who { font-size:9px;letter-spacing:.15em;text-transform:uppercase;color:var(--cream3);margin-bottom:4px; }
.bot-verdict { border-radius:var(--r);padding:.7rem .85rem;font-size:11px;line-height:1.6;border:1px solid; }
.bot-verdict-yes { background:var(--warn-dim);border-color:var(--warn);color:var(--warn); }
.bot-verdict-no  { background:var(--surface2);border-color:var(--border2);color:var(--cream3); }
.bot-verdict-mute{ background:var(--surface3);border-color:var(--cream3);color:var(--cream2); }
.bot-verdict b { color:var(--cream); }
/* Ce qu'un garde-fou a repris au moteur (bots.js, 26/07). Le filet le sépare
   de la raison DONNÉE PAR LE MODÈLE, juste au-dessus : les deux lignes ne
   viennent pas du même auteur et ne doivent pas se lire comme une seule. */
.bot-verdict-bride { margin-top:.45rem; padding-top:.45rem; border-top:1px solid currentColor; color:var(--cream2); }
.bot-run { width:100%;background:var(--gold3);border:1px solid var(--gold2);border-radius:var(--r);padding:.6rem;font-size:12px;font-weight:500;color:var(--gold);cursor:pointer;font-family:inherit;transition:all .15s;margin-top:.5rem; }
.bot-run:hover:not(:disabled) { background:var(--gold2);color:#000; }
.bot-run:disabled { opacity:.45;cursor:not-allowed; }
.bot-lore-btn { background:var(--surface3);border:1px solid var(--border2);border-radius:4px;color:var(--cream3);font-size:10px;padding:2px 8px;cursor:pointer;font-family:inherit;transition:all .12s; }
.bot-lore-btn:hover { border-color:var(--gold2);color:var(--gold); }
/* Pavé directionnel de cadrage de l'image d'en-tête */
.lf-pos-pad { display:flex; flex-direction:column; align-items:center; gap:4px; }
.lf-pos-mid { display:flex; gap:4px; }
.lf-pos-btn { width:30px; height:26px; background:var(--surface3); border:1px solid var(--border2); border-radius:4px; color:var(--cream3); font-size:9px; cursor:pointer; font-family:inherit; transition:all .12s; display:flex; align-items:center; justify-content:center; }
.lf-pos-btn:hover { border-color:var(--gold2); color:var(--gold); background:var(--gold3); }
.lf-pos-reset { color:var(--cream2); font-size:11px; }
.lf-pos-read { text-align:center; font-size:10px; color:var(--cream3); margin-top:5px; }

/* SIDEBAR DROITE GLOBALE */
#sidebar-right-global {
  width:260px; flex-shrink:0; border-left:1px solid var(--border);
  padding:1.25rem; display:flex; flex-direction:column; gap:var(--sb-gap);
  background:var(--surface); overflow-y:auto; overflow-x:hidden;
}
.persona-card-global {
  display:flex; align-items:center; gap:10px;
  background:var(--surface2); border:1px solid var(--border2);
  border-radius:var(--r); padding:.75rem .875rem;
}
.pcg-avatar {
  width:36px; height:36px; border-radius:50%; background:var(--gold3);
  border:2px solid var(--gold2); display:flex; align-items:center;
  justify-content:center; font-size:11px; font-weight:700;
  color:var(--gold); flex-shrink:0; letter-spacing:.03em;
}
.pcg-info { display:flex; flex-direction:column; gap:2px; min-width:0; }
.pcg-name { font-size:13px; font-weight:500; color:var(--cream); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.pcg-sub  { font-size:10px; color:var(--cream3); }
/* Fondu de remplacement du contenu de la popup ticket */
@keyframes urgentSwap { from { opacity:.35; transform:scale(.985); } to { opacity:1; transform:scale(1); } }
.urgent-box.swapping { animation:urgentSwap .22s ease-out; }

/* Les styles .claude-* (bulle de discussion de la sidebar droite) ont été
   retirés avec l'encadré lui-même (Victor, 22/07). */

/* ── LAYOUT FIXE ── */
html, body { height:100%; overflow:hidden; }
body { display:flex; flex-direction:column; }
.topbar { flex-shrink:0; }
.nav-tabs { flex-shrink:0; }
.app { flex:1; min-height:0; display:flex; flex-direction:row; }
/* Voile de démarrage (boot-theme.js) : on masque le shell tant que
   demarrerApp() n'a pas tranché entre « rester ici » et « ouvrir l'écran de
   choix du produit ». Évite le flash de Prompt Engine avant le launcher. Le
   fond de page (thème déjà appliqué) reste visible dessous. */
:root[data-booting] .app { visibility:hidden; }
.views-zone { flex:1; min-height:0; display:flex; flex-direction:column; min-width:0; }
.view { flex:1; min-height:0; }
.view.active { display:flex; flex-direction:column; }
.gen-layout { flex:1; min-height:0; display:flex; }
.sidebar { overflow-y:auto; height:100%; }
.sidebar-right { overflow-y:auto; height:100%; }
#sidebar-right-global { overflow-y:auto; height:100%; flex-shrink:0; }

/* ══ COLONNES LATÉRALES : SCROLLER, JAMAIS COMPRIMER ══
   (Marcel, 16/07 — « ça se superpose / disparaît »)

   Un enfant de flex column a flex-shrink:1 par défaut. Quand le
   contenu dépasse, le navigateur ÉCRASE les enfants au lieu de
   déclencher le scroll. Normalement min-height:auto l'en empêche…
   sauf que cette valeur tombe à ZÉRO dès que l'élément est en
   overflow:hidden — ce qui est le cas de .sb-fold.

   Résultat mesuré avant correctif (1920x880) :
     fold-engine    rendu 127px / naturel 264px  → 137px coupés
     fold-services  rendu  50px / naturel 101px  →  51px coupés
     sidebar scrollable : NON — rien ne débordait, tout était écrasé.
   D'où « Modèles utilisés » tronqué et les blocs qui se chevauchent.

   Le symptôme avait été traité une fois, sur une seule boîte. On le
   généralise à toutes les colonnes plutôt que boîte par boîte. */
.sidebar > *,
.sidebar-right > *,
#sidebar-right-global > *,
.hist-filter-scroll > *,
.cal-side-body > *,
.lora-sidebar > *,
.doc-sidebar > * { flex-shrink:0; }

/* ══════════════════════════════════════════════════════════
   L'ÉCART ENTRE TUILES EST CELUI DU CONTENEUR, ET LUI SEUL
   (Victor, 25/07 (6) — « applique l'espace de l'encadré gauche de partout »)
   ------------------------------------------------------------
   ⚠️ POURQUOI LE `gap` NE SUFFISAIT PAS. Les deux colonnes étaient bien passées
   à `gap:.6rem`, et pourtant la droite restait visiblement plus aérée : les
   TUILES portent leurs propres marges verticales — `.pending-tile` a
   `margin:8px 0`, `.svc-refresh-btn` a `margin:2px 0 8px`. Ces marges
   S'AJOUTENT au gap (elles ne fusionnent pas, un conteneur flex ne fusionne
   pas les marges). Écart réel à droite : 9,6 + 8 + 8 ≈ 25 px, contre 9,6 px à
   gauche. D'où « à gauche c'est moins espacé qu'à droite ».

   La règle : dans une colonne latérale, un enfant DIRECT ne décide pas de son
   écart — c'est le conteneur. On neutralise donc les marges verticales à ce
   niveau, et le `gap` devient la seule valeur à changer si on veut resserrer
   ou aérer les trois pages d'un coup.

   ⚠️ Ça ne touche QUE les enfants directs : les marges internes aux cartes
   (boutons empilés dans `.sb-fold-body`) sont conservées, elles règlent autre
   chose.
   ⚠️ Les styles en ligne l'emportent : le `margin-top:auto` qui pousse le bloc
   moteur/services en bas de colonne continue de fonctionner.
   ══════════════════════════════════════════════════════════ */
.sidebar > *,
.sidebar-right > *,
#sidebar-right-global > *,
.hist-filter-scroll > *,
.cal-side-body > * { margin-top:0; margin-bottom:0; }

/* ══════════════════════════════════════════════════════════
   LES DEUX SEULES EXCEPTIONS AUTORISÉES (Victor, 26/07)
   ------------------------------------------------------------
   La règle ci-dessus est absolue : dans une colonne, l'écart appartient au
   conteneur. Mais deux besoins reviennent tout le temps, et les traiter à
   coups de `style="margin-top:…"` est exactement ce qui a fait diverger les
   trois outils. On leur donne donc UN nom chacun.

   ⚠️ ELLES DOIVENT PASSER APRÈS LA REMISE À ZÉRO, ET AVEC PLUS DE POIDS :
   `.sidebar > *` (0,1,0) vient plus bas dans le fichier que `.gen-btn-wrap`
   (0,1,0) — à poids égal, c'est le dernier qui gagne, et il écrasait donc le
   `margin-top:auto` du bloc « Générer ». C'EST POURQUOI CE BLOC N'ÉTAIT PLUS
   COLLÉ EN BAS de l'encadré gauche. `.sidebar > .sb-push` pèse 0,2,0 et
   reprend la main.
   ══════════════════════════════════════════════════════════ */
/* Pousse ce qui suit en bas de la colonne. Remplace les
   `<div style="margin-top:auto">` recopiés dans les trois pages. */
.sidebar > .sb-push,
.sidebar-right > .sb-push,
#sidebar-right-global > .sb-push,
.hist-filter-scroll > .sb-push,
.cal-side-body > .sb-push { margin-top:auto; }
/* Filet de séparation d'une colonne. ⚠️ IL LUI FALLAIT UNE CLASSE (26/07) :
   il était écrit en ligne — `style="border-top:…;margin:0 -.25rem"` — et ce
   `margin` raccourci pose AUSSI `margin-top:0`. Un style en ligne l'emportant
   sur toute règle, il annulait le `.sb-gap24` qu'on venait de lui donner : le
   séparateur restait collé à 12 px au lieu de 24, sans que rien ne l'explique.
   Ici, les marges latérales seules sont posées, et le haut reste libre. */
.sb-sep { border-top:1px solid var(--border); margin-left:-.25rem; margin-right:-.25rem; }
/* Écart DOUBLE au-dessus de cet élément : 12 (gap) + 12 = 24 px. À réserver
   aux vraies coupures de lecture — un séparateur, un changement de nature. */
.sidebar > .sb-gap24,
.sidebar-right > .sb-gap24,
#sidebar-right-global > .sb-gap24,
.hist-filter-scroll > .sb-gap24,
.cal-side-body > .sb-gap24 { margin-top:var(--sb-gap); }

/* Rangée « Démarrer / Éteindre + Actualiser ». ⚠️ Elle a sa classe parce que
   `.svc-refresh-btn` porte `margin:2px 0 8px` : dans une rangée, ces 8 px
   s'ajoutaient SOUS les boutons et repoussaient « Moteur de génération » à
   20 px au lieu de 12. Les boutons empilés dans une carte, eux, gardent leur
   marge — c'est elle qui les sépare. */
.svc-btn-row { display:flex; gap:8px; }
.svc-btn-row .svc-refresh-btn { margin:0; }

/* ⚠️ DEUX EMPLACEMENTS VIDES QUI PRENAIENT QUAND MÊME UN ÉCART (26/07).
   `#hist-ia-card` (Historique) et `#api-master-picker` (onglet API) sont des
   restes du sélecteur d'IA retiré le 23/07 : leurs fonctions de rendu ne font
   plus que `innerHTML = ""`, et les div sont gardées pour ne pas casser leurs
   appelants. Vides, elles restent des éléments flex — elles ajoutaient donc un
   `gap` complet au-dessus du premier titre, qui tombait 12 px plus bas que
   dans les autres colonnes.
   ⚠️ Ciblé PAR IDENTIFIANT, jamais par un `> *:empty` général : les
   séparateurs de colonne sont eux aussi des div vides, et une règle générale
   les ferait tous disparaître. */
#hist-ia-card:empty,
#api-master-picker:empty { display:none; }
.main-output { flex:1; overflow:hidden; min-width:0; }
.history-view { flex:1; overflow-y:auto; }
.donnees-view { flex:1; min-height:0; }
.donnees-content { overflow-y:auto; }

/* ── BOUTON CLÉS API (topbar) ── */
.api-keys-btn {
  display:flex; align-items:center; gap:6px;
  background:var(--surface2); border:1px solid var(--border2);
  border-radius:6px; padding:6px 14px; cursor:pointer;
  font-family:inherit; color:var(--cream2); font-size:12px;
  font-weight:500; transition:all .15s; white-space:nowrap;
}
/* ⚠️ `text-decoration:none` (Victor, 26/07 : « les boutons IA et TUBE sont
   soulignés »). Cette classe a été écrite pour des `<button>`, qui ne sont
   jamais soulignés ; dans l'Atelier elle habille deux `<a href>`, et un lien
   porte un soulignement PAR DÉFAUT du navigateur. Un bouton qui se comporte
   visuellement en lien selon la balise qui le porte, c'est la définition même
   du décalage entre deux pages. La règle vaut donc pour les deux balises. */
.api-keys-btn { text-decoration:none; }
.api-keys-btn:hover { border-color:var(--gold2); color:var(--gold); }
.api-keys-dot { width:6px; height:6px; border-radius:50%; background:var(--cream3); }
.api-keys-dot.configured { background:var(--sfw); }

/* BULLE PROFIL UTILISATEUR */
/* Lignes du panneau d'administration des comptes */
.adm-row { display:flex; align-items:center; gap:.75rem; padding:.6rem .75rem; border:1px solid var(--border); border-radius:var(--r); margin-bottom:.5rem; background:var(--surface2); }
.adm-av { width:28px; height:28px; border-radius:50%; background:var(--gold3); border:1px solid var(--gold2); color:var(--gold); display:flex; align-items:center; justify-content:center; font-size:11px; font-weight:700; flex-shrink:0; text-transform:uppercase; }
.adm-id { flex:1; min-width:0; }
.adm-pseudo { font-size:13px; font-weight:600; color:var(--cream); }
.adm-mail { font-size:10px; color:var(--cream3); word-break:break-all; }
.adm-tag { font-size:9px; letter-spacing:.1em; text-transform:uppercase; padding:2px 6px; border-radius:4px; border:1px solid var(--gold2); color:var(--gold); background:var(--gold3); flex-shrink:0; }
.adm-tag-warn { border-color:var(--nsfw); color:var(--nsfw); background:var(--nsfw-dim); }
.adm-act { background:none; border:1px solid var(--border2); color:var(--cream3); border-radius:var(--r); padding:.3rem .5rem; font-size:11px; cursor:pointer; font-family:inherit; transition:all .15s; flex-shrink:0; }
.adm-act:hover { border-color:var(--gold); color:var(--gold); }
.adm-act-del:hover { border-color:var(--nsfw); color:var(--nsfw); }
/* Filtre IA global (encadré droit, au-dessus des Agents). */
.master-filter-card .mf-head { display:flex; align-items:center; justify-content:space-between; padding:.55rem .75rem; border-bottom:1px solid var(--border); }
.master-filter-card .mf-body { padding:.45rem .55rem .55rem; display:flex; flex-direction:column; gap:2px; }
.mf-opt { display:flex; align-items:center; gap:.5rem; padding:.32rem .4rem; border-radius:6px; font-size:11.5px; color:var(--cream2); cursor:pointer; transition:background .12s; }
.mf-opt:hover { background:var(--surface3); }
.mf-opt.on { color:var(--cream); }
.mf-opt input { width:14px; height:14px; accent-color:var(--gold); cursor:pointer; flex-shrink:0; }
.mf-opt .mf-name { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.mf-clear { margin-top:.35rem; width:100%; background:transparent; border:1px solid var(--border2); border-radius:6px; padding:5px; font-size:10.5px; color:var(--cream3); cursor:pointer; font-family:inherit; transition:all .15s; }
.mf-clear:hover:not(:disabled) { border-color:var(--gold2); color:var(--gold); }
.mf-clear:disabled { opacity:.4; cursor:default; }
/* Filtre IA — encadré droit (résumé) et liste de sélection. */
.mf-summary { font-size:11.5px; color:var(--cream2); line-height:1.5; word-break:break-word; }
.mf-popup-list { display:flex; flex-direction:column; gap:2px; margin:.25rem 0 .5rem; max-height:50vh; overflow-y:auto; }
/* Séparateur entre IA cochées (en haut) et non cochées. */
.mf-sep { height:1px; background:var(--border); margin:.45rem .3rem; }

/* ══ FILTRE IA : LE NOM EST LE BOUTON (Victor, 25/07 (3)) ══
   « je dois avoir le nom de l'ia centré au milieu, quand je clique dessus ca
   déroule un menu et pas un popup ». Le menu se déroule DANS la carte : il
   pousse le contenu suivant vers le bas au lieu de le recouvrir — dans une
   colonne étroite, une liste flottante déborderait de l'encadré.
   ⚠️ Dimensions reprises de `.svc-refresh-btn` (l. « padding:.5rem .75rem »,
   font-size 11px) : c'est le bouton voisin, ils doivent faire la même taille. */
.mf-current {
  display:flex; align-items:center; justify-content:center; gap:6px; width:100%;
  padding:.5rem .75rem; cursor:pointer;
  background:var(--surface2); border:1px solid var(--border); border-radius:var(--r);
  color:var(--cream); font-family:inherit; font-size:12px; font-weight:600;
  transition:background .12s, border-color .12s;
}
.mf-current:hover { background:var(--surface3); border-color:var(--gold2); }
.mf-current-name { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.mf-current-arrow { font-size:10px; color:var(--cream3); flex-shrink:0; transition:transform .15s; }
.mf-current.open .mf-current-arrow { transform:rotate(180deg); }
/* Fermé par défaut : la carte ne montre que le nom tant qu'on ne clique pas. */
.mf-menu { display:none; margin-top:.4rem; }
.mf-menu.open { display:block; }
.mf-menu .mf-popup-list { margin:0 0 .35rem; max-height:38vh; }

/* ══ VOILE DE VERROU D'UNE CARTE DE L'ENCADRÉ (Victor, 25/07 (3)) ══
   La carte Agents est grisée quand le Filtre IA ne désigne pas exactement une
   IA. Même principe que `#api-lock` : on RECOUVRE, on ne désactive pas — les
   attributs `disabled` appartiennent déjà à d'autres logiques (permissions du
   compte), et un troisième propriétaire finirait par rouvrir un bouton à un
   compte en lecture seule.
   ⚠️ `position:relative` sur la carte est posé ici, sinon le voile se
   positionnerait par rapport à la page entière. */
/* ══════════════════════════════════════════════════════════
   L'HABILLAGE D'UNE CARTE PLIABLE EST ICI (Victor, 26/07)
   ------------------------------------------------------------
   « L'affichage des tuiles MOTEUR DE GÉNÉRATION et SERVICES dans l'Atelier
   n'est toujours pas identique à ceux des prompt engines ! »

   ⚠️⚠️ IL AVAIT RAISON, ET LA CAUSE ÉTAIT ICI : cette règle ne portait QUE
   `position:relative`. Le fond, la bordure et le rayon d'une carte pliable
   étaient écrits EN LIGNE, `style="background:var(--surface2);border:1px solid
   var(--border);border-radius:var(--r);overflow:hidden"`, répétés SIX fois
   dans index.html et tube.html. L'Atelier, lui, ne les avait pas : ses cartes
   sortaient sans fond, sans bordure et sans coins — d'où « ce n'est toujours
   pas pareil » après deux passes qui n'avaient touché que le CONTENU.

   ⚠️ C'est le même piège que les colonnes et les séparateurs, une troisième
   fois : chercher la différence dans le CSS ne servait à rien, puisque la
   valeur n'y était pas. Quand deux pages diffèrent malgré une feuille commune,
   regarder les styles EN LIGNE avant toute chose.
   ══════════════════════════════════════════════════════════ */
.sb-fold {
  position:relative;
  background:var(--surface2); border:1px solid var(--border);
  border-radius:var(--r); overflow:hidden;
}
/* ⚠️ Le voile commence SOUS l'en-tête (2.55rem = hauteur de `.sb-fold-head`,
   padding .75rem + ligne de titre). Avec `inset:0` il recouvrait aussi le
   titre : la carte verrouillée ne pouvait plus être ni repliée ni dépliée,
   et son infobulle « i » devenait inaccessible. Le verrou empêche d'AGIR, pas
   de regarder. */
.sb-fold-lock {
  display:none; position:absolute; inset:2.55rem 0 0 0; z-index:5;
  align-items:center; justify-content:center; text-align:center;
  background:rgba(10,9,8,.72); backdrop-filter:blur(1.5px);
  border-radius:var(--r); padding:.75rem;
  font-size:11px; line-height:1.6; color:var(--warn); cursor:not-allowed;
}
/* Repliée, il n'y a rien à recouvrir : le voile ne s'affiche que sur une carte
   ouverte, sinon il flotterait sur les cartes voisines. */
.sb-fold.locked.open .sb-fold-lock { display:flex; }
.sb-fold.locked .sb-fold-body { filter:grayscale(1); }

/* ══ ÉTIQUETTE « CETTE ACTION COÛTE » (Victor, 25/07 (3)) ══
   « Marquer les boutons qui coûtent … pour ne pas cliquer pour tester. »
   Elle porte le montant réel dès qu'on le connaît (interrogé chez OpenArt),
   et le mot « coûte » tant qu'on ne l'a pas — jamais un chiffre inventé. */
.btn-cout {
  margin-left:auto; flex-shrink:0; padding:1px 6px; border-radius:20px;
  background:var(--warn-dim); border:1px solid var(--warn);
  color:var(--warn); font-size:9px; font-weight:700; letter-spacing:.06em;
  text-transform:uppercase; white-space:nowrap;
}
/* Bandeau cible du Générateur/API : état ambigu (0 ou 2+ IA cochées). */
.gen-target-banner.gt-ambigu { border-color:var(--warn); }
.gt-warn { color:var(--warn); }
/* Cases « produits accessibles » du formulaire de création de compte. */
.adm-acces-opt { display:flex; align-items:center; gap:.5rem; font-size:12px; color:var(--cream2); cursor:pointer; padding:.25rem 0; }
.adm-acces-opt input { width:15px; height:15px; accent-color:var(--gold); cursor:pointer; }
/* Droits par IA à la création de compte (Victor, 24/07). */
.adm-ia-list { margin-top:.4rem; border:1px solid var(--border2); border-radius:var(--r); padding:.5rem; max-height:200px; overflow-y:auto; display:flex; flex-direction:column; gap:.35rem; }
.adm-ia-row { display:flex; align-items:center; justify-content:space-between; gap:.5rem; }
.adm-ia-check { display:flex; align-items:center; gap:.5rem; font-size:12px; color:var(--cream2); cursor:pointer; flex:1; min-width:0; }
.adm-ia-check input { width:15px; height:15px; accent-color:var(--gold); cursor:pointer; flex-shrink:0; }
.adm-ia-lvl { background:var(--surface2); border:1px solid var(--border2); border-radius:6px; color:var(--cream); font-size:11px; padding:3px 6px; cursor:pointer; font-family:inherit; flex-shrink:0; }
.adm-ia-empty { font-size:11px; color:var(--cream3); }
/* Puce d'accès dans l'annuaire (à côté du rôle). */
.adm-tag-prod { border-color:var(--border2); color:var(--cream3); background:var(--surface2); }
/* Conteneur de la bulle profil : il ancre le menu déroulant, posé en absolu.
   `position:relative` était en style EN LIGNE sur les trois pages — une valeur
   structurelle recopiée trois fois (Victor, 26/07). */
.user-bubble-wrap { position:relative; }
.user-bubble { display:flex; align-items:center; gap:8px; background:var(--surface2); border:1px solid var(--border2); border-radius:20px; padding:5px 12px 5px 6px; cursor:pointer; font-family:inherit; color:var(--cream); font-size:12px; font-weight:500; transition:all .15s; white-space:nowrap; }
.user-bubble:hover { border-color:var(--gold); color:var(--gold); }
/* ⚠️ LE NOM AFFICHÉ A UNE CLASSE, ET PLUS SEULEMENT UN IDENTIFIANT (26/07) :
   les trois pages le nommaient différemment (`user-bubble-name`,
   `tb-user-name`, `profile-name`), donc aucun code partagé ne pouvait le
   peindre. C'est ce qui obligeait chaque page à écrire son propre libellé
   d'attente — et donc à clignoter à sa façon. Voir src/core/identite.js.
   ⚠️ La largeur minimale RÉSERVE la place du nom : sans elle, la bulle
   s'élargit quand le nom arrive et pousse le reste de la barre du haut. */
.user-bubble-name { display:inline-block; min-width:4.5rem; }
/* Identité pas encore connue : la place est tenue, le contenu est masqué.
   `visibility` et non `display` — la bulle doit garder exactement sa taille
   finale, sinon on remplace un clignotement par un saut de mise en page. */
.user-bubble.identite-attente .user-bubble-name,
.user-bubble.identite-attente .user-bubble-avatar { visibility:hidden; }
.user-bubble-avatar { width:24px; height:24px; border-radius:50%; background:var(--gold3); border:1px solid var(--gold2); color:var(--gold); display:flex; align-items:center; justify-content:center; font-size:11px; font-weight:700; flex-shrink:0; text-transform:uppercase; }
.user-menu { display:none; position:absolute; top:calc(100% + 8px); right:0; background:var(--surface); border:1px solid var(--border2); border-radius:var(--r); min-width:230px; z-index:400; overflow:hidden; box-shadow:0 8px 24px rgba(0,0,0,.5); }
.user-menu.open { display:block; }
.um-item { display:block; width:100%; text-align:left; background:none; border:none; padding:.6rem 1rem; font-size:12px; color:var(--cream2); cursor:pointer; font-family:inherit; transition:all .12s; }
.um-item:hover { background:var(--surface2); color:var(--gold); }
.um-logout:hover { background:var(--nsfw-dim); color:var(--nsfw); }
.um-theme-row { display:flex; gap:6px; padding:.25rem .75rem .5rem; }
.um-theme { flex:1; display:flex; flex-direction:column; align-items:center; gap:5px; background:var(--surface2); border:1px solid var(--border2); border-radius:var(--r); padding:.55rem .3rem; cursor:pointer; font-family:inherit; font-size:10.5px; color:var(--cream3); transition:all .15s; }
.um-theme:hover { border-color:var(--gold2); color:var(--cream2); }
.um-theme.active { border-color:var(--gold); color:var(--gold); background:var(--gold3); font-weight:600; }
.um-theme-chip { width:100%; height:18px; border-radius:2px; border:1px solid rgba(255,255,255,.14); }
.chip-classic { background:linear-gradient(115deg,#0b0b0b 0 42%,#c9a96e 42% 58%,#181818 58%); }
.chip-shadow  { background:linear-gradient(115deg,#08060F 0 42%,#B57BFF 42% 58%,#1A1528 58%); }
.um-switch { display:flex; align-items:center; justify-content:space-between; gap:10px; padding:.5rem .75rem .7rem; font-size:11px; color:var(--cream2); cursor:pointer; user-select:none; }
.um-switch input { accent-color:var(--gold2); cursor:pointer; width:15px; height:15px; flex-shrink:0; }
.um-switch.disabled { opacity:.4; cursor:not-allowed; }
.um-switch.disabled input { cursor:not-allowed; }
/* Raison du grisage, affichée sous le libellé : un réglage désactivé sans
   explication, c'est un bug pour celui qui le voit. */
.um-switch-hint { display:block; font-size:9.5px; color:var(--cream3); margin-top:2px; font-style:italic; }

/* ── Popup ⚙ Paramètres ──
   Trois blocs cartonnés, un titre chacun, rien qui flotte. */
.set-block { background:var(--surface2); border:1px solid var(--border); border-radius:var(--r); margin-bottom:1rem; overflow:hidden; }
.set-block:last-child { margin-bottom:0; }
.set-block-head { padding:.55rem .875rem; background:var(--surface3); border-bottom:1px solid var(--border); font-size:10px; letter-spacing:.2em; text-transform:uppercase; color:var(--gold2); font-weight:500; }
.set-block > :not(.set-block-head) { margin:.75rem .875rem; }
.set-ident { display:flex; align-items:center; gap:11px; }
.set-ident-av { width:40px; height:40px; border-radius:50%; background:var(--gold3); border:2px solid var(--gold2); display:flex; align-items:center; justify-content:center; font-size:14px; font-weight:700; color:var(--gold); flex-shrink:0; }
.set-ident-name { font-size:14px; font-weight:600; color:var(--cream); }
/* Pseudonyme éditable inline (Victor, 20/07) — remplace le bouton « Modifier ». */
.set-ident-name-input { width:100%; font-size:14px; font-weight:600; color:var(--cream); font-family:inherit;
  background:var(--surface); border:1px solid var(--border2); border-radius:6px; padding:.4rem .6rem; outline:none; transition:border-color .12s; }
.set-ident-name-input:focus { border-color:var(--gold2); }
.set-ident-name-input:disabled { opacity:.55; cursor:not-allowed; }
.set-ident-mail { font-size:10.5px; color:var(--cream3); margin-top:4px; word-break:break-all; }
.set-rows { display:flex; flex-direction:column; gap:5px; }
.set-row { display:flex; align-items:center; justify-content:space-between; gap:10px; width:100%; background:var(--surface); border:1px solid var(--border2); border-radius:6px; padding:.55rem .75rem; font-size:12px; color:var(--cream2); cursor:pointer; font-family:inherit; transition:all .12s; text-align:left; }
.set-row:hover { border-color:var(--gold2); color:var(--gold); background:var(--gold3); }
.set-row-l { display:flex; align-items:center; gap:9px; min-width:0; }
.set-row-i { width:16px; text-align:center; flex-shrink:0; opacity:.8; }
.set-row-go { color:var(--cream3); font-size:14px; flex-shrink:0; }
.set-switch { padding:0 !important; }

/* Ces deux modals doivent pouvoir s'ouvrir par-dessus l'écran de connexion */
#modal-mailsim, #modal-newpass { z-index:1000; }

/* ÉCRAN DE CONNEXION */
/* L'aplat noir plein écran est parti (Victor, 22/07) : l'écran de connexion
   porte maintenant la MÊME ambiance que le reste de l'app — halo violet en
   haut + grille de scan. `body::before` ne suffit pas : l'écran est opaque et
   au-dessus (z-index 900), il redessine donc le décor lui-même. */
.auth-screen { display:none; position:fixed; inset:0; z-index:900; align-items:center; justify-content:center; padding:2rem; overflow-y:auto;
  background:var(--bg);
  background-image:
    radial-gradient(1100px 620px at 50% -12%, var(--gold3), transparent 62%),
    radial-gradient(760px 480px at 88% 108%, var(--gold3), transparent 66%);
}
.auth-screen.open { display:flex; }
/* Grille de scan, calquée sur `[data-theme="shadow"] body::before`. */
.auth-screen::before {
  content:""; position:absolute; inset:0; pointer-events:none; opacity:.45;
  background-image:linear-gradient(var(--border) 1px,transparent 1px),linear-gradient(90deg,var(--border) 1px,transparent 1px);
  background-size:64px 64px;
  mask-image:radial-gradient(ellipse 80% 60% at 50% 0%,#000,transparent 75%);
  -webkit-mask-image:radial-gradient(ellipse 80% 60% at 50% 0%,#000,transparent 75%);
}
.auth-card { position:relative; z-index:1; }
.auth-card { width:100%; max-width:380px; background:var(--surface); border:1px solid var(--border2); border-radius:var(--r); padding:2rem 1.75rem; box-shadow:0 12px 48px rgba(0,0,0,.6); }
.auth-brand { text-align:center; margin-bottom:1.5rem; }
.auth-brand-name {
  font-family:var(--font-display); font-size:26px; color:var(--gold);
  font-weight:700; letter-spacing:.1em;
}
.auth-brand-sub { font-size:10px; color:var(--cream3); letter-spacing:.3em; text-transform:uppercase; }
.auth-tabs { display:flex; gap:0; border-bottom:1px solid var(--border); margin-bottom:1.25rem; }
.auth-tab { flex:1; background:none; border:none; border-bottom:2px solid transparent; padding:.6rem; font-size:12px; color:var(--cream3); cursor:pointer; font-family:inherit; transition:all .15s; }
.auth-tab.active { color:var(--gold); border-bottom-color:var(--gold); }
.auth-field { margin-bottom:.875rem; }
.auth-label { display:block; font-size:11px; color:var(--cream2); margin-bottom:4px; font-weight:500; }
.auth-input { width:100%; background:var(--surface2); border:1px solid var(--border2); border-radius:6px; padding:.6rem .75rem; font-size:13px; color:var(--cream); font-family:inherit; outline:none; transition:border-color .15s; }
.auth-input:focus { border-color:var(--gold2); }
.auth-err { display:none; font-size:11px; color:var(--nsfw); background:var(--nsfw-dim); border:1px solid var(--nsfw); border-radius:6px; padding:.5rem .7rem; margin-bottom:.75rem; line-height:1.5; }
.auth-err.show { display:block; }
.auth-remember { display:flex; align-items:center; gap:.5rem; font-size:11px; color:var(--cream3); margin:.1rem 0 .5rem; cursor:pointer; user-select:none; }
.auth-remember input { width:14px; height:14px; accent-color:var(--gold); cursor:pointer; }
.auth-submit { width:100%; background:var(--gold3); border:1px solid var(--gold2); border-radius:var(--r); padding:.75rem; font-size:13px; font-weight:600; color:var(--gold); cursor:pointer; font-family:inherit; transition:all .15s; margin-top:.25rem; }
.auth-submit:hover { background:var(--gold2); color:#000; }
.auth-link { display:block; width:100%; background:none; border:none; color:var(--cream3); font-size:11px; cursor:pointer; font-family:inherit; margin-top:.6rem; text-align:center; transition:color .15s; }
.auth-link:hover { color:var(--gold); }
.auth-sep { display:flex; align-items:center; gap:10px; margin:1.25rem 0 .875rem; color:var(--cream3); font-size:10px; text-transform:uppercase; letter-spacing:.15em; }
.auth-sep::before, .auth-sep::after { content:""; flex:1; height:1px; background:var(--border); }
.auth-skip { width:100%; background:var(--surface2); border:1px dashed var(--border2); border-radius:var(--r); padding:.65rem; font-size:12px; color:var(--cream3); cursor:pointer; font-family:inherit; transition:all .15s; }
.auth-skip:hover { border-color:var(--gold2); color:var(--gold); }
.auth-note { font-size:10px; color:var(--cream3); text-align:center; margin-top:1rem; line-height:1.6; opacity:.75; }

/* ══════════════════════════════════════════════════════════
   ÉCRAN DE CHOIX DU PRODUIT : SUPPRIMÉ (Victor, 25/07)
   ------------------------------------------------------------
   Tout le préfixe `.lx-*` (écran, diptyque de tuiles, couleurs par
   produit, case « ouvrir directement ») a été retiré avec l'écran
   lui-même : plus de page intermédiaire entre la connexion et le
   produit. On bascule d'un clic depuis la barre du haut, par un bouton
   qui nomme sa destination — ▶ TUBE côté IA, ✦ IA côté Tube.
   ⚠️ Ne pas réintroduire ces règles : leur seul usage était l'écran
   supprimé, et un CSS orphelin finit par être recopié « parce qu'il
   existe ». L'historique Git le garde si besoin.
   ══════════════════════════════════════════════════════════ */

/* Bascule d'un produit à l'autre, dans la barre du haut de Prompt Engine.
   Le style de `.pe-appswitch` est dans styles/tube.css (avec son jumeau
   `.tb-appswitch`) : les deux boutons doivent rester identiques, et deux
   règles dans deux fichiers finiraient par diverger. tube.css n'est PAS
   chargé par index.html, donc la règle est recopiée ici — c'est la seule
   duplication assumée, et elle est signalée des deux côtés. */
.pe-appswitch {
  background:transparent; border:1px solid var(--border2); border-radius:6px;
  padding:5px 12px; font-size:11px; color:var(--cream3); cursor:pointer;
  font-family:inherit; transition:all .15s; display:flex; align-items:center; gap:6px;
}
.pe-appswitch:hover { color:#FF4E45; border-color:#CC0000; }

/* COMPTE / MAIL SIM / PARTAGE */
.acc-info { font-size:11px; color:var(--cream2); background:var(--surface2); border:1px solid var(--border); border-left:3px solid var(--gold2); border-radius:0 var(--r) var(--r) 0; padding:.7rem .875rem; line-height:1.6; }
.mailsim-note { font-size:10.5px; color:var(--cream3); background:var(--warn-dim); border-left:3px solid var(--warn); border-radius:0 6px 6px 0; padding:.6rem .75rem; margin-bottom:1rem; line-height:1.6; }
.mailsim-mail { background:#fff; border-radius:8px; padding:1.125rem; color:#1a1a1a; }
.mailsim-head { border-bottom:1px solid #e5e5e5; padding-bottom:.7rem; margin-bottom:.9rem; font-size:11px; color:#444; display:flex; flex-direction:column; gap:3px; }
.mailsim-k { display:inline-block; width:44px; color:#999; font-weight:600; text-transform:uppercase; font-size:9px; letter-spacing:.08em; }
.mailsim-body { font-size:12.5px; line-height:1.7; color:#333; margin-bottom:1.125rem; }
.mailsim-btn { width:100%; background:#3d0b1c; border:none; border-radius:6px; padding:.7rem; font-size:12.5px; font-weight:600; color:#f5e6d3; cursor:pointer; font-family:inherit; transition:opacity .15s; }
.mailsim-btn:hover { opacity:.85; }
.share-row { display:flex; align-items:center; gap:8px; background:var(--surface2); border:1px solid var(--border); border-radius:6px; padding:.5rem .7rem; font-size:11.5px; color:var(--cream2); }
.share-row.editing { border-color:var(--gold2); background:var(--gold3); }
.share-empty { font-size:11px; color:var(--cream3); font-style:italic; padding:.4rem 0; }
.share-badge { background:var(--gold3); border:1px solid var(--gold2); border-radius:10px; color:var(--gold); font-size:9px; font-weight:600; padding:1px 7px; white-space:nowrap; }
.share-badge.partial { background:var(--vid-dim); border-color:var(--vid); color:var(--vid); }

/* ══════════════════════════════════════════════════════════
   DROITS D'ACCÈS SUR IA PARTAGÉE
   .perm-locked = section refusée au compte courant.
   On n'utilise PAS l'attribut `disabled` : un élément disabled
   n'émet aucun événement de survol, donc le `title` ne
   s'afficherait jamais. Le clic est intercepté en phase de
   capture par le listener global (voir applyPermissions).
   ══════════════════════════════════════════════════════════ */
.perm-locked { opacity:.32 !important; cursor:not-allowed !important; filter:grayscale(1); position:relative; }
.mode-btn.perm-locked:hover { background:var(--surface2) !important; border-color:var(--border) !important; }
.nav-tab.perm-locked:hover { color:var(--cream3) !important; }
.pd-item-edit.perm-locked:hover { background:none !important; color:var(--cream3) !important; border-color:transparent !important; }
.mode-btn.perm-locked::after { content:"🔒"; position:absolute; top:7px; right:9px; font-size:10px; filter:none; opacity:.85; }
.perm-locked * { pointer-events:none; }

/* ══════════════════════════════════════════════════════════
   FEATURES DE CONTENU (content.js) — créneaux + hashtags
   ══════════════════════════════════════════════════════════ */
.ct-slots { margin-top:.5rem; }
.ct-slots-head { font-size:9px; letter-spacing:.14em; text-transform:uppercase; color:var(--gold2); margin-bottom:5px; }
.ct-tz { color:var(--cream3); letter-spacing:.05em; text-transform:none; font-size:9px; }
.ct-slot-row { display:flex; flex-wrap:wrap; gap:5px; }
.ct-slot { display:flex; flex-direction:column; align-items:flex-start; gap:1px;
  background:var(--surface2); border:1px solid var(--border2); border-radius:6px;
  padding:4px 9px; cursor:pointer; font-family:inherit; transition:all .15s; }
.ct-slot:hover { border-color:var(--gold2); }
.ct-slot-h { font-size:12px; color:var(--cream); font-variant-numeric:tabular-nums; }
.ct-slot:hover .ct-slot-h { color:var(--gold); }
.ct-slot-q { font-size:9px; color:var(--cream3); }

.ct-tags { display:flex; flex-wrap:wrap; gap:5px; margin-top:.45rem; }
.ct-tag-set { display:inline-flex; align-items:center; gap:5px; background:var(--surface2);
  border:1px solid var(--border2); border-radius:20px; padding:3px 10px; font-size:11px;
  color:var(--cream2); cursor:pointer; font-family:inherit; transition:all .15s; }
.ct-tag-set:hover { border-color:var(--gold2); color:var(--gold); }
.ct-tag-n { font-size:9px; color:var(--cream3); background:var(--surface3); border-radius:8px; padding:0 5px; }
.ct-tag-manage { background:transparent; border:1px dashed var(--border2); border-radius:20px;
  padding:3px 10px; font-size:11px; color:var(--cream3); cursor:pointer; font-family:inherit; transition:all .15s; }
.ct-tag-manage:hover { border-color:var(--gold2); color:var(--gold); }
.ct-tag-row { display:flex; align-items:center; gap:8px; background:var(--surface); border:1px solid var(--border);
  border-radius:6px; padding:.42rem .6rem; }
.ct-tag-row.editing { border-color:var(--gold2); }
.ct-tag-row-nom { font-size:12px; color:var(--cream); flex-shrink:0; }
.ct-tag-row-tags { flex:1; min-width:0; font-size:10px; color:var(--cream3);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

/* Compte « lecture seule » (droit du COMPTE, ≠ droits de partage).
   Étiquette du menu profil ; le verrouillage des boutons réutilise
   .perm-locked, posé par applyReadOnlyMode(). */
.um-role-tag { margin-top:6px; display:inline-block; font-size:9px; letter-spacing:.12em;
  text-transform:uppercase; color:var(--vid); background:var(--vid-dim);
  border:1px solid var(--vid); border-radius:4px; padding:2px 6px; }

/* Grille des droits (modal partage) */
.perm-grid { display:flex; flex-direction:column; gap:4px; }
.perm-row { display:flex; align-items:center; gap:8px; background:var(--surface); border:1px solid var(--border); border-radius:6px; padding:.42rem .6rem; cursor:pointer; transition:all .12s; }
.perm-row:hover { border-color:var(--border2); }
.perm-row.on { background:var(--gold3); border-color:var(--gold2); }
.perm-row.ro { cursor:default; }
.perm-row.ro:hover { border-color:var(--border); }
.perm-row input { accent-color:var(--gold); flex-shrink:0; cursor:pointer; }
.perm-row.ro input { cursor:default; }
.perm-ico { font-size:12px; width:16px; text-align:center; flex-shrink:0; }
.perm-txt { display:flex; flex-direction:column; flex:1; min-width:0; }
.perm-lbl { font-size:11.5px; color:var(--cream); font-weight:500; }
.perm-desc { font-size:9.5px; color:var(--cream3); }
.perm-state { font-size:9px; font-weight:600; padding:1px 6px; border-radius:9px; flex-shrink:0; }
.perm-state.ok { background:var(--sfw-dim); border:1px solid var(--sfw); color:var(--sfw); }
.perm-state.ko { background:var(--nsfw-dim); border:1px solid var(--nsfw); color:var(--nsfw); }
.perm-quick { background:none; border:1px solid var(--border2); border-radius:4px; color:var(--cream3); font-size:9px; font-family:inherit; padding:1px 6px; cursor:pointer; margin-left:5px; letter-spacing:0; text-transform:none; }
.perm-quick:hover { color:var(--gold); border-color:var(--gold2); }

/* Marqueur "IA partagée avec moi" */
.pd-guest-tag { background:var(--vid-dim); border:1px solid var(--vid); border-radius:9px; color:var(--vid); font-size:9px; font-weight:600; padding:1px 6px; white-space:nowrap; }
/* Bandeau latéral : uniquement sur l'IA ACTIVE, comme la couleur du nom.
   Or si elle est à moi, bleu si elle m'a été partagée. */
.pd-item-row.active       { box-shadow:inset 2px 0 0 var(--gold); }
/* Ligne « Profil maître » du sélecteur d'IA (Victor 20/07). */
.pd-master-row .pd-guest-tag { background:var(--surface3); border-color:var(--border2); color:var(--cream2); }
.pd-master-row.active { box-shadow:inset 2px 0 0 var(--cream2); }
.pd-item-row.guest.active { box-shadow:inset 2px 0 0 var(--vid); color:var(--vid); }
.ia-banner { font-size:11px; color:var(--cream2); border-radius:var(--r); padding:.55rem .8rem; line-height:1.5; }
.ia-banner.owner { background:var(--gold3); border:1px solid var(--gold2); }
.ia-banner.guest { background:var(--vid-dim); border:1px solid var(--vid); }
.ia-banner-btn { margin-top:.5rem; width:100%; background:transparent; border:1px solid var(--border2); border-radius:var(--r); color:var(--cream2); font-size:10.5px; font-family:inherit; padding:.35rem; cursor:pointer; transition:all .12s; }
.ia-banner.owner .ia-banner-btn:hover { color:var(--gold); border-color:var(--gold2); }
.ia-banner.guest .ia-banner-btn:hover { color:var(--vid); border-color:var(--vid); }
.share-scope { background:var(--surface2); border:1px solid var(--border); border-radius:var(--r); padding:.75rem .875rem; margin-bottom:1.125rem; }
.share-scope-label { font-size:9px; letter-spacing:.18em; text-transform:uppercase; color:var(--cream3); font-weight:600; margin-bottom:.6rem; }
.share-scope-grid { display:grid; grid-template-columns:1fr 1fr; gap:.4rem .75rem; font-size:11px; color:var(--cream2); }

/* ── MODAL CLÉS API ── */
.api-field-row {
  display:flex; flex-direction:column; gap:4px; margin-bottom:1rem;
}
.api-field-label {
  font-size:10px; letter-spacing:.2em; text-transform:uppercase;
  color:var(--cream3); font-weight:500; display:flex; align-items:center; gap:6px;
}
.api-field-badge {
  font-size:9px; padding:1px 6px; border-radius:3px; font-weight:500;
  text-transform:uppercase; letter-spacing:.05em;
}
.api-badge-required { background:var(--sfw-dim); color:var(--sfw); border:1px solid var(--sfw); }
.api-badge-optional { background:var(--surface3); color:var(--cream3); border:1px solid var(--border2); }
.api-badge-soon { background:var(--vid-dim); color:var(--vid); border:1px solid var(--vid); }
.api-field-input {
  width:100%; background:var(--bg); border:1px solid var(--border2);
  border-radius:6px; padding:.55rem .875rem; font-size:12px;
  color:var(--cream); font-family:monospace; outline:none;
  transition:border-color .15s;
}
.api-field-input:focus { border-color:var(--gold2); }
.api-field-input:disabled { opacity:.35; cursor:not-allowed; }
.api-field-hint { font-size:10px; color:var(--cream3); line-height:1.5; }

/* ── CLÉ POSÉE : CASE VERROUILLÉE + STYLO (Victor, 25/07) ──
   « une fois qu'une clé est renseignée et qu'elle est valide, on ne peut pas
   la retirer pour en mettre une autre ; j'aimerais que si elle est valide = la
   case se grise + apparition d'un bouton Stylo afin de rentrer en mode
   édition ».
   ⚠️ Ce n'est PAS un <input disabled> : la clé n'existe pas côté navigateur
   (elle est chiffrée sur le serveur et ne redescend jamais). On affiche donc
   un résumé — les 4 derniers caractères — et non un champ dont la valeur
   serait forcément fausse. */
.api-locked {
  display:flex; align-items:center; gap:10px;
  background:var(--surface2); border:1px solid var(--border2);
  border-radius:6px; padding:.5rem .75rem;
}
.api-locked-key { font-family:monospace; font-size:12px; color:var(--cream2); letter-spacing:.06em; }
.api-locked-info { font-size:10px; color:var(--cream3); margin-left:auto; text-align:right; line-height:1.4; }
.api-icon-btn {
  background:none; border:1px solid var(--border2); border-radius:6px;
  color:var(--cream3); cursor:pointer; font-size:13px; line-height:1;
  padding:5px 9px; font-family:inherit; transition:all .15s; flex-shrink:0;
}
.api-icon-btn:hover { border-color:var(--gold); color:var(--gold); }
/* Verdict du test. Le texte porte la couleur ici (contrairement au pied de
   page) : c'est une ligne unique qu'on vient de demander explicitement, elle
   doit se lire d'un coup d'œil. */
.api-test-ok   { color:var(--sfw); }
.api-test-ko   { color:var(--nsfw); }
.api-test-warn { color:var(--warn); }

/* ── BOUTONS PROMPT BLOCK ── */
.pb-actions { display:flex; gap:5px; }
.pb-copy-btn {
  background:transparent; border:1px solid var(--border2); border-radius:5px;
  padding:3px 10px; font-size:10px; color:var(--cream3); cursor:pointer;
  font-family:inherit; transition:all .15s;
}
.pb-copy-btn:hover { color:var(--gold); border-color:var(--gold2); }
.pb-open-btn {
  background:var(--surface3); border:1px solid var(--border2); border-radius:5px;
  padding:3px 10px; font-size:10px; color:var(--cream2); cursor:pointer;
  font-family:inherit; transition:all .15s; display:flex; align-items:center; gap:4px;
}
.pb-open-btn:hover { background:var(--vid-dim); border-color:var(--vid); color:var(--vid); }
.pb-open-btn.sent { background:var(--sfw-dim); border-color:var(--sfw); color:var(--sfw); }

/* ── SIDEBAR DROITE — info boxes ── */
.model-info-box {
  background:var(--surface2); border:1px solid var(--border);
  border-left:3px solid var(--gold2); border-radius:0 var(--r) var(--r) 0;
  padding:.875rem 1rem;
}

/* Tuile « contenus en attente » (colonne droite, sous Claude · Victor 20/07). */
.pending-tile {
  display:flex; align-items:center; gap:9px; width:100%; margin:8px 0;
  padding:.7rem .9rem; cursor:pointer; font-family:inherit; text-align:left;
  border-radius:var(--r); transition:all .15s; font-size:12px; font-weight:600;
}
.pending-tile .pending-ico { font-size:15px; flex-shrink:0; }
.pending-tile .pending-label { flex:1; min-width:0; }
.pending-tile .pending-go { flex-shrink:0; font-size:15px; opacity:.6; }
/* Rien en attente : discret. */
.pending-tile.empty {
  background:var(--surface2); border:1px solid var(--border); color:var(--cream3); font-weight:500;
}
.pending-tile.empty:hover { border-color:var(--border2); color:var(--cream2); }
/* Des contenus attendent : mis en avant (ambre). */
.pending-tile.has-pending {
  background:var(--warn-dim); border:1px solid var(--warn); color:var(--warn);
  animation:pendingPulse 2s ease-in-out infinite;
}
.pending-tile.has-pending:hover { background:var(--warn); color:#12100c; }
@keyframes pendingPulse { 0%,100%{ box-shadow:0 0 0 0 rgba(212,136,74,0); } 50%{ box-shadow:0 0 0 3px rgba(212,136,74,.18); } }
.model-info-title {
  font-size:10px; letter-spacing:.2em; text-transform:uppercase;
  color:var(--gold2); font-weight:500; margin-bottom:.5rem;
}

/* ── SIDEBAR DROITE — zone preview (standby) ── */
.preview-zone {
  border:1px dashed var(--border2); border-radius:var(--r);
  padding:1rem; text-align:center; color:var(--cream3);
  font-size:11px; line-height:1.6;
}
.preview-zone-icon { font-size:22px; margin-bottom:6px; opacity:.35; }

/* ── ONGLET LORA ── */
.lora-view { flex:1; display:flex; flex-direction:column; min-height:0; }
.lora-layout { display:flex; flex:1; min-height:0; }
.lora-sidebar {
  width:260px; flex-shrink:0; border-right:1px solid var(--border);
  padding:1.25rem; display:flex; flex-direction:column; gap:1rem;
  background:var(--surface); overflow-y:auto;
}
.lora-main { flex:1; overflow-y:auto; padding:1.5rem; }
/* Le badge de progression est l'info clé de l'onglet LoRA : il est centré
   sur sa ligne (position absolue = centrage réel, indépendant du titre). */
.lora-header { display:flex; align-items:center; justify-content:space-between; margin-bottom:1.25rem; position:relative; min-height:34px; }
.lora-header .lora-count-badge { position:absolute; left:50%; transform:translateX(-50%); }
.lora-title { font-family:var(--font-display); font-size:16px; color:var(--gold); }
.lora-label { font-size:10px; letter-spacing:.25em; text-transform:uppercase; color:var(--cream3); font-weight:500; }
.lora-info {
  background:var(--vid-dim); border:1px solid var(--vid);
  border-radius:var(--r); padding:.75rem 1rem;
  font-size:11px; color:var(--cream2); line-height:1.6;
}
.lora-info strong { color:var(--vid); }
.lora-count-badge {
  display:inline-flex; align-items:center; gap:7px;
  background:var(--gold3); border:1px solid var(--gold2); border-radius:16px;
  font-size:13px; color:var(--gold); padding:6px 18px; font-weight:600;
  letter-spacing:.02em; white-space:nowrap;
  box-shadow:0 0 0 4px rgba(201,169,110,.06);
}
/* Pastille de progression à gauche du compteur */
.lora-count-badge::before {
  content:""; width:7px; height:7px; border-radius:50%;
  background:var(--gold2); flex-shrink:0;
}
.lora-count-badge.complete { border-color:var(--sfw); color:var(--sfw); background:var(--sfw-dim); box-shadow:0 0 0 4px rgba(90,158,114,.10); }
.lora-count-badge.complete::before { background:var(--sfw); }
/* Version compacte dans la sidebar (place restreinte) */
#lora-count-sidebar { font-size:10px; padding:2px 10px; border-radius:10px; box-shadow:none; }
#lora-count-sidebar::before { display:none; }
.lora-prompt-block {
  background:var(--surface); border:1px solid var(--border);
  border-left:3px solid var(--border2); border-radius:0 var(--r) var(--r) 0;
  margin-bottom:8px; overflow:hidden; transition:border-left-color .15s;
}
.lora-prompt-block:hover { border-left-color:var(--vid); }
.lp-header {
  padding:.5rem 1rem; display:flex; align-items:center;
  justify-content:space-between; background:var(--surface2);
  border-bottom:1px solid var(--border);
}
.lp-num { font-size:10px; letter-spacing:.15em; color:var(--cream3); text-transform:uppercase; font-weight:500; }
.lp-tag { font-size:10px; color:var(--vid); background:var(--vid-dim); border:1px solid var(--vid); border-radius:4px; padding:1px 7px; }
.lp-body { padding:.625rem 1rem; display:flex; align-items:flex-start; justify-content:space-between; gap:8px; }
.lp-text { font-size:11.5px; color:var(--cream); font-family:'Courier New',monospace; line-height:1.65; flex:1; word-break:break-word; }
.lp-actions { display:flex; gap:5px; flex-shrink:0; align-items:flex-start; padding-top:2px; }

/* ── BOUTON VALIDER LoRA ── */
.lora-validate-btn {
  background:transparent; border:1px solid var(--border2); border-radius:5px;
  padding:3px 10px; font-size:10px; color:var(--cream3); cursor:pointer;
  font-family:inherit; transition:all .15s; white-space:nowrap; min-width:64px; text-align:center;
  position:relative;
}
.lora-validate-btn:hover { border-color:var(--sfw); color:var(--sfw); }
.lora-validate-btn.validated { background:var(--sfw-dim); border-color:var(--sfw); color:var(--sfw); font-weight:500; }
/* Hover sur validé : fond rouge + texte "✕ Annuler" via font-size 0 sur le span + ::after */
.lora-validate-btn.validated:hover { background:var(--nsfw-dim); border-color:var(--nsfw); color:var(--nsfw); font-weight:500; }
.lora-validate-btn.validated .lora-btn-label { display:inline; }
.lora-validate-btn.validated:hover .lora-btn-label { display:none; }
.lora-validate-btn.validated::after { content:""; display:none; }
.lora-validate-btn.validated:hover::after { content:"✕ Annuler"; display:inline; }

/* ── STATUT HISTORIQUE envoyé vers OpenArt ── */
.hi-sent-badge {
  font-size:9px; background:var(--vid-dim); color:var(--vid);
  border:1px solid var(--vid); border-radius:4px; padding:1px 6px;
  font-weight:500; letter-spacing:.05em;
}


/* ── HISTORY FILTER SIDEBAR ── */
.hist-filter-sidebar {
  width:260px; flex-shrink:0; border-right:1px solid var(--border);
  background:var(--surface); display:flex; flex-direction:column;
  overflow:hidden;
}
/* Le scroll est passé sur cette zone interne pour que le bouton
   Corbeille reste collé en bas de la colonne (cf. .trash-btn). */
.hist-filter-scroll {
  flex:1; overflow-y:auto; padding:1.25rem; display:flex; flex-direction:column; gap:var(--sb-gap);
}
/* Corbeille — épinglée en pied de colonne, dans l'esprit du pied d'action
   des panneaux API (.api-col-save). */
.trash-btn {
  flex-shrink:0; display:flex; align-items:center; justify-content:center; gap:8px;
  width:100%; padding:.75rem 1rem; background:var(--surface2);
  border:none; border-top:1px solid var(--border);
  font-family:inherit; font-size:12px; font-weight:600; letter-spacing:.02em;
  color:var(--cream3); cursor:pointer; transition:all .15s;
}
.trash-btn:hover { background:var(--surface3); color:var(--cream2); }
.trash-btn-ico { font-size:14px; line-height:1; }
.trash-btn-count {
  min-width:18px; height:18px; padding:0 5px; border-radius:9px;
  display:inline-flex; align-items:center; justify-content:center;
  background:var(--border2); color:var(--cream2);
  font-size:10px; font-weight:700; line-height:1;
}
.trash-btn:hover .trash-btn-count { background:var(--nsfw-dim); color:var(--nsfw); }
/* Strictement identique à .sidebar-label du Générateur : même font-size,
   même letter-spacing (.25em, pas .2em) et surtout AUCUN padding-top —
   c'est lui qui décalait le 1er titre de 4px au changement d'onglet. */
/* ⚠️ AUCUN PADDING (26/07) : le `padding-bottom:.5rem` s'ajoutait au `gap` et
   éloignait un titre de SA PROPRE carte (20 px) plus que de la catégorie du
   dessus (12 px) — on lisait le titre comme appartenant au bloc précédent.
   Strictement identique à `.sidebar-label` maintenant, comme annoncé. */
.hist-fs-label { font-size:10px; letter-spacing:.25em; text-transform:uppercase; color:var(--cream3); font-weight:500; }
/* ══════════════════════════════════════════════════════════
   CATÉGORIES PLIABLES DES ENCADRÉS GAUCHE — ALIGNÉES SUR L'ENCADRÉ DROIT
   (Victor, 25/07)
   ------------------------------------------------------------
   « Refait tous les menus qui se plie et se déplie dans le même design :
   comme ceux dans l'encadré droit, un bloc, le titre et la flèche, une
   séparation et les informations, et ça dans les deux prompts engine. »

   AVANT : le titre flottait AU-DESSUS d'une carte séparée — deux éléments
   distincts, sans séparation, avec une flèche « ▾ » de 8 px tournant dans
   l'autre sens que celle de droite. À droite : UN bloc contenant le titre,
   une ligne de séparation, puis le contenu.

   ⚠️ Le HTML de `fsCat()` n'a pas eu à changer : sa structure
   (`.fs-cat` > bouton + corps) était déjà la bonne, seul l'habillage
   différait. C'est le CSS qui fait du conteneur le BLOC, et du corps un
   contenu séparé par un filet — exactement `.sb-fold` / `.sb-fold-body`.

   ⚠️ Les valeurs sont copiées de `.sb-fold-head` (padding, taille de flèche,
   hover) et non réinventées : si les deux divergeaient d'un pixel, la forme
   dirait de quel côté de l'écran on se trouve — ce que la règle d'uniformité
   interdit précisément.
   ══════════════════════════════════════════════════════════ */
/* ⚠️ PLUS DE `margin-bottom` (26/07) : elle s'ajoutait au `gap` du conteneur
   et rendait les catégories de la Bibliothèque plus espacées que celles de
   l'Historique, qui n'utilise pas `.fs-cat`. L'écart vient de `--sb-gap`. */
.fs-cat {
  background:var(--surface2); border:1px solid var(--border);
  border-radius:var(--r); overflow:hidden;
}
.fs-cat-head {
  display:flex; align-items:center; gap:7px; width:100%;
  background:none; border:none; cursor:pointer; font-family:inherit; text-align:left;
  padding:.75rem .5rem .75rem 1rem;   /* = .sb-fold-head */
  transition:background .12s;
}
.fs-cat-head:hover { background:var(--surface3); color:var(--cream2); }
/* Flèche identique à `.sb-fold-arrow` : 10 px, et elle pointe vers le bas
   quand c'est OUVERT (rotation 90°), pas l'inverse.
   ⚠️ ELLE EST À DROITE DU LIBELLÉ (Victor, 25/07 (4)) : « les flèches pour
   plier/déplier un menu qui se trouve dans l'encadré GAUCHE doivent se trouver
   à DROITE de l'élément ».
   ⚠️ Fait en `order` + `margin-left:auto`, PAS en déplaçant le DOM. La flèche
   est insérée en premier enfant par `fsCat()` et par `fsEnhanceStatic()`
   (net.js) ; changer l'ordre du DOM aurait obligé à le changer aux deux
   endroits, et le libellé est un simple nœud de texte — donc un élément flex
   anonyme qu'on ne peut pas réordonner. C'est la flèche qui bouge. */
.fs-cat-arrow { font-size:10px; color:var(--cream3); transition:transform .15s; flex-shrink:0; transform:rotate(90deg); order:1; margin-left:auto; }
.fs-cat.collapsed .fs-cat-arrow { transform:none; }
.fs-cat.collapsed .fs-cat-body { display:none; }
/* ⚠️ Le corps perd son propre fond/bordure/rayon : il est DANS le bloc
   maintenant, et un cadre dans un cadre se verrait. Il ne garde que le filet
   de séparation — comme `.sb-fold-body`. */
.fs-cat .fs-cat-body.hist-fs-card {
  background:none; border:none; border-radius:0;
  border-top:1px solid var(--border);
}
/* Filtres gauche statiques rendus pliables (fsEnhanceStatic, 24/07).
   Même traitement : le libellé devient l'en-tête d'un bloc. */
.fs-static-head { display:flex; align-items:center; gap:7px; cursor:pointer;
  padding:.75rem .5rem .75rem 1rem; transition:background .12s; margin:0; }

/* ══════════════════════════════════════════════════════════
   TOUS LES EN-TÊTES DE CARTE ONT LA MÊME HAUTEUR (Victor, 26/07)
   ------------------------------------------------------------
   « Quand je switch d'onglet les tuiles ne sont pas au même endroit, ça fait
   un léger décalage visible quand je switch en boucle entre les deux. »

   ⚠️⚠️ LA CAUSE N'EST NI LE PADDING NI LA POLICE — ils étaient déjà identiques
   (`.75rem .5rem .75rem 1rem`, 10 px, .25em). C'est le TYPE DE BALISE :
     · Bibliothèque et Calendrier construisent leur en-tête avec un `<button>`
       (`.fs-cat-head`, `.cal-fold-head`), tout comme l'encadré droit
       (`.sb-fold-head`) ;
     · l'Historique part d'un `<div>` statique que `fsEnhanceStatic()` (net.js)
       transforme en en-tête (`.fs-static-head`).
   Or la feuille de style du NAVIGATEUR applique aux boutons un raccourci
   `font:` — et un raccourci `font` remet `line-height` à `normal`. Le bouton
   ne descend donc PAS le `line-height:1.6` du `body`, le div si.
   Mesuré à 10 px : 1,6 × 10 = 16 px de hauteur de ligne pour le div contre
   ≈ 12 px pour le bouton → **4 px d'écart**, et toute la colonne de
   l'Historique décalée de 4 px par rapport à celle de la Bibliothèque.

   D'où une hauteur de ligne EXPLICITE, la même pour les quatre en-têtes.
   ⚠️ Valeur numérique et non `normal` : `normal` dépend de la police chargée,
   donc il rendrait le décalage possible à nouveau le jour où la police change
   ou ne se charge pas.
   ⚠️ Ne jamais régler ce genre d'écart en retouchant le padding d'un seul
   côté : le padding est déjà commun, on masquerait la cause au lieu de la
   traiter — et le décalage reviendrait au premier changement de police.
   ══════════════════════════════════════════════════════════ */
.sb-fold-head,
.fs-cat-head,
.fs-static-head,
.cal-fold-head { line-height:1.2; }
.fs-static-head:hover { background:var(--surface3); }
.fs-static-head.collapsed .fs-cat-arrow { transform:none; }
/* Bloc derrière chaque groupe de filtres (Marcel) : même fond que les tuiles
   de l'app (surface2 + bordure + rayon), aucun style nouveau. */
.hist-fs-card {
  background:var(--surface2); border:1px solid var(--border); border-radius:var(--r);
  padding:.4rem; display:flex; flex-direction:column; gap:3px;
}
/* ══ CHAMP DE RECHERCHE D'UN ENCADRÉ GAUCHE (Victor, 26/07) ══
   Nouveau composant du SOCLE, pas de l'Atelier : c'est le premier encadré
   gauche qui en a besoin, mais la Bibliothèque et l'Historique des deux
   prompt engines chercheront un jour aussi — et le jour où ils le feront, ils
   doivent trouver la valeur ici, pas en recopier une autre.
   ⚠️ Valeurs reprises de `.hdr-row input` (le seul champ déjà posé dans une
   colonne latérale) : même fond, même bordure, même `color-scheme:dark` — un
   champ clair dans une colonne sombre saute aux yeux. La taille suit
   `.hist-filter-btn` (12px) pour que le champ et les boutons du même bloc
   soient sur la même ligne de base. */
.fs-search {
  width:100%; background:var(--surface2); border:1px solid var(--border2);
  border-radius:6px; padding:.5rem .75rem; font-size:12px; color:var(--cream);
  font-family:inherit; outline:none; color-scheme:dark;
  transition:border-color .15s;
}
.fs-search:focus { border-color:var(--gold2); }
.fs-search::placeholder { color:var(--cream3); }
/* Sur ce fond surface2, le survol des boutons monte d'un cran pour rester lisible. */
.hist-fs-card .hist-filter-btn:hover,
.hist-fs-card .hist-date-btn:hover { background:var(--surface3); color:var(--cream2); }
.hist-filter-btn {
  width:100%; text-align:left; background:transparent; border:none;
  border-radius:6px; padding:.5rem .75rem; font-size:12px; color:var(--cream3);
  cursor:pointer; font-family:inherit; transition:all .15s;
}
.hist-filter-btn:hover { background:var(--surface2); color:var(--cream2); }
.hist-filter-btn.active { background:var(--gold3); color:var(--gold); font-weight:500; }
/* Filtre de date — classe distincte de .hist-filter-btn pour ne pas être
   désactivée par la synchronisation des filtres "type de contenu". */
.hist-date-btn {
  display:block; width:100%; text-align:left; background:none; border:none;
  padding:.5rem .75rem; font-size:12px; color:var(--cream3); cursor:pointer;
  font-family:inherit; border-radius:6px; transition:all .12s;
}
.hist-date-btn:hover { background:var(--surface2); color:var(--cream2); }
.hist-date-btn.active { background:var(--gold3); color:var(--gold); font-weight:500; }
.hist-date-range { padding:.5rem .75rem 0; display:flex; flex-direction:column; gap:5px; }
.hdr-row { display:flex; align-items:center; gap:6px; }
.hdr-row span { font-size:10px; color:var(--cream3); width:18px; flex-shrink:0; }
.hdr-row input {
  flex:1; min-width:0; background:var(--surface2); border:1px solid var(--border2);
  border-radius:4px; padding:3px 5px; font-size:10.5px; color:var(--cream);
  font-family:inherit; outline:none; color-scheme:dark;
}
.hdr-row input:focus { border-color:var(--gold2); }
.hdr-clear {
  background:none; border:1px solid var(--border2); border-radius:4px;
  color:var(--cream3); font-size:10px; padding:3px; cursor:pointer;
  font-family:inherit; transition:all .12s; margin-top:2px;
}
.hdr-clear:hover { border-color:var(--gold2); color:var(--gold); }
.hist-ia-filter-btn {
  width:100%; text-align:left; background:transparent; border:none;
  border-radius:6px; padding:.45rem .75rem; font-size:11px; color:var(--cream3);
  cursor:pointer; font-family:inherit; transition:all .15s; display:flex; align-items:center; gap:6px;
}
.hist-ia-filter-btn:hover { background:var(--surface2); color:var(--cream2); }
.hist-ia-filter-btn.active { background:var(--gold3); color:var(--gold); }
.hist-ia-dot { width:5px; height:5px; border-radius:50%; background:var(--gold); flex-shrink:0; }

/* ══════════════════════════════════════════════════════════
   BLOCS PLIABLES DE LA SIDEBAR DROITE
   Repliés, ils doivent rester lisibles d'un coup d'œil : une
   pastille résume l'état des services du bloc.
   vert = tout démarré · orange = partiel · rouge = rien
   gris = pas encore sondé
   ══════════════════════════════════════════════════════════ */
/* L'en-tête est maintenant enveloppé (.sb-fold-head-wrap) pour loger
   l'icône « i » à côté du bouton de repli sans la rendre cliquable-repli.
   Conséquence : les sélecteurs enfant-direct `>` visaient .sb-fold-head
   depuis .sb-fold — ils passent par le wrapper (Marcel, 16/07). */
.sb-fold-head-wrap { display:flex; align-items:center; position:relative; }
.sb-fold-head {
  display:flex; align-items:center; gap:7px; flex:1; min-width:0;
  background:none; border:none; padding:.75rem .5rem .75rem 1rem; cursor:pointer;
  font-family:inherit; text-align:left; transition:background .12s;
}
.sb-fold-head:hover { background:var(--surface3); }
/* Flèche de pliage à GAUCHE de la tuile (Victor, 18/07). On réordonne en
   flex plutôt que de toucher le DOM : flèche à gauche, puis titre, puis
   pastille poussée à droite. */
.sb-fold-arrow { font-size:10px; color:var(--cream3); flex-shrink:0; transition:transform .15s; order:1; }
.sb-fold-title { font-size:10px; letter-spacing:.25em; text-transform:uppercase; color:var(--cream3); font-weight:500; order:2; }
.sb-fold.open .sb-fold-head .sb-fold-arrow { transform:rotate(90deg); }
/* Pastille de synthèse : visible UNIQUEMENT replié — dépliée, l'état
   est déjà lisible ligne par ligne, elle ferait doublon. Poussée à droite. */
.sb-fold-pill {
  margin-left:auto; order:3; flex-shrink:0; width:9px; height:9px; border-radius:50%;
  background:var(--cream3); transition:background .2s;
}
.sb-fold.open .sb-fold-head .sb-fold-pill { display:none; }
.sb-fold-pill.up      { background:var(--sfw); }
.sb-fold-pill.partial { background:var(--warn); }
.sb-fold-pill.down    { background:var(--nsfw); }

/* ── PASTILLE QUI PORTE DU TEXTE (Victor, 25/07) ──
   ⚠️ `.sb-fold-pill` est un POINT de 9×9 px : c'est un voyant d'état, il ne
   contient rien. Le Filtre IA y écrivait pourtant son compte (« 2 IA ») —
   texte rogné par les 9 px et le border-radius, donc invisible. C'était le
   « numéro pas visible » signalé.
   Toute pastille qui affiche une VALEUR doit donc porter `.sb-fold-pill-txt`,
   qui la dimensionne sur son contenu. Ne pas élargir `.sb-fold-pill` lui-même :
   les voyants Moteur / Services doivent rester des points. */
.sb-fold-pill.sb-fold-pill-txt {
  width:auto; height:auto; border-radius:20px;
  padding:2px 8px; background:var(--gold3); border:1px solid var(--gold2);
  color:var(--gold); font-size:9.5px; font-weight:600; letter-spacing:.08em;
  text-transform:uppercase; line-height:1.5; white-space:nowrap;
}
.sb-fold-body { border-top:1px solid var(--border); }
.sb-fold:not(.open) > .sb-fold-body { display:none; }

/* Bouton « Actualiser » (au-dessus du bloc Moteur de génération, Victor 20/07).
   Force un contrôle immédiat de l'état moteur + services. */
.svc-refresh-btn {
  display:flex; align-items:center; justify-content:center; gap:6px; width:100%;
  margin:2px 0 8px; padding:.5rem .75rem; cursor:pointer;
  background:var(--surface2); border:1px solid var(--border); border-radius:var(--r);
  color:var(--cream2); font-family:inherit; font-size:11px; font-weight:600;
  letter-spacing:.04em; transition:background .12s, border-color .12s;
}
.svc-refresh-btn:hover:not(:disabled) { background:var(--surface3); border-color:var(--cream3); }
.svc-refresh-btn:disabled { opacity:.6; cursor:default; }
/* Bouton d'alimentation en mode « Éteindre » (tout est en ligne) : teinte
   d'alerte pour signaler que le clic coupe les services. */
.svc-refresh-btn.svc-power-on { color:var(--nsfw); border-color:var(--nsfw); }
.svc-refresh-btn.svc-power-on:hover:not(:disabled) { background:var(--nsfw); color:#fff; border-color:var(--nsfw); }
.svc-refresh-icon { display:inline-block; font-size:13px; line-height:1; }
.svc-refresh-icon.spinning { animation:svcSpin .8s linear infinite; }
@keyframes svcSpin { from { transform:rotate(0); } to { transform:rotate(360deg); } }
/* Démarrer : rebond (Victor 20/07) — plus vivant qu'une rotation. */
.svc-refresh-icon.bouncing { animation:svcBounce .6s cubic-bezier(.28,.84,.42,1) infinite; }
@keyframes svcBounce {
  0%,100% { transform:translateY(0) scale(1); }
  30%     { transform:translateY(-5px) scale(1.12); }
  55%     { transform:translateY(1px) scale(.94); }
  75%     { transform:translateY(-2px) scale(1.04); }
}
/* Replié, l'en-tête ne garde aucune bordure : le trait est porté par
   .sb-fold-body, qui est masqué. Rien à annuler. */

/* ══ ICÔNE « i » + INFOBULLE (en-têtes de la colonne droite) ══
   La bulle est en position:fixed et repositionnée en JS. Raison : la
   colonne droite a `overflow-y:auto`, une bulle en absolute y serait
   rognée net par le bord de la colonne. */
.sb-info {
  flex-shrink:0; width:15px; height:15px; margin-right:10px;
  border:1px solid var(--border2); border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  font-family:Georgia, serif; font-style:italic; font-size:10px; font-weight:600;
  color:var(--cream3); cursor:help; user-select:none;
  transition:color .12s, border-color .12s, background .12s;
}
.sb-info:hover, .sb-info:focus-visible {
  color:var(--gold); border-color:var(--gold2); background:var(--gold3); outline:none;
}
.sb-tip {
  position:fixed; z-index:900; width:290px; max-width:calc(100vw - 24px);
  background:var(--surface); border:1px solid var(--border2);
  border-left:3px solid var(--gold2); border-radius:0 8px 8px 0;
  padding:.8rem .9rem; font-family:var(--font-ui); font-style:normal;
  font-size:11.5px; line-height:1.65; color:var(--cream2); text-align:left;
  font-weight:400; letter-spacing:normal; text-transform:none;
  box-shadow:0 10px 34px rgba(0,0,0,.5);
  opacity:0; visibility:hidden; transform:translateY(3px);
  transition:opacity .14s, transform .14s, visibility .14s;
  pointer-events:none;
}
.sb-info:hover .sb-tip, .sb-info:focus-visible .sb-tip { opacity:1; visibility:visible; transform:translateY(0); }
.sb-tip strong { color:var(--gold); display:block; margin-bottom:.25rem; }
.sb-tip b { color:var(--cream); font-weight:600; }
.sb-tip code {
  background:var(--surface3); border:1px solid var(--border); border-radius:3px;
  padding:0 4px; font-family:'SF Mono',Consolas,monospace; font-size:10.5px; color:var(--gold);
}
.sb-tip-sep { display:block; height:1px; background:var(--border); margin:.55rem 0; }

/* ── RESPONSIVE MINIMAL — n'affecte que les écrans étroits, desktop inchangé ── */
@media (max-width: 1024px){
  .sidebar, .sidebar-right { width:214px; padding:1rem .85rem; }
  .doc-sidebar { width:214px; }
  .donnees-tabs { width:186px; }
  .key-input { width:120px; }
  .nav-tab { padding:.75rem .8rem; }
}
@media (max-width: 860px){
  .sidebar, .sidebar-right, .doc-sidebar { width:188px; padding:.85rem .7rem; }
  .topbar { padding:0 .75rem; gap:.5rem; }
  .brand-sub { display:none; }
  .nav-quick-link { padding:4px 7px; }
  .api-tile-grid { grid-template-columns:1fr; }
  .donnees-content, .history-view { padding:1.25rem 1rem; }
}
/* ── Barre du haut sur écran étroit (23/07) ──
   La bulle centrale était centrée en ABSOLU : quand la fenêtre rétrécit, la
   marque (gauche) et les boutons (droite) grandissaient vers elle et la
   recouvraient. Sous 1100px on la remet EN FLUX : les trois blocs se partagent
   la barre, plus aucun chevauchement. La marque et les libellés de boutons se
   réduisent d'abord, la bulle rétrécit avec une ellipse plutôt que déborder. */
@media (max-width: 1100px){
  .header-center { position:static; transform:none; margin:0 auto; min-width:0; }
  .topbar { gap:.5rem; }
  .header-center .pd-trigger { max-width:38vw; }
  .header-center .pd-trigger > span:not(.pd-dot):not(.pd-arrow){ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
}
@media (max-width: 720px){
  .brand-sep, .brand-sub { display:none; }
  .topbar { padding:0 .6rem; gap:.4rem; }
  .api-keys-btn { padding:5px 9px; }
  /* Le pseudo peut être long : on l'écrête plutôt que de pousser la barre. */
  #user-bubble-name, #tb-user-name { max-width:22vw; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .header-center .pd-trigger { max-width:44vw; }
}

/* ══ TABLEAU DE BORD (onglet de synthèse) ══
   Charte AEL Studio : variables existantes (--gold, --cream, --sfw…). */
#dash-root { flex:1; min-height:0; overflow-y:auto; overflow-x:hidden; padding:1.5rem 1.75rem 2.5rem; }
.dsh-empty { color:var(--cream3); padding:2rem; }   /* centrage : bloc partagé */
/* En-tête de page : donne un vrai point de départ à la vue (elle « commençait »
   directement sur les chiffres, d'où l'impression de brouillon). */
.dsh-topbar { display:flex; align-items:flex-end; justify-content:space-between; gap:1rem; flex-wrap:wrap; padding-bottom:1.1rem; margin-bottom:1.25rem; border-bottom:1px solid var(--border); }
.dsh-h1 { font-family:var(--font-display); font-size:20px; color:var(--gold); line-height:1.2; }
.dsh-h1sub { font-size:12px; color:var(--cream3); margin-top:4px; }
.dsh-today { font-size:12px; color:var(--cream2); text-transform:capitalize; white-space:nowrap; }
/* Intertitre de section (« Chiffres clés », « Détail ») pour hiérarchiser. */
.dsh-section { margin:0 0 .75rem; }
.dsh-section + .dsh-grid, .dsh-section + .dsh-kpis { margin-top:0; }
.dsh-grid + .dsh-section, .dsh-kpis + .dsh-section { margin-top:1.5rem; }
.dsh-kpis { display:grid; grid-template-columns:repeat(4,1fr); gap:12px; margin-bottom:0; }
.dsh-kpi { background:var(--surface); border:1px solid var(--border); border-radius:var(--r); padding:13px 14px 11px; }
.dsh-kpi-l { font-size:11px; color:var(--cream3); margin-bottom:7px; }
.dsh-kpi-v { font-size:24px; font-weight:600; letter-spacing:-.01em; font-variant-numeric:tabular-nums; }
.dsh-kpi-s { font-size:11px; color:var(--cream2); margin-top:3px; }
.dsh-up { color:var(--sfw); } .dsh-gold { color:var(--gold); }
.dsh-kpi-v.up,.dsh-up { color:var(--sfw); } .dsh-kpi-v.warn { color:var(--warn); } .dsh-kpi-v.down { color:var(--nsfw); }
.dsh-grid { display:grid; grid-template-columns:1.55fr 1fr; gap:16px; align-items:start; }
.dsh-col { display:flex; flex-direction:column; gap:16px; min-width:0; }
.dsh-panel { background:var(--surface); border:1px solid var(--border); border-radius:var(--r); padding:18px; }
.dsh-kpi { transition:border-color .15s; }
.dsh-kpi:hover { border-color:var(--border2); }
.dsh-phead { display:flex; align-items:center; justify-content:space-between; margin-bottom:14px; }
.dsh-ptitle { font-size:13px; font-weight:600; color:var(--cream); }
.dsh-pill { font-size:10px; padding:2px 8px; border-radius:20px; font-weight:500; }
.dsh-pill.ready { background:var(--sfw-dim); color:var(--sfw); border:1px solid var(--sfw); }
.dsh-pill.soon { background:var(--surface3); color:var(--cream3); border:1px solid var(--border2); }
.dsh-eyebrow { font-size:10px; letter-spacing:.22em; text-transform:uppercase; color:var(--cream3); font-weight:500; margin-bottom:9px; }
.dsh-sub { border-top:1px solid var(--border); margin-top:14px; padding-top:13px; }
.dsh-bars { display:flex; align-items:flex-end; gap:6px; height:126px; padding-top:6px; }
.dsh-b { flex:1; display:flex; flex-direction:column; justify-content:flex-end; gap:5px; height:100%; }
.dsh-b i { display:block; background:var(--gold2); border-radius:4px 4px 0 0; min-height:2px; }
/* Week-end d'abord, PUIS aujourd'hui : à spécificité égale, la dernière règle
   gagne — aujourd'hui doit primer même un dimanche (Victor, 20/07). Couleur
   « Aujourd'hui » passée au bleu (--vid) : le doré était trop proche de
   « Semaine » (--gold2). */
.dsh-b.wk i { background:var(--surface3); }
.dsh-b.today i { background:var(--vid); }
.dsh-b span { font-size:9.5px; color:var(--cream3); text-align:center; }
.dsh-legend { display:flex; flex-wrap:wrap; align-items:center; gap:14px; margin-top:12px; font-size:11.5px; color:var(--cream2); }
.dsh-legend span { display:flex; align-items:center; gap:6px; }
.sw { width:10px; height:10px; border-radius:3px; display:inline-block; flex-shrink:0; }
.dsh-split { display:grid; grid-template-columns:140px 1fr; gap:18px; align-items:center; }
.dsh-donut { position:relative; width:140px; height:140px; margin:0 auto; }
.dsh-donut svg { display:block; }
.dsh-mid { position:absolute; inset:0; display:flex; flex-direction:column; align-items:center; justify-content:center; }
.dsh-mid b { font-size:21px; font-weight:600; font-variant-numeric:tabular-nums; }
.dsh-mid small { font-size:10px; color:var(--cream3); letter-spacing:.1em; text-transform:uppercase; }
.dsh-rows { display:flex; flex-direction:column; gap:9px; }
.dsh-row { display:grid; grid-template-columns:14px 1fr auto; align-items:center; gap:9px; font-size:12.5px; }
.dsh-track { height:7px; border-radius:5px; background:var(--surface3); overflow:hidden; }
.dsh-track i { display:block; height:100%; border-radius:5px; }
.dsh-n { color:var(--cream2); font-size:12px; white-space:nowrap; }
/* Bénéfices Fanvue (Victor, 20/07). */
.dsh-fv-grid { display:grid; grid-template-columns:repeat(2,1fr); gap:9px; }
.dsh-fv-cell { background:var(--surface2); border:1px solid var(--border); border-radius:9px; padding:11px 12px; }
.dsh-fv-v { font-size:18px; font-weight:700; color:var(--gold); font-variant-numeric:tabular-nums; }
.dsh-fv-l { font-size:10.5px; color:var(--cream3); margin-top:2px; }
.dsh-fv-buyer { display:grid; grid-template-columns:20px 1fr auto; align-items:center; gap:10px; padding:6px 0; border-bottom:1px solid var(--border); font-size:12.5px; }
.dsh-fv-buyer:last-child { border-bottom:none; }
.dsh-fv-rank { width:20px; height:20px; display:flex; align-items:center; justify-content:center; background:var(--gold3); color:var(--gold); border-radius:50%; font-size:11px; font-weight:700; }
.dsh-fv-name { color:var(--cream2); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.dsh-fv-amt { color:var(--cream); font-weight:600; font-variant-numeric:tabular-nums; }
.dsh-fv-wait { font-size:12px; color:var(--cream3); line-height:1.6; background:var(--surface2); border:1px dashed var(--border2); border-radius:9px; padding:12px; }
.dsh-cal { display:grid; grid-template-columns:repeat(7,1fr); gap:5px; }
.dsh-calh,.dsh-cald { font-size:11px; text-align:center; border-radius:7px; position:relative; }
.dsh-calh { color:var(--cream3); font-size:9.5px; text-transform:uppercase; padding:2px 0; }
.dsh-cald { background:var(--surface2); color:var(--cream2); padding:7px 0; }
.dsh-cald.mut { background:none; }
.dsh-cald.tod { outline:1px solid var(--gold2); }
.dsh-cald.ev { background:var(--gold3); color:var(--gold); border:1px solid var(--gold2); font-weight:600; }
.dsh-pt { position:absolute; bottom:3px; left:50%; transform:translateX(-50%); width:5px; height:5px; border-radius:50%; }
.dsh-evlist { display:flex; flex-direction:column; gap:8px; margin-top:14px; }
.dsh-upi { display:flex; align-items:center; gap:10px; font-size:12.5px; }
.dsh-cd { margin-left:auto; font-size:11px; color:var(--gold); font-weight:600; white-space:nowrap; }
.dsh-arch { display:flex; flex-direction:column; }
.dsh-node { display:flex; align-items:center; gap:10px; background:var(--surface2); border:1px solid var(--border2); border-radius:9px; padding:9px 11px; font-size:12.5px; }
.dsh-node .dsh-meta { margin-left:auto; font-size:11px; color:var(--cream3); }
.dsh-st { width:8px; height:8px; border-radius:50%; flex-shrink:0; }
.up-ok { background:var(--sfw); } .up-warn { background:var(--warn); } .up-off { background:var(--nsfw); } .up-mut { background:var(--cream3); }
.dsh-conn { width:2px; height:13px; background:var(--border2); margin:0 auto; }
.dsh-pair { display:grid; grid-template-columns:1fr 1fr; gap:10px; }
.dsh-gauges { display:flex; flex-direction:column; gap:10px; margin-top:14px; }
.dsh-ga { display:grid; grid-template-columns:92px 1fr auto; align-items:center; gap:10px; font-size:12px; font-variant-numeric:tabular-nums; }
.dsh-feed { display:flex; flex-direction:column; }
.dsh-fi { display:grid; grid-template-columns:8px 1fr auto; gap:10px; align-items:start; padding:9px 0; border-top:1px solid var(--border); }
.dsh-fi:first-child { border-top:none; }
.dsh-fi .dsh-st { margin-top:5px; }
.dsh-act { font-size:12.5px; } .dsh-who { font-size:11px; color:var(--cream3); }
.dsh-t { font-size:10.5px; color:var(--cream3); white-space:nowrap; }
.dsh-soongrid { display:grid; grid-template-columns:1fr 1fr; gap:12px; }
.dsh-soonbox { background:var(--surface2); border:1px dashed var(--border2); border-radius:10px; padding:13px; }
.dsh-soonbox .h { font-size:12.5px; font-weight:600; margin-bottom:6px; color:var(--cream); }
.dsh-soonbox p { margin:0; font-size:11.5px; color:var(--cream3); line-height:1.5; }
@media(max-width:1000px){ .dsh-kpis{ grid-template-columns:repeat(2,1fr); } .dsh-grid{ grid-template-columns:1fr; } }
@media(max-width:560px){ .dsh-kpis{ grid-template-columns:repeat(2,1fr); } .dsh-split{ grid-template-columns:1fr; } .dsh-soongrid{ grid-template-columns:1fr; } }

/* Bouton « ✨ Améliorer » (outils IA de texte) */
.ai-improve-btn {
  font-family:inherit; font-size:11px; font-weight:500; cursor:pointer;
  color:var(--gold); background:var(--gold3); border:1px solid var(--gold2);
  border-radius:6px; padding:3px 9px; transition:all .12s; white-space:nowrap;
}
.ai-improve-btn:hover:not(:disabled) { background:var(--gold2); color:var(--cream); }
.ai-improve-btn:disabled { opacity:.6; cursor:default; }

/* Cases conformité du calendrier (garde-fou §2) */
.cal-compliance { display:flex; flex-direction:column; gap:7px; margin-top:.6rem; padding-top:.6rem; border-top:1px solid var(--border); }
.cal-check { display:flex; align-items:center; gap:8px; font-size:12px; color:var(--cream2); cursor:pointer; }
.cal-check input { accent-color:var(--gold); width:15px; height:15px; flex-shrink:0; cursor:pointer; }
.cal-check.locked { cursor:default; } .cal-check.locked input { cursor:default; }

/* ══ BIBLIOTHÈQUE (Feature 3) ══ */
/* ⚠️ PLUS DE PADDING ICI (26/07) : il s'ajoutait à celui de `.history-view`
   qui l'enveloppe, et c'est cette addition qui décalait la Bibliothèque de
   l'Historique. Le retrait appartient à la zone, pas à son contenu. */
#lib-root { }
.lib-head { display:flex; align-items:flex-start; justify-content:space-between; gap:1rem; margin-bottom:14px; flex-wrap:wrap; }
.lib-title { font-family:var(--font-display); font-size:16px; color:var(--gold); }
.lib-sub { font-size:12px; color:var(--cream3); margin-top:3px; }
.lib-addbtn { font-family:inherit; font-size:12.5px; font-weight:500; cursor:pointer; color:#1a1206; background:var(--gold); border:1px solid var(--gold); border-radius:8px; padding:8px 14px; transition:all .12s; }
.lib-addbtn:hover { background:var(--gold2); color:var(--cream); }
.lib-filters { display:flex; flex-direction:column; gap:8px; margin-bottom:14px; }
.lib-chips { display:flex; flex-wrap:wrap; gap:6px; }
.lib-chip { font-family:inherit; font-size:11.5px; cursor:pointer; color:var(--cream2); background:var(--surface2); border:1px solid var(--border); border-radius:20px; padding:4px 11px; transition:all .12s; }
.lib-chip:hover { border-color:var(--gold2); color:var(--cream); }
.lib-chip.on { color:var(--gold); background:var(--gold3); border-color:var(--gold2); }
.lib-drop { border:1px dashed transparent; border-radius:12px; transition:border-color .12s; }
.lib-drop.over { border-color:var(--gold2); background:var(--gold3); }
.lib-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(180px,1fr)); gap:14px; }
.lib-card { background:var(--surface); border:1px solid var(--border); border-radius:12px; overflow:hidden; display:flex; flex-direction:column; }
/* Contenu déjà « Utilisé » : grisé pour le distinguer des images encore dispo. */
.lib-card.used { opacity:.62; }
.lib-card.used .lib-thumb { filter:grayscale(.85); }
.lib-card.used:hover { opacity:.9; }
.lib-thumb { position:relative; aspect-ratio:3/4; background-size:cover; background-position:center; background-color:var(--surface3); }
.lib-badge { position:absolute; top:8px; left:8px; font-size:10px; font-weight:600; color:#12100c; padding:2px 8px; border-radius:20px; }
.lib-status { position:absolute; top:8px; right:8px; font-size:10px; font-weight:500; padding:2px 8px; border-radius:20px; }
.lib-status.warn { background:var(--warn-dim); color:var(--warn); } .lib-status.ok { background:var(--sfw-dim); color:var(--sfw); }
.lib-status.off { background:var(--nsfw-dim); color:var(--nsfw); } .lib-status.mut { background:var(--surface3); color:var(--cream3); }
/* Badge « IA propriétaire » sur les vignettes, vue Profil maître (Victor 20/07). */
.lib-persona { position:absolute; bottom:8px; left:8px; max-width:calc(100% - 16px); font-size:10px; font-weight:600; color:var(--cream); background:rgba(0,0,0,.62); padding:2px 8px; border-radius:20px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.lib-card-body { padding:10px; display:flex; flex-direction:column; gap:7px; }
.lib-type-sel, .lib-tags { font-family:inherit; font-size:11.5px; color:var(--cream2); background:var(--surface2); border:1px solid var(--border2); border-radius:6px; padding:5px 7px; width:100%; }
.lib-actions { display:flex; gap:6px; flex-wrap:wrap; }
.lib-btn { flex:1; font-family:inherit; font-size:11px; font-weight:500; cursor:pointer; color:var(--cream2); background:var(--surface2); border:1px solid var(--border2); border-radius:6px; padding:6px 4px; transition:all .12s; white-space:nowrap; }
.lib-btn:hover { border-color:var(--gold2); color:var(--cream); }
.lib-btn.ok { color:var(--sfw); border-color:var(--sfw); } .lib-btn.ok:hover { background:var(--sfw-dim); }
.lib-btn.off { color:var(--nsfw); border-color:var(--nsfw); } .lib-btn.off:hover { background:var(--nsfw-dim); }
.lib-btn.plan { color:#1a1206; background:var(--gold); border-color:var(--gold); } .lib-btn.plan:hover { background:var(--gold2); color:var(--cream); }
.lib-empty { grid-column:1/-1; color:var(--cream3); padding:2.5rem 1rem; border:1px dashed var(--border2); border-radius:12px; }   /* centrage : bloc partagé */

/* ══ Bibliothèque : miniature cliquable + visionneuse (lightbox) ══ */
.lib-thumb { cursor:zoom-in; }
.lib-zoom { position:absolute; bottom:8px; right:8px; width:26px; height:26px; border-radius:7px; display:flex; align-items:center; justify-content:center; font-size:13px; background:rgba(0,0,0,.55); color:var(--cream); opacity:0; transition:opacity .15s; pointer-events:none; }
.lib-thumb:hover .lib-zoom { opacity:1; }
.lib-viewer-overlay { padding:0; background:rgba(0,0,0,.9); }
.lib-viewer-stage { position:relative; display:flex; flex-direction:column; align-items:center; justify-content:center; max-width:92vw; max-height:94vh; }
.lib-viewer-img { max-width:92vw; max-height:82vh; object-fit:contain; border-radius:8px; box-shadow:0 12px 48px rgba(0,0,0,.6); background:var(--surface3); }
.lib-viewer-bar { display:flex; align-items:center; gap:12px; margin-top:12px; width:100%; background:var(--surface); border:1px solid var(--border2); border-radius:10px; padding:8px 12px; }
.lib-viewer-meta { display:flex; align-items:center; gap:8px; flex:1; min-width:0; font-size:12px; color:var(--cream2); flex-wrap:wrap; }
.lib-viewer-pos { font-size:11px; color:var(--cream3); font-variant-numeric:tabular-nums; white-space:nowrap; }
.lib-viewer-close { flex-shrink:0; width:28px; height:28px; border-radius:7px; border:1px solid var(--border2); background:var(--surface2); color:var(--cream2); cursor:pointer; font-size:14px; line-height:1; }
.lib-viewer-close:hover { border-color:var(--nsfw); color:var(--nsfw); }
.lib-viewer-nav { flex-shrink:0; width:48px; height:48px; margin:0 10px; border-radius:50%; border:1px solid var(--border2); background:rgba(20,18,14,.7); color:var(--cream); font-size:26px; line-height:1; cursor:pointer; transition:all .12s; }
.lib-viewer-nav:hover { background:var(--gold); color:#1a1206; border-color:var(--gold); }

/* ══ Corbeille : lignes du modal (partagées Historique + Bibliothèque) ══ */
.trash-row { display:flex; align-items:center; gap:10px; padding:8px 10px; background:var(--surface2); border:1px solid var(--border); border-radius:var(--r); }
.trash-row-ico { font-size:16px; line-height:1; width:32px; text-align:center; flex-shrink:0; }
.trash-row-thumb { width:34px; height:44px; flex-shrink:0; border-radius:5px; background-size:cover; background-position:center; background-color:var(--surface3); border:1px solid var(--border2); }
.trash-row-txt { flex:1; min-width:0; }
.trash-row-title { font-size:12px; color:var(--cream); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.trash-row-sub { font-size:10.5px; color:var(--cream3); margin-top:2px; }
.trash-row-sub.soon { color:var(--warn); }
.trash-restore { flex-shrink:0; font-family:inherit; font-size:11px; cursor:pointer; color:var(--cream2); background:var(--surface3); border:1px solid var(--border2); border-radius:6px; padding:5px 9px; transition:all .12s; }
.trash-restore:hover { border-color:var(--sfw); color:var(--sfw); }
.trash-empty-state { text-align:center; padding:2rem 1rem; color:var(--cream3); }
.tes-ico { font-size:28px; opacity:.6; margin-bottom:.5rem; }
.tes-txt { font-size:12px; line-height:1.6; }

/* Animation de suppression : l'élément s'écrase sur lui-même puis disparaît.
   Ajoutée par crushRemove() (utils.js) avant le retrait réel des données. */
@keyframes crushOut {
  0%   { transform:scale(1,1);       opacity:1; }
  30%  { transform:scale(1.04,.9);   opacity:1; }
  100% { transform:scale(.25,0);     opacity:0; }
}
.crushing { animation:crushOut .3s cubic-bezier(.55,0,.75,.2) forwards; transform-origin:center; pointer-events:none; overflow:hidden; }
@media (prefers-reduced-motion: reduce) { .crushing { animation:none; opacity:0; } }

/* En-tête des cases de prompt dans les Paramètres du profil IA (libellé + ✨ Améliorer) */
.params-prompt-head { display:flex; align-items:center; justify-content:space-between; gap:8px; margin-bottom:6px; }
.params-prompt-lbl { font-size:11px; color:var(--cream3); letter-spacing:.02em; }

/* ══════════════════════════════════════════════════════════
   CHOIX DE L'IA DE TRAVAIL (« La centrale » · generator.js)
   ------------------------------------------------------------
   ⚠️ Ce bloc s'appelait « Droits du compte parent ». Le filtre global
   a été retiré le 22/07 : il ne reste que la LISTE du popup
   « Sur quelle IA ? », partagée par le Générateur et l'onglet API.
   Les styles de cases à cocher, de pastilles et de résumé qui
   servaient au volet de l'encadré droit ont été supprimés avec lui —
   du CSS que plus rien n'utilise finit toujours par être recopié.
   ══════════════════════════════════════════════════════════ */
.pf-list { display:flex; flex-direction:column; gap:4px; max-height:300px; overflow-y:auto;
  background:var(--surface2); border:1px solid var(--border); border-radius:var(--r); padding:.4rem; }
.pf-empty { font-size:11.5px; color:var(--cream3); font-style:italic; padding:.6rem; text-align:center; }
/* Bandeau « Tu travailles sur … » + choix de l'IA cible (Centrale).
   Le MÊME dans le Générateur et dans l'onglet API : un seul choix, une seule
   façon de l'afficher, une seule façon d'en changer. */
.gen-target-banner {
  display:flex; align-items:center; justify-content:space-between; gap:10px;
  background:var(--gold3); border:1px solid var(--gold2); border-radius:var(--r);
  padding:.5rem .75rem; font-size:11.5px; color:var(--gold); margin-bottom:.75rem;
}
.gen-target-banner strong { color:var(--cream); }
.gt-change {
  background:transparent; border:1px solid var(--gold2); border-radius:6px;
  color:var(--gold); font-size:10.5px; padding:3px 10px; cursor:pointer;
  font-family:inherit; transition:all .15s; flex-shrink:0;
}
.gt-change:hover { background:var(--gold2); color:#000; }
/* Étiquette de cible du Générateur (au-dessus de « Générer », 24/07) : version
   compacte du bandeau, cliquable pour ouvrir le Filtre IA. */
.gen-target-label {
  background:var(--gold3); border:1px solid var(--gold2); border-radius:var(--r);
  padding:.45rem .6rem; font-size:11px; color:var(--gold); cursor:pointer;
  text-align:left; transition:all .15s;
}
.gen-target-label:hover { background:var(--gold2); color:#000; }
.gen-target-label strong { color:var(--cream); }
.gen-target-label.gt-ambigu { background:transparent; border-color:var(--warn); color:var(--warn); }
.gen-target-label.gt-ambigu strong { color:var(--warn); }
.gt-row {
  display:flex; align-items:center; gap:9px; width:100%; text-align:left;
  background:transparent; border:none; border-radius:6px; padding:.55rem .65rem;
  font-size:12.5px; color:var(--cream2); cursor:pointer; font-family:inherit;
  transition:background .12s, color .12s;
}
.gt-row:hover { background:var(--gold3); color:var(--gold); }
.gt-row-name { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.gt-row-go { color:var(--cream3); font-size:14px; flex-shrink:0; }

/* ══ AGENTS (agents.js, préfixe ag) — volet + fenêtre ══ */
.ag-feed { display:flex; flex-direction:column; gap:6px; max-height:230px; overflow-y:auto; }
.ag-empty { font-size:10.5px; color:var(--cream3); line-height:1.6; font-style:italic; padding:.3rem 0; }
.ag-line { display:flex; gap:7px; align-items:flex-start; background:var(--surface);
  border:1px solid var(--border); border-left:3px solid var(--border2);
  border-radius:0 var(--r) var(--r) 0; padding:.4rem .5rem; }
.ag-line.veto   { border-left-color:var(--nsfw); background:var(--nsfw-dim); }
.ag-line.retenu { border-left-color:var(--sfw); }
.ag-line.big { padding:.6rem .75rem; margin-bottom:6px; }
.ag-ico { font-size:12px; flex-shrink:0; line-height:1.5; }
.ag-body { min-width:0; flex:1; }
.ag-head { display:flex; align-items:center; gap:6px; flex-wrap:wrap; font-size:10px;
  font-weight:600; letter-spacing:.04em; margin-bottom:2px; }
.ag-who  { color:var(--cream3); font-weight:400; }
.ag-time { color:var(--cream3); font-weight:400; margin-left:auto; }
.ag-tag  { background:var(--nsfw); color:#fff; border-radius:8px; font-size:8.5px;
  padding:1px 6px; letter-spacing:.1em; }
.ag-line.retenu .ag-tag { background:var(--sfw); color:#062018; }
.ag-txt  { font-size:11px; color:var(--cream2); line-height:1.55; word-break:break-word; }
.ag-note { font-size:11.5px; color:var(--cream2); background:var(--surface2);
  border:1px solid var(--border); border-left:3px solid var(--gold2);
  border-radius:0 var(--r) var(--r) 0; padding:.7rem .875rem; line-height:1.6; margin-bottom:.75rem; }
.ag-card { background:var(--surface2); border:1px solid var(--border);
  border-radius:var(--r); padding:.7rem .875rem; margin-bottom:8px; }
.ag-card-h { display:flex; align-items:center; justify-content:space-between; gap:8px;
  font-size:13px; font-weight:600; margin-bottom:5px; }
.ag-badge { background:var(--surface3); border:1px solid var(--border2); color:var(--cream3);
  border-radius:10px; font-size:9px; letter-spacing:.1em; text-transform:uppercase; padding:2px 8px; }
.ag-card-role { font-size:12px; color:var(--cream2); line-height:1.6; }
.ag-card-lit  { font-size:10px; color:var(--cream3); margin-top:5px; font-style:italic; }

/* ══ FOURNÉE LORA — barre de progression (lora.js, 25/07 (3)) ══
   Une fournée dure des minutes : sans jauge, on ne sait pas si ça travaille ou
   si c'est bloqué, et on relance « au cas où » — en repayant. */
.lora-bar { height:5px; border-radius:3px; background:var(--surface3); overflow:hidden; margin:.5rem 0 .35rem; }
.lora-bar > span { display:block; height:100%; background:var(--gold); transition:width .4s ease; }

/* ══ ASSISTANT « PRÉPARER LA SEMAINE » — 3 étapes (agents.js, 25/07 (3)) ══
   « Met des bulles de navigation afin de savoir qu'on est à l'étape X sur X. »
   Les bulles sont cliquables VERS L'ARRIÈRE seulement : revenir sur ses pas est
   sans conséquence, sauter en avant supposerait un travail pas encore fait. */
.wz-steps { display:flex; align-items:center; gap:0; margin:.25rem 0 1rem; }
.wz-step { display:flex; align-items:center; gap:7px; background:none; border:none;
  font-family:inherit; color:var(--cream3); font-size:11px; cursor:default; padding:0; }
.wz-step.clickable { cursor:pointer; }
.wz-step.clickable:hover .wz-num { border-color:var(--gold); color:var(--gold); }
.wz-num { width:22px; height:22px; border-radius:50%; flex-shrink:0;
  display:flex; align-items:center; justify-content:center;
  border:1px solid var(--border2); background:var(--surface2);
  font-size:11px; font-weight:700; transition:all .15s; }
.wz-step.on  .wz-num { border-color:var(--gold); background:var(--gold3); color:var(--gold); }
.wz-step.on  .wz-lbl { color:var(--cream); font-weight:600; }
.wz-step.done .wz-num { border-color:var(--sfw); color:var(--sfw); }
.wz-lbl { white-space:nowrap; }
.wz-line { flex:1; height:1px; background:var(--border); margin:0 8px; min-width:12px; }
.wz-body { min-height:180px; max-height:52vh; overflow-y:auto; }

/* ── VARIANTE « POINTS » (Victor, 26/07 · REVUE LE 27/07) ──
   ⚠️ CE N'EST PAS UN SECOND COMPOSANT, c'est un MODIFICATEUR de `.wz-steps` :
   il n'en change que la FORME (un point, pas un numéro). Employé par la seule
   fenêtre d'une IA (Atelier) ; l'assistant « Préparer la semaine » utilise
   `.wz-steps` nu, avec ses numéros — les toucher ici ne l'affecte pas.

   ⚠️⚠️ LA COULEUR DIT OÙ L'ON EST, PAS L'AVANCEMENT (Victor, 27/07 : « met la
   couleur principale, et si on est page deux, le point deux d'une couleur
   différente »). C'est l'inverse de la règle du 26/07, et c'est un choix
   assumé : trois points de trois couleurs différentes selon le remplissage,
   plus un halo pour la position, faisaient quatre signaux sur 17 pixels — on
   ne lisait plus lequel répondait à « où suis-je ». Un seul signal, une seule
   question. L'avancement reste lisible : il est dans l'infobulle du point
   (« Détails — en cours (3/8) »), là où on va le chercher quand on le veut. */

/* La barre entière : ‹ · trois points · ›.
   ⚠️ ELLE A QUITTÉ LE HAUT DE LA FENÊTRE POUR SON PIED (Victor, 27/07). Le
   conteneur vertical `.wz-nav` (qui portait aussi le nom de la page en
   dessous) n'a plus d'emploi et a été retiré : le nom de la page est passé
   dans le titre, et le centrage est désormais tenu par le pied en trois zones
   (`.iaf-pied`, atelier.css). Ne pas le recréer ici — deux centrages
   concurrents, c'est ce qui avait collé les points au ✕. */
.wz-nav-row { display:flex; align-items:center; justify-content:center; gap:14px; }
.wz-arrow {
  background:transparent; border:1px solid var(--border2); border-radius:6px;
  width:26px; height:26px; display:flex; align-items:center; justify-content:center;
  color:var(--cream3); font-size:14px; line-height:1; font-family:inherit;
  cursor:pointer; transition:all .15s; padding:0;
}
.wz-arrow:hover:not(:disabled) { color:var(--gold); border-color:var(--gold2); background:var(--gold3); }
/* ⚠️ Une flèche de bout de course reste VISIBLE, seulement éteinte : la faire
   disparaître déplacerait les points d'une page à l'autre, et on perdrait le
   repère qu'ils sont censés donner. */
.wz-arrow:disabled { opacity:.3; cursor:default; }

.wz-dots { gap:12px; margin:0; }
/* `line-height:0` : sans lui, la hauteur de ligne du bouton (11 px hérités de
   `.wz-step`) s'ajoute sous le point, qui cesse d'être centré dans son halo. */
.wz-dots .wz-step { padding:0; border-radius:50%; cursor:pointer; line-height:0; background:none; }
.wz-dots .wz-num {
  width:11px; height:11px; border-width:1.5px; border-color:var(--border2);
  background:transparent; font-size:0;                 /* le point ne porte pas de texte */
  transition:all .18s;
}
/* Toutes les pages : la couleur principale du produit. */
.wz-dots .wz-num { border-color:var(--gold2); background:var(--gold2); }
/* La page ouverte, et elle seule : une autre couleur, plus un halo.
   ⚠️ Deux différences pour un seul signal, et c'est voulu — sur un point de
   11 px, la teinte seule ne se voit pas à la première seconde. */
.wz-dots .wz-step.ici .wz-num {
  border-color:var(--cream); background:var(--cream);
  box-shadow:0 0 0 4px var(--gold3);
}
.wz-dots .wz-step:hover .wz-num { border-color:var(--cream); }

/* ── Séparateur entre deux préparations dans le fil (Victor, 25/07 (3)) ──
   « Dans le fil des échanges faire une séparation visible entre chaque
   préparation de semaine, ça évite de tout mélanger. » Un filet ne suffisait
   pas : il faut dire QUELLE semaine et QUAND, sinon deux préparations de la
   même semaine restent indiscernables. */
.ag-session { display:flex; align-items:center; gap:10px; margin:1.1rem 0 .6rem; }
.ag-session:first-child { margin-top:.15rem; }
.ag-session::before, .ag-session::after { content:""; flex:1; height:1px; background:var(--gold2); opacity:.55; }
.ag-session-lbl { font-size:10px; letter-spacing:.18em; text-transform:uppercase;
  color:var(--gold); white-space:nowrap; flex-shrink:0; }

/* ══ RÈGLES D'AUTOMATISATION (automation.js, préfixe au) ══ */
.au-row { display:flex; gap:10px; align-items:flex-start; background:var(--surface2);
  border:1px solid var(--border); border-left:3px solid var(--border2);
  border-radius:0 var(--r) var(--r) 0; padding:.7rem .875rem; margin-bottom:8px;
  cursor:pointer; transition:border-color .12s, background .12s; }
.au-row:hover { background:var(--surface3); }
.au-row.on { border-left-color:var(--gold); }
.au-row input, .au-sub input { accent-color:var(--gold); cursor:pointer; flex-shrink:0; margin-top:2px; }
.au-txt { min-width:0; flex:1; }
.au-t { font-size:12.5px; font-weight:600; color:var(--cream); margin-bottom:3px; }
.au-d { font-size:11px; color:var(--cream2); line-height:1.55; }
.au-sub { display:flex; gap:7px; align-items:flex-start; margin-top:7px;
  font-size:10.5px; color:var(--cream3); cursor:pointer; }
.au-sub.on { color:var(--cream2); }
.au-heure { margin-top:.9rem; }

/* ── Compteur d'un bouton de filtre (encadrés gauches, Victor 25/07) ──
   L'Historique et la Bibliothèque de Tube affichaient déjà « (12) » ; ceux de
   Prompt Engine IA non. Même écran, deux comportements — d'où cette classe,
   utilisée par histMajCompteursFiltres() et calMajCompteursFiltres().
   Dimensions calées sur `.nav-count` : un compteur doit se lire pareil qu'il
   soit sur un onglet ou sur un filtre. */
.hist-fcount {
  background:var(--surface3); color:var(--cream3); font-size:10px;
  padding:1px 6px; border-radius:10px; margin-left:4px;
}
.hist-filter-btn.active .hist-fcount { background:var(--gold3); color:var(--gold); }

/* Bouton d'ajout de média désactivé. Il l'est quand il manque le serveur ou la
   chaîne : on le grise sans le faire disparaître, pour que la raison (title)
   reste lisible au survol. */
.lib-addbtn:disabled { opacity:.4; cursor:not-allowed; }
.lib-addbtn:disabled:hover { background:var(--gold); color:#1a1206; }

/* ══════════════════════════════════════════════════════════
   VERROU « UNE SEULE IA » (Victor, 25/07)
   ------------------------------------------------------------
   Le Générateur et l'onglet API travaillent forcément POUR UNE IA : ses
   prompts, sa langue, ses comptes. Quand le Filtre IA en désigne zéro ou
   plusieurs, il n'y a pas de réponse à « pour qui ? » — on bloque donc
   l'action au lieu de la laisser partir au hasard.

   ⚠️ POURQUOI UNE CLASSE ET PAS `disabled` : l'attribut est déjà piloté par
   selectMode() (un type de contenu est-il choisi ?) et applyPermissions()
   (compte en lecture seule). Un troisième propriétaire aurait fini par
   ré-ouvrir « Générer » à un compte qui n'a pas le droit d'écrire. Cette
   classe se superpose sans rien écraser.
   ══════════════════════════════════════════════════════════ */
.gen-locked { opacity:.4; pointer-events:none; filter:saturate(.4); }
/* ⚠️ `.gen-terni` = l'APPARENCE du verrou, SANS le blocage des clics (27/07).
   Les tuiles de type de contenu ne dépensent rien : cliquer dessus sans IA
   cochée ouvre justement le choix des IA. Les rendre inertes retirerait le
   chemin qui débloque la situation — la même faute que verrouiller la carte
   du filtre. Ce qu'on corrige, c'est qu'elles avaient l'air normales. */
.gen-terni { opacity:.5; filter:saturate(.5); }

/* Le voile de l'onglet API. Il couvre la zone CENTRALE uniquement : l'encadré
   de gauche reste vivant, donc on peut continuer à naviguer entre les API —
   seule la configuration est fermée. */
.api-lock { display:none; position:absolute; inset:0; z-index:5;
  align-items:center; justify-content:center; padding:2rem;
  /* Noir translucide plutôt qu'un color-mix() sur --bg : les deux thèmes du
     produit sont sombres, et une valeur simple ne dépend d'aucun support. */
  background:rgba(0,0,0,.62); }
#api-center.api-locked > .api-lock { display:flex; }
/* Tout le reste du centre est grisé et sourd. `:not(.api-lock)` : le voile,
   lui, doit rester net et cliquable — c'est le seul endroit où l'on peut agir. */
#api-center.api-locked > *:not(.api-lock) { opacity:.3; pointer-events:none; filter:saturate(.3); }

.api-lock-card { max-width:420px; text-align:center; background:var(--surface2);
  border:1px solid var(--border2); border-radius:var(--r); padding:1.6rem 1.5rem;
  box-shadow:0 20px 50px -20px rgba(0,0,0,.7); }
.api-lock-ico   { font-size:30px; margin-bottom:.5rem; }
.api-lock-title { font-family:var(--font-display); font-size:19px; color:var(--gold); margin-bottom:.5rem; }
.api-lock-txt   { font-size:12px; color:var(--cream2); line-height:1.65; margin-bottom:1.1rem; }

/* ── Jeu de hashtags ACTIF, et compteur de plafond (Victor, 25/07) ──
   Les jeux sont devenus des bascules : le bouton doit dire s'il est appliqué,
   sinon on reclique en croyant ajouter et on retire. */
.ct-tag-set.on { border-color:var(--gold); background:var(--gold3); color:var(--gold); }
.ct-tag-count {
  display:inline-flex; align-items:center; font-size:9.5px; color:var(--cream3);
  border:1px solid var(--border); border-radius:20px; padding:0 7px; margin-left:2px;
  font-family:'IBM Plex Mono',monospace;
}
/* Au-dessus du plafond : la légende contient des hashtags tapés à la main. On
   le signale sans bloquer — c'est du texte libre, pas un formulaire. */
.ct-tag-count.over { color:var(--nsfw); border-color:var(--nsfw); }

/* ══ CE QUI COÛTE DE L'ARGENT PORTE UNE PASTILLE (Victor, 27/07) ══
   « Tous les boutons qui ont une action payante, mets une indication. » La
   pastille dit QU'IL Y A un coût, l'infobulle dit LEQUEL — deux niveaux, pour
   que l'interface reste lisible quand trente boutons la portent.
   ⚠️ Le signe est posé par `src/core/cout.js`, jamais écrit dans le HTML : les
   tarifs vivent à un seul endroit, et la moitié de ces boutons est redessinée
   en JavaScript à chaque rendu.
   ⚠️ `position:relative` est ajouté par la classe elle-même : sans lui, le ✦ se
   positionnerait par rapport au premier ancêtre positionné — souvent la modale
   entière, donc dans un coin de l'écran. */
.cout-payant { position:relative; }
.cout-payant::after {
  content:"✦";
  position:absolute; top:1px; right:2px;
  font-size:8px; line-height:1; color:var(--gold);
  pointer-events:none;                 /* le signe ne doit jamais voler le clic */
  opacity:.9;
}
/* Sur un bouton rond et minuscule (le ⟳ d'un champ, 22 px), le coin est trop
   étroit : on décale le signe juste au-dessus du bord. */
.iaf-gen.cout-payant::after { top:-3px; right:-3px; font-size:7px; }

/* ⚠️ LA BARRE D'ONGLETS DES ÉDITEURS A ÉTÉ RETIRÉE (27/07, Victor : « finalement
   ça fait vide »). Ses règles `.lfx-nav` / `.lfx-tab` sont supprimées avec elle :
   du CSS qui ne vise plus aucun élément finit par être recopié ailleurs « parce
   qu'il existait déjà ». Seul `.lfx-sec` reste — les sections gardent leur
   classe et leur forme de carte. */

/* ══ CHAQUE SECTION EST UNE CARTE (Victor, 27/07 : « ça fait brouillon, juste
   posé là ») ══
   Les sections n'avaient aucun contenant : une étiquette, puis des champs, puis
   l'étiquette suivante — rien ne disait où l'une finissait et où l'autre
   commençait, et sous des onglets ce flottement se voit d'autant plus qu'il
   reste peu de blocs à l'écran.
   ⚠️ La carte reprend la forme des cartes de colonne du socle (`.sb-fold`) :
   même fond, même bordure, même rayon. On ne dessine pas un troisième style de
   carte dans la même application. */
.lfx-sec {
  background:var(--surface2); border:1px solid var(--border);
  border-radius:var(--r); padding:.85rem .95rem; margin-bottom:1rem;
}
/* Le titre de la carte : filet dessous, comme une tête de carte pliable — il
   sépare le nom du contenu au lieu de flotter au-dessus. */
.lfx-sec > .lf-section-label {
  margin:0 0 .7rem; padding-bottom:.5rem;
  border-bottom:1px solid var(--border);
}
/* Dernier champ collé au bas de la carte : on retire la marge qui pendait. */
.lfx-sec > *:last-child { margin-bottom:0; }
/* ⚠️ Une section masquée l'est par `style="display:none"` posé en JS, pas par
   une classe : `lfxOnglet()` remet `display:""` pour rendre la valeur au CSS,
   et une règle `.lfx-sec.off{display:none}` entrerait en concurrence avec lui. */

/* ── LinkFORGE : ajout d'un lien ou d'un séparateur (Victor, 27/07) ── */
.lf-add-wrap { position:relative; display:inline-block; }
.lf-add-menu {
  display:none; position:absolute; right:0; top:calc(100% + 4px); z-index:5;
  background:var(--surface2); border:1px solid var(--border2); border-radius:var(--r);
  box-shadow:0 10px 28px rgba(0,0,0,.5); overflow:hidden; min-width:150px;
}
.lf-add-menu.open { display:block; }
.lf-add-menu button {
  display:block; width:100%; text-align:left; background:transparent; border:none;
  color:var(--cream2); font-family:inherit; font-size:11px; padding:.5rem .7rem; cursor:pointer;
}
.lf-add-menu button:hover { background:var(--gold3); color:var(--gold); }
/* Carte d'un séparateur : volontairement plus discrète qu'une carte de lien —
   ce n'est pas un contenu, c'est une respiration. */
.lf-sep-card { opacity:.75; border-style:dashed; }
/* La vignette de l'image d'une tuile, dans l'éditeur. */
.lf-icon-mini {
  width:26px; height:26px; flex-shrink:0; border-radius:5px;
  border:1px solid var(--border2); background:var(--surface3) center/contain no-repeat;
}
/* Le menu d'animation d'une tuile : plus petit que les champs, il n'est pas au
   même niveau d'importance que le libellé et l'adresse. */
.lf-anim-un { font-size:10px; padding:.3rem .4rem; }

/* ── Les cartes de lien, dans l'onglet Contenu (Victor, 27/07) ──
   Elles étaient au même niveau visuel que les champs qui les entourent : trois
   rangées d'entrées empilées sans contour, ce qui donnait la même impression de
   « posé là ». Elles vivent maintenant DANS la carte de section, donc elles
   prennent le ton du dessous — fond plus sombre, bordure discrète — et se
   distinguent par le relief au survol plutôt que par un cadre permanent. */
.lfx-sec .lf-link-card {
  background:var(--surface); border:1px solid var(--border2); border-radius:var(--r);
  padding:.6rem; transition:border-color .15s;
}
.lfx-sec .lf-link-card:hover { border-color:var(--gold2); }
/* La barre d'options d'une tuile : détachée du reste par un filet, elle cesse
   de se confondre avec les champs de saisie juste au-dessus. */
.lfx-sec .lf-link-opts {
  margin-top:.55rem; padding-top:.5rem; border-top:1px solid var(--border);
}

/* ── Carte d'un lien : chaque champ porte son nom (Victor, 27/07) ──
   « On peut facilement tout mélanger lors de la configuration. » Une carte
   était une pile de cases nues — le libellé ressemblait à l'adresse, l'emoji à
   un champ texte. Trois niveaux, maintenant : la TÊTE (numéro + type), les
   BLOCS étiquetés, et la barre d'options en bas. */
.lf-lc-head {
  display:flex; align-items:center; gap:8px;
  padding-bottom:.5rem; margin-bottom:.6rem; border-bottom:1px solid var(--border);
}
/* Le numéro : c'est lui qui permet de désigner « la tuile 3 » au lieu de
   « celle du milieu ». */
.lf-lc-num {
  width:18px; height:18px; flex-shrink:0; border-radius:50%;
  background:var(--gold3); border:1px solid var(--gold2); color:var(--gold);
  font-size:9px; display:flex; align-items:center; justify-content:center;
}
.lf-lc-type { font-size:11px; color:var(--cream2); letter-spacing:.04em; }
/* Un séparateur n'est pas un contenu : sa tête est en gris, pas en or. */
.lf-lc-head.sep .lf-lc-num { background:var(--surface3); border-color:var(--border2); color:var(--cream3); }
.lf-lc-bloc { margin-bottom:.55rem; }
.lf-lc-bloc:last-child { margin-bottom:0; }
.lf-lc-lbl {
  display:block; font-size:9.5px; letter-spacing:.1em; text-transform:uppercase;
  color:var(--cream3); margin-bottom:.28rem;
}
.lf-lc-lbl b { color:var(--gold); font-weight:600; letter-spacing:0; }
/* Les champs d'un bloc occupent toute sa largeur — sauf ceux qui portent une
   largeur en ligne (l'emoji), qui restent étroits volontairement. */
.lf-lc-bloc > input:not([style*="max-width"]),
.lf-lc-bloc > select { width:100%; }

/* ── LinkFORGE : adresse publique et fraîcheur de la page en ligne (27/07) ── */
.lf-etat {
  font-size:10.5px; line-height:1.55; margin-top:.5rem;
  padding:.5rem .6rem; border-radius:var(--r);
  background:var(--surface2); border:1px solid var(--border2); color:var(--cream3);
}
.lf-etat code { color:var(--gold); font-family:'IBM Plex Mono',monospace; font-size:10.5px; }
.lf-etat.ok { border-color:var(--sfw); color:var(--sfw); background:var(--sfw-dim); }
/* ⚠️ La page périmée est en ORANGE, pas en rouge : rien n'est cassé, la page
   en ligne fonctionne — elle est seulement en retard. Le rouge est réservé à ce
   qui empêche d'agir. */
.lf-etat.vieux { border-color:var(--warn); color:var(--warn); background:var(--warn-dim); }

/* ── LinkFORGE · fenêtre de publication (Victor, 27/07) ──
   L'adresse se lit comme une URL, pas comme un formulaire : la partie fixe est
   collée au champ, sans espace, pour qu'on voie le lien final se former en
   tapant. C'est ce qui évite de saisir une adresse qu'on n'aurait pas relue. */
.lfp-url { display:flex; align-items:stretch; border:1px solid var(--border2); border-radius:var(--r); overflow:hidden; }
.lfp-url-fixe {
  display:flex; align-items:center; padding:0 .55rem; white-space:nowrap;
  background:var(--surface3); color:var(--cream3);
  font-family:'IBM Plex Mono',monospace; font-size:11px;
}
.lfp-url .lf-input { border:none; border-radius:0; flex:1; min-width:0; font-family:'IBM Plex Mono',monospace; }

/* ── Petit bouton d'action dans un titre (⧉ copier) ──
   Né dans l'Atelier pour copier la bio, employé aussi par LinkFORGE pour le
   lien public : il vit donc dans le socle, pas dans la feuille d'un seul outil.
   ⚠️ Sa taille est volontairement en dessous d'un `.modal-cancel` : il complète
   un titre, il n'est pas une action de la fenêtre. */
.ia-mini {
  background:transparent; border:1px solid var(--border2); border-radius:5px;
  color:var(--cream3); font-size:10px; line-height:1; font-family:inherit;
  padding:2px 5px; margin-left:6px; cursor:pointer; transition:all .15s;
}
.ia-mini:hover { color:var(--gold); border-color:var(--gold2); }

/* ── Tube · aucune chaîne : le seul chemin vers la création (27/07) ──
   ⚠️ Ce n'est pas un message d'écran vide (ceux-là restent supprimés depuis le
   26/07) : c'est l'unique porte d'entrée quand la base est vide. Sans lui,
   toutes les vues de Tube sont muettes et l'application paraît cassée. */
.tb-nochan {
  margin:auto; display:flex; flex-direction:column; align-items:center; gap:.75rem;
  padding:2rem; text-align:center;
}
.tb-nochan-t { font-family:var(--font-display); font-size:20px; color:var(--cream); }
.tb-nochan-d { font-size:12.5px; color:var(--cream3); max-width:30rem; line-height:1.6; }

/* ── Boutons d'action d'une carte de colonne (Victor, 27/07) ──
   « Aligne-les à gauche de leur emplacement. » `.svc-refresh-btn` centre son
   contenu — ce qui convient à un bouton isolé, pas à une PILE : cinq libellés
   de longueurs différentes donnaient cinq points de départ différents, et la
   liste cessait de se lire comme une liste.
   ⚠️ L'écart vient d'ici et non d'un `margin-top` répété sur chaque bouton :
   c'était le cas avant, et il fallait penser à le retirer sur le premier. */
.sb-act { justify-content:flex-start; text-align:left; width:100%; margin-top:5px; }
/* Le compteur de coût reste collé à droite, lui : c'est une valeur, pas un
   libellé, et on la cherche toujours au même endroit. */
.sb-act .btn-cout { margin-left:auto; }
/* Séparateur INVISIBLE : il écarte, il ne trace rien. Un filet aurait annoncé
   deux familles d'actions, alors qu'il n'isole que la seule qui vérifie au lieu
   de faire avancer le planning. */
.sb-act-sep { height:14px; }

/* ⚠️ LA MISE EN PAGE « FEUILLE DE DOCUMENT » A ÉTÉ RETIRÉE (27/07, quelques
   heures après avoir été écrite). Elle supposait un panneau large ; l'éditeur
   de LinkFORGE est devenu l'aperçu lui-même, et le panneau n'est plus qu'une
   colonne de 340 px. Des marges de 3 rem et une colonne d'étiquettes de 180 px
   n'y laissaient plus de place au contenu.
   ⚠️ Ce n'est PAS un aveu d'échec de l'idée : elle reste valable pour un écran
   large, et le CHANGELOG du 27/07 (12) la décrit. Si un éditeur large revient
   un jour, elle est à reprendre là — pas à réinventer. */

/* ── Boutons d'action d'une carte de colonne (Victor, 27/07) ──
   « Aligne-les à gauche de leur emplacement. » `.svc-refresh-btn` centre son
   contenu — ce qui convient à un bouton isolé, pas à une PILE : cinq libellés
   de longueurs différentes donnaient cinq points de départ différents, et la
   liste cessait de se lire comme une liste.
   ⚠️ L'écart vient d'ici et non d'un `margin-top` répété sur chaque bouton :
   c'était le cas avant, et il fallait penser à le retirer sur le premier. */
.sb-act { justify-content:flex-start; text-align:left; width:100%; margin-top:5px; }
/* Le compteur de coût reste collé à droite, lui : c'est une valeur, pas un
   libellé, et on la cherche toujours au même endroit. */
.sb-act .btn-cout { margin-left:auto; }
/* Séparateur INVISIBLE : il écarte, il ne trace rien. Un filet aurait annoncé
   deux familles d'actions, alors qu'il n'isole que la seule qui vérifie au lieu
   de faire avancer le planning. */
.sb-act-sep { height:14px; }

/* ══════════════════════════════════════════════════════════
   LINKFORGE EN FEUILLE DE DOCUMENT (Victor, 27/07)
   ------------------------------------------------------------
   « Titre, catégorie, case à remplir, case cochable, photo — comme une feuille
   Word. » Un éditeur de réglages devient un DOCUMENT : on le lit de haut en
   bas, chaque section est numérotée, et les champs sont alignés sur une même
   colonne comme sur un formulaire imprimé.

   ⚠️ UNE SEULE COLONNE, contrairement aux deux bots restés en quatre. Un
   document ne se lit pas en colonnes de journal : l'œil doit revenir à gauche
   au même endroit à chaque ligne, c'est ce qui donne l'impression de feuille.
   ⚠️ AUCUN CHAMP N'EST REDÉFINI ICI. On ne touche qu'à la mise en page — les
   `.lf-input`, `.lf-check`, `.lf-color` gardent leur apparence du socle. Le
   jour où cette feuille ne plaît pas, on retire la classe `.lfdoc` et tout
   revient exactement comme avant.
   ══════════════════════════════════════════════════════════ */
.lfdoc {
  column-count:1;                     /* annule la mise en colonnes des éditeurs d'API */
  max-width:820px; margin:0 auto; padding:2.5rem 3rem 3rem;
  background:var(--surface); border:1px solid var(--border2); border-radius:var(--r);
  /* L'ombre porte l'idée de « feuille posée » sans imiter le papier blanc :
     le thème reste sombre, on ne bascule pas en mode clair pour un panneau. */
  box-shadow:0 18px 50px rgba(0,0,0,.35);
  counter-reset:lfdoc;
}

/* ── L'en-tête du document ── */
.lfdoc-head { border-bottom:2px solid var(--gold2); padding-bottom:1.25rem; margin-bottom:2rem; }
.lfdoc-kicker {
  font-size:10px; letter-spacing:.22em; text-transform:uppercase;
  color:var(--gold); margin-bottom:.4rem;
}
.lfdoc-title { font-family:var(--font-display); font-size:34px; line-height:1.1; color:var(--cream); margin:0; }
.lfdoc-lead { font-size:12.5px; color:var(--cream3); line-height:1.7; margin:.6rem 0 0; max-width:52ch; }

/* ── Une section = un chapitre numéroté ──
   ⚠️ Le numéro vient d'un COMPTEUR CSS, pas du HTML : insérer une section au
   milieu ne demande donc pas de renuméroter les suivantes à la main — et une
   numérotation qui se décale est le premier signe qu'un document n'est plus
   tenu à jour. */
.lfdoc .lfx-sec {
  background:transparent; border:none; border-radius:0;
  padding:0; margin:0 0 2.25rem;
  counter-increment:lfdoc;
}
.lfdoc .lfx-sec > .lf-section-label {
  display:flex; align-items:baseline; gap:.6rem;
  font-family:var(--font-display); font-size:19px; letter-spacing:0;
  text-transform:none; color:var(--cream);
  border-bottom:1px solid var(--border); padding-bottom:.5rem; margin:0 0 1.1rem;
}
.lfdoc .lfx-sec > .lf-section-label::before {
  content:counter(lfdoc) ".";
  font-size:15px; color:var(--gold); font-family:var(--font-ui); font-weight:600;
  min-width:1.4rem;
}

/* ── Les champs, alignés comme sur un formulaire imprimé ──
   L'étiquette occupe une colonne fixe à gauche, la valeur remplit le reste :
   c'est cet alignement qui fait « document » plutôt que « pile de champs ». */
.lfdoc .lf-field {
  display:grid; grid-template-columns:180px 1fr; gap:0 1.25rem;
  align-items:center; margin-bottom:.85rem;
}
.lfdoc .lf-field > .lf-label {
  margin:0; text-align:right; font-size:11.5px; color:var(--cream2);
  letter-spacing:.02em; text-transform:none;
}
.lfdoc .lf-field > .lf-input,
.lfdoc .lf-field > .lf-color,
.lfdoc .lf-field > .lf-range { margin:0; }
/* Une case à cocher n'a pas d'étiquette à gauche : elle EST sa propre ligne,
   décalée pour rester alignée sur la colonne des valeurs. */
.lfdoc .lf-check { margin:0 0 .6rem 180px; padding-left:1.25rem; font-size:12px; }
/* Les blocs qui ne sont pas des champs (zones d'image, listes de liens,
   boutons) reprennent la même marge de gauche : la feuille garde une seule
   ligne de fuite du haut en bas. */
.lfdoc .lf-hero-drop,
.lfdoc #lf-links-editor,
.lfdoc .lf-export-btn,
.lfdoc .lf-etat,
.lfdoc .bot-hint { margin-left:180px; padding-left:1.25rem; }
.lfdoc .lf-export-btn { width:calc(100% - 180px - 1.25rem); margin-left:calc(180px + 1.25rem); padding-left:0; }
/* La grille des couleurs garde ses deux colonnes, mais dans la zone de valeur. */
.lfdoc .lfx-sec > div[style*="grid-template-columns:1fr 1fr"] { margin-left:180px; padding-left:1.25rem; }

/* Sur un écran étroit, la colonne d'étiquettes n'a plus de sens : tout repasse
   en pile, comme n'importe quel formulaire. */
@media (max-width:900px){
  .lfdoc { padding:1.5rem; }
  .lfdoc .lf-field { grid-template-columns:1fr; gap:.3rem; }
  .lfdoc .lf-field > .lf-label { text-align:left; }
  .lfdoc .lf-check,
  .lfdoc .lf-hero-drop,
  .lfdoc #lf-links-editor,
  .lfdoc .lf-etat,
  .lfdoc .bot-hint,
  .lfdoc .lfx-sec > div[style*="grid-template-columns:1fr 1fr"] { margin-left:0; padding-left:0; }
  .lfdoc .lf-export-btn { width:100%; margin-left:0; }
}

/* ── LinkFORGE · édition directe sur l'aperçu (Victor, 27/07) ──
   La fenêtre accueille les VRAIS champs, déplacés depuis le panneau : elle ne
   pose donc que l'espacement, jamais l'apparence des cases — celles-ci gardent
   la leur, et c'est ce qui garantit qu'on édite bien la même chose. */
.lfedit-body { display:flex; flex-direction:column; gap:.4rem; margin:.5rem 0 1.25rem; }
.lfedit-body .lf-link-card { background:transparent; border:none; padding:0; }
.lfedit-body .lf-link-card:hover { border-color:transparent; }
/* Dans la fenêtre, les flèches de classement n'ont plus de sens : on réordonne
   en glissant les tuiles sur l'aperçu. */
.lfedit-body .lf-lc-head > span:last-of-type { display:none; }

/* ══ LinkFORGE · L'APERÇU EST TOUT L'ÉCRAN (Victor, 27/07, second passage) ══
   Le panneau de réglages a été RETIRÉ de la vue : il est `hidden` dans le HTML
   et ses champs sont empruntés par la fenêtre d'édition. Il ne reste donc plus
   qu'une colonne, et l'aperçu la remplit.
   ⚠️ `order:-1` et les largeurs de la colonne d'appoint ont disparu avec lui :
   des règles qui ne visent plus rien finissent par être recopiées ailleurs
   « parce qu'elles existaient déjà ». */
/* ⚠️⚠️ `hidden` NE SUFFIT PAS ICI, ET C'EST UN PIÈGE CLASSIQUE. L'attribut
   `hidden` vaut `display:none` par la feuille de style du navigateur — la
   moins prioritaire de toutes. `.api-col` déclare `display:flex` : elle gagne,
   et le panneau resterait affiché à côté de l'aperçu alors que le HTML dit le
   contraire. La règle explicite est donc obligatoire, pas décorative.
   ⚠️ Le panneau reste DANS le document : `readLfState()` lit ses champs pour
   construire la page. Le retirer publierait une page vide. */
#lf-reglages[hidden] { display:none !important; }

.lf-atelier { display:flex; flex-direction:column; flex:1; min-height:0; }
.lf-preview-grand { flex:1; width:auto; min-height:0; }
.lf-preview-grand .lf-preview-zone { padding:2rem; }
/* Le téléphone grandit : c'est devenu la surface de travail, plus une vignette
   de contrôle. ⚠️ Il garde une largeur de téléphone (400 px) — l'élargir
   montrerait une page que personne ne verra jamais ainsi. */
.lf-preview-grand .lf-phone { max-width:400px; }

/* La barre d'outils : ce qui n'a pas d'ancrage sur la page. ⚠️ Les boutons
   reprennent `.lf-opt-btn` en tout point sauf la taille — même famille, même
   forme ; il ne s'agit pas d'un nouveau style de bouton. */
.lf-outils { margin-left:auto; display:flex; gap:6px; flex-shrink:0; }
.lf-outil {
  background:var(--surface3); border:1px solid var(--border2); border-radius:6px;
  color:var(--cream2); font-size:11px; padding:.35rem .7rem; cursor:pointer;
  font-family:inherit; transition:all .12s; white-space:nowrap;
}
.lf-outil:hover { border-color:var(--gold2); color:var(--gold); background:var(--gold3); }
/* Le rappel des gestes passe SOUS l'aperçu : dans l'en-tête, il entrait en
   concurrence avec la barre d'outils sur la même ligne. */
.lf-aide-bas {
  flex-shrink:0; text-align:center; font-size:10.5px; color:var(--cream3);
  padding:.5rem 1rem .75rem;
}

/* ── Le pied : Sauvegarder sur toute la largeur ──
   ⚠️ « Annuler » ne prend QUE sa place (`flex:0 0 auto`) : les deux boutons à
   parts égales donneraient le même poids à « je garde » et « je jette ». */
.lf-pied { display:flex; gap:8px; flex-shrink:0; }
.lf-pied .api-col-save { flex:1; }
.lf-pied-annuler {
  flex:0 0 auto !important; background:transparent;
  border-left:1px solid var(--border); color:var(--cream3); padding-left:1.5rem; padding-right:1.5rem;
}
.lf-pied-annuler:not(:disabled) { background:transparent; color:var(--cream2); }
.lf-pied-annuler:not(:disabled):hover { background:var(--surface3); color:var(--cream); }

/* ── La tuile « contenus en attente » CLIGNOTE (Victor, 27/07) ──
   Elle demande une action de Victor, et c'est la seule de la colonne : sans
   signal, elle se confond avec les cartes d'information qui l'entourent.
   ⚠️ Un battement LENT et de faible amplitude (2,4 s, opacité 1 → .55) : un
   clignotement franc dans une colonne qu'on regarde toute la journée devient
   pénible en dix minutes, et on finit par ne plus le voir.
   ⚠️ Uniquement quand il Y A quelque chose (`.has-pending`) : une tuile vide
   qui clignote serait un faux signal. */
.pending-tile.has-pending { animation:pendPulse 2.4s ease-in-out infinite; }
@keyframes pendPulse { 0%,100% { opacity:1 } 50% { opacity:.55 } }
/* ⚠️ Respecte le réglage système « réduire les animations » : un battement
   permanent est exactement ce que ce réglage existe pour supprimer. */
@media (prefers-reduced-motion:reduce){
  .pending-tile.has-pending { animation:none; border-color:var(--gold); }
}

/* ── Bibliothèque · une image en cours d'import (Victor, 27/07) ──
   ⚠️ La carte a exactement la taille d'une carte d'image : sans ça, la grille
   se réorganiserait à l'arrivée de chaque image et tout bougerait sous le
   curseur. Elle est simplement plus sobre — il n'y a rien à décider. */
.lib-card-attente { opacity:.85; border-style:dashed; }
.lib-thumb-attente {
  display:flex; align-items:center; justify-content:center;
  background:var(--surface3); aspect-ratio:3/4; cursor:default;
}
.lib-spin {
  width:26px; height:26px; border-radius:50%;
  border:2px solid var(--border2); border-top-color:var(--gold);
  animation:libSpin .9s linear infinite;
}
@keyframes libSpin { to { transform:rotate(360deg); } }
.lib-card-prompt { font-size:11px; color:var(--cream3); line-height:1.5; margin-top:.35rem; }
/* ⚠️ Le tourniquet est le SEUL élément animé de la vue : il dit qu'un travail
   est en cours. Sous « réduire les animations », on le fige et on garde le
   contour pointillé, qui porte la même information sans mouvement. */
@media (prefers-reduced-motion:reduce){ .lib-spin { animation:none; } }

/* ══════════════════════════════════════════════════════════
   BIBLIOTHÈQUE · LES QUATRE ACTIONS EN ICÔNES (Victor, 27/07)
   ------------------------------------------------------------
   « Mets des icônes au lieu de mots, comme ça les 4 actions passent sur une
   seule ligne. »
   ⚠️ `flex:1` sur quatre boutons de largeur égale : c'est ce qui garantit la
   ligne unique quelle que soit la largeur de la carte. Les anciens `.lib-btn`
   portaient un libellé, donc une largeur imprévisible — d'où le retour à la
   ligne dès qu'une carte était un peu étroite.
   ⚠️ `aspect-ratio` PLUTÔT QU'UNE HAUTEUR FIXE : les icônes restent centrées
   quand le zoom d'interface change (scale.js). */
.lib-ico {
  flex:1; min-width:0; aspect-ratio:1.9/1;
  display:flex; align-items:center; justify-content:center;
  font-family:inherit; font-size:14px; line-height:1; cursor:pointer;
  color:var(--cream2); background:var(--surface2);
  border:1px solid var(--border2); border-radius:6px; transition:all .12s;
}
.lib-ico:hover:not(:disabled) { border-color:var(--gold2); color:var(--cream); background:var(--surface3); }
.lib-ico.ok   { color:var(--sfw); border-color:var(--sfw); }
.lib-ico.ok:hover:not(:disabled)   { background:var(--sfw-dim); }
.lib-ico.off  { color:var(--nsfw); border-color:var(--nsfw); }
.lib-ico.off:hover:not(:disabled)  { background:var(--nsfw-dim); }
.lib-ico.plan { color:#1a1206; background:var(--gold); border-color:var(--gold); }
.lib-ico.plan:hover:not(:disabled) { background:var(--gold2); color:var(--cream); }
.lib-ico.lock.on { color:var(--gold); border-color:var(--gold2); background:var(--gold3); }
.lib-ico:disabled { opacity:.3; cursor:not-allowed; }

/* ── VERROUILLÉE ──
   ⚠️ Gris ET non éditable : « la carte devient grise et non éditable avant un
   déverrouillage ». Le gris seul serait décoratif ; c'est `disabled` sur les
   champs et sur trois des quatre boutons (posé en JS) qui tient la promesse.
   ⚠️ La vignette reste CLIQUABLE : regarder n'est pas modifier, et c'est
   précisément ce qu'on vient faire sur une image gardée pour dans trois mois. */
.lib-card-locked { border-color:var(--cream3); }
.lib-card-locked .lib-thumb { filter:grayscale(.9) brightness(.82); }
.lib-card-locked .lib-type-sel,
.lib-card-locked .lib-tags { opacity:.45; cursor:not-allowed; }

/* ── ⟳ RÉGÉNÉRER · les trois modes ── */
.lib-regen-top { display:flex; gap:12px; align-items:flex-start; }
.lib-regen-apercu {
  width:96px; flex-shrink:0; aspect-ratio:3/4; border-radius:8px;
  background-size:cover; background-position:center; background-color:var(--surface3);
  border:1px solid var(--border2);
}
.lib-regen-modes { flex:1; min-width:0; display:flex; flex-direction:column; gap:6px; }
.lib-regen-m {
  text-align:left; font-family:inherit; cursor:pointer; padding:8px 10px;
  background:var(--surface2); border:1px solid var(--border2); border-radius:8px;
  transition:all .12s; display:flex; flex-direction:column; gap:2px;
}
.lib-regen-m:hover { border-color:var(--gold2); }
.lib-regen-m b { font-size:12px; color:var(--cream); font-weight:600; }
.lib-regen-m span { font-size:11px; color:var(--cream3); line-height:1.45; }
.lib-regen-m.on { background:var(--gold3); border-color:var(--gold2); }
.lib-regen-m.on b { color:var(--gold); }
.lib-regen-cout { flex:1; font-size:11px; color:var(--gold2); }

/* ── Motifs proposés au moment de supprimer ── */
.lib-motif-chips { display:flex; flex-wrap:wrap; gap:5px; margin-top:.5rem; }
.lib-motif-chip {
  font-family:inherit; font-size:10.5px; cursor:pointer; padding:3px 9px;
  color:var(--cream3); background:var(--surface2);
  border:1px solid var(--border2); border-radius:20px; transition:all .12s;
}
.lib-motif-chip:hover { border-color:var(--gold2); color:var(--gold); }

/* ── 💬 La liste des remarques ── */
.lib-ret { background:var(--surface2); border:1px solid var(--border); border-radius:var(--r); padding:.6rem .75rem; margin-bottom:8px; }
.lib-ret-h { display:flex; align-items:center; gap:8px; margin-bottom:5px; }
.lib-ret-quand { font-size:10px; color:var(--cream3); font-variant-numeric:tabular-nums; }
.lib-ret-ia, .lib-ret-type { font-size:10px; color:var(--cream3); background:var(--surface3); border-radius:20px; padding:1px 8px; }
.lib-ret-del { margin-left:auto; background:transparent; border:1px solid var(--border2); border-radius:4px; color:var(--cream3); font-size:10px; padding:2px 7px; cursor:pointer; font-family:inherit; transition:all .12s; }
.lib-ret-del:hover { border-color:var(--nsfw); color:var(--nsfw); }
.lib-ret-motif { font-size:12px; color:var(--cream2); line-height:1.55; }

/* ══════════════════════════════════════════════════════════
   VISIONNEUSE · EN-TÊTE ET PANNEAU ⓘ (Victor, 27/07)
   ------------------------------------------------------------
   ⚠️ LE PANNEAU POUSSE L'IMAGE, IL NE LA COUVRE PAS. En surimpression il
   masquerait la moitié de ce qu'on est venu regarder — or on ouvre le prompt
   justement pour le confronter à l'image.
   ⚠️ La largeur est fixe (300 px) et la transition porte sur elle : animer
   `display` ne marche pas, et animer la marge ferait glisser l'image. */
.lib-viewer-head {
  display:flex; align-items:center; gap:10px; width:100%; margin-bottom:10px;
  background:var(--surface); border:1px solid var(--border2); border-radius:10px;
  padding:8px 12px;
}
.lib-viewer-titre { display:flex; align-items:center; gap:8px; flex:1; min-width:0; flex-wrap:wrap; }
.lib-viewer-ia { font-size:11px; color:var(--cream3); }
.lib-viewer-ico {
  flex-shrink:0; width:28px; height:28px; border-radius:7px;
  border:1px solid var(--border2); background:var(--surface2);
  color:var(--cream2); cursor:pointer; font-size:14px; line-height:1;
}
.lib-viewer-ico:hover { border-color:var(--gold2); color:var(--gold); }
.lib-viewer-ico.on { border-color:var(--gold2); color:var(--gold); background:var(--gold3); }
.lib-viewer-corps { display:flex; align-items:stretch; gap:0; max-width:92vw; }
.lib-viewer-info {
  width:0; overflow:hidden; flex-shrink:0;
  transition:width .18s ease, margin-left .18s ease;
  background:var(--surface); border-radius:10px;
}
.lib-viewer-info.open { width:300px; margin-left:12px; border:1px solid var(--border2); }
.lib-viewer-info-body { width:300px; max-height:82vh; overflow-y:auto; padding:12px 14px; box-sizing:border-box; }
.lib-info-l { display:flex; justify-content:space-between; gap:10px; font-size:11px; padding:4px 0; border-bottom:1px solid var(--border); }
.lib-info-l span { color:var(--cream3); }
.lib-info-l b { color:var(--cream2); font-weight:500; text-align:right; }
.lib-info-l b.mono { font-family:monospace; font-size:10px; word-break:break-all; }
.lib-info-titre { font-size:10px; letter-spacing:.2em; text-transform:uppercase; color:var(--gold2); margin:.9rem 0 .5rem; }
.lib-info-prompt { font-size:11px; color:var(--cream2); line-height:1.6; white-space:pre-wrap; word-break:break-word; background:var(--surface2); border:1px solid var(--border); border-radius:6px; padding:.55rem .65rem; }
.lib-info-vide { font-size:11px; color:var(--cream3); line-height:1.55; }
.lib-info-copy { width:100%; margin-top:8px; background:var(--surface3); border:1px solid var(--border2); border-radius:6px; color:var(--cream2); font-size:11px; padding:.45rem; cursor:pointer; font-family:inherit; transition:all .12s; }
.lib-info-copy:hover { border-color:var(--gold2); color:var(--gold); }
/* Sous « réduire les animations », le panneau s'ouvre d'un coup. */
@media (prefers-reduced-motion:reduce){ .lib-viewer-info { transition:none; } }

/* ══════════════════════════════════════════════════════════
   GÉNÉRATEUR · LA BOÎTE DE DIALOGUE → OPENART (Victor, 27/07)
   ------------------------------------------------------------
   « On sélectionne au milieu de l'écran, une boîte de dialogue est présente,
   je dis ce que je souhaite, et je clique sur générer. »
   ⚠️ La carte est CENTRÉE et bornée en largeur : une zone de saisie étalée sur
   1 400 px demande de balayer la ligne des yeux pour la relire. 560 px, c'est
   la largeur d'un paragraphe qu'on lit d'un coup.
   ⚠️ Elle reprend les classes du socle pour ses champs (`.modal-label`,
   `.modal-textarea`, `.modal-aide`) : ce ne sont pas des champs d'un nouveau
   genre, seulement des champs posés ailleurs. */
.goa-wrap { flex:1; min-height:0; overflow-y:auto; display:flex; padding:2rem 1.5rem; }
.goa-card {
  width:100%; max-width:560px; margin:auto;
  background:var(--surface2); border:1px solid var(--border); border-radius:var(--r);
  padding:1.5rem;
}
.goa-card-centre { text-align:center; }
.goa-glyph { font-size:34px; color:var(--gold); margin-bottom:.5rem; }
.goa-titre {
  font-family:var(--font-display); font-size:16px; color:var(--gold);
  margin-bottom:1rem;
}
.goa-texte { font-size:12.5px; color:var(--cream2); line-height:1.7; margin-bottom:1.5rem; text-align:left; }

/* Combien d'images. ⚠️ Un segment, pas un menu déroulant : trois valeurs qui se
   lisent d'un coup d'œil et se cliquent d'un geste — un menu demanderait deux
   clics pour un choix à trois options. */
.goa-ligne { display:flex; align-items:center; gap:12px; margin-top:1rem; }
.goa-ligne-lbl { font-size:11px; color:var(--cream3); text-transform:uppercase; letter-spacing:.15em; }
.goa-nb { display:flex; gap:6px; }
.goa-nb-b {
  min-width:42px; padding:.4rem .7rem; font-family:inherit; font-size:12px; cursor:pointer;
  color:var(--cream2); background:var(--surface3);
  border:1px solid var(--border2); border-radius:6px; transition:all .12s;
}
.goa-nb-b:hover { border-color:var(--gold2); color:var(--gold); }
.goa-nb-b.on { background:var(--gold3); border-color:var(--gold2); color:var(--gold); font-weight:600; }

/* La cible et le prix, juste au-dessus du bouton : les deux choses qu'on veut
   relire avant de dépenser. */
.goa-pied {
  display:flex; align-items:center; justify-content:space-between; gap:12px;
  margin:1.25rem 0 .6rem; font-size:11.5px;
}
.goa-cible { color:var(--cream2); }
.goa-cible-ko { color:var(--warn); }
.goa-cout { color:var(--gold2); white-space:nowrap; }
/* ⚠️ Le bouton est CENTRÉ ici, alors que `.gen-btn` de la colonne aligne à
   gauche : dans une colonne on lit une liste d'actions, au centre d'une carte
   on lit une conclusion. */
.goa-go { justify-content:center; padding:.9rem 1rem; font-size:14px; }
.goa-note { margin-top:.7rem; font-size:11px; color:var(--cream3); line-height:1.55; text-align:center; }

/* La tuile « Préparer la semaine » — même famille que les autres types de
   contenu, couleur de l'or parce que c'est la seule qui fait travailler les six
   agents d'un coup. */
.mode-btn.sel-semaine { border-left-color:var(--gold); background:var(--gold3); border-color:var(--gold2); }

/* ── 🧪 Test — 1 image, au-dessus de l'IA sélectionnée ──
   ⚠️ Volontairement plus DISCRET que « Générer » : c'est une vérification, pas
   une production. Il reprend `.svc-refresh-btn` (le bouton d'action de colonne)
   plutôt que d'inventer une troisième forme de bouton dans la même colonne. */
.gen-test-btn {
  width:100%; display:flex; align-items:center; gap:8px;
  background:var(--surface2); border:1px solid var(--border2); border-radius:var(--r);
  padding:.55rem .75rem; font-size:12px; color:var(--cream2);
  cursor:pointer; font-family:inherit; transition:all .15s;
}
.gen-test-btn:hover { background:var(--surface3); border-color:var(--gold2); color:var(--gold); }

/* ══════════════════════════════════════════════════════════
   LA FENÊTRE DE CONFIRMATION DE DÉPENSE (Victor, 27/07 (2))
   ------------------------------------------------------------
   ⚠️ LE MONTANT EST LA SEULE CHOSE QU'ON DOIT LIRE SANS CHERCHER. Il est donc
   dans un bloc à lui, encadré, avec la taille la plus grande de la fenêtre —
   pas dans une phrase. C'est tout ce que `confirm()` ne savait pas faire.
   ⚠️ L'unité (€ Claude / crédits OpenArt) est écrite SOUS le montant : un
   nombre sans unité ne veut rien dire, et les deux monnaies de l'application
   n'ont pas du tout le même ordre de grandeur. */
.dep-quoi { font-size:12.5px; color:var(--cream2); line-height:1.7; }
.dep-ia { margin-top:.5rem; font-size:12px; color:var(--gold); }
.dep-cout {
  margin-top:1rem; padding:.85rem 1rem; border-radius:var(--r);
  background:var(--gold3); border:1px solid var(--gold2);
  display:flex; flex-direction:column; gap:4px;
}
.dep-cout b { font-size:16px; color:var(--gold); font-weight:600; letter-spacing:.01em; }
.dep-cout span { font-size:11px; color:var(--cream2); line-height:1.55; }
/* Gratuit = pas de clé Claude : le vrai coût est du TEMPS, et l'or annoncerait
   une dépense qui n'existe pas. */
.dep-cout.dep-gratuit { background:var(--sfw-dim); border-color:var(--sfw); }
.dep-cout.dep-gratuit b { color:var(--sfw); }
.dep-detail { margin-top:.75rem; font-size:11.5px; color:var(--cream3); line-height:1.6; }
