/* SPDX-License-Identifier: LicenseRef-CMSD-1.0
 * Copyright (c) 2026 CyberMind — Gerald Kerma <devel@cybermind.fr>
 *
 * SecuBox-Deb :: BBS — feuille de style de l'interface membre.
 *
 * EXTRAITE TELLE QUELLE DE www/mockup.html, qui reste la reference visuelle.
 * Toute divergence entre ce fichier et la maquette est un defaut : la maquette
 * a ete validee, pas une reinterpretation.
 *
 * Ce n'est PAS la charte des panneaux d'administration. Celle-ci est le
 * terminal cyan (Courier Prime, #00d4ff) decrit dans WEBUI-PANEL-GUIDELINES.md
 * et appliquee dans www/bbs/index.html. Deux publics, deux langages :
 * les membres lisent, le sysop pilote.
 */
/* Polices AletheiaVox auto-hebergees — la CSP (default-src 'self') interdit les
   polices externes, donc on les sert depuis /static/fonts. Variables : une seule
   fonte couvre toute la plage de graisses. */
@font-face{font-family:"Cinzel";src:url("/static/fonts/cinzel.woff2") format("woff2");font-weight:400 700;font-display:swap}
@font-face{font-family:"Inter";src:url("/static/fonts/inter.woff2") format("woff2");font-weight:100 900;font-display:swap}
@font-face{font-family:"JetBrains Mono";src:url("/static/fonts/jetbrainsmono.woff2") format("woff2");font-weight:100 800;font-display:swap}
/* Fraunces : serif éditoriale moderne à fort contraste (variable opsz+wght),
   la « bonne skin » validée en maquette — titres de la gazette et de la coquille. */
@font-face{font-family:"Fraunces";src:url("/static/fonts/fraunces.woff2") format("woff2");font-weight:400 700;font-display:swap}

:root{
  /* AletheiaVox — clair par defaut, fond BLANC. Bleu structure, cyan relie,
     dore souligne. Mappe sur le vocabulaire natif du BBS (sky/violet/pink…). */
  --ink:#17232d; --paper:#f5f3ec; --panel:#ffffff; --line:#d9e0e4;
  --soft:#efeadd; --dim:#697985; --text:#17232d;
  --coral:#a73d42; --coral-soft:#f6e7e8;
  --mint:#28765e;  --mint-soft:#e4f2eb;
  --amber:#c87832; --amber-soft:#fbf0e4;
  --violet:#00a8c8;--violet-soft:#dff5f7;   /* cyan : connexions / interactions */
  --sky:#104a88;   --sky-soft:#e8f2f8;       /* bleu : structure primaire */
  --pink:#c9a84c;  --pink-soft:#f5ecd2;      /* dore : accent, souligne */
  --accent:#b3402f; --accent-soft:#f5e3df;   /* cinabre : le SEUL accent fort (tampons, vif), maquette #1092 */
  --shadow:0 1px 2px rgba(20,45,60,.05), 0 6px 22px rgba(20,45,60,.09);
  --lift:0 2px 4px rgba(20,45,60,.08), 0 14px 34px -12px rgba(20,45,60,.22);
  --radius:14px;
}
/* AletheiaVox : fond PAPIER (crème) clair par défaut, même sur OS sombre — la
   MÊME teinte que la rédaction (accueil, newsroom.css) et billets, pour que la
   coquille lise comme du papier et non comme une appli blanche : les panneaux
   blancs (`--panel`) flottent alors sur le grain. Le sombre n'est JAMAIS imposé
   par l'OS — c'est un choix explicite via [data-theme="dark"]. */
:root[data-theme="dark"]{
  /* AletheiaVox sombre = bleu-nuit profond (aligné newsroom), jamais noir pur. */
  --ink:#eef3f6; --paper:#0c141c; --panel:#14212d; --line:#2a3b49;
  --soft:#16242f; --dim:#8598a5; --text:#eef3f6;
  --coral:#e07d82; --coral-soft:#3a1d1e;
  --mint:#5fce9f;  --mint-soft:#14302a;
  --amber:#e0b06a; --amber-soft:#3a2a12;
  --violet:#33c4de;--violet-soft:#123642;
  --sky:#5aa0e0;   --sky-soft:#173049;
  --pink:#d9bd6a;  --pink-soft:#2b2718;
  --accent:#dd6a55; --accent-soft:#2a1712;
  --shadow:0 1px 2px rgba(0,0,0,.4), 0 10px 30px -14px rgba(0,0,0,.6);
  --lift:0 2px 6px rgba(0,0,0,.5), 0 18px 40px -16px rgba(0,0,0,.7);
}
*{box-sizing:border-box}
html,body{margin:0;padding:0}
body{
  background-color:var(--paper); color:var(--text); min-height:100vh;
  font-family:"Inter","Source Sans 3",system-ui,-apple-system,"Segoe UI",sans-serif;
  font-size:15px; line-height:1.55; -webkit-font-smoothing:antialiased;
  /* grain de papier — cohérent avec la rédaction (accueil) et billets */
  background-image:radial-gradient(var(--line) .5px,transparent .5px),
                   radial-gradient(var(--line) .5px,var(--paper) .5px);
  background-size:22px 22px,22px 22px; background-position:0 0,11px 11px;
}
.serif{font-family:"Fraunces","Cinzel","Cormorant Garamond","Iowan Old Style",Palatino,Georgia,serif;font-optical-sizing:auto;letter-spacing:-.005em}
.mono{font-family:"JetBrains Mono","SF Mono",ui-monospace,Menlo,Consolas,monospace;font-size:.86em}
h1,h2,h3{margin:0;text-wrap:balance;letter-spacing:-.02em}
/* Lecture d'un fil (#1092) : mesure d'article de gazette — colonne centrée, le
   texte ne s'étire pas d'un bord à l'autre du volet de lecture. */
.lecture{max-width:780px;margin:0 auto;width:100%}
a{color:inherit}
button{font:inherit;color:inherit;cursor:pointer}
:focus-visible{outline:2px solid var(--violet);outline-offset:2px;border-radius:6px}

/* ── enveloppe ────────────────────────────────────────────────────────── */
/* ═══ COQUILLE A TROIS COLONNES ═══════════════════════════════════════════
 *
 * Reference : www/mockup-reference.html.
 *
 * Trois zones qui defilent CHACUNE DE LEUR COTE — on garde sa place dans la
 * liste en lisant un fil. C'est ce qui fait la sensation d'un poste de travail
 * plutot que d'une suite de pages.
 *
 * `100dvh` et non `100vh` : sur telephone, la barre d'adresse du navigateur se
 * retracte et `vh` reste fige sur la hauteur SANS elle. La ligne d'etat se
 * retrouvait alors sous le pli, invisible tant qu'on ne faisait pas defiler.
 *
 * DEFAUT = TELEPHONE, une seule colonne. Les paliers ajoutent les colonnes, et
 * ils sont ecrits A LA FIN DU FICHIER : une media query n'ajoute aucune
 * specificite, placee ici elle serait ecrasee par les regles qui suivent. */
.shell{
  display:grid; grid-template-rows:auto 1fr auto auto;
  height:100dvh; overflow:hidden;
}
.corps{display:grid; grid-template-columns:1fr; min-height:0; overflow:hidden}

