Refonte de l'interface : direction « Sober Tech »

Reprise du design d'après la maquette fournie, sans changer d'architecture :
l'UI reste du HTML/CSS/JS assemblé dans le binaire (voir plus bas).

Charte
- Palette ardoise + sauge en remplacement des bruns « Terre ». Deux variantes :
  claire par défaut (celle de la maquette) et « Deep Dark » (fond #0F172A,
  cartes #1E293B), à un clic depuis l'en-tête. Une variable --on-accent porte ce
  qui s'écrit sur les aplats de sauge, pour que le contraste tienne dans les
  deux variantes.
- Typographie Inter (interface) + JetBrains Mono (code, chiffres, chemins), en
  sous-ensemble latin, embarquées comme l'étaient Bricolage et IBM Plex Mono —
  toujours aucune requête vers un service de polices.
- Boutons sans contour, fond au survol, léger enfoncement au clic.
- Coque à plat : plus de cartes flottantes séparées par une gouttière, la barre
  latérale est collée au bord et le fil occupe tout le reste.

Coque
- Barre d'en-tête : titre de la discussion + sélecteur de modèle. Changer de
  preset demandait d'ouvrir la barre latérale et de descendre jusqu'aux
  presets ; c'est désormais une liste déroulante, toujours visible.
- Barre latérale en trois zones — marque, contenu défilant, moniteur machine —
  et escamotable d'un clic sur grand écran. Les discussions y passent au
  premier plan, avec une recherche textuelle (filtrage côté client : la liste
  est déjà chargée, sans les messages) ; les réglages descendent sous un repli
  unique, d'où openDetails(), qui déplie aussi les parents.
- Moniteur machine en pied de colonne : une jauge par ressource (charge GPU,
  puis VRAM et température en détail, mémoire vive), sauge jusqu'à 75 %, ambre
  puis rouille quand la machine sature. Il remplace la section « Machine », qui
  disait la même chose en plus verbeux et en plus loin.
- L'option « barre latérale escamotable » disparaît d'Apparence : elle faisait
  de la barre un tiroir posé SUR la conversation, là où le bouton d'en-tête
  l'escamote pour de bon. Deux mécanismes concurrents pour la même intention.

Fil et saisie
- La réponse de l'IA redevient une carte, la question un aplat de sauge : sur un
  fond de fil coloré, du texte nu flottait sans ancrage.
- Saisie en pilule, ses outils (joindre, fichiers, envoyer) dans la barre
  elle-même. L'envoi est une pastille ronde à flèche ; le mot « Envoyer » reste
  porté par aria-label et l'infobulle.

Corrections croisées
- L'heure d'une discussion prenait la moitié de la ligne (flex:1 hérité de
  `.preset>span`), le titre était tronqué au premier mot.
- Une liste déroulante posée directement dans une ligne de modale débordait
  sous son intitulé quand sa valeur était longue (chemin de destination).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LPyFxVHNAN9u5pVzSYMwjd
This commit is contained in:
Claude committed 2026-08-16 16:17:59 +00:00
1 parent 7310c84129
commit f99e5b59b8
16 files changed
+1160 -255

No files matched your search

+280 -51
View File
@@ -1,42 +1,52 @@
/* ===== Polices =====================================================
Bricolage Grotesque (titres et texte) + IBM Plex Mono (chiffres, chemins,
code). Servies par Loki (/fonts/, embarquées dans le binaire) et NON par
Google Fonts : une instance locale ou coupée d'internet doit s'afficher
correctement, sans qu'un tiers apprenne qui consulte l'interface.
Inter (interface) + JetBrains Mono (chiffres, chemins, code). Servies par Loki
(/fonts/, embarquées dans le binaire) et NON par Google Fonts : une instance
locale ou coupée d'internet doit s'afficher correctement, sans qu'un tiers
apprenne qui consulte l'interface.
Sous-ensemble LATIN uniquement (~48 + ~31 Ko) : il couvre le français, y
compris œ et les guillemets. Les symboles de l'UI (↻ ⌫ ☰ 🗑) n'ont jamais été
dessinés par la police d'interface — ils viennent du système, ici comme avant.
swap : le texte s'affiche tout de suite dans la police de repli, puis bascule
— jamais de blanc au chargement. */
@font-face{font-family:'Bricolage Grotesque';src:url('/fonts/bricolage.woff2') format('woff2');font-weight:400 700;font-display:swap;font-style:normal}
@font-face{font-family:'IBM Plex Mono';src:url('/fonts/plexmono-400.woff2') format('woff2');font-weight:400;font-display:swap;font-style:normal}
@font-face{font-family:'IBM Plex Mono';src:url('/fonts/plexmono-500.woff2') format('woff2');font-weight:500;font-display:swap;font-style:normal}
@font-face{font-family:'Inter';src:url('/fonts/inter.woff2') format('woff2');font-weight:100 900;font-display:swap;font-style:normal}
@font-face{font-family:'JetBrains Mono';src:url('/fonts/jetbrainsmono.woff2') format('woff2');font-weight:100 800;font-display:swap;font-style:normal}
/* ===== Palette « Terre » ===========================================
Bruns chauds, vert sauge et argile. Deux variantes — claire par défaut,
sombre via data-theme="dark" sur <html>. Toutes les couleurs de l'UI passent
par ces variables ; rien d'autre ne code une couleur en dur.
/* ===== Palette « Sober Tech » ======================================
Ardoise et sauge. Deux variantes — claire par défaut (celle de la maquette),
sombre « Deep Dark » via data-theme="dark" sur <html>. Toutes les couleurs de
l'UI passent par ces variables ; rien d'autre ne code une couleur en dur.
Rôles : --accent = sauge (sélection, marche, liens) · --warn = argile
(chiffres, mesures, avertissements) · --err = terre cuite (arrêt, danger).
--row-bg / --code-bg sont les deux fonds encastrés d'un panneau. */
Rôles : --accent = sauge (sélection, marche, liens, bulle utilisateur) ·
--warn = ambre (chiffres, mesures, avertissements) · --err = rouille (arrêt,
danger). --row-bg / --code-bg sont les deux fonds encastrés d'un panneau.
--on-accent : ce qui s'écrit SUR un aplat de sauge. Il change avec la
variante — blanc sur la sauge foncée du thème clair, ardoise sur la sauge
claire du thème sombre — sinon un des deux thèmes écrit du texte à 2:1 de
contraste sur la bulle utilisateur et le bouton d'envoi. */
:root{color-scheme:light;
--font:'Bricolage Grotesque',-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif;
--mono:'IBM Plex Mono',ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
--bg:#f3efe8;--panel:#ffffff;--bubble:#efe9df;--border:rgba(44,38,34,.12);
--text:#2c2622;--dim:#7a7069;
--accent:#5f7f52;--accent-bg:rgba(95,127,82,.12);
--row-bg:#f7f4ee;--row-bg-on:#f4f1ea;
--ok:#5f7f52;--warn:#a9662f;--err:#c05a3f;--mag:#a9662f;
--code-bg:#f7f4ee;--danger-bg:#f8e9e4;--warn-bg:#f6efe4;
--add:#5f7f52;--add-bg:rgba(95,127,82,.12);--del:#c05a3f;--del-bg:rgba(192,90,63,.12);
--shadow:0 1px 2px rgba(44,38,34,.06)}
--font:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif;
--mono:'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
--bg:#eceff0;--panel:#ffffff;--bubble:#f6f8f7;--border:rgba(30,41,59,.11);
--text:#1e293b;--dim:#64748b;
--accent:#5e7f5a;--accent-bg:rgba(94,127,90,.11);--on-accent:#ffffff;--on-err:#ffffff;
--accent-soft:#8fab8b;
--side:#fafbfa;--row-bg:#f5f7f6;--row-bg-on:#eef2f0;
--ok:#5e7f5a;--warn:#a86a2a;--err:#c0503f;--mag:#a86a2a;
--code-bg:#f4f6f5;--danger-bg:#faeae7;--warn-bg:#f7f0e5;
--add:#5e7f5a;--add-bg:rgba(94,127,90,.11);--del:#c0503f;--del-bg:rgba(192,80,63,.12);
--shadow:0 1px 2px rgba(15,23,42,.05);--shadow-lift:0 4px 16px rgba(15,23,42,.08)}
/* « Deep Dark » : fond #0F172A, cartes #1E293B. */
[data-theme="dark"]{color-scheme:dark;
--bg:#211d1a;--panel:#2b2622;--bubble:#38312a;--border:rgba(236,229,221,.12);
--text:#ece5dd;--dim:#a99d92;
--accent:#9ab68c;--accent-bg:rgba(154,182,140,.15);
--row-bg:#332d28;--row-bg-on:#38312a;
--ok:#9ab68c;--warn:#d2a07a;--err:#c9705c;--mag:#d2a07a;
--code-bg:#211d1a;--danger-bg:#3a2621;--warn-bg:#332b23;
--add:#9ab68c;--add-bg:rgba(154,182,140,.15);--del:#c9705c;--del-bg:rgba(201,112,92,.15);
--shadow:none}
--bg:#0f172a;--panel:#1e293b;--bubble:#1e293b;--border:rgba(148,163,184,.17);
--text:#e2e8f0;--dim:#94a3b8;
--accent:#8fb08c;--accent-bg:rgba(143,176,140,.16);--on-accent:#0f172a;--on-err:#0f172a;
--accent-soft:#6d8f6a;
--side:#0d1424;--row-bg:#172033;--row-bg-on:#243349;
--ok:#8fb08c;--warn:#d9a55f;--err:#e0705c;--mag:#d9a55f;
--code-bg:#131c2f;--danger-bg:#3a1f1c;--warn-bg:#33291b;
--add:#8fb08c;--add-bg:rgba(143,176,140,.16);--del:#e0705c;--del-bg:rgba(224,112,92,.16);
--shadow:none;--shadow-lift:0 4px 16px rgba(0,0,0,.35)}
/* Safari iOS peint un rectangle gris/noir sur TOUT élément touché (bouton, lien,
résumé de section, bulle repliable…). C'est un retour tactile par défaut, hors
de notre charte et impossible à styler : on le coupe. Nos états :active/:focus
@@ -465,7 +475,6 @@ button:disabled{opacity:.5;cursor:not-allowed}
@media (max-width:720px){
.side{padding:calc(10px + env(safe-area-inset-top)) 12px calc(10px + env(safe-area-inset-bottom)) calc(12px + env(safe-area-inset-left))}
}
[data-hide-side="1"] .side{padding:calc(10px + env(safe-area-inset-top)) 12px calc(10px + env(safe-area-inset-bottom)) calc(12px + env(safe-area-inset-left))}
.sidehead{margin-bottom:10px}
/* --- En-tête de section --- */
@@ -601,24 +610,10 @@ textarea.sctl{min-height:76px;resize:vertical;line-height:1.5}
data-hide-reasoning : masque les bulles de raisonnement
data-hide-tools : masque les bulles d'appel d'outil
data-fold-tools : les garde repliées (déplia­bles au clic)
data-hide-side : barre latérale en tiroir, même sur ordinateur
Quand raisonnement et outils sont masqués, l'activité reste signalée par
l'indicateur de frappe (.msg.typing) maintenu pendant tout le tour. */
[data-hide-reasoning="1"] #chat .msg.reasoning{display:none}
[data-hide-tools="1"] #chat .msg.tool{display:none}
/* Barre latérale en tiroir sur TOUTES les tailles d'écran : mêmes règles que
la version mobile, activées par l'attribut au lieu de la largeur. */
[data-hide-side="1"] .side{position:fixed;inset:0 auto 0 0;width:80%;max-width:320px;z-index:15;transform:translateX(-100%);transition:transform .2s;background:var(--bg);padding-top:calc(16px + env(safe-area-inset-top));padding-left:calc(16px + env(safe-area-inset-left));border-right:1px solid var(--border)}
@media (min-width:721px){[data-hide-side="1"] .side{width:380px;max-width:380px}}
/* Le bouton ☰ est posé au-dessus du fil : on dégage le haut de la
conversation pour qu'il ne recouvre aucun message. */
@media (min-width:721px){html[data-hide-side="1"] #chat{padding-top:60px}}
[data-hide-side="1"] .side.open{transform:translateX(0)}
[data-hide-side="1"] #menubtn{display:block}
[data-hide-side="1"] #lc-chip:not([hidden]){display:block}
[data-hide-side="1"] #backdrop.open{display:block}
[data-hide-side="1"] body.drawer-open #menubtn,[data-hide-side="1"] body.drawer-open #lc-chip{display:none}
[data-hide-side="1"] .main{width:100%}
/* Hauteur animée en JS (mesure scrollHeight → 0) plutôt que grid 1fr→0fr qui
peut « dépasser ». overflow:hidden clippe le contenu pendant tout le repli.
width revient à auto SANS délai à l'ouverture (pas de saut), et passe à 0
@@ -1290,10 +1285,10 @@ button:hover{border-color:var(--dim);color:var(--text)}
/* Action principale en sauge : c'est la couleur d'action de la charte, et elle
se distingue enfin du texte (le bouton reprenait --text, donc un brun sombre
indistinct du reste). L'arrêt, lui, reste en terre cuite. */
#send{background:var(--accent);border-color:var(--accent);color:#fff}
#send:hover:not(:disabled){background:var(--accent);border-color:var(--accent);color:#fff;opacity:.88}
#stop{background:var(--err);border-color:var(--err);color:#fff}
#stop:hover{background:var(--err);border-color:var(--err);color:#fff;opacity:.88}
#send{background:var(--accent);border-color:var(--accent);color:var(--on-accent)}
#send:hover:not(:disabled){background:var(--accent);border-color:var(--accent);color:var(--on-accent);opacity:.88}
#stop{background:var(--err);border-color:var(--err);color:var(--on-err)}
#stop:hover{background:var(--err);border-color:var(--err);color:var(--on-err);opacity:.88}
/* --- Terre : largeur de lecture --------------------------------------
La mesure était plafonnée à 760 px et centrée sur la fenêtre : sur un écran
@@ -1441,3 +1436,237 @@ html[data-files="1"] #files-btn{color:var(--accent)}
#files{background:var(--panel);border-left-color:transparent;border-radius:20px;
box-shadow:var(--shadow);margin:0}
}
/* =====================================================================
===== Refonte « Sober Tech » ========================================
=====================================================================
Dernière section du fichier, donc dernier mot de la cascade : elle porte la
coque de la refonte (barre d'en-tête, zones de la barre latérale, moniteur
machine) et redresse les quelques règles historiques qu'elle contredit.
Trois principes, tirés de la maquette :
1. Le fil est la seule chose qui compte : tout le reste est atténué, sans
cadre superflu, et la barre latérale s'escamote d'un clic.
2. Les données machine sont TOUJOURS visibles mais jamais au premier plan —
un pied de barre latérale, trois jauges, pas un tableau.
3. Aucun contour permanent sur les boutons : l'affordance vient du survol
(fond qui apparaît, léger enfoncement au clic).
===================================================================== */
/* --- Coque ---------------------------------------------------------- */
.side{background:var(--side);display:flex;flex-direction:column;gap:0;padding:0;overflow:hidden}
/* Les trois zones de la barre : en-tête fixe, contenu qui défile, moniteur
posé en pied. Sans ça, la liste des discussions poussait les jauges hors de
l'écran dès la dixième discussion. */
.sidehead{flex:none;display:flex;align-items:center;gap:8px;margin:0;
padding:14px 14px 10px;padding-top:calc(14px + env(safe-area-inset-top))}
#side-scroll{flex:1;min-height:0;overflow-y:auto;padding:0 12px 12px;
scrollbar-gutter:stable;display:flex;flex-direction:column;gap:2px}
#side-perf{flex:none;border-top:1px solid var(--border);padding:12px 14px;
padding-bottom:calc(12px + env(safe-area-inset-bottom))}
@media (max-width:720px){
.side{padding:0}
#side-scroll{padding-left:calc(12px + env(safe-area-inset-left))}
.sidehead{padding-left:calc(14px + env(safe-area-inset-left))}
}
/* Escamotage sur grand écran : la barre disparaît, le fil reprend la place.
L'état est persisté (localStorage) et posé sur <html> avant le rendu, comme
le thème — sinon la barre apparaît puis se referme sous les yeux. */
@media (min-width:721px){
html[data-side="0"] .side{display:none}
html[data-side="0"] #sidetoggle svg{transform:none}
}
#sidetoggle svg{transform:scaleX(-1)}
/* --- Barre d'en-tête du fil ----------------------------------------- */
/* Elle porte ce qui décrit la conversation en cours : de quoi on parle, et avec
QUEL modèle. Le choix du modèle vivait au fond de la barre latérale, dans une
liste de presets — trois clics et un défilement pour une action qu'on fait
plusieurs fois par session. */
#topbar{flex:none;display:flex;align-items:center;gap:10px;padding:10px 16px;
padding-top:calc(10px + env(safe-area-inset-top));
border-bottom:1px solid var(--border);background:var(--bg);z-index:5}
#topbar-title{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
font-size:13.5px;font-weight:500;color:var(--text)}
#topbar .iconbtn{width:30px;height:30px;flex:none;display:inline-flex;align-items:center;
justify-content:center;border:0;border-radius:9px;background:transparent;color:var(--dim);
cursor:pointer;margin:0;padding:0;transition:background .15s,color .15s,transform .1s}
#topbar .iconbtn:hover{background:var(--row-bg-on);color:var(--text)}
#topbar .iconbtn:active{transform:scale(.92)}
#topbar .iconbtn svg{display:block}
/* Sélecteur de modèle : le libellé n'est PAS une étiquette flottante mais le nom
du preset actif, lisible d'un coup d'œil. */
#model-switch{max-width:230px;font:inherit;font-size:12.5px;color:var(--text);
background:var(--panel);border:1px solid var(--border);border-radius:999px;
padding:6px 30px 6px 12px;cursor:pointer;appearance:none;
background-image:linear-gradient(45deg,transparent 50%,var(--dim) 50%),linear-gradient(135deg,var(--dim) 50%,transparent 50%);
background-position:calc(100% - 15px) 52%,calc(100% - 11px) 52%;
background-size:4px 4px,4px 4px;background-repeat:no-repeat}
#model-switch:hover{border-color:var(--accent)}
#model-switch:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
@media (max-width:720px){
#topbar{padding-left:56px} /* la place du bouton ☰, en position fixe */
#model-switch{max-width:130px}
}
/* --- Discussions ----------------------------------------------------- */
/* La liste EST la barre latérale : titre à gauche, heure à droite, une ligne.
Le reste (réglages) descend sous un unique repli. */
#conv-search{width:100%;font:inherit;font-size:12.5px;color:var(--text);
background:var(--panel);border:1px solid var(--border);border-radius:10px;
padding:7px 11px;margin:0 0 6px}
#conv-search::placeholder{color:var(--dim)}
#conv-search:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-bg)}
.side .preset{padding:8px 11px;margin:1px 0;gap:10px}
.side .preset .preset-name{font-size:13px;font-weight:450;white-space:nowrap;overflow:hidden;
text-overflow:ellipsis;word-break:normal;overflow-wrap:normal}
.side .preset.active .preset-name{font-weight:550}
/* Heure de la dernière activité : à DROITE du titre, sur la même ligne, en
monospace pour que les colonnes s'alignent d'une ligne à l'autre.
⚠️ Sélecteur en TROIS classes : `.preset>span` (plus haut) pose flex:1 sur
tout span enfant d'une ligne — l'heure prenait donc la MOITIÉ de la ligne et
le titre était tronqué au premier mot. */
.side .preset .conv-when{flex:none;font-family:var(--mono);font-size:10.5px;
color:var(--dim);letter-spacing:-.01em;overflow:visible}
.side .preset:hover .conv-when{opacity:0}
.conv-empty{font-size:12px;color:var(--dim);padding:8px 11px}
/* Repli des réglages : tout ce qui n'est pas la conversation tient sous cette
seule ligne, pour que la barre latérale de la maquette (discussions +
machine) reste la vue par défaut. */
#side-settings{margin-top:8px;border-top:1px solid var(--border)}
#side-settings>summary{height:38px}
#side-settings>.sidegroup{margin-top:2px}
/* --- Moniteur machine (pied de barre latérale) ----------------------- */
.perf-head{display:flex;align-items:center;gap:8px;margin-bottom:9px;
font-family:var(--mono);font-size:10px;text-transform:uppercase;letter-spacing:.1em;color:var(--dim)}
.perf-head .perf-toggle{margin-left:auto;border:0;background:transparent;color:var(--dim);
padding:0 2px;margin:0;font-size:11px;cursor:pointer;line-height:1}
.perf-row{margin:0 0 9px}
.perf-row:last-child{margin-bottom:0}
.perf-l{display:flex;align-items:baseline;gap:8px;font-size:11.5px;color:var(--dim);margin-bottom:4px}
.perf-l b{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
font-weight:450;color:var(--text)}
.perf-l .perf-pct{font-family:var(--mono);font-size:11px;color:var(--text);font-variant-numeric:tabular-nums}
/* La jauge : 4 px, coins ronds, remplissage sauge. Elle passe à l'ambre puis au
rouille quand la machine sature — c'est là que l'information devient utile. */
.perf-track{height:4px;border-radius:999px;background:var(--row-bg-on);overflow:hidden}
.perf-fill{height:100%;width:0;border-radius:999px;background:var(--accent-soft);
transition:width .4s ease,background .4s ease}
.perf-fill.hot{background:var(--warn)}
.perf-fill.max{background:var(--err)}
.perf-sub{margin-top:3px;font-family:var(--mono);font-size:9.5px;color:var(--dim);opacity:.85}
html[data-perf="0"] .perf-body{display:none}
/* --- Boutons : sans contour, micro-interaction au survol ------------- */
button{border-color:transparent;background:var(--row-bg);border-radius:9px;
transition:background .15s ease,color .15s ease,transform .08s ease}
button:hover{background:var(--row-bg-on);border-color:transparent;color:var(--text)}
button:active:not(:disabled){transform:translateY(1px)}
.side .preset,.slist,#conv-search{transition:background .15s ease,border-color .15s ease}
/* --- Fil : la réponse redevient une carte --------------------------- */
/* La maquette encadre la réponse de l'IA : sur un fond de fil coloré, du texte
nu flotte sans ancrage, et la frontière entre deux réponses se perd. */
#chat{padding-top:22px}
#chat .msg.assistant{background:var(--bubble);border:1px solid var(--border);
border-radius:16px;padding:14px 18px;max-width:100%;box-shadow:var(--shadow)}
#chat .msg.assistant.errmsg{background:var(--danger-bg);border-color:color-mix(in srgb,var(--err) 45%,transparent)}
/* Question : aplat de sauge, coin bas-droit rentré. --on-accent garantit le
contraste dans les DEUX variantes. */
#chat .msg.user{background:var(--accent);color:var(--on-accent);border-radius:18px 18px 6px 18px;
padding:11px 16px;max-width:75%}
#chat .msg.user .label{color:var(--on-accent);opacity:.75}
#chat .msg.user .body a{color:var(--on-accent);text-decoration:underline}
#chat .msg.user code{background:rgba(255,255,255,.18);color:inherit}
[data-theme="dark"] #chat .msg.user code{background:rgba(15,23,42,.22)}
/* Les pastilles de fichier d'une question sont posées SUR l'aplat de sauge :
sans fond propre, elles s'y fondaient. */
#chat .msg.user .msg-files .chip-file{background:var(--panel);color:var(--text);border-color:transparent}
/* --- Saisie : la pilule de la maquette ------------------------------ */
/* Le bouton d'envoi devient une pastille ronde à flèche : c'est la forme de la
maquette, et elle laisse toute la largeur au texte. Le mot « Envoyer » reste
porté par aria-label et par l'infobulle — l'action n'est pas moins nommée
pour un lecteur d'écran, elle l'est moins à l'écran. */
#send,#stop{width:34px;height:34px;min-height:34px;padding:0;border-radius:999px;
display:inline-flex;align-items:center;justify-content:center;flex:none}
#send svg,#stop svg{display:block}
#send:hover:not(:disabled){opacity:1;transform:none;background:color-mix(in srgb,var(--accent) 85%,var(--text))}
#send:active:not(:disabled){transform:scale(.92)}
#stop:hover{opacity:1;background:color-mix(in srgb,var(--err) 85%,var(--text))}
/* Les outils de la saisie (joindre, fichiers, envoyer) montent DANS la pilule,
à droite du texte : c'est là qu'on les cherche, et le pied de carte redevient
ce qu'il doit être — une jauge de contexte, rien d'autre. */
#inputbar{align-items:flex-end;padding:8px 8px 8px 18px;gap:4px;box-shadow:var(--shadow)}
#composer:focus-within #inputbar,#composer:focus-within #ctxmeta{border-color:var(--accent-soft)}
#inputbar #attach,#inputbar #files-btn{width:34px;height:34px;min-height:34px;border-radius:999px;
align-self:center}
#inputbar #attach svg,#inputbar #files-btn svg{width:16px;height:16px}
#ctxmeta{padding-bottom:12px}
#ctxtrack{border-radius:999px}
/* --- Coque à plat ---------------------------------------------------
La direction précédente posait deux CARTES flottantes séparées par une
gouttière ; la maquette est bord à bord : barre latérale collée au bord,
simple filet de séparation, fil qui occupe tout le reste. On neutralise donc
ici les règles de coque de la section « Terre » plutôt que de les réécrire
plus haut — la cascade fait le tri, et l'historique reste lisible. */
@media (min-width:721px){
body{padding:0;gap:0}
html[data-pwa="1"] body{padding-top:env(safe-area-inset-top)}
.side{width:300px;border-right:1px solid var(--border);border-radius:0;
box-shadow:none;background:var(--side);overflow:hidden}
/* Dans la barre, plus de cartes : des lignes posées sur le fond. Deux niveaux
de cadre imbriqués (carte de section + carte de liste) pour une liste de
titres, c'était deux de trop. */
#side-scroll>details,#side-scroll>.sidegroup{background:transparent;box-shadow:none;
border-radius:0;padding:0;margin:0}
#side-scroll>details>summary{height:34px}
.side .slist{background:transparent;border-color:transparent;border-radius:12px}
.side #side-settings .slist{background:var(--row-bg);border-radius:12px}
/* Le fil n'est plus une carte : il EST la page. */
.main{background:transparent;border-radius:0;box-shadow:none;overflow:visible}
#composer{background:linear-gradient(to bottom,transparent 0,var(--bg) var(--fade))}
#inputbar,#ctxmeta{background:var(--panel);border-color:var(--border)}
#input{background:transparent}
}
/* Ligne de discussion : les actions (renommer, supprimer) sont posées EN
SURIMPRESSION à droite plutôt qu'en flux. En flux, elles réservaient leur
largeur en permanence — invisibles mais présentes — et le titre était tronqué
au tiers de la place disponible. */
.side .preset{position:relative;border-radius:12px}
.side .preset .conv-acts{position:absolute;right:6px;top:50%;transform:translateY(-50%);
margin:0;background:var(--row-bg-on);border-radius:9px;padding:1px}
.side .preset.active .conv-acts{background:var(--accent-bg)}
.side .preset .preset-info{overflow:hidden}
/* La sélection est un aplat, pas un cadre : un liseré autour d'une ligne de
liste alourdit là où un fond suffit à dire « c'est celle-ci ». */
.side .preset.active{background:var(--accent-bg);border-color:transparent}
.side .preset.active .preset-name{color:var(--text)}
/* --- Téléphone : un seul bouton de barre latérale -------------------- */
/* Le ☰ flottant ouvre déjà le tiroir ; garder EN PLUS l'icône d'escamotage de
l'en-tête donnait deux boutons voisins pour la même action, et grignotait la
place du titre sur un écran qui n'en a pas. */
@media (max-width:720px){
/* Sélecteur en DEUX id : `#topbar .iconbtn` (plus haut) pose déjà un display,
et il l'emporterait sur un simple `#sidetoggle`. */
#topbar #sidetoggle{display:none}
#topbar{padding-left:52px;padding-right:10px;gap:6px}
#topbar-title{font-size:13px}
}
/* Le ☰ suit la charte des boutons : pas de contour, un fond qui apparaît au
survol. Il flotte au-dessus de l'en-tête, il doit donc en reprendre le fond
pour ne pas laisser voir le fil défiler dessous. */
#menubtn{background:var(--bg);border:0;color:var(--dim);border-radius:10px;
width:34px;height:34px;font-size:16px;top:calc(11px + env(safe-area-inset-top));left:9px}
#menubtn:hover{background:var(--row-bg-on);color:var(--text)}
#lc-chip{border-radius:10px;height:34px;top:calc(11px + env(safe-area-inset-top));left:calc(50px + env(safe-area-inset-left))}
/* Jauges : la piste doit se VOIR, sinon une barre presque vide ressemble à un
trait souligné sous l'intitulé. */
.perf-track{background:color-mix(in srgb,var(--text) 9%,transparent)}
/* Une liste déroulante posée directement dans une ligne de modale n'a pas de
garde-fou de largeur (contrairement à .pe-row-c) : une valeur très longue —
un chemin de destination, par exemple — débordait sous l'intitulé. */
.pe-row>.pe-selc{max-width:62%}