/* ── barre haute ──────────────────────────────────────────────────────── */
.bar{
  display:flex; align-items:center; gap:10px; padding:9px 14px;
  border-bottom:1.5px solid var(--ink);
  background:color-mix(in srgb, var(--paper) 90%, transparent); backdrop-filter:blur(12px);
}
.bar .sp{flex:1}
.marque{
  display:flex; align-items:center; gap:10px; font-family:"Fraunces","Cinzel",Georgia,serif; font-weight:600;
  font-optical-sizing:auto; font-size:19px; letter-spacing:-.01em; white-space:nowrap; text-decoration:none; color:var(--ink);
}
.marque .mark{
  width:32px;height:32px;border-radius:3px;flex:0 0 auto;display:grid;place-items:center;position:relative;
  background:var(--soft);border:1.5px solid var(--ink);color:var(--ink);
  font-family:"Fraunces","Cinzel",Georgia,serif;font-weight:700;font-size:15px;
}
.marque .mark::after{content:"◆";position:absolute;bottom:-5px;right:-5px;font-size:.55rem;color:var(--accent)}
.chip{
  font-family:"JetBrains Mono","SF Mono",ui-monospace,Menlo,Consolas,monospace; font-size:10.5px;
  text-transform:uppercase; letter-spacing:.06em;
  padding:3px 8px; border-radius:3px; border:1px solid var(--line);
  color:var(--dim); white-space:nowrap;
}
.search{
  flex:0 1 300px; display:flex; align-items:center; gap:8px;
  background:var(--panel); border:1.5px solid var(--ink); border-radius:3px;
  padding:6px 11px; color:var(--dim); font-size:13px;
}
.search input{border:0;background:none;outline:0;color:var(--text);width:100%;font:inherit}
.iconbtn{border:1.5px solid var(--ink);background:var(--panel);border-radius:3px;padding:6px 10px;font-size:13px;transition:.14s}
.iconbtn:hover{background:var(--ink);color:var(--paper)}
.me{display:flex;align-items:center;gap:8px;font-size:13px;font-weight:600;text-decoration:none}
.av{width:28px;height:28px;border-radius:3px;display:grid;place-items:center;color:var(--paper);font-size:11px;font-weight:700;
    font-family:"JetBrains Mono","SF Mono",ui-monospace,monospace;
    background:var(--sky)}

/* ── colonne 1 : le rail ──────────────────────────────────────────────── */
.rail{
  background:var(--panel); border-right:1px solid var(--line);
  display:flex; flex-direction:column; gap:2px; padding:10px 8px;
  overflow-y:auto; min-height:0;
}
.railhead{
  font-family:"JetBrains Mono","SF Mono",ui-monospace,Menlo,Consolas,monospace;
  font-size:10px;text-transform:uppercase;letter-spacing:.12em;color:var(--dim);
  padding:14px 10px 5px;font-weight:600;
}
.nav{
  display:flex;align-items:center;gap:9px;width:100%;text-align:left;
  border:0;background:none;padding:7px 10px;border-radius:3px;color:var(--text);
}
.nav:hover{background:var(--soft)}
.nav[aria-current="page"]{background:var(--soft);font-weight:650}
.nav .ic{
  /* LA BOITE NE BOUGE PAS, LE DESSIN OUI. Le rail portait des glyphes
     geometriques (◈ ▤ ✦) qui remplissaient la case a 12px ; un emoji, lui, y
     paraissait riquiqui. On monte d'un point et on cale la hauteur de ligne —
     la case reste a 22px, donc aucun alignement ne bouge.
     `line-height:1` evite qu'un emoji a selecteur de variante (✉️, 🛠️) ne
     descende d'un pixel par rapport a ses voisins sans variante. */
  width:22px;height:22px;border-radius:3px;display:grid;place-items:center;flex:0 0 auto;
  font-size:13px;line-height:1;background:var(--soft);
}
.nav[aria-current="page"] .ic{background:var(--c,var(--accent));color:var(--paper)}
.nav .n{margin-left:auto;font-family:"JetBrains Mono","SF Mono",ui-monospace,monospace;font-size:10.5px;color:var(--dim);font-variant-numeric:tabular-nums}
.nav .n.p-new{background:var(--accent);color:var(--paper);padding:1px 6px;border-radius:3px}
.nav.off{opacity:.42}
.nav.off .n{font-size:10px;text-transform:uppercase;letter-spacing:.06em}

/* ── colonne 2 : la liste des fils ────────────────────────────────────── */
.liste{
  border-right:1px solid var(--line); background:var(--panel);
  overflow-y:auto; min-height:0;
}
.liste-tete{
  position:sticky; top:0; z-index:2; background:var(--panel);
  padding:11px 14px 9px; border-bottom:1px solid var(--line);
  display:flex; align-items:baseline; gap:9px;
}
.liste-tete h2{font-family:"Fraunces","Cinzel","Cormorant Garamond",Georgia,serif;font-optical-sizing:auto;font-weight:600;font-size:17px;letter-spacing:-.01em;margin:0}
.liste-tete .cnt{margin-left:auto;font-size:10.5px;color:var(--dim);text-transform:uppercase;letter-spacing:.06em;
  font-family:"JetBrains Mono","SF Mono",ui-monospace,Menlo,Consolas,monospace}
.fil-item{
  display:block; padding:12px 14px; border-bottom:1px solid var(--line);
  text-decoration:none; color:inherit; border-left:2px solid transparent;
}
.fil-item:hover{background:var(--soft)}
.fil-item[aria-current="page"]{background:var(--soft);border-left-color:var(--accent)}
.fil-item h3{font-family:"Fraunces","Cinzel","Cormorant Garamond",Georgia,serif;font-optical-sizing:auto;font-size:15px;font-weight:600;line-height:1.3;letter-spacing:-.01em;margin:0}
.fil-item .meta{
  margin-top:5px;display:flex;align-items:center;gap:7px;flex-wrap:wrap;
  font-family:"JetBrains Mono","SF Mono",ui-monospace,Menlo,Consolas,monospace;
  font-size:10.5px;text-transform:uppercase;letter-spacing:.05em;
  color:var(--dim);font-variant-numeric:tabular-nums;
}

/* ── colonne 3 : la vue ───────────────────────────────────────────────── */
.vue{overflow-y:auto; min-height:0; padding:20px 24px 32px}
.view{display:none} .view.on{display:block}

/* ── ligne d'etat ─────────────────────────────────────────────────────── */
.etat{
  display:flex; align-items:center; gap:12px; flex-wrap:wrap;
  padding:5px 14px; border-top:1px solid var(--line);
  background:var(--ink); color:var(--paper);
  font-family:ui-monospace,"SF Mono",Menlo,Consolas,monospace;
  font-size:11px; letter-spacing:.02em;
}
.etat .sep{opacity:.35}
.etat .vert{color:var(--mint)}

/* ── barre basse (telephone) ──────────────────────────────────────────── */
.basse{display:flex; border-top:1px solid var(--line); background:var(--soft)}
.basse a{
  flex:1; text-align:center; padding:8px 4px 6px; text-decoration:none; color:var(--dim);
  font-family:ui-monospace,"SF Mono",Menlo,Consolas,monospace; font-size:10px;
  display:flex; flex-direction:column; gap:3px; align-items:center;
}
.basse a .ic{font-size:16px}
.basse a[aria-current="page"]{color:var(--accent)}
.basse .n.p-new{background:var(--accent);color:var(--paper);padding:0 5px;border-radius:3px;font-size:9px}
.view{display:none} .view.on{display:block}

.ph{display:flex;align-items:flex-end;gap:14px;margin-bottom:20px;flex-wrap:wrap}
.ph h1{font-family:"Fraunces","Cinzel","Cormorant Garamond","Iowan Old Style",Palatino,Georgia,serif;font-optical-sizing:auto;font-size:28px;font-weight:600;letter-spacing:-.02em}
.ph p{margin:4px 0 0;color:var(--dim);font-size:13.5px;max-width:60ch}

/* ── briques ──────────────────────────────────────────────────────────── */
.card{background:var(--panel);border:1px solid var(--line);border-radius:4px;box-shadow:var(--shadow)}
.pad{padding:16px 18px}
.stack{display:flex;flex-direction:column;gap:14px}
.grid{display:grid;gap:14px}
.g2{grid-template-columns:repeat(auto-fit,minmax(290px,1fr))}
.g3{grid-template-columns:repeat(auto-fit,minmax(230px,1fr))}
.row{display:flex;align-items:center;gap:10px;flex-wrap:wrap}

.pill{
  display:inline-flex;align-items:center;gap:5px;
  font-family:"JetBrains Mono","SF Mono",ui-monospace,Menlo,Consolas,monospace;
  font-size:10px;font-weight:600;text-transform:uppercase;
  padding:3px 8px;border-radius:3px;letter-spacing:.06em;white-space:nowrap;
}
.p-local {background:var(--soft);color:var(--dim)}
.p-pub   {background:var(--mint-soft);color:var(--mint)}
.p-billet{background:var(--pink-soft);color:var(--pink)}
.p-src   {background:var(--violet-soft);color:var(--violet)}
.p-new   {background:var(--coral-soft);color:var(--coral)}
.p-warn  {background:var(--amber-soft);color:var(--amber)}
.p-sky   {background:var(--sky-soft);color:var(--sky)}

.btn{
  border:1.5px solid var(--ink);background:var(--panel);border-radius:3px;
  padding:8px 14px;font-family:"JetBrains Mono","SF Mono",ui-monospace,Menlo,Consolas,monospace;
  font-size:11.5px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;
  color:var(--ink);transition:.14s;
}
.btn:hover{background:var(--ink);color:var(--paper)}
.btn.primary{background:var(--accent);border-color:var(--accent);color:var(--paper)}
.btn.primary:hover{background:var(--accent);filter:brightness(1.08)}
.btn.pinkish{background:var(--pink);border-color:var(--pink);color:var(--paper)}
.btn.pinkish:hover{background:var(--pink);filter:brightness(1.08)}
.btn.sm{padding:5px 10px;font-size:10.5px}

/* ── forums ───────────────────────────────────────────────────────────── */
.board{
  display:flex;gap:14px;padding:15px 16px;border-bottom:1px solid var(--line);align-items:flex-start;
}
.board:last-child{border-bottom:0}
.board:hover{background:var(--soft)}
.board .ic{
  width:36px;height:36px;border-radius:3px;display:grid;place-items:center;font-size:15px;flex:0 0 auto;
  background:var(--bs,var(--soft));color:var(--bc,var(--dim));
}
.board b{font-family:"Fraunces","Cinzel","Cormorant Garamond",Georgia,serif;font-optical-sizing:auto;font-size:16px;font-weight:600;letter-spacing:-.01em;display:block}
.board .d{color:var(--dim);font-size:12.5px;margin-top:2px}
.board .meta{margin-left:auto;text-align:right;color:var(--dim);white-space:nowrap;flex:0 0 auto;
  font-family:"JetBrains Mono","SF Mono",ui-monospace,Menlo,Consolas,monospace;
  font-size:10.5px;text-transform:uppercase;letter-spacing:.05em}

.thread{display:flex;gap:12px;padding:14px 16px;border-bottom:1px solid var(--line);align-items:flex-start}
.thread:last-child{border-bottom:0}
.thread:hover{background:var(--soft)}
.thread .t{font-family:"Fraunces","Cinzel","Cormorant Garamond",Georgia,serif;font-optical-sizing:auto;font-weight:600;font-size:15px;letter-spacing:-.01em}
.thread .sub{color:var(--dim);margin-top:3px;display:flex;gap:7px;flex-wrap:wrap;align-items:center;
  font-family:"JetBrains Mono","SF Mono",ui-monospace,Menlo,Consolas,monospace;
  font-size:10.5px;text-transform:uppercase;letter-spacing:.05em}
.thread .cnt{margin-left:auto;text-align:right;color:var(--dim);flex:0 0 auto;
  font-family:"JetBrains Mono","SF Mono",ui-monospace,Menlo,Consolas,monospace;
  font-size:10.5px;text-transform:uppercase;letter-spacing:.05em}

/* ── fil ──────────────────────────────────────────────────────────────── */
.post{display:grid;grid-template-columns:52px 1fr;gap:14px;padding:18px 18px;border-bottom:1px solid var(--line)}
.post:last-of-type{border-bottom:0}
.post .who{text-align:center}
.post .who .av{width:38px;height:38px;border-radius:3px;margin:0 auto 5px;font-size:13px}
.post .who small{font-family:"JetBrains Mono","SF Mono",ui-monospace,monospace;font-size:10px;color:var(--dim);display:block;line-height:1.3;text-transform:uppercase;letter-spacing:.03em}
.post .bd{font-size:14.5px}
.post .bd p{margin:0 0 9px}
.post .hd{display:flex;align-items:center;gap:8px;margin-bottom:8px;color:var(--dim);flex-wrap:wrap;
  font-family:"JetBrains Mono","SF Mono",ui-monospace,Menlo,Consolas,monospace;
  font-size:10.5px;text-transform:uppercase;letter-spacing:.05em}
.post.localnote{background:color-mix(in srgb,var(--amber-soft) 42%,transparent)}

/* ── éditeur ──────────────────────────────────────────────────────────── */
.editor{border:1px solid var(--line);border-radius:4px;overflow:hidden;background:var(--panel)}
.tools{display:flex;gap:2px;padding:7px 9px;border-bottom:1px solid var(--line);background:var(--soft);flex-wrap:wrap;align-items:center}
.tool{border:0;background:none;padding:5px 9px;border-radius:3px;font-size:13.5px;min-width:31px}
.tool:hover{background:var(--panel)}
.tool.act{background:var(--panel);box-shadow:inset 0 0 0 1px var(--line)}
/* ENREGISTREMENT EN COURS : le bouton doit se distinguer SANS l'animation, car
   le compteur qu'il affiche suffit deja a dire que ca tourne. La pulsation ne
   fait que rendre l'etat visible du coin de l'oeil. */
.tool.enregistrer.en-cours{background:var(--coral);color:#fff;
  font-variant-numeric:tabular-nums;animation:sbx-pulse 1.6s ease-in-out infinite}
@keyframes sbx-pulse{50%{opacity:.62}}
@media (prefers-reduced-motion:reduce){.tool.enregistrer.en-cours{animation:none}}
.sep{width:1px;height:19px;background:var(--line);margin:0 5px}
.area{padding:14px 16px;min-height:130px;font-size:14.5px}
.area p{margin:0 0 9px}
.area .ph2{color:var(--dim)}
.foot{display:flex;align-items:center;gap:10px;padding:9px 12px;border-top:1px solid var(--line);background:var(--soft);flex-wrap:wrap}

/* ── modules ──────────────────────────────────────────────────────────── */
.mod{padding:15px 17px;display:flex;gap:13px;align-items:flex-start}
.mod .ic{width:38px;height:38px;border-radius:3px;display:grid;place-items:center;font-size:16px;flex:0 0 auto;
         background:var(--bs,var(--soft));color:var(--bc,var(--dim))}
.mod b{font-family:"Fraunces","Cinzel","Cormorant Garamond",Georgia,serif;font-optical-sizing:auto;font-size:15px;font-weight:600;letter-spacing:-.01em;display:block}
.mod .d{color:var(--dim);font-size:12.5px;margin-top:2px}
.mod .k{margin-top:9px;display:flex;gap:6px;flex-wrap:wrap}

.sw{width:40px;height:23px;border-radius:999px;background:var(--line);position:relative;flex:0 0 auto;border:0;padding:0;transition:background .16s}
.sw::after{content:"";position:absolute;top:2.5px;left:2.5px;width:18px;height:18px;border-radius:50%;background:#fff;
           box-shadow:0 1px 3px rgba(0,0,0,.3);transition:transform .16s}
.sw[aria-checked="true"]{background:var(--mint)}
.sw[aria-checked="true"]::after{transform:translateX(17px)}

/* ── companion ────────────────────────────────────────────────────────── */
.phone{
  width:250px;flex:0 0 auto;border:9px solid var(--ink);border-radius:30px;overflow:hidden;
  background:var(--paper);box-shadow:var(--shadow)
}
.phone .st{background:var(--ink);color:var(--paper);font-size:9.5px;padding:3px 12px;display:flex;justify-content:space-between}
.phone .bdy{padding:11px}
.appcard{background:var(--panel);border:1px solid var(--line);border-radius:3px;padding:10px;margin-bottom:8px}
.appcard b{font-size:12.5px;display:block}
.appcard .d{font-size:10.5px;color:var(--dim);margin-top:1px}
.bar{height:5px;border-radius:999px;background:var(--soft);overflow:hidden;margin-top:7px}
.bar i{display:block;height:100%;border-radius:999px;background:linear-gradient(90deg,var(--violet),var(--pink))}

/* ── table ────────────────────────────────────────────────────────────── */
.tw{overflow-x:auto}
table{border-collapse:collapse;width:100%;font-size:13.5px;min-width:520px}
th,td{text-align:left;padding:9px 14px;border-bottom:1px solid var(--line)}
th{font-family:"JetBrains Mono","SF Mono",ui-monospace,Menlo,Consolas,monospace;font-size:10px;text-transform:uppercase;letter-spacing:.08em;color:var(--dim);font-weight:600}
tbody tr:hover{background:var(--soft)}
td.num{font-variant-numeric:tabular-nums;text-align:right}

.note{
  border-left:2px solid var(--amber);background:var(--amber-soft);
  padding:11px 15px;border-radius:0 3px 3px 0;font-size:13px;
}
.note b{display:block;margin-bottom:2px}
.note.v{border-color:var(--violet);background:var(--violet-soft)}
.note.m{border-color:var(--mint);background:var(--mint-soft)}

.kv{display:flex;justify-content:space-between;gap:14px;padding:7px 0;border-bottom:1px dashed var(--line);font-size:13px}
.kv:last-child{border-bottom:0}
.kv span{color:var(--dim)}
.kv b{font-variant-numeric:tabular-nums}
.stat{padding:14px 16px}
.stat .n{font-family:"Fraunces","Cinzel","Cormorant Garamond",Georgia,serif;font-optical-sizing:auto;font-size:27px;font-weight:600;letter-spacing:-.03em;font-variant-numeric:tabular-nums}
.stat .l{font-family:"JetBrains Mono","SF Mono",ui-monospace,monospace;font-size:10px;color:var(--dim);text-transform:uppercase;letter-spacing:.08em;margin-top:3px}
.crumb{font-family:"JetBrains Mono","SF Mono",ui-monospace,monospace;font-size:11px;color:var(--dim);text-transform:uppercase;letter-spacing:.05em;margin-bottom:10px}
.crumb b{color:var(--text)}

.loop{display:flex;align-items:stretch;gap:8px;flex-wrap:wrap}
.lp{
  flex:1 1 150px;background:var(--panel);border:1px solid var(--line);border-top:2px solid var(--lc,var(--accent));
  border-radius:3px;padding:11px 14px;box-shadow:var(--shadow)
}
.lp b{display:block;font-size:13.5px;color:var(--lc,var(--violet))}
.lp span{font-size:12px;color:var(--dim)}
.ar{align-self:center;color:var(--dim);font-size:17px}
@media (max-width:700px){ .ar{display:none} }

.player .scrn{
  aspect-ratio:16/9;display:grid;place-items:center;font-size:34px;color:var(--paper);
  background:var(--ink);border-radius:3px 3px 0 0;
}
.wave{display:flex;align-items:flex-end;gap:2.5px;height:26px;margin-top:9px}
.wave i{flex:1;background:var(--violet);border-radius:2px;opacity:.5}
.wave i:nth-child(3n){height:100%;opacity:1}
.wave i:nth-child(3n+1){height:52%}
.wave i:nth-child(3n+2){height:74%}
.wave i:nth-child(-n+5){opacity:1}

/* ==== styles extraits des gabarits (scripts/styles-en-classes.py) ==== */
/* Genere. Ne pas editer a la main : relancer le script. */
.s09c487b{--c:var(--mint)}
.s15cb695{text-decoration:none;color:inherit}
.s24a7a9f{font-size:14px;margin-bottom:9px;color:var(--dim);text-transform:uppercase;letter-spacing:.07em}
.s296d727{max-width:640px}
.s2bdf8e4{margin-top:18px}
.s33ee298{font-size:11px}
.s3608041{font-size:11.5px;color:var(--dim)}
.s381c37d{gap:14px;align-items:flex-start;margin-top:8px}
.s39203eb{background:#fff;border-radius:10px;padding:6px;flex:0 0 auto}
.s3fb7839{font-size:12px;color:var(--dim)}
.s42a6ced{margin-bottom:14px}
.s43459be{min-width:180px}
.s4430722{opacity:.5}
.s491198d{gap:8px}
.s4b54525{margin-bottom:18px}
.s51eedcb{margin-top:6px;font-size:12px}
.s5f32656{text-align:right}
.s621f16f{max-width:720px}
.s63eb706{word-break:break-all;font-size:12.5px}
.s6622767{--c:var(--amber)}
.s6b8b63d{display:inline}
.s791fdb1{--c:var(--sky)}
.s7b1eb4e{font-size:15px;margin-bottom:9px}
.s883d028{margin-top:6px;font-size:11.5px;color:var(--dim)}
.s8bad342{margin-top:8px;font-size:12px}
.s92c18f6{margin-top:16px}
.s92ca4fb{max-width:400px;margin:40px auto}
.sa2d3b14{margin-bottom:14px;border-color:var(--coral);background:var(--coral-soft)}
.sa3970b9{font-size:15px}
.sa49cca5{font-size:12px}
.sa9ef1c9{width:100%;margin-top:4px;padding:9px 11px;border:1px solid var(--line);border-radius:3px;background:var(--panel);color:inherit;font:inherit}
.sa9ef1c9:focus-visible{outline:0;border-color:var(--ink)}
.sab2784b{font-size:14px;margin:20px 0 9px;color:var(--dim);text-transform:uppercase;letter-spacing:.07em}
.sb0455c2{color:var(--dim)}
.sb908d7d{--c:var(--violet)}
.sbe9c346{font-size:14px;margin:22px 0 9px;color:var(--dim);text-transform:uppercase;letter-spacing:.07em}
.sbf2a49c{color:var(--dim);font-size:12.5px;margin-top:2px}
.sbf4a218{margin-bottom:16px}
.sc146614{border:0;background:none;color:inherit;font:inherit;flex:1;outline:none}
.sc53ea1d{margin-top:14px}
.sc9cd860{color:var(--pink)}
.sca6fc03{margin-left:auto}
.sdf3b93c{font-size:12.5px;color:var(--dim)}
.sdf92814{margin-top:10px}
.se2e86c5{color:inherit}
.se3ffd0e{font-size:23px}
.se78b7e7{width:100%;border:0;outline:none;background:none;color:inherit;font:inherit;resize:vertical}
.sf017a92{--c:var(--coral)}
.sf381ae3{--bs:var(--sky-soft);--bc:var(--sky)}
.sf85c702{margin-bottom:6px}
.sf9f81af{margin-top:11px}
.sfe562e2{--c:var(--pink)}

/* Etats semantiques.
 *
 * Des CLASSES conditionnelles plutot que des attributs `style` calcules : la
 * politique de securite interdit les styles en ligne, et un etat qui porte un
 * nom se relit mieux qu'une couleur codee dans le gabarit. */
.etat-ok        { color: var(--mint); }
.etat-attention { color: var(--amber); }
.etat-mauvais   { color: var(--coral); }
.compte-inactif { opacity: .5; }


/* ── adaptatif : telephone ────────────────────────────────────────────────
 *
 * EN DERNIER, DELIBEREMENT. Ces regles corrigent des composants definis plus
 * haut ; placees avant eux, elles seraient annulees a specificite egale — ce
 * qui est exactement ce qui s'est produit.
 *
 * LE RAIL N'EST PAS MASQUE, il devient une barre horizontale. Le masquer
 * laisserait un telephone sans aucune navigation : ni salons, ni console, ni
 * moyen de revenir. Une largeur reduite ne justifie pas d'amputer le site. */
/* ── lecteur de media ─────────────────────────────────────────────────────
 *
 * Le fil PORTE le media : on ecoute ou regarde a l'endroit meme ou l'on en
 * discute. Renvoyer vers un autre onglet perd la conversation en chemin. */
.lecteur { overflow: hidden; margin-bottom: 16px; }
.cadre-video { position: relative; aspect-ratio: 16/9; background: #000; }
.cadre-video iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.lecteur-audio { width: 100%; }

/* OBJET MÉDIA (#1227) — un lien média rendu dans un message devient un OBJET
   cadré, relayé par la box, avec sa barre d'escalade (voir / garder souverain
   ytsas / diffuser à venir). Première pierre du méta-objet. */
.sbx-mediaobj { border: 1px solid var(--line); border-radius: 12px; overflow: hidden;
  background: var(--panel); margin: .5rem 0; max-width: 640px; }
.sbx-mediaobj-vue { position: relative; aspect-ratio: 16/9; background: #000; }
.sbx-mediaobj-vue iframe, .sbx-mediaobj-vue video { position: absolute; inset: 0;
  width: 100%; height: 100%; border: 0; }
.sbx-mediaobj-bar { display: flex; align-items: center; gap: .6rem; padding: .4rem .6rem;
  font-size: .72rem; background: var(--soft); border-top: 1px solid var(--line); }
.sbx-mediaobj-bar .mo-id { color: var(--dim); }
.sbx-mediaobj-bar .mo-src { color: var(--dim); font-size: .82em; }
.sbx-mediaobj-bar .mo-act { color: var(--violet); text-decoration: none;
  border: 1px solid var(--line); border-radius: 7px; padding: .15rem .5rem; }
.sbx-mediaobj-bar .mo-act:hover { border-color: var(--violet); }
/* Le groupe d'actions (voir · souverain · diffuser) est poussé à droite ; la
   première action porte la marge auto. #1227. */
.sbx-mediaobj-bar .mo-voir { margin-left: auto; }
.sbx-mediaobj-bar .mo-diff[aria-disabled="true"] { opacity: .5; cursor: default; }

/* Le point de partage : l'adresse du fil, lisible et selectionnable, plus un
 * code QR pour passer sur un telephone sans la recopier. */
.partage {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 10px 14px; border-top: 1px solid var(--line); background: var(--soft);
  font-size: 12.5px;
}
.lien-partage {
  flex: 1; min-width: 180px; color: var(--dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  user-select: all;   /* un clic selectionne l'adresse entiere */
}

/* ── messagerie interne (#1008) ───────────────────────────────────────────
 *
 * LA MESSAGERIE N'A PLUS SA PROPRE GRILLE. Elle en avait une, a deux colonnes,
 * imbriquee dans la vue de la coquille : deux dispositions superposees, chacune
 * reprenant la moitie de la place, et la conversation finissait dans un quart
 * d'ecran. Les conversations occupent desormais la colonne du milieu, comme les
 * fils — une seule coquille pour tout le module, et les memes paliers.
 *
 * Ne restent ici que la conversation elle-meme et ses bulles.            */

/* Une entree de conversation est un `fil-item` avec un avatar et un apercu. */
.fil-item.conv{display:flex; align-items:center; gap:9px}
.conv-txt{display:flex; flex-direction:column; min-width:0; flex:1}
/* min-width:0 — sans lui, un apercu long refuse de se tronquer et pousse la
 * colonne au-dela de sa largeur, ce qui fait defiler la page en travers. */
.conv-txt h3{margin:0}
.conv-txt .apercu{
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  font-size:12px; color:var(--dim);
}
.mp-choix{display:flex; flex-wrap:wrap; gap:6px; padding:8px 14px 14px}
.mp-membre{
  padding:4px 9px; border:1px solid var(--line); border-radius:3px;
  font-size:12.5px; text-decoration:none; color:var(--dim);
}
.mp-membre:hover{color:var(--text); background:var(--soft)}

.mp-entete{display:flex; align-items:center; gap:10px; margin-bottom:14px}
.mp-bulles{display:flex; flex-direction:column; gap:10px; margin-bottom:16px}
.bulle{max-width:78%; align-self:flex-start}
.bulle-corps{
  background:var(--soft); border:1px solid var(--line);
  border-radius:14px 14px 14px 4px; padding:8px 12px;
}
.bulle-corps p{margin:0}
.bulle-pied{font-size:11.5px; margin-top:3px; color:var(--dim)}
/* Les siens a droite, en couleur d'accent : la conversation se lit d'un coup
 * d'oeil sans avoir a chercher le nom sous chaque message. */
.bulle.moi{align-self:flex-end}
.bulle.moi .bulle-corps{
  background:var(--sky-soft); border-color:var(--sky);
  border-radius:14px 14px 4px 14px;
}
.bulle.moi .bulle-pied{text-align:right}
.mp-ecrire .editor{background:var(--panel)}
/* `.area` impose 130px, taille d'un premier message de forum. Une reponse de
 * messagerie est courte : specificite (0,2,0) contre (0,1,0), la regle passe
 * sans !important. */
.mp-ecrire .area{min-height:88px}

/* ═══ PALIERS ═════════════════════════════════════════════════════════════
 *
 * ECRITS APRES LES COMPOSANTS, volontairement. Une media query n'ajoute AUCUNE
 * specificite : placee avant les regles qu'elle corrige, elle est ecrasee par
 * elles. C'est le defaut qui avait laisse le rail visible sur telephone —
 * il occupait toute la largeur et repoussait le contenu hors de l'ecran.
 *
 * DEFAUT = TELEPHONE. Une seule colonne, la vue ; le rail et la liste sont
 * masques, la barre basse porte les cinq destinations. On ne lit qu'une chose
 * a la fois sur cinq pouces.                                                */
.rail, .liste { display: none; }
  /* LE SERVEUR DIT QUEL VOLET PORTE LE CONTENU (voir layout.html). Sur un forum
   * precis, ce sont les fils — donc la liste — et masquer celle-ci rendait un
   * ecran vide. Sur la racine c'est l'inverse. Une seule classe suffit, et
   * personne n'a plus a deviner. */
  /* ── TELEPHONE : DEUX VOLETS QUI GLISSENT ────────────────────────────────
   *
   * AUCUN VOLET N'EST MASQUE, et c'est tout l'interet. Masquer obligeait a
   * decider lequel montrer — decision que j'ai ratee trois fois : la racine
   * des forums porte son contenu dans la vue, un forum precis dans la liste,
   * et un fil ouvert de nouveau dans la vue alors que le serveur posait encore
   * `volet-liste`. Chaque reglage corrigeait un cas en cassant le suivant.
   *
   * Ici les deux existent cote a cote et l'on passe de l'un a l'autre d'un
   * glissement. Il n'y a plus rien a deviner : ce qui n'est pas sous les yeux
   * reste a un geste.
   *
   * `scroll-snap` cale le volet en place — sans lui on s'arrete entre les
   * deux, et une demi-colonne de chaque cote est le pire des deux mondes. */
  .corps {
    grid-auto-flow: column;
    grid-auto-columns: 100%;
    grid-template-columns: none;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    overscroll-behavior-x: contain;
    /* PAS D'ANCRAGE DE DEFILEMENT. Quand le contenu d'un volet change de
     * taille — une image qui arrive, une vignette qui se remplit — le
     * navigateur corrige la position pour garder le meme point sous les yeux.
     * Dans un conteneur a calage horizontal, cette correction fait revenir au
     * volet precedent SANS QUE PERSONNE NE L'AIT DEMANDE. */
    overflow-anchor: none;
    scrollbar-width: none;
  }
  .corps::-webkit-scrollbar { display: none; }
  .corps > .liste,
  .corps > .vue {
    display: block;
    scroll-snap-align: start;
    min-width: 0;
    overflow-y: auto;
  }
  .liste { border-right: 1px solid var(--line); }
  /* Le tiroir est ATTEIGNABLE la ou il sert le plus : le bouton n'existait
   * qu'au-dessus de 620 px, donc jamais sur telephone. */
  .menu-bascule { display: inline-block; }
.basse { display: flex; }

/* Le rail reste ATTEIGNABLE sur petit ecran : le bouton ☰ le fait glisser
 * par-dessus. En surimpression et non en colonne — a cette largeur, une
 * colonne de plus ne laisserait rien a lire. */
body.rail-ouvert .rail,
body.rail-ouvert .liste {
  display: flex; flex-direction: column; position: fixed; z-index: 40;
  width: min(84vw, 300px); box-shadow: var(--shadow); overflow-y: auto;
}
/* Le rail en haut, la liste dessous : UNE seule colonne verticale, qui donne
 * d'abord les sections puis ce qu'elles contiennent. Deux tiroirs separes
 * auraient demande deux gestes pour une seule intention. */
body.rail-ouvert .rail  { inset: 0 auto 55% 0; border-bottom: 1px solid var(--line); }
body.rail-ouvert .liste { inset: 45% auto 0 0; border-right: 1px solid var(--line); }
body.rail-ouvert::after {
  content: ""; position: fixed; inset: 0; z-index: 39;
  background: rgba(13, 27, 36, .45);
}

/* ── tablette et televiseur : deux colonnes ──────────────────────────────
 * Liste et vue. Le rail reste au bouton : a trois metres comme au pouce, on
 * vise une cible large, et la liste vaut mieux qu'un menu toujours ouvert. */
/* SUR TELEPHONE, LE SERVEUR DESIGNE LE VOLET (classe `volet-liste`).
 *
 * Aucune regle de largeur ne peut le deviner : sur la racine des forums le
 * contenu est dans la vue, sur un forum precis c'est la liste qui porte les
 * fils. Deux tentatives de deviner ont produit deux ecrans vides, un dans
 * chaque sens, puis des volets superposes.
 *
 * restait d'une suppression ou j'avais efface le contenu en laissant
 * l'accolade. Tout ce qui suit se trouvait donc enferme dans une condition
 * impossible (< 620 ET >= 620) : la disposition en colonnes ne s'appliquait
 * JAMAIS, sur aucune largeur. C'est ce qui rendait le BBS inutilisable.
 */
@media (min-width: 620px) {
  .corps { grid-template-columns: minmax(240px, 320px) 1fr; }
  .liste { display: block; }
  /* Le marqueur ne sert plus qu'a placer le regard sur telephone : rien n'est
   * masque, donc rien n'est a retablir ici. On remet seulement la grille, que
   * le mode glissant remplace en dessous du seuil. */
  .corps { grid-auto-flow: row; overflow-x: visible; scroll-snap-type: none; }
  .basse { display: none; }
  .menu-bascule { display: inline-block; }
}

/* ── ordinateur : les trois colonnes, rail deplie ─────────────────────── */
@media (min-width: 1000px) {
  .corps { grid-template-columns: 232px minmax(280px, 360px) 1fr; }
  .rail { display: flex; }
  .menu-bascule { display: none; }
  body.rail-ouvert::after { content: none; }
}

/* Une page sans liste (compte, sysop, connexion) ne laisse pas une colonne
 * creuse : la vue prend la place. `:not(:has())` est lu par tous les
 * navigateurs qui nous concernent ; la ou il ne l'est pas, on retombe sur la
 * disposition a trois colonnes, qui reste utilisable. */
@media (min-width: 620px) {
  .corps:not(:has(.liste)) { grid-template-columns: 1fr; }
}
@media (min-width: 1000px) {
  .corps:not(:has(.liste)) { grid-template-columns: 232px 1fr; }
}

@media (max-width: 560px) {
  .grid.g3, .grid.g2 { grid-template-columns: 1fr; }
  .thread .cnt, .board .meta { display: none; }
  .ph h1 { font-size: 21px; }
}

/* ── annuaire (#1008) ─────────────────────────────────────────────────────
 *
 * Le selecteur de destinataire etait un mur de pastilles listant TOUS les
 * comptes. L'annuaire est une recherche : un champ, des lignes, deux gestes
 * par ligne — ecrire, ou garder sous la main. */
.annuaire-recherche{display:flex; gap:8px; margin:0 0 14px; max-width:520px}
.annuaire-recherche input{
  flex:1; padding:9px 12px; font:inherit; color:var(--text);
  background:var(--panel); border:1px solid var(--line); border-radius:3px;
}
.annuaire-recherche input:focus-visible{outline:2px solid var(--accent); outline-offset:-1px}
.annuaire-ligne{
  display:flex; align-items:center; gap:11px;
  padding:9px 14px; border-bottom:1px solid var(--line);
}
.annuaire-ligne:last-child{border-bottom:0}
/* min-width:0 — sans lui, un nom long pousse les boutons hors de la carte. */
.annuaire-qui{display:flex; flex-direction:column; min-width:0; flex:1}
.annuaire-qui b{font-size:14.5px}
.annuaire-qui .d{font-size:12px; color:var(--dim)}
.annuaire-actions{display:flex; gap:6px; align-items:center; flex:none}

/* Sous 560px, les actions passent SOUS le nom : cote a cote, elles reduisaient
 * le nom a deux lettres. Regle placee ici, apres le composant. */
@media (max-width: 560px){
  .annuaire-ligne{flex-wrap:wrap}
  .annuaire-actions{width:100%; padding-left:45px}
}

/* ── pieces jointes (#1008) ───────────────────────────────────────────────
 *
 * Le champ de fichier est masque a l'oeil mais RESTE DANS LE FLUX et reste
 * atteignable : `display:none` le retirerait de l'ordre de tabulation, et
 * l'envoi deviendrait impossible sans souris. */
.s-invisible{
  position:absolute; width:1px; height:1px; overflow:hidden;
  clip-path:inset(50%); white-space:nowrap;
}
/* Un media joint ne deborde jamais de la colonne qui le porte — la coquille a
 * trois colonnes rend cette borne indispensable. */
.jointe{max-width:100%; border-radius:10px; display:block; margin:.6rem 0}
img.jointe{height:auto; background:var(--soft)}
audio.jointe{width:100%}
video.jointe{max-height:60vh; background:#000}

/* Avatar : l'image remplace les initiales, meme gabarit. */
.av img, img.av{
  width:100%; height:100%; object-fit:cover; border-radius:inherit; display:block;
}

/* ── Lu / non-lu (#1020) ──────────────────────────────────────────────────
   La pastille DOUBLE le mot « nouveau », elle ne le remplace pas : la couleur
   seule disparait a l'impression, sur un ecran delave, et pour un lecteur
   daltonien. Le fil non-lu porte aussi un titre plus appuye — deux signaux
   valent mieux qu'un, et aucun ne depend de la vue des couleurs. */
.fil-neuf h3 { font-weight: 700; }
.pastille {
  color: var(--sky);
  margin-right: .4em;
  font-size: .8em;
  vertical-align: middle;
}
/* Texte reserve aux synthese vocales : present dans le flux, invisible a
   l'oeil. `display:none` l'aurait retire aussi des lecteurs d'ecran. */
.sr {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%);
  overflow: hidden; white-space: nowrap;
}
.tout-lu { display: inline; margin-left: .5rem; }

/* Vignettes des cartes media (#1020).
   Le glyphe est POSE SOUS l'image, pas remplace par elle : si la vignette
   echoue — format refuse, cache purge, fichier disparu — la case garde un
   reperage au lieu de devenir un trou blanc dans la grille. */
.carte-media { display: flex; gap: .85rem; align-items: flex-start; }
.carte-media .corps-carte { min-width: 0; flex: 1; }
.carte-media .vignette {
  position: relative; flex: 0 0 auto;
  width: 4.5rem; height: 4.5rem; border-radius: 8px; overflow: hidden;
  display: grid; place-items: center;
  background: var(--fond-2, rgba(255,255,255,.04));
  border: 1px solid var(--bord, rgba(255,255,255,.12));
}
.carte-media .vignette .glyphe { font-size: 1.6rem; line-height: 1; opacity: .75; }
.carte-media .vignette img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover; display: block;
}
/* Le titre d'un fichier peut etre un nom sans espaces de quatre-vingts
   caracteres : sans cela il elargit la carte et deborde la grille. */
.carte-media h3 { overflow-wrap: anywhere; }

@media (max-width: 30rem) {
  .carte-media .vignette { width: 3.25rem; height: 3.25rem; }
}

/* Vitrine des billets (#1066 phase A) — le contenu REEL du module billets,
   en liste : un extrait de plusieurs lignes n'a pas sa place dans la grille
   de cartes-media, pensee pour un titre et une ligne de sous-titre. */
.billet-pills { margin-bottom: 8px; }
.billet-titre { font-size: 17px; margin-bottom: 6px; overflow-wrap: anywhere; }
.billet-resume { margin: 0 0 10px; color: var(--dim); font-size: 13.5px; line-height: 1.55; }
.billet-pied { align-items: center; gap: 10px; }
.billet-date { color: var(--dim); font-size: 11.5px; }
/* Une PANNE du flux (billets.gk2 injoignable) n'est pas la meme chose que
   « zero billet publié » — les confondre ferait chercher une publication
   manquee la ou le service est simplement injoignable. Couleur amber, comme
   les autres avertissements de la charte, jamais coral (reserve aux erreurs
   qui bloquent une action de l'utilisateur). */
.panne-billets { border-color: var(--amber); background: var(--amber-soft); }

/* ── hierarchie des sous-salons dans le rail (#1044) ──────────────────────
   Le decalage est porte par une CLASSE : la politique de securite de contenu
   interdit les styles en ligne, et un `style=` calcule serait silencieusement
   ignore. La fleche remplace l'icone de salon pour dire « sous » sans mot. */
.nav.sub1{padding-left:26px}
.nav.sub2{padding-left:40px}
.nav.sub3{padding-left:54px}
.nav.sub1 .ic,.nav.sub2 .ic,.nav.sub3 .ic{background:transparent;font-size:11px;color:var(--dim)}
/* La meme indentation sur la liste d'accueil : deux affichages du meme arbre
   doivent raconter la meme chose, sinon l'un des deux ment. */
.board.sub1{padding-left:30px}
.board.sub2{padding-left:46px}
.board.sub3{padding-left:62px}

/* Le texte debalise d'une publication garde ses retours a la ligne :
   sans cela, un fil de trois paragraphes s'affiche en un seul bloc. */
.pre-ligne{white-space:pre-line}

/* REGLAGES REPLIES : rares mais retrouvables. Le fil est ce qu'on vient lire ;
   l'instance, l'invitation et la liaison se font une fois puis ne servent
   plus. */
.reglages-masto > summary{cursor:pointer;font-weight:650;list-style:none}
.reglages-masto > summary::-webkit-details-marker{display:none}
.reglages-masto > summary::before{content:"▸ ";color:var(--dim)}
.reglages-masto[open] > summary::before{content:"▾ "}
.reglages-masto h4{margin:14px 0 4px;font-size:13px;color:var(--dim);
  text-transform:uppercase;letter-spacing:.06em}

/* ── PUBLICATION DISTANTE (fil Mastodon) ──────────────────────────────────
   PAS `.post`, qui est une grille 52px + 1fr pour la vignette de l'auteur :
   une publication distante n'a pas d'avatar a montrer, et l'y forcer ecrasait
   tout le contenu dans la colonne de l'avatar. Une seule colonne, donc. */
.pub{padding:13px 0;border-bottom:1px solid var(--line)}
.pub:last-of-type{border-bottom:0}
.pub-tete{display:flex;align-items:center;gap:9px;flex-wrap:wrap;
  font-size:11.5px;color:var(--dim);margin-bottom:5px}
.pub-quand{font-family:ui-monospace,"SF Mono",Menlo,Consolas,monospace;white-space:nowrap}
.pub-sp{flex:1}
.pub-cpt{font-variant-numeric:tabular-nums;white-space:nowrap}
.pub-lien{color:var(--sky);text-decoration:none;white-space:nowrap}
.pub-lien:hover{text-decoration:underline}
.pub-avert{margin:0 0 6px;font-weight:650}
.pub-txt{margin:0;font-size:14.5px}
.pub-medias{display:flex;gap:6px;flex-wrap:wrap;margin-top:8px}
/* Une pastille, pas un bouton : la piece jointe accompagne le texte, elle ne
   se dispute pas la place avec lui. */
.pub-media{display:inline-flex;align-items:center;gap:5px;max-width:100%;
  padding:3px 9px;border:1px solid var(--line);border-radius:3px;
  font-size:11.5px;color:var(--dim);text-decoration:none;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pub-media:hover{background:var(--soft);color:var(--text)}

/* Les medias servis par l'instance du compte s'affichent ; les autres restent
   des pastilles. Bornes en hauteur : une publication ne doit pas chasser la
   suivante hors de l'ecran. */
.pub-vue{display:block;max-width:100%;border-radius:10px;overflow:hidden;
  border:1px solid var(--line);line-height:0}
/* MEMES REGLES QUE `.jointe`, ET POUR LES MEMES RAISONS.
   Le premier jet posait `width:100%` et `object-fit:cover` : une capture de
   200 px etait etiree sur toute la colonne, et une image verticale rognee en
   bandeau. `max-width` sans `width` n'agrandit jamais ; `height:auto` sans
   `object-fit` ne rogne jamais. On ne borne que la hauteur, pour qu'une
   publication ne chasse pas la suivante hors de l'ecran. */
.pub-vue img,video.pub-vue{display:block;max-width:100%;width:auto;height:auto;
  max-height:60vh}
.pub-son{width:100%;max-width:420px}
/* UNE IMAGE PREND SA LIGNE, une pastille se range a cote. `flex-basis:100%`
   sur le media affiche l'obtient sans `:has()`, donc sans dependre d'un
   selecteur que les vieux navigateurs ignorent — et dont l'absence aurait
   empile les pastilles en colonne. */
.pub-medias{display:flex;flex-wrap:wrap;align-items:flex-start;gap:8px}
.pub-medias > .pub-vue,.pub-medias > .pub-son{flex:0 1 100%}

/* ── FICHE D'UN SERVICE SECUBOX ────────────────────────────────────────────
 *
 * Un lien vers notre radio, notre PeerTube ou notre podcaster s'habille au
 * lieu de rester une adresse nue. Elle reste UN LIEN : si le service est
 * absent, le membre garde quelque chose de cliquable.
 *
 * `inline-flex` et non `block` : une fiche apparait au fil du texte, et un
 * bloc couperait la phrase qui l'entoure. Plusieurs fiches cote a cote
 * s'alignent d'elles-memes — c'est la mosaique, sans grille a declarer. */
.fiche-sbx{
  display:inline-flex; align-items:center; gap:8px; vertical-align:baseline;
  max-width:100%; margin:2px 0; padding:5px 11px 5px 7px;
  border:1px solid var(--line); border-radius:3px; background:var(--soft);
  text-decoration:none; color:var(--text); font-size:14px; line-height:1.35;
}
.fiche-sbx:hover{border-color:var(--ink); background:var(--panel)}
.fiche-svc{
  flex:0 0 auto; padding:.1em .5em; border-radius:3px;
  background:var(--panel2, rgba(127,127,127,.14)); color:var(--dim);
  font-family:"JetBrains Mono","SF Mono",ui-monospace,monospace;
  font-size:10.5px; font-weight:600; letter-spacing:.06em; text-transform:uppercase;
}
/* L'adresse peut etre longue : on la coupe plutot que d'etirer la ligne. */
.fiche-txt{overflow:hidden; text-overflow:ellipsis; white-space:nowrap; min-width:0}
@media (max-width:34rem){ .fiche-sbx{display:flex} }

/* Pièces jointes dans un fil : bornées pour ne pas déborder / se faire couper
   (#1114) ; PDF rendu inline (#1102). */
.jointe{max-width:100%;height:auto;border-radius:8px;display:block;margin:8px 0}
video.jointe{width:100%;max-height:70vh;background:#000}
audio.jointe{width:100%}
/* #1180 : dans le CORPS d'un message, les images jointes s'affichaient à leur
   taille native — une capture d'écran finissait minuscule dans la colonne. On
   les porte à toute la largeur (jusqu'à 720 px, 3-4× plus grand) et on les rend
   cliquables pour un agrandissement plein écran (lightbox, cf. coquille.js). */
.post .bd img.jointe{width:100%;max-width:720px;cursor:zoom-in}
/* Lightbox : calque plein écran créé par coquille.js au clic sur img.jointe ;
   clic n'importe où ou touche Échap pour fermer. */
.lightbox{position:fixed;inset:0;z-index:9999;display:flex;align-items:center;justify-content:center;
  background:rgba(0,0,0,.92);cursor:zoom-out;padding:2vmin}
.lightbox img{max-width:96vw;max-height:96vh;width:auto;height:auto;border-radius:6px;box-shadow:0 10px 50px rgba(0,0,0,.6)}
.jointe-pdf{width:100%;height:min(70vh,720px);border:1px solid var(--line);border-radius:10px;margin:8px 0}
.jointe-pdf-lien{display:inline-flex;align-items:center;gap:6px;font-size:.85rem;color:var(--sky);text-decoration:none;margin-bottom:8px}
.jointe-pdf-lien:hover{text-decoration:underline}
/* Fiche fichier (#1114) : repli generique pour une piece jointe non jouable
   (PDF, archive, document). Toujours visible, cliquable — jamais une zone vide. */
.jointe-fichier{display:inline-flex;align-items:center;gap:10px;max-width:100%;margin:8px 0;padding:10px 14px;
  border:1px solid var(--line);border-radius:10px;background:var(--soft);text-decoration:none;color:inherit}
.jointe-fichier:hover{border-color:var(--sky)}
.jointe-fichier .jf-ic{font-size:1.5rem;line-height:1}
.jointe-fichier .jf-tx{display:flex;flex-direction:column;min-width:0}
.jointe-fichier .jf-tx b{font-size:.9rem}
.jointe-fichier .jf-tx small{font-size:.72rem;opacity:.6;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Lecteur PDF integre (#1131) : le navigateur remplit l'<iframe> de sa
   visionneuse native ; le lien reste visible dessous comme repli. */
.jointe-pdf{display:block;margin:8px 0;max-width:100%}
.jointe-pdf .jpdf-vue{width:100%;height:min(80vh,720px);border:1px solid var(--line);
  border-radius:10px 10px 0 0;background:var(--soft);display:block}
.jointe-pdf .jpdf-lien{display:inline-flex;align-items:center;gap:6px;font-size:.8rem;
  padding:6px 12px;border:1px solid var(--line);border-top:0;border-radius:0 0 10px 10px;
  background:var(--soft);text-decoration:none;color:inherit}
.jointe-pdf .jpdf-lien:hover{border-color:var(--sky)}
