:root{
  /* VALEUR PAR DÉFAUT DE LA COULEUR PRINCIPALE — l'ardoise de l'espace établissement
     depuis le 2026-08-04, l'orange #ef7f4f auparavant. Elle est écrasée sur toute page
     du shell par le bloc <style id="stu-role-couleur"> que shell_haut.php émet
     quelques lignes après le <link> de cette feuille : ce qui s'affiche vient donc
     TOUJOURS de inclusions/role_utilisateur.php. Elle n'en était pas moins un piège —
     on lisait ici « la couleur de l'application est orange », ce qui n'était plus vrai
     depuis deux refontes. */
  --blue:#475569;--blue-6:#404d5f;--blue-7:#374252;--blue-8:#2d3643;--bl:#e9ebed;--bl2:#cfd3d8;--bl-025:#f8f8f9;
  --green:#16a06a;--green-l:#e6f6ee;--amber:#eaa03a;--amber-l:#fdf1dd;
  /* --ink3 assombri de #6b7385 à #666e81 (audit #4, WCAG 1.4.3) : l'ancienne valeur
     donnait 4,43:1 sur --surf2 (#f4f7fc) et 4,47:1 sur --paper, sous le seuil AA de
     4,5:1 pour du texte normal — or ce ton sert de `color` dans ~130 règles (méta de
     la barre du haut, libellés de KPI, états vides). La nouvelle valeur donne 4,76:1
     sur surf2, 4,80:1 sur paper et 5,11:1 sur blanc. Le thème sombre a son propre
     --ink3, redéfini plus bas dans le bloc du thème sombre, et passait déjà : il
     n'est pas touché.
     ⚠ On ÉCRIT ce sélecteur en toutes lettres plutôt qu'en code : un outil qui
       cherche le bloc sombre par sa chaîne le trouverait ici, dans un commentaire,
       avant sa vraie déclaration. */
  --ink:#1b2333;--ink2:#5a6478;--ink3:#666e81;--line:#e7ecf4;--line2:#eef1f7;
  --paper:#f5f8fd;--surf:#ffffff;--surf2:#f4f7fc;
  /* dégradé doux façon entête (.hero) appliqué aux surfaces de toute la page */
  --cardg:linear-gradient(160deg,#ffffff,#f4f8fe);
  --pageg:linear-gradient(180deg,#eef4fc,#f5f8fd 340px);
  --sh:0 1px 2px rgba(24,44,90,.04),0 6px 18px rgba(24,44,90,.05);
  --sh2:0 2px 6px rgba(24,44,90,.05),0 16px 40px rgba(24,44,90,.09);
  --side:250px;--sans:system-ui,-apple-system,"Segoe UI",Roboto,"Noto Sans",Arial,sans-serif;--serif:"Iowan Old Style",Georgia,serif;
  /* Couleurs de TEXTE porteuses de sens. Elles etaient ecrites en dur dans les
     pages : sur fond sombre, un rouge a 85 de luminance passait a 2,6:1 de
     contraste. Ces jetons sont eclaircis en mode sombre, comme --ink. */
  --danger:#c0392b;--info:#0a78c8;--info2:#0a6fb0;
  --grad-or:linear-gradient(160deg,#fff6f0,#fde7d9);--grad-bl:linear-gradient(160deg,#eaf4ff,#dbecff);--grad-bl2:linear-gradient(180deg,transparent,var(--grad-bl-bd));--grad-bl-bd:#cfe6fa;--tint-k:#00a1ff;--tint-k2:#0090ea;--tint-bl:#e7f5ff;--tint-bl2:#eef8ff;--tint-bl3:#f4fbff;--tint-bl4:#d6eeff;--tint-bl5:var(--grad-bl-bd);--tint-bl6:#dceefc;--tint-rg:#fbeceb;--tint-rg2:#fdeceb;--tint-rg3:#fdecea;--mh-or:#fbe9df;--mh-or-b:#f4d6c6;--mh-or-k:#d3562a;--mh-bl:#e7f2fb;--mh-bl-b:#cfe6f7;--mh-bl-k:#0a6fb0;--mh-rg:#fdeceb;--mh-rg-b:#f6d0cc;--mh-rg-k:#c0392b;--mh-x:#fff;--mh-vt:#e6f6ee;--mh-vt-b:#bfe6d3;--mh-vt-k:#10744d;--btn-noir:#252f45;--btn-noir-h:#161d2c;--purple:#5b4fb9;--warn-ink:#8a6414;--rank:#5b6b86;
}
/* Défauts sombres des trois fonds discrets : l'ardoise fondue dans le papier #0c1122,
   comme les calcule role_utilisateur.php. Écrasés eux aussi par le bloc de rôle. */
:root[data-theme="dark"]{--ink:#e9edf7;--ink2:#a4aec4;--ink3:#8a93aa;--line:#232c44;--line2:#1d253a;--paper:#0c1122;--surf:#141b30;--surf2:#1a2238;--bl:#171d2f;--bl2:#1e2537;--bl-025:#121829;--green-l:#12291f;--amber-l:#2a2413;
  --cardg:linear-gradient(160deg,#161e35,#111830);--pageg:linear-gradient(180deg,#0a0f1e,#0c1122 340px);
  --sh:0 2px 6px rgba(0,0,0,.35),0 14px 36px rgba(0,0,0,.45);--sh2:0 2px 8px rgba(0,0,0,.45),0 24px 56px rgba(0,0,0,.55);
  --danger:#ff8f80;--info:#5cb8f7;--info2:#7cc4f7;--purple:#b0a4f5;--warn-ink:#e3b35a;--rank:#8c9ab4;}
*{box-sizing:border-box}
body{margin:0;background:var(--pageg);color:var(--ink);font-family:var(--sans);letter-spacing:-.011em;-webkit-font-smoothing:antialiased}
/* L'attribut hidden ne vaut qu'une règle du navigateur : la moindre déclaration
   display le fait sauter, et les boutons cachés d'une fenêtre s'affichaient tous
   en même temps. On lui rend la priorité, une fois, pour toutes les pages. */
[hidden]{display:none !important}
a{color:inherit;text-decoration:none}
.tnum{font-variant-numeric:tabular-nums}

/* ---------------- shell ---------------- */
.shell{display:flex;min-height:100vh;width:100%}
.side{width:var(--side);flex:0 0 var(--side);align-self:flex-start;position:sticky;top:0;height:100vh;overflow:hidden;
  background:var(--cardg);border-inline-end:1px solid var(--line);display:flex;flex-direction:column;padding:20px 14px;gap:2px}
/* Le menu latéral a quitté cette feuille le 2026-08-03 : son style est
   partagé avec les pages héritées et vit dans assets/css/menu-lateral.css,
   que shell_haut.php et entete.php chargent tous les deux. */
.side .cap:not(:first-child){margin-top:8px}
/* Carte profil : bannière dégradée + photo en médaillon */
/* La carte de profil a quitté cette feuille le 2026-08-03 : elle est partagée
   avec les pages héritées et vit dans assets/css/menu-lateral.css, aux côtés
   du menu qu'elle coiffe. */
.side .cap{font-size:.66rem;font-weight:800;letter-spacing:.16em;text-transform:uppercase;color:var(--ink3);padding:10px 12px 6px}
.side .sp{flex:1}
.side .out{margin-top:6px;display:flex;align-items:center;gap:9px;padding:11px 12px;border-radius:11px;color:var(--blue);font-size:.85rem;font-weight:700;border:1px solid var(--line);justify-content:center}
@media (hover:hover){.side .out:hover{background:var(--surf2);color:var(--danger)}}

.main{flex:1;min-width:0;display:flex;flex-direction:column}
.topbar{position:sticky;top:0;z-index:20;display:flex;align-items:center;gap:12px;padding:15px 30px;background:var(--surf);border-bottom:1px solid var(--line)}
.topbar .burger{display:none}
.topbar h1{font-size:1.1rem;font-weight:800;letter-spacing:-.03em;margin:0}
.topbar .meta{color:var(--ink3);font-size:.8rem}
.topbar .sp{flex:1}
/* ⚠ `position:relative` N'EST PAS DÉCORATIF ICI : le compteur `.bdg` est en
 * `position:absolute` et se cale sur le premier ancêtre positionné. Sans cette
 * ligne, il s'ancrait sur `.hd` — ce qui marchait par chance, tant que `.hd`
 * restait `relative` et qu'aucun bouton à badge ne vivait ailleurs. Le badge
 * appartient au BOUTON, pas au conteneur : on le dit. (Sans quoi, en passant
 * `.hd` en `static` sur téléphone — voir la requête média plus bas —, tous les
 * compteurs se seraient empilés dans le coin de la barre.) */
.icbtn{position:relative;width:40px;height:40px;border-radius:12px;border:1px solid var(--line);background:var(--surf);color:var(--blue);display:grid;place-items:center;cursor:pointer;font-size:1.05rem;transition:.14s}
@media (hover:hover){.icbtn:hover{color:var(--blue);border-color:var(--blue)}}
/* Le micro EN TRAIN D'ÉCOUTER (assets/js/stu-voix.js). Sans cet état, la dictée
   fonctionnait sans que rien ne bouge à l'écran — donc, pour l'utilisateur,
   elle ne fonctionnait pas. La pulsation dit que l'écoute est en cours ; le
   second clic l'arrête. */
.stu-mic-on{background:#fdecec!important;border-color:#e2564d!important;color:#c9372c!important;animation:stuMicPulse 1.1s ease-in-out infinite}
@keyframes stuMicPulse{0%,100%{box-shadow:0 0 0 0 rgba(201,55,44,.35)}50%{box-shadow:0 0 0 6px rgba(201,55,44,0)}}
@media (prefers-reduced-motion:reduce){.stu-mic-on{animation:none}}
.content{padding:30px;max-width:1200px;width:100%;margin:0 auto}

/* ── LE FIL D'ARIANE PASSE SOUS LE BANDEAU DE LA PAGE (2026-08-04) ────────────
 * Demandé par le client : le fil doit être « juste en dessous de l'entête de
 * chaque page ». Or shell_haut.php l'écrit AVANT le contenu — il tombait donc
 * au-dessus du bandeau.
 *
 * On ne touche pas les soixante pages : elles posent déjà leur bandeau et le
 * fil comme FRÈRES, enfants directs de .content. Mesuré sur le rendu réel :
 * 54 pages sur 60 dans ce cas. Une colonne flex et trois ordres suffisent donc
 * — le bandeau d'abord, le fil ensuite, le reste après. L'ordre du document est
 * conservé à l'intérieur d'un même rang, rien d'autre ne bouge.
 *
 * ⚠ Les six pages dont le bandeau est NICHÉ dans leur .cc (mon-espace et les
 *   cinq pages de création) ne peuvent pas être servies ainsi : leur bandeau
 *   n'est pas frère du fil. Elles emploient $shell_fil_differe et placent le
 *   fil elles-mêmes — voir inclusions/shell_haut.php.
 *
 * ⚠ Ne PAS transformer .content en flex sans ce commentaire : un lecteur qui
 *   verrait « display:flex » sur un conteneur de page croirait à une erreur. */
.content{display:flex;flex-direction:column}
.content > *{order:3}
.content > .hero{order:1}
.content > .fil{order:2}

/* ---------------- barre : dropdowns, badges, avatar ---------------- */
.hd{position:relative}
.avbtn{position:relative;border:0;background:transparent;padding:0;cursor:pointer;display:grid;place-items:center}
.avatar{width:40px;height:40px;border-radius:50%;object-fit:cover;border:2px solid var(--line);background:var(--surf2);display:block;transition:.14s}
@media (hover:hover){.avbtn:hover .avatar{border-color:var(--blue)}}
.avatar.lg{width:46px;height:46px;border-radius:12px;flex:none}
/* ⚠ `inset-inline-end`, PAS `right` (2026-09-10) : en session arabe le compteur
   doit suivre le bord de FIN du bouton, pas rester collé à droite. */
.bdg{position:absolute;top:-5px;inset-inline-end:-5px;min-width:18px;height:18px;padding:0 5px;background:var(--blue);color:#fff;font-size:.63rem;font-weight:800;border-radius:999px;display:grid;place-items:center;border:2px solid var(--surf);line-height:1}
.bdg.am{background:var(--amber)}
.menu{position:absolute;inset-inline-end:0;top:calc(100% + 10px);width:330px;max-width:86vw;background:var(--surf);border:1px solid var(--line);border-radius:16px;box-shadow:var(--sh2);padding:8px;display:none;z-index:60;animation:fade .16s ease}
.menu.menu-sm{width:236px}
.hd.open .menu{display:block}
.mhead{display:flex;align-items:center;justify-content:space-between;font-size:.82rem;font-weight:800;color:var(--ink);padding:8px 10px 10px}
.mhead .c{background:var(--bl);color:var(--blue-7);border-radius:999px;font-size:.7rem;font-weight:800;padding:1px 8px}
.mrow{display:flex;align-items:center;gap:10px;justify-content:space-between;padding:9px 11px;border-radius:10px;color:var(--ink2);font-size:.88rem;font-weight:600}
.mrow span{display:inline-flex;align-items:center;gap:9px}
.mrow i{color:var(--stu-or-k)}
@media (hover:hover){.mrow:hover{background:var(--surf2);color:var(--ink)}}
.mrow.cur{background:var(--bl);color:var(--blue-7)}.mrow.cur i{color:var(--stu-or-k)}
@media (hover:hover){.mrow.logout:hover{color:var(--danger)}}@media (hover:hover){.mrow.logout:hover i{color:var(--danger)}}
.mscroll{max-height:300px;overflow-y:auto;display:flex;flex-direction:column;gap:6px;padding:0 2px}
.mit{background:var(--surf2);border:1px solid var(--line);border-radius:11px;padding:9px 11px}
.mit-t{font-size:.84rem;font-weight:700;color:var(--ink);line-height:1.3}
.mit-x{font-size:.8rem;color:var(--ink2);margin-top:2px}
.mit-d{font-size:.72rem;color:var(--ink3);margin-top:5px;display:flex;align-items:center;gap:5px}
.mempty{color:var(--ink3);font-size:.85rem;text-align:center;padding:20px 6px;display:flex;flex-direction:column;align-items:center;gap:8px}
.mempty i{font-size:1.4rem;opacity:.5}
.mcta{display:flex;align-items:center;justify-content:center;gap:8px;margin-top:8px;padding:10px;border-radius:11px;background:var(--blue);color:#fff;font-size:.85rem;font-weight:750}
@media (hover:hover){.mcta:hover{background:var(--blue-6)}}
.me-head{display:flex;align-items:center;gap:11px;padding:6px 6px 12px;border-bottom:1px solid var(--line);margin-bottom:6px}
.me-n{font-weight:800;font-size:.9rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.me-head small{color:var(--ink3)}

/* ---------------- hero (clair, aéré) ---------------- */
/* LE BANDEAU SUIT LE RÔLE (2026-08-03). Son fond, sa bordure, son surtitre et
   ses pictogrammes étaient écrits en HEXADÉCIMAL : la couche de couleur du rôle
   ne redéfinit que des VARIABLES, elle ne pouvait donc rien y faire. Résultat
   signalé par l'utilisateur — un parent (violet) recevait un bandeau bleu, avec
   des icônes violettes posées dessus. Quatre jetons, valeurs de repli à
   l'identique de l'ancien dégradé : voir stu_role_css() dans role_utilisateur.php. */
.hero{position:relative;border-radius:22px;padding:32px 32px;overflow:hidden;background:var(--hero-bg,linear-gradient(160deg,#1aa9ff,#0090ea));border:1px solid var(--hero-bd,#0a86d8)}
.hero::after{content:"";position:absolute;right:-50px;top:-50px;width:220px;height:220px;border-radius:50%;background:radial-gradient(circle,rgba(255,255,255,.22),transparent 70%);z-index:1}
/* icônes éducatives en filigrane, couleur orange */
.hero-deco{position:absolute;inset:0;z-index:0;pointer-events:none}
/* Le filigrane doit se détacher DU BANDEAU, pas de la page : le teindre avec la
   couleur du rôle le rendrait invisible maintenant que le fond porte cette même
   couleur. Blanc à faible opacité — la seule valeur qui tienne sur les 19 rôles. */
.hero-deco i{position:absolute;color:var(--hero-ic,var(--blue))}
.hero-deco i:nth-child(1){top:-12px;left:8%;font-size:5rem;opacity:.20;transform:rotate(-12deg)}
.hero-deco i:nth-child(2){bottom:-18px;left:24%;font-size:4rem;opacity:.16;transform:rotate(9deg)}
.hero-deco i:nth-child(3){top:26%;left:44%;font-size:2.6rem;opacity:.14;transform:rotate(15deg)}
.hero-deco i:nth-child(4){top:-8px;right:30%;font-size:3.4rem;opacity:.15;transform:rotate(-6deg)}
.hero-deco i:nth-child(5){bottom:-10px;right:20%;font-size:4.4rem;opacity:.18;transform:rotate(8deg)}
.hero-deco i:nth-child(6){top:14%;right:6%;font-size:6rem;opacity:.22;transform:rotate(-10deg)}
.hero-deco i:nth-child(7){bottom:12%;left:2%;font-size:3rem;opacity:.13;transform:rotate(6deg)}
.hero-deco i:nth-child(8){top:40%;right:40%;font-size:2.2rem;opacity:.13;transform:rotate(-14deg)}
.hero-deco i:nth-child(9){bottom:30%;right:12%;font-size:2.4rem;opacity:.12;transform:rotate(10deg)}
.hero-deco i:nth-child(10){top:6%;left:34%;font-size:2.2rem;opacity:.12;transform:rotate(-8deg)}
.hero .k,.hero h2,.hero .m{position:relative;z-index:2}
.hero>*{position:relative}
/* Le surtitre était #ffe0cf — une crème pensée du temps où le bandeau était
   orange, restée posée sur un dégradé bleu. Il devient une teinte claire de la
   couleur du rôle, donc lisible sur son propre fond quel que soit le profil. */
.hero .k{font-size:.72rem;font-weight:800;letter-spacing:.16em;text-transform:uppercase;color:var(--hero-k,#ffe0cf)}
.hero h2{margin:8px 0 0;font-size:clamp(1.7rem,4vw,2.5rem);font-weight:900;letter-spacing:-.04em;color:#fff;line-height:1.05}
.hero .m{margin-top:16px;display:flex;flex-wrap:wrap;gap:8px 10px}
.hero .m span{display:inline-flex;align-items:center;gap:7px;background:rgba(255,255,255,.16);border:1px solid rgba(255,255,255,.32);border-radius:999px;padding:7px 13px;font-size:.85rem;font-weight:650;color:#fff}
/* Même raison que le filigrane : ces icônes sont DANS le bandeau, sur des
   pastilles de blanc translucide. Les teindre en couleur de rôle les noyait. */
.hero .m i{color:var(--hero-ic,var(--blue))}
/* ⚠ SUR TÉLÉPHONE, UNE PASTILLE QUI PORTE UNE PHRASE LA LAISSE COULER. En
     `inline-flex`, « texte + <strong> » faisait DEUX colonnes côte à côte :
     « Vous validez avant tout envoi » tenait sur 52 px et cinq lignes
     (assistant-agent, 320 px, mesuré le 2026-09-18). En bloc, l’icône et le
     texte s’enchaînent comme une ligne ordinaire.
   ⚠ `>` : les pastilles emboîtent des `<span translate="no">`, qui restent en ligne.
   L’écart de 7 px devient 4 px + l’espace qui suit l’icône dans le balisage.
   🔴 L’ESPACE APRÈS `@media` EST VOULU : tests/test_affichage_mobile.php prend la
     PREMIÈRE requête téléphone écrite sans espace pour celle de la barre du haut. */
@media (max-width:640px){
  .hero .m>span{display:block}
  .hero .m>span>i:first-child{margin-inline-end:4px}
}

/* ---------------- KPI ---------------- */
.kpis{display:flex;flex-wrap:wrap;gap:16px;margin-top:18px}
.kpi{flex:1 1 170px;min-width:150px;background:var(--cardg);border:1px solid var(--line);border-radius:18px;padding:18px;box-shadow:var(--sh);transition:transform .16s,box-shadow .16s,border-color .16s}
@media (hover:hover){.kpi:hover{transform:translateY(-4px);box-shadow:var(--sh2);border-color:var(--bl2)}}
.kpi .ic{width:44px;height:44px;border-radius:13px;display:grid;place-items:center;font-size:1.25rem;margin-bottom:12px;background:var(--stu-or-tint);color:var(--stu-or-k)}
.kpi.g .ic,.kpi.am .ic{background:var(--stu-or-tint);color:var(--stu-or-k)}
.kpi .n{font-size:2.3rem;font-weight:900;letter-spacing:-.05em;line-height:1;color:var(--ink)}
.kpi .t{color:var(--ink3);font-size:.75rem;font-weight:800;text-transform:uppercase;letter-spacing:.08em;margin-top:5px}
.kpi .dl{margin-top:14px;display:flex;gap:12px;border-top:1px solid var(--line2);padding-top:12px}
.kpi .dl a{font-size:1rem;color:var(--ink3);transition:.14s}
@media (hover:hover){.kpi .dl a:hover{color:var(--blue)}}
/* ══ LES TROIS ÉTATS D'UNE TUILE QUI NE PEUT PAS DONNER SON CHIFFRE ═══════════
 * 🔴 REMONTÉES ICI LE 2026-09-04, DEPUIS `shell_ministere.php`.
 *
 *   Ces trois règles y étaient déclarées pour le seul module ministère. Le
 *   centre de gouvernance en a besoin des mêmes — et une SECONDE copie aurait
 *   recréé exactement la divergence qu'on venait de corriger sur les onze
 *   grilles de KPI et les huit pastilles.
 *
 *   `.n.abs`  un indicateur SANS COUVERTURE : il n'affiche pas « 0 », il écrit
 *             sa raison, en plus petit et en gris — un zéro se lirait comme une
 *             mesure.
 *   `.s`      la ligne qui dit d'où vient le chiffre, ou pourquoi il manque.
 *   `.ic.mu`  la pastille muette, quand l'indicateur refuse de se prononcer.
 *
 * ⚠ La copie de `shell_ministere.php` peut être retirée : elle porte les mêmes
 *   valeurs et n'est plus nécessaire. On ne la touche pas ici — elle appartient
 *   à un chantier voisin. */
.kpi .n.abs{font-size:1.02rem;font-weight:700;color:var(--ink3);letter-spacing:0;line-height:1.45}
.kpi .s{font-size:.75rem;color:var(--ink3);margin-top:8px;line-height:1.45}
.kpi .ic.mu{background:var(--surf2);color:var(--ink3)}

.sec{margin-top:28px}
.lbl{font-size:.72rem;font-weight:800;letter-spacing:.14em;text-transform:uppercase;color:var(--ink3);margin-bottom:14px;display:flex;align-items:center;gap:12px}
.lbl::after{content:"";flex:1;height:1px;background:var(--line)}

/* ---------------- graphes moyennes & performance (hauteur égale) ---------------- */
.charts2{display:grid;grid-template-columns:1.9fr 1fr;gap:18px;align-items:stretch}
.chart-panel{display:flex;flex-direction:column}
.ch-h{display:flex;align-items:center;gap:12px}
.ch-ic{width:40px;height:40px;flex:none;border-radius:12px;display:grid;place-items:center;font-size:1.15rem}
.ch-ic.mo{background:var(--bl);color:var(--blue-7)}
.ch-ic.pe{background:var(--bl);color:var(--blue-7)}
.ch-t{flex:1;min-width:0}
.ch-v{flex:none;text-align:end;line-height:1}
.ch-v b{font-size:1.6rem;font-weight:900;letter-spacing:-.04em;color:var(--blue-7)}
.ch-v.g b{color:var(--green)}
.ch-v small{font-size:.72rem;color:var(--ink3);font-weight:700;margin-inline-start:2px}
/* boîte à hauteur FIXE identique pour les deux graphes */
.chartbox{height:320px;margin-top:16px;width:100%}
.alie-wide{flex:1 1 100%!important}
.chartbox.empty{display:flex;align-items:center;justify-content:center;gap:8px;text-align:center;color:var(--ink3);font-size:.9rem;background:var(--surf2);border-radius:14px}
/* jauge de performance (taux de réussite) */
.gaugebox{position:relative;display:grid;place-items:center}
.gaugesvg{width:186px;height:186px;display:block}
.gnum{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);text-align:center;line-height:1}
.gnum b{display:block;font-size:2.2rem;font-weight:900;letter-spacing:-.05em;color:var(--green)}
.gnum small{font-size:.85rem;font-weight:800;color:var(--green)}
.gnum span{display:block;margin-top:5px;font-size:.7rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--ink3)}
.chart{width:100%;height:100%;display:block}
.chart .cg{stroke:var(--line);stroke-width:1;vector-effect:non-scaling-stroke}
.ch-x{display:flex;justify-content:space-between;margin-top:8px;padding:0 2px;color:var(--ink3);font-size:.68rem;font-weight:700}
.ch-x span{flex:1;text-align:center}
@media(max-width:760px){.charts2{grid-template-columns:1fr}}

/* ---------------- perf + alie ---------------- */
.row2{display:flex;gap:18px;flex-wrap:wrap}
.row2>.panel{flex:2 1 380px}.row2>.alie{flex:1 1 280px}
.panel{background:var(--cardg);border:1px solid var(--line);border-radius:20px;padding:24px;box-shadow:var(--sh)}
.panel h3{margin:0;font-size:1.05rem;font-weight:800;letter-spacing:-.02em}.panel .s{color:var(--ink3);font-size:.85rem;margin-top:4px}
/* Le sous-titre de panneau existait en <div> — une ligne posée sous le titre.
   Les écrans du direct s'en servent pour de VRAIS paragraphes : ils expliquent
   ce que le panneau engage, et font trois lignes. Un <p> arrive avec la marge
   par défaut du navigateur (1 em en haut comme en bas) et sans interligne
   confortable : on lui donne les siennes, sans toucher à la forme en <div>. */
/* ⚠ ON NE TOUCHE PAS À LA MARGE DU HAUT : `.panel .s` la pose déjà, et les
     pages la remplacent parfois par une classe d'espacement (`.lv-mt`). La
     réécrire ici, avec un sélecteur plus spécifique, écraserait ces choix sans
     que rien ne le dise. On ne corrige que ce que le navigateur impose : le
     1 em du bas et l'interligne d'un paragraphe de texte courant. */
.panel p.s{margin-bottom:14px;line-height:1.55}
.panel p.s:last-child{margin-bottom:0}
/* ── LE TITRE D'UN PANNEAU NE DOIT PAS TOUCHER SON CONTENU (2026-08-14) ────────
 * `.panel h3` porte `margin:0` — voulu, pour que le titre s'aligne sur le haut
 * du panneau. Mais rien ne reprenait l'écart EN DESSOUS : une grille de cartes,
 * une liste ou une bande d'indicateurs commençait au ras du titre. Sur le direct,
 * le coin arrondi de la première carte venait littéralement se poser sous le « S »
 * de « Séances passées ».
 *
 * ⚠ ON ÉPARGNE `.s`, ET C'EST NÉCESSAIRE. Le sous-titre d'un panneau se pose
 *   volontairement PRÈS du titre (4 px, ligne 223) : il le complète, il ne le
 *   suit pas. Lui appliquer 16 px le détacherait et casserait la paire
 *   titre + sous-titre partout où elle existe. Le contenu, lui, arrive après le
 *   sous-titre, qui porte déjà sa propre marge basse. */
.panel h3 + :not(.s){margin-top:16px}
/* ── DEUX PANNEAUX QUI SE SUIVENT NE DOIVENT PAS SE TOUCHER (2026-08-14) ───────
 * `.sec` porte l'écart entre RUBRIQUES (28 px) ; `.panel`, lui, n'avait aucune
 * marge. Tant qu'une page range un panneau par rubrique — le cas de la plupart —
 * l'écart vient de `.sec` et rien ne manque. Mais live.php empile SIX panneaux
 * dans une seule rubrique : ils arrivaient collés, bord contre bord, et l'œil ne
 * distinguait plus où finissait « À venir » et où commençait « Ce qu'il vous
 * reste à faire ».
 *
 * ⚠ LA RÈGLE NE PEUT QUE SÉPARER, JAMAIS DOUBLER : `.panel` n'a aucune marge
 *   verticale par ailleurs, et le sélecteur exige l'adjacence ENTRE ENFANTS
 *   DIRECTS d'une rubrique. Les panneaux rangés dans une grille ou une colonne
 *   flex (`.row2`, `.lv-grid`, `.lv-cote`) ne sont pas enfants directs de `.sec` :
 *   leur `gap` continue de commander seul, sans s'additionner. */
/* ⚠ ON N'ÉCRASE PAS UN ÉCART CHOISI. Le direct pose `.lv-mt`, `.lv-mt2`,
 *   `.lv-mt3` sur certains panneaux — des marges décidées bloc par bloc. Ma règle
 *   a trois classes (0-3-0) contre deux pour `.cc .lv-mt3` (0-2-0) : elle
 *   l'emportait, et remplaçait 14 px voulus par 18 px génériques. Les marges ne
 *   s'additionnent pas, il n'y avait donc pas de doublement — mais une décision
 *   d'auteur passait à la trappe sans que rien ne le signale. L'exclusion rend la
 *   règle utile là où rien n'est dit, et muette là où quelqu'un a tranché. */
.sec > .panel + .panel:not([class*="lv-mt"]){margin-top:18px}
/* 🔴 UN PANNEAU QUI SUIT UN BLOC DE TUILES ÉTAIT COLLÉ À LUI (2026-08-23).
 *   La règle du dessus ne couvre que « panneau après panneau ». `.kpis` porte
 *   une marge HAUTE (l.185) mais aucune marge basse : le premier panneau qui
 *   suivait une rangée de tuiles n'avait donc AUCUN écart, et venait toucher
 *   les tuiles. Signalé sur live-memoire.php entre les quatre chiffres et
 *   « De quoi parlent vos comptes rendus » — mais le défaut est celui du
 *   système, pas de la page : **15 pages** enchaînent ces deux blocs, dont
 *   a-propos-du-cours, alie-portfolio, desiderata et performances-enseignant.
 *   Même valeur que l'enchaînement panneau→panneau, pour que le rythme
 *   vertical d'une section reste le même quel que soit le bloc précédent.
 *   ⚠ La même exclusion `lv-mt` est reprise : une page qui a tranché son
 *     écart garde le sien, comme le veut la règle du dessus. */
.sec > .kpis + .panel:not([class*="lv-mt"]){margin-top:18px}
.gauge{display:flex;align-items:center;gap:24px;margin-top:20px;flex-wrap:wrap}
.ring{--p:0;width:116px;height:116px;border-radius:50%;flex:none;display:grid;place-items:center;position:relative;background:conic-gradient(var(--blue) calc(var(--p)*1%),var(--surf2) 0)}
.ring::before{content:"";position:absolute;width:90px;height:90px;border-radius:50%;background:var(--surf)}
.ring b{position:relative;font-size:1.65rem;font-weight:900;color:var(--ink)}
.ring b small{font-size:.68rem;color:var(--ink3);font-weight:700}
.spark{display:flex;align-items:flex-end;gap:5px;height:78px;margin-top:8px}
.spark i{flex:1;min-width:6px;background:linear-gradient(var(--blue),var(--blue-8));border-radius:6px 6px 3px 3px;opacity:.9}
.empty{color:var(--ink3);font-size:.9rem;padding:18px 0;display:flex;align-items:center;gap:9px}
/* ⚠ SUR TÉLÉPHONE, UN ÉTAT VIDE QUI PORTE UNE PHRASE LA LAISSE COULER. En flex,
     la phrase, son lien et sa `.hint` devenaient trois colonnes côte à côte :
     « Retour aux classes & cours » tenait sur 55 px et quatre lignes
     (a-propos-du-cours, 320 px, mesuré le 2026-09-19). En bloc, l’icône, la
     phrase et le lien s’enchaînent comme une ligne de texte.
   ⚠ `.tall` (colonne centrée) et `.chartbox.empty` gardent leur flex.
   🔴 L’ESPACE APRÈS `@media` EST VOULU (voir `.hero .m` plus haut). */
@media (max-width:640px){
  .empty:not(.tall):not(.chartbox){display:block}
  .empty:not(.tall):not(.chartbox)>i:first-child{margin-inline-end:4px}
}
.alie{background:var(--grad-or);border:1px solid var(--bl2);border-radius:20px;padding:24px;box-shadow:var(--sh);position:relative;overflow:hidden}
/* La carte ALIE prend var(--grad-or), donc la couleur du rôle ; son halo était
   resté orange. Même correction. */
.alie::after{content:"";position:absolute;right:-40px;top:-40px;width:150px;height:150px;border-radius:50%;background:radial-gradient(circle,rgba(var(--stu-role-rgb,239,127,79),.16),transparent 70%);z-index:0}
.alie>*{position:relative;z-index:1}
/* L'ORBE D'ALIE — la plus grande pastille des pages d'accueil. Elle mélangeait
   --amber, un jeton SÉMANTIQUE (celui des alertes), et --blue-7, la principale :
   deux couleurs qui ne se répondaient pas, dont l'une n'avait rien à faire là.
   Elle prend le dégradé d'accent, comme les cinq pastilles du menu et des
   sections — c'est la même chose, en plus grand. */
.alie .orb{width:52px;height:52px;border-radius:16px;display:grid;place-items:center;color:#fff;font-size:1.45rem;background:var(--grad-ic)}
.alie .say{font-family:var(--serif);font-size:1.06rem;margin:16px 0 18px;color:var(--ink);line-height:1.5}
/* icônes éducatives/thématiques en filigrane (comme l'entête) */
.alie-deco{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden}
.alie-deco i{position:absolute;color:var(--stu-or-k)}
.alie-deco i:nth-child(1){top:-6px;right:5%;font-size:4.6rem;opacity:.11;transform:rotate(-10deg)}
.alie-deco i:nth-child(2){bottom:-12px;right:22%;font-size:3.4rem;opacity:.10;transform:rotate(8deg)}
.alie-deco i:nth-child(3){top:40%;right:34%;font-size:2.4rem;opacity:.08;transform:rotate(14deg)}
.alie-deco i:nth-child(4){bottom:10%;right:3%;font-size:3rem;opacity:.10;transform:rotate(-6deg)}
.alie-deco i:nth-child(5){top:8%;right:40%;font-size:2rem;opacity:.08;transform:rotate(10deg)}
.alie-deco i:nth-child(6){bottom:36%;right:46%;font-size:2rem;opacity:.07;transform:rotate(-12deg)}
/* ⚠ LES OMBRES SUIVENT LE FOND (2026-08-03). Le bouton prenait bien la couleur
   du rôle en fond — var(--blue) est repeint par la couche de rôle — mais son
   ombre portée restait rgba(239,127,79), L'ORANGE DE LA DIRECTION, écrit en dur.
   Un élève avait donc un bouton bleu posé sur un halo orange, un parent un
   bouton violet sur ce même halo. Signalé par l'utilisateur. Le fallback garde
   l'orange d'origine, pour un rendu identique en l'absence de rôle. */
.btn{border:0;border-radius:12px;padding:12px 18px;font:inherit;font-size:.9rem;font-weight:800;cursor:pointer;display:inline-flex;gap:9px;align-items:center;background:var(--blue);color:#fff;box-shadow:0 8px 20px rgba(var(--stu-role-rgb,239,127,79),.28);transition:.15s;text-decoration:none}
@media (hover:hover){/* ⚠ `:not(:disabled)` À LA SOURCE, plutôt qu'une contre-règle. Neutraliser le
   survol après coup obligerait à réécrire la couleur de fond — or elle dépend
   de la variante (.btn-info, .btn-g, .btn-ghost…), qu'une contre-règle ne peut
   pas connaître : un bouton désactivé aurait viré au bleu au survol. */
.btn:hover:not(:disabled){transform:translateY(-2px);background:var(--blue-6)}}
/* ── UN BOUTON DÉSACTIVÉ DOIT AVOIR L'AIR DÉSACTIVÉ (2026-08-11) ──────────────
   🔴 SIGNALÉ PAR LE CLIENT : « je veux que les boutons d'enregistrement soient
      inactifs pendant le traitement ». Ils l'ÉTAIENT déjà — les neuf pages de
      création et de modification posent `disabled` avant l'envoi et le retirent
      à la réponse, échec compris. Mais RIEN NE LE MONTRAIT : la seule règle
      `[disabled]` du projet vivait sous `.stumodal-f`, donc uniquement dans les
      pieds de fenêtre. Ailleurs, le bouton gardait sa couleur, son relief et son
      effet de survol — on cliquait donc à nouveau, en croyant que rien ne s'était
      passé. Le défaut n'était pas dans le comportement, il était dans ce qui se
      voit.
   ⚠ `pointer-events:none` serait une erreur ici : il empêcherait le curseur
     « progress » d'apparaître, et masquerait un éventuel `title`. `disabled`
     suffit à bloquer le clic — on ne s'occupe que de l'apparence. */
.btn:disabled,.btn[disabled]{opacity:.55;cursor:progress;filter:saturate(.65);transform:none}
/* duo « Assistant & communauté » : deux blocs impactants côte à côte, largeur égale */
.row2.duo{display:grid;grid-template-columns:1fr 1fr;gap:18px}
.row2.duo>.alie{flex:none}
.alie-t{font-weight:900;margin-top:12px;font-size:1.05rem}
/* Bloc communauté : fond bleu subtil (teinte légèrement distincte), icônes orange */
.alie2{background:linear-gradient(160deg,var(--stu-or-tint),var(--stu-or-tint3));border-color:var(--stu-or-bd)}
/* Le halo du bloc communauté gardait un bleu écrit en dur, alors que son fond
   (--grad-bl) suit la secondaire : halo bleu sur fond orange chez un enseignant. */
.alie2::after{background:radial-gradient(circle,rgba(var(--stu-or-rgb,244,125,74),.14),transparent 70%)}
.orb2{background:#fff!important;color:var(--stu-or-k)!important;font-size:1.35rem!important}
.alie-links{display:flex;gap:10px;flex-wrap:wrap}
.btn-g{background:var(--blue);box-shadow:0 8px 20px rgba(var(--stu-role-rgb,239,127,79),.28)}
@media (hover:hover){.btn-g:hover:not(:disabled){background:var(--blue-6)}}
/* Le bouton principal de la carte « Communauté connectée » : elle est teintée
   d orange, il l est aussi. La règle vise .alie2 et non .btn-g, qui sert
   ailleurs sur des cartes au rôle (alie-portfolio.php). */
.alie2 .btn-g{background:var(--stu-or-btn);box-shadow:0 8px 20px rgba(var(--stu-or-rgb,244,125,74),.28)}
@media (hover:hover){.alie2 .btn-g:hover:not(:disabled){background:var(--stu-or-btn-h)}}
.btn-ghost{background:var(--surf);color:var(--ink2);border:1px solid var(--line);box-shadow:none}
@media (hover:hover){.btn-ghost:hover:not(:disabled){background:var(--surf2);color:var(--blue);border-color:var(--blue)}}
.btn-ghost i{color:var(--stu-or-k)}
@media(max-width:680px){.row2.duo{grid-template-columns:1fr}}

/* ---------------- répertoire ---------------- */
.rep{background:var(--cardg);border:1px solid var(--line);border-radius:20px;box-shadow:var(--sh);overflow:hidden}
.tabs{display:flex;gap:4px;padding:14px 16px 0;overflow-x:auto;border-bottom:1px solid var(--line)}
.tabs::-webkit-scrollbar{height:0}
.tab{border:0;background:transparent;font:inherit;font-size:.88rem;font-weight:750;color:var(--ink3);padding:11px 15px;cursor:pointer;white-space:nowrap;display:flex;gap:8px;align-items:center;transition:.14s;border-bottom:2.5px solid transparent;margin-bottom:-1px}
.tab .c{background:var(--surf2);color:var(--ink2);border-radius:999px;font-size:.72rem;padding:1px 9px;font-weight:800}
.tab i{color:var(--stu-or-k)}
@media (hover:hover){.tab:hover{color:var(--blue)}}
.tab.on{color:var(--blue-7);border-bottom-color:var(--blue)}
.tab.on .c{background:var(--blue);color:#fff}
.pane{display:none;padding:20px}.pane.on{display:block;animation:fade .28s ease}
@keyframes fade{from{opacity:0;transform:translateY(5px)}to{opacity:1;transform:none}}
.panehead{display:flex;gap:10px;align-items:center;margin-bottom:14px;flex-wrap:wrap}
/* ══ LES COMMANDES POSÉES DANS UN EN-TÊTE DE PANNEAU (2026-09-05) ════════════
 * 🔴 UN `<select>` NU DANS UN `.panehead` RESSORT COMME UN CONTRÔLE DU SYSTÈME
 *    au milieu d'un écran dessiné : bordure grise du navigateur, coins carrés,
 *    police et taille qui ne sont pas celles de la page. Le défaut se voyait sur
 *    « Réseau Studirium — Période », et il se voit partout où un panneau porte
 *    son propre filtre.
 *
 * ⚠ LES VALEURS NE SONT PAS INVENTÉES : ce sont celles de `.mk-filtres select`
 *   (inclusions/marche_css.php), qui habille déjà les filtres du marché. Deux
 *   listes déroulantes voisines ne doivent pas avoir deux allures.
 *
 * ⚠ ON NE STYLE PAS `select` TOUT COURT. La portée est l'en-tête de panneau —
 *   là où une commande accompagne un titre. Une règle générale aurait touché les
 *   listes des formulaires, qui ont déjà la leur (`.cc .form-select`, les
 *   modales), et les aurait mises en désaccord. */
.panehead select,
.panehead input[type=search],
.panehead input[type=text],
.panehead input[type=date],
.panehead input[type=number]{border:1px solid var(--line);border-radius:10px;
  background:var(--surf);color:var(--ink2);font:inherit;font-size:.82rem;
  padding:7px 9px;min-width:0;max-width:100%}
.panehead select:focus-visible,
.panehead input:focus-visible{outline:2px solid var(--blue);outline-offset:1px}
@media (hover:hover){
  .panehead select:hover,
  .panehead input:hover{border-color:var(--stu-or-k,#b35c36)}
}
/* La commande d'un panneau se range à droite de son titre, comme la pastille de
   compte : sans cela, elle colle au titre et l'on ne sait plus ce qu'elle règle.
   ⚠ `it.css` pose déjà `.cc .panehead > :last-child:not(:first-child)` — quatre
     classes, plus lourd que ces deux-ci : là où les deux s'appliquent, elles
     disent la MÊME chose, et aucune ne défait l'autre. */
.panehead > form:last-child,
.panehead > select:last-child{margin-inline-start:auto}
.panehead > form{display:flex;gap:8px;align-items:center;margin:0}

/* ══ LE TEXTE RÉSERVÉ AUX LECTEURS D'ÉCRAN ═══════════════════════════════════
 * 🔴 `.visually-hidden` ÉTAIT ÉCRITE VINGT-TROIS FOIS ET DÉFINIE NULLE PART.
 *    Elle vient de Bootstrap, que le shell NE CHARGE PAS (seules ses icônes le
 *    sont). Résultat : chaque étiquette censée être invisible s'affichait en
 *    clair — « Période » à côté d'une liste déroulante, « Votre message » au-
 *    dessus d'un champ, « Toggle Dropdown » dans un menu. Le défaut avait déjà
 *    été DIAGNOSTIQUÉ dans `its-hub.php` (un commentaire l'explique et le
 *    contourne par `aria-label`), sans que personne pose la règle manquante.
 *
 * ⚠ NEUF ÉCRANS EN DÉPENDENT — marche-impact, marche-messages, banque-messages,
 *   capital-plans, capital-convertibles, shell_capital, rapport-enfant et deux
 *   fragments de tableau de bord. Les corriger un par un aurait été neuf
 *   occasions d'en oublier un ; la règle est ici, une fois.
 *
 * ⚠ ON REPREND LA DÉFINITION DE BOOTSTRAP À L'IDENTIQUE (y compris
 *   `:not(caption)`, sans quoi une légende de tableau se déplacerait hors de son
 *   tableau) : le HTML a été écrit pour elle, pas pour une approximation. */
.visually-hidden,
.visually-hidden-focusable:not(:focus):not(:focus-within){width:1px!important;
  height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;
  clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}
.visually-hidden:not(caption),
.visually-hidden-focusable:not(:focus):not(:focus-within):not(caption){position:absolute!important}
.search{flex:1;min-width:200px;display:flex;align-items:center;gap:9px;background:var(--surf2);border:1px solid var(--line);border-radius:12px;padding:10px 14px;transition:.15s}
/* L'anneau de focus est le même cas : la bordure suivait le rôle, l'anneau non. */
.search:focus-within{border-color:var(--blue);box-shadow:0 0 0 4px rgba(var(--stu-role-rgb,239,127,79),.12);background:var(--surf)}
.search input{border:0;background:transparent;outline:0;font:inherit;font-size:.92rem;color:var(--ink);width:100%}
.search i{color:var(--stu-or-k)}

/* ══ LA BASCULE DE VUES : DÉMÉNAGÉE DANS assets/css/stu-vues.css (2026-09-20) ══
 * `.stu-vues` / `.stu-vbtn` et leur repli téléphone vivent maintenant dans leur
 * propre feuille, parce que `index.php` en a besoin et ne charge pas shell.css.
 * Rien n'a changé de valeur ; shell_haut.php sert les deux feuilles à la suite.
 * 🔴 NE PAS LES RÉÉCRIRE ICI : deux définitions du même rail divergeraient.
 * La règle contextuelle `.stu-rep .stu-vues` reste ci-dessous — elle appartient
 * au répertoire, pas au rail, et sa spécificité la fait gagner quel que soit
 * l'ordre de chargement des deux feuilles. */

/* ══ `.stu-rep` — LE RÉPERTOIRE, LE MÊME PARTOUT (2026-09-06) ════════════════
 *
 * 🔴 IL N'EXISTAIT QU'À UN SEUL ENDROIT, ET SOUS UN NOM DE PAGE.
 *    `liste-des-classes-et-cours.php` déclarait `.cc .lcc-boite`, `.lcc-toolbar`,
 *    `.lcc-grid` et `.lcc-card` dans son propre `$stu_css` : la forme de
 *    répertoire la plus aboutie du produit — une carte blanche qui réunit la
 *    recherche, la bascule de vues, la grille et sa pagination — était
 *    inaccessible à toute autre page. Demande du propriétaire, 2026-09-06 :
 *    « exactement similaire à celui de la page liste des classes et cours ».
 *
 * ⚠ « EXACTEMENT LE MÊME » SE TIENT EN PARTAGEANT LA RÈGLE, PAS EN LA RECOPIANT.
 *   Recopier ces déclarations dans `mon-marche.php`, c'était parier que deux
 *   endroits changeront ensemble ; ils ne le font jamais. C'est la leçon déjà
 *   tirée ici même pour la bascule de vues, et pour les dix copies de `.stu-al`.
 *
 * ⚠ LE NOUVEAU NOM N'EXIGE PAS `.cc`, et c'est nécessaire : `mon-marche.php`
 *   n'ouvre aucun conteneur `.cc` — il empile des `<section class="panel">`.
 *   Exactement la situation de `fiabilite-donnees.php` quand `.lcc-views` est
 *   devenue `.stu-vues`. L'ancien nom, lui, garde sa portée : on ne desserre pas
 *   ce qui marche.
 *
 * ⚠ LES VALEURS SONT CELLES DE LA PAGE DE RÉFÉRENCE, AU PIXEL PRÈS — déplacées,
 *   pas réinventées. Son rendu ne doit pas bouger d'un cheveu, et c'est
 *   vérifiable : les déclarations sont les siennes, elles ont seulement changé
 *   de fichier. */
.stu-rep,.cc .lcc-boite{background:var(--surf);border:1px solid var(--line);
  border-radius:20px;box-shadow:var(--sh);padding:16px 16px 18px}
/* La barre d'outils : recherche à gauche, bascule de vues à droite. */
.stu-rep-bar,.cc .lcc-toolbar{display:flex;gap:12px;align-items:center;flex-wrap:wrap;
  margin-bottom:14px}
.stu-rep .stu-rep-bar,.cc .lcc-boite .lcc-toolbar{margin-bottom:16px}
.stu-rep-bar .search,.cc .lcc-toolbar .search{flex:1;min-width:220px}
/* ⚠ LE CHAMP ET LA BASCULE PASSENT SUR --surf2 DANS UNE CARTE BLANCHE : sur le
 *   fond de page ils se détachaient déjà, mais posés sur du blanc pur ils s'y
 *   fondraient. Le gris léger leur rend leur relief. */
.stu-rep .search,.stu-rep .stu-vues,
.cc .lcc-boite .search,.cc .lcc-boite .stu-vues{background:var(--surf2)}
/* La grille, et la carte qu'elle range. */
.stu-rep-grid,.cc .lcc-grid{display:grid;
  grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:16px}
.stu-rep-card,.cc .lcc-card{background:var(--cardg);border:1px solid var(--line);
  border-radius:18px;box-shadow:var(--sh);padding:18px;display:flex;
  flex-direction:column;gap:12px;
  transition:transform .16s,box-shadow .16s,border-color .16s}
@media (hover:hover){
  .stu-rep-card:hover,.cc .lcc-card:hover{transform:translateY(-4px);
    box-shadow:var(--sh2);border-color:var(--bl2)}
}
/* ⚠ SUR TÉLÉPHONE, LA RECHERCHE PREND SA LIGNE. Le champ et la bascule sur une
 *   même ligne de 375 px ne laissent lisible ni l'un ni l'autre. Reprise de la
 *   requête de média de la page de référence, pour la même raison. */
@media (max-width:640px){
  .stu-rep-bar,.cc .lcc-toolbar{gap:9px}
  .stu-rep-bar .search,.cc .lcc-toolbar .search{flex:1 1 100%;max-width:none;min-width:0}
}

.exp{display:flex;gap:8px}
.exp a{display:inline-flex;align-items:center;gap:7px;font-size:.82rem;font-weight:700;color:var(--ink2);border:1px solid var(--line);border-radius:11px;padding:9px 13px;background:var(--surf);transition:.14s}
@media (hover:hover){.exp a:hover{border-color:var(--blue);color:var(--blue)}}
.tbl-wrap{overflow-x:auto;border:1px solid var(--line);border-radius:14px}
table.stu{width:100%;border-collapse:collapse;font-size:.9rem}
/* L'en-tête de colonne était en --ink3 sur --surf2, soit 2,84:1 — sous le seuil
   même d'un pictogramme, alors que c'est du TEXTE (petit, capitales, espacé,
   donc déjà exigeant à lire). --ink2 donne 5,58:1 sans rien changer d'autre au
   dessin. Corrigé le 2026-08-03, après l'avoir d'abord contourné dans la seule
   feuille du module IA — le défaut valait pour les trente-trois pages. */
table.stu th{background:var(--surf2);color:var(--ink2);font-size:.72rem;font-weight:800;text-transform:uppercase;letter-spacing:.07em;text-align:start;padding:12px 16px;white-space:nowrap;border-bottom:1px solid var(--line)}
table.stu td{padding:12px 16px;border-top:1px solid var(--line2);color:var(--ink2);vertical-align:middle}
table.stu tbody tr{transition:background .12s}
@media (hover:hover){table.stu tbody tr:hover td{background:var(--bl-025)}}
table.stu tbody td:first-child{font-weight:700;color:var(--ink)}
/* ⚠ SUR TÉLÉPHONE, LA LIGNE SECONDAIRE D’UNE CELLULE NE TOMBE PAS SOUS 10em.
     Le tableau défile déjà de côté (sous 900 px, plus bas) ; ses en-têtes et
     ses pastilles ne passent pas à la ligne, donc la colonne de texte retombait
     à la largeur de son plus long mot : « [DÉMO] Les moyennes ne se
     recalculent pas après saisie » sur 61 px et six lignes (it-tickets,
     it-alertes, it-interventions, 320-375 px, mesuré le 2026-09-19).
   ⚠ `min-width` ne prend que sur un `<small>` posé EN BLOC : en ligne, rien ne bouge. */
@media (max-width:640px){table.stu td>small{min-width:10em}}
.pill{font-size:.72rem;font-weight:800;border-radius:999px;padding:3px 10px;display:inline-block}
.pill.f{background:var(--tint-bl);color:var(--info)}.pill.m{background:var(--bl);color:var(--blue-7)}
:root[data-theme="dark"] .pill.f{background:#122a3f;color:#6cc4ff}
.count{color:var(--ink3);font-size:.8rem;margin-top:12px}
.norow{color:var(--ink3);text-align:center;padding:28px}

/* ── PAGINATION DES RÉPERTOIRES (2026-08-09) ─────────────────────────────────
   Les tableaux du répertoire rendaient TOUTES leurs lignes d'un coup : lisible
   sur un petit établissement, illisible dès quelques centaines d'apprenants —
   et rien ne bornait la requête. Le découpage est posé par shell_js.php sur
   tout tableau « .stu[id^=t-] », donc sur les écrans à venir comme sur
   accueil.php. Il coopère avec la recherche : on pagine ce qui est FILTRÉ. */
.pager{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:12px}
.pager .pg-etat{color:var(--ink3);font-size:.8rem;margin-inline-end:auto}
/* 🔴 `.pg-nav` N'AVAIT AUCUNE RÈGLE, ALORS QUE SES TROIS VOISINES EN ONT UNE.
   C'est le <span> qui porte les boutons de page — shell_js.php et le hub du
   direct l'écrivent tous les deux, en citant « mêmes classes .pager / .pg-etat /
   .pg-nav / .pg-b » : trois sur quatre existaient. Sans elle, ce span reste en
   ligne, donc le `gap:10px` du pager ne s'applique pas à son contenu ; et comme
   les boutons y sont ajoutés par appendChild, il n'y a même pas d'espace de
   balisage entre eux. Ils se touchaient bord à bord, posés sur la ligne de base.
   Le défaut vaut PARTOUT où une liste est paginée — répertoires, paie, hub du
   direct. Trouvé par le contrôle de classes orphelines de test_live_rendu.php. */
.pager .pg-nav{display:inline-flex;align-items:center;gap:6px;flex-wrap:wrap}
.pager .pg-b{display:inline-flex;align-items:center;justify-content:center;min-width:32px;height:32px;
  padding:0 9px;border:1px solid var(--line);border-radius:8px;background:var(--surf);color:var(--ink2);
  font-size:.82rem;font-weight:700;cursor:pointer;transition:background .15s,border-color .15s,color .15s}
@media (hover:hover){.pager .pg-b:hover:not(:disabled){background:var(--bl-025);border-color:var(--bl2);color:var(--ink)}}
.pager .pg-b:disabled{opacity:.45;cursor:default}
/* La page courante prend l'orange de l'action, comme les autres commandes du
   projet — « le menu dit le rôle, l'orange du logo dit l'action ».
   ⚠ --stu-or-btn ET NON --stu-or : c'est le jeton prévu pour un APLAT portant du
     texte blanc (les boutons de .alie2 l'emploient déjà). --stu-or est la teinte
     nue, calibrée pour du texte et des icônes, pas pour un fond. Le premier jet
     visait « --stu-or-t », qui n'existe pas : la couleur du texte serait tombée
     en « invalide », donc héritée, et le libellé aurait pu disparaître dans
     l'aplat. */
.pager .pg-b[aria-current="page"]{background:var(--stu-or-btn);border-color:var(--stu-or-btn);color:#fff;cursor:default}
.pager .pg-gap{color:var(--ink3);padding:0 2px;font-size:.82rem}
.pager .pg-taille{display:inline-flex;align-items:center;gap:6px;color:var(--ink3);font-size:.78rem}
.pager .pg-taille select{border:1px solid var(--line);border-radius:8px;background:var(--surf);color:var(--ink2);
  font:inherit;font-size:.8rem;padding:5px 7px}
@media(max-width:620px){.pager .pg-etat{margin-inline-end:0;width:100%}}

/* bouton documents + modal « À télécharger » */
.docbtn{border:1px solid var(--line);background:var(--surf);color:var(--ink2);border-radius:9px;padding:6px 11px;font:inherit;font-size:.8rem;font-weight:700;cursor:pointer;display:inline-flex;gap:6px;align-items:center;transition:.14s}
@media (hover:hover){.docbtn:hover{border-color:var(--blue);color:var(--blue)}}
.faint{color:var(--ink3)}
.wait{display:inline-block;padding:.1rem .5rem;border-radius:999px;background:var(--bl);color:var(--blue-7);font-size:.72rem;font-weight:600}
.stumodal{position:fixed;inset:0;z-index:200;display:none;align-items:center;justify-content:center;padding:20px;background:rgba(18,28,55,.5)}
.stumodal.open{display:flex;animation:fade .18s ease}
/* ── Modals : taille normale, fond neutre, EN-TÊTE ORANGE SUBTIL bien visible ── */
.stumodal-card{background:var(--surf);border:1px solid var(--line);border-radius:20px;box-shadow:var(--sh2);width:1040px;max-width:100%;max-height:86vh;overflow:hidden;display:flex;flex-direction:column;animation:rise .3s cubic-bezier(.2,.75,.2,1)}
/* ⚠ « xl » VIVAIT DANS LE STYLE D'UNE SEULE PAGE (a-propos-du-cours.php:668) :
   les fenêtres des autres écrans ne pouvaient pas s'en servir, la classe n'y
   existait pas. Remontée ici, dans la feuille partagée, où toute fenêtre du
   projet peut la demander. `max-width:100%` est déjà porté par .stumodal-card :
   sur un écran étroit, la carte se réduit d'elle-même. */
.stumodal-card.xl{width:1160px}
/* ══ LA FENÊTRE PORTE SA COULEUR, ET SON CONTENU PEUT LA LIRE ════════════════
 * Les cinq variantes réécrivaient chacune les mêmes DEUX règles sur l'en-tête,
 * avec leurs trois jetons recopiés. La teinte devient une VARIABLE de la carte :
 * le bandeau la lit — et le compte rendu d'un contrôleur aussi (voir .stu-cr,
 * plus bas, à côté de la famille .alert dont il doit passer devant).
 * Ajouter une sixième teinte, c'est désormais UNE ligne au lieu de trois.
 *
 * ⚠ LES JETONS SONT POSÉS SUR .stumodal-card, PAS SUR :root : deux fenêtres de
 *   teintes différentes coexistent dans la même page, et une variable globale
 *   ne saurait pas de laquelle elle parle. Sur la carte, chaque contenu hérite
 *   de SA fenêtre.
 *
 * ⚠ LA SPÉCIFICITÉ DES RÈGLES D'EN-TÊTE EST CONSERVÉE À L'IDENTIQUE (0,3,0 avec
 *   !important). La note ci-dessous rappelle qu'une page avait tenté sa propre
 *   règle et qu'elle était restée INERTE, perdant à spécificité égale : la
 *   descendre à (0,2,0) en factorisant aurait pu réveiller, ailleurs, une règle
 *   qu'on croit morte depuis des mois. On ne change pas le poids, on change la
 *   source de la couleur. */
.stumodal-card{--mdl-tint:var(--mh-or);--mdl-bd:var(--mh-or-b);--mdl-k:var(--mh-or-k)}
/* Gestion & outils : bleu subtil. */
.stumodal.blue   .stumodal-card{--mdl-tint:var(--mh-bl);--mdl-bd:var(--mh-bl-b);--mdl-k:var(--mh-bl-k)}
/* Action destructive : rouge subtil, la couleur du bouton de suppression. */
.stumodal.red    .stumodal-card{--mdl-tint:var(--mh-rg);--mdl-bd:var(--mh-rg-b);--mdl-k:var(--mh-rg-k)}
.stumodal.stu-or .stumodal-card{--mdl-tint:var(--stu-or-tint2,#fbe9df);--mdl-bd:var(--stu-or-bd,#f4d6c6);--mdl-k:var(--stu-or-kk,#8f492b)}
.stumodal.green  .stumodal-card{--mdl-tint:var(--mh-vt,#e6f6ee);--mdl-bd:var(--mh-vt-b,#bfe6d3);--mdl-k:var(--mh-vt-k,#10744d)}
.stumodal-card .stumodal-h,
.stumodal .stumodal-card .stumodal-h{background:var(--mdl-tint) !important;border-bottom-color:var(--mdl-bd) !important}
.stumodal-card .stumodal-k,
.stumodal .stumodal-card .stumodal-k{color:var(--mdl-k)}
.stumodal-card .stumodal-x{background:var(--mh-x)}
/* ── Modal d'une action ORANGE DE MARQUE : en-tête orange ────────────────────
 * ⚠ « --mh-or » NE VEUT PLUS DIRE ORANGE. Il portait bien l'orange à l'origine,
 *   mais inclusions/role_utilisateur.php:1222 le réémet depuis la couleur du
 *   RÔLE : sur un profil vert il est vert, sur un profil bleu il est bleu. C'est
 *   voulu — l'en-tête par défaut suit le rôle, comme le bouton par défaut. Le
 *   nom, lui, est resté celui de la première couleur qu'il a portée.
 *
 * 🔴 IL N'Y AVAIT DONC AUCUN MOYEN DE DEMANDER UN EN-TÊTE ORANGE. Les écrans
 *    dont l'action principale est orange — « Nouvelle matière » et ses pareils,
 *    dont le bouton prend --stu-or-btn — recevaient un en-tête de la couleur du
 *    rôle : bouton orange, bandeau bleu, dans la même fenêtre. Une page l'avait
 *    contourné par une règle à elle ; elle était INERTE, les trois règles
 *    ci-dessus portant `!important` et gagnant à spécificité égale.
 *
 * ⚠ LES JETONS SONT DES rgba(), comme le reste de la famille --stu-or : ils se
 *   composent avec le papier, donc une seule déclaration sert les deux thèmes —
 *   contrairement à --mh-bl / --mh-rg, qui en demandent deux.
 *
 * ⚠ LE SUR-TITRE PREND --stu-or-kk, PAS --stu-or-k, ET C'EST MESURÉ.
 *   `.stumodal-k` fait .7 rem (≈ 11 px) : du PETIT texte au sens WCAG, donc
 *   4,5:1 exigés. Sur ce bandeau à 22 % d'orange, --stu-or-k tombe à **3,79:1**
 *   — il est calibré contre la SURFACE, pas contre une teinte de sa propre
 *   couleur. --stu-or-kk existe exactement pour ce cas (role_utilisateur.php
 *   :1190) et donne 5,4:1. C'est le même écueil que le compteur du menu. */

/* ── Modal d'une action VERTE : en-tête vert ─────────────────────────────────
 * Quatrième teinte du même jeu, demandée le 2026-08-19 pour la saisie rapide.
 * Elle suit la construction de ses trois sœurs — trois jetons, déclarés dans les
 * quatre blocs de :root, et une règle en `!important` pour passer devant
 * l'en-tête par défaut.
 *
 * ⚠ LE SUR-TITRE N'EST PAS `--green`, ET C'EST MESURÉ. `.stumodal-k` fait
 *   .7 rem : du petit texte au sens WCAG, donc 4,5:1 exigés. Posé sur le vert
 *   clair du bandeau, --green (#16a06a) ne donne que **3,00:1** — il est calibré
 *   contre la surface, pas contre une teinte de sa propre couleur, exactement
 *   comme --stu-or-k plus haut. D'où --mh-vt-k : #10744d en clair (5,18:1) et
 *   #48b58a en sombre (5,09:1, mesuré sur le bandeau composé, pas sur le papier
 *   — un premier candidat plus foncé n'y atteignait que 4,07:1). */
/* ══ UN SEUL DÉCLENCHEUR DE THÈME : [data-theme] ═════════════════════════════
 *
 * 🔴 CE BLOC ÉTAIT UN `@media (prefers-color-scheme:dark)`, ET IL CASSAIT
 *    L'APPLICATION ENTIÈRE POUR QUI N'A JAMAIS TOUCHÉ LE BOUTON ◐.
 *
 *    L'intention écrite ici était juste — « le bouton et la préférence système
 *    doivent mener au même endroit » — mais elle n'a été tenue QUE SUR LA
 *    MOITIÉ DES JETONS. Les TEINTES (ce bloc, plus les familles de rôle de
 *    `stu_role_css()`) suivaient la préférence système ; les NEUTRES — `--ink`,
 *    `--surf`, `--paper`, `--line`, ligne 37 — n'ont jamais eu que
 *    `[data-theme="dark"]`. Le thème se règle par `localStorage['stu-theme']`
 *    (shell_haut.php:68) : sans choix explicite, il n'y a AUCUN attribut.
 *
 *    Donc, sur un système réglé en sombre et un compte qui n'a jamais cliqué :
 *    surfaces CLAIRES, encre CLAIRE-sur-clair… et teintes SOMBRES. Mesuré au
 *    navigateur sur `repetiteur-seance.php` : `--ink` (#1b2333) sur `--bl-025`
 *    (#0c1f2e) donne **1,07:1** — le texte disparaît. Cela vise toute règle qui
 *    lit une encre neutre sur une teinte : survol de ligne `table.stu`,
 *    pastilles, entrées de menu actives, bandeaux d'information.
 *
 * ⚠ POURQUOI SUPPRIMER LA BRANCHE PLUTÔT QUE L'AJOUTER AUX NEUTRES. C'était
 *   l'autre issue, et elle est PIRE ici : `tutelle.css:210`,
 *   `shell_delegation.php:213` et `shell_ministere.php:209` ont chacun mesuré ce
 *   même piège et écrit leurs corrections sous `[data-theme="dark"]` SEUL, de
 *   propos délibéré. Faire basculer le thème sur la préférence système les
 *   laisserait toutes les trois hors-jeu, et ferait réapparaître dans ces trois
 *   modules la couleur de rôle illisible qu'ils viennent de corriger.
 *   Un déclencheur, et un seul.
 *
 * ⚠ RIEN N'EST PERDU POUR LE THÈME SOMBRE : la règle `:root[data-theme="dark"]`
 *   juste en dessous porte EXACTEMENT les mêmes valeurs. C'est la copie
 *   média-query qui disparaît, pas le thème.
 *
 * ⚠ ET LE BOUTON RESTE LA SEULE PORTE. Le jour où l'on voudra suivre le
 *   système, la manière juste est de laisser shell_haut.php POSER l'attribut
 *   depuis `matchMedia` quand `localStorage` est vide — un seul endroit, une
 *   seule vérité, et les trois modules ci-dessus continuent de fonctionner. */

:root[data-theme="dark"]{--grad-or:linear-gradient(160deg,rgba(239,127,79,.17),rgba(239,127,79,.08));--grad-bl:linear-gradient(160deg,rgba(0,161,255,.17),rgba(0,161,255,.08));--grad-bl2:linear-gradient(180deg,transparent,rgba(0,161,255,.30));--grad-bl-bd:rgba(0,161,255,.30);--tint-k:#7cc4f7;--tint-k2:#a8d8fb;--tint-bl:rgba(0,161,255,.15);--tint-bl2:rgba(0,161,255,.11);--tint-bl3:rgba(0,161,255,.08);--tint-bl4:rgba(0,161,255,.22);--tint-bl5:rgba(0,161,255,.30);--tint-bl6:rgba(0,161,255,.18);--tint-rg:rgba(192,57,43,.20);--tint-rg2:rgba(192,57,43,.20);--tint-rg3:rgba(192,57,43,.20);--mh-or:rgba(239,127,79,.20);--mh-or-b:rgba(239,127,79,.30);--mh-or-k:#ffb59a;--mh-bl:rgba(0,161,255,.18);--mh-bl-b:rgba(0,161,255,.30);--mh-bl-k:#7cc4f7;--mh-rg:rgba(192,57,43,.22);--mh-rg-b:rgba(192,57,43,.32);--mh-rg-k:#ff8f80;--mh-x:#1a2238;--mh-vt:rgba(22,160,106,.20);--mh-vt-b:rgba(22,160,106,.32);--mh-vt-k:#48b58a;--btn-noir:#0f1626;--btn-noir-h:#070c16;}
:root[data-theme="light"]{--grad-or:linear-gradient(160deg,#fff6f0,#fde7d9);--grad-bl:linear-gradient(160deg,#eaf4ff,#dbecff);--grad-bl2:linear-gradient(180deg,transparent,var(--grad-bl-bd));--grad-bl-bd:#cfe6fa;--tint-k:#00a1ff;--tint-k2:#0090ea;--tint-bl:#e7f5ff;--tint-bl2:#eef8ff;--tint-bl3:#f4fbff;--tint-bl4:#d6eeff;--tint-bl5:var(--grad-bl-bd);--tint-bl6:#dceefc;--tint-rg:#fbeceb;--tint-rg2:#fdeceb;--tint-rg3:#fdecea;--mh-or:#fbe9df;--mh-or-b:#f4d6c6;--mh-or-k:#d3562a;--mh-bl:#e7f2fb;--mh-bl-b:#cfe6f7;--mh-bl-k:#0a6fb0;--mh-rg:#fdeceb;--mh-rg-b:#f6d0cc;--mh-rg-k:#c0392b;--mh-x:#fff;--mh-vt:#e6f6ee;--mh-vt-b:#bfe6d3;--mh-vt-k:#10744d;--btn-noir:#252f45;--btn-noir-h:#161d2c;}

/* Même défaut que le bandeau : un bleu en dur qu'aucune variable n'atteignait,
   alors que ce bouton est l'accent de marque de cinq pages du shell. Il prend
   la nuance -dd, la seule qui porte du texte blanc sur les 19 rôles. */
.btn-blue{background:var(--stu-role-dd,#00a1ff)!important;box-shadow:0 8px 20px rgba(var(--stu-role-rgb,0,161,255),.28)!important}

.stumodal-h{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;padding:20px 22px;border-bottom:1px solid var(--line)}
.stumodal-k{font-size:.7rem;font-weight:800;letter-spacing:.14em;text-transform:uppercase;color:var(--blue);display:flex;align-items:center;gap:7px}
.stumodal-h h4{margin:6px 0 0;font-size:1.15rem;font-weight:800;letter-spacing:-.02em}
.stumodal-x{width:38px;height:38px;flex:none;border:1px solid var(--line);background:var(--surf);border-radius:11px;color:var(--ink2);cursor:pointer;display:grid;place-items:center}
@media (hover:hover){.stumodal-x:hover{color:var(--danger);border-color:#e0b4b4}}
.stumodal-b{padding:16px 18px 20px;overflow-y:auto;display:flex;flex-direction:column;gap:10px;min-height:0}
/* 🔴 LE CORPS D'UNE FENÊTRE DÉFILE ; SES ENFANTS NE SE COMPRIMENT PAS.
   C'est un flex en COLONNE : ses enfants ont `flex-shrink:1` par défaut. Tant
   qu'un enfant est en `overflow:visible`, son `min-height:auto` vaut la hauteur
   de son contenu et il refuse de rétrécir — le corps déborde donc, et défile
   comme prévu. Mais dès qu'un enfant porte un `overflow` autre que `visible`,
   ce `min-height:auto` retombe à ZÉRO : l'enfant se laisse écraser à la hauteur
   restante et CACHE le reste, pendant que le corps, désormais à la bonne taille,
   n'a plus rien à faire défiler. Le contenu est là, dans le document, et
   inatteignable.
   ⚠ C'EST EXACTEMENT CE QUI EST ARRIVÉ AUX DOCUMENTS ALIE le 2026-08-15 : tant
     que leur feuille n'était pas chargée, `.alie-doc` était un <article> nu qui
     défilait bien ; le jour où elle l'a été, il a gagné son `overflow:hidden` et
     les fenêtres de live-preparation.php ont cessé de montrer la fin des fiches.
     Le correctif d'affichage a créé le défaut d'affichage suivant.
   ⚠ `flex:none` ET NON `flex-shrink:0` SEUL : il faut aussi `flex-basis:auto`,
     sinon un enfant à qui une autre règle donnerait `flex:1` garderait sa base. */
.stumodal-b > *{flex:none}
/* Le pied de fenêtre n'était défini nulle part : les boutons se plaçaient donc
   au hasard, chaque page les habillant à sa façon — et seulement quand ils
   tombaient dans une zone .cc. Il est ici, une fois, pour toutes les pages du
   shell : boutons alignés à droite, même gabarit, « Fermer » sur fond sombre. */
/* Les menus déroulants des fragments repris. Sans Bootstrap, .dropdown-menu
   n'était rien : la liste restait ouverte en permanence, avec ses puces, et
   chaque ligne de tableau en devenait trois fois trop haute. */
.cc .dropdown{position:relative;display:inline-block}
.cc .dropdown-menu{position:absolute;inset-inline-end:0;top:calc(100% + 6px);z-index:40;display:none;
  min-width:190px;margin:0;padding:6px;list-style:none;text-align:start;
  background:var(--surf);border:1px solid var(--line);border-radius:14px;box-shadow:var(--sh2)}
.cc .dropdown-menu.stuouvert{display:block}
.cc .dropdown-menu li{list-style:none}
.cc .dropdown-item{display:flex;align-items:center;gap:9px;padding:8px 11px;border-radius:10px;
  font-size:.85rem;font-weight:650;color:var(--ink2);white-space:nowrap;cursor:pointer}
@media (hover:hover){.cc .dropdown-item:hover{background:var(--tint-bl);color:var(--blue-7)}}
.cc .dropdown-item i{font-size:.95rem;color:var(--stu-or-k)}
.cc .dropdown-toggle::after{content:"";display:inline-block;margin-inline-start:6px;
  border:4px solid transparent;border-top-color:currentColor;translate:0 2px}

/* Les champs d'une fenêtre, décrits une fois pour toutes. Auparavant chaque page
   ne stylait que les siens, et seulement ceux placés dans un .ed-fld : les
   listes posées dans un tableau — les groupes, coefficients et barèmes de
   l'assignation multi-matière, par exemple — restaient à l'état brut. */
.stumodal-b select,.stumodal-b input[type=text],.stumodal-b input[type=search],
.stumodal-b input[type=number],.stumodal-b input[type=password],.stumodal-b input[type=date],
.stumodal-b input[type=email],.stumodal-b input[type=tel],.stumodal-b textarea{
  width:100%;padding:9px 12px;border:1px solid var(--line);border-radius:11px;background:var(--surf);
  color:var(--ink);font:inherit;font-size:.88rem;transition:border-color .13s,box-shadow .13s}
.stumodal-b select:focus,.stumodal-b input:focus,.stumodal-b textarea:focus{
  outline:0;border-color:var(--blue);box-shadow:0 0 0 4px var(--tint-bl)}
.stumodal-b select[disabled],.stumodal-b input[disabled],.stumodal-b textarea[disabled]{
  opacity:.62;cursor:not-allowed;background:var(--surf2)}
.stumodal-b select[multiple]{padding:6px;height:auto}
.stumodal-b select[multiple] option{padding:6px 8px;border-radius:7px}
.stumodal-b td select,.stumodal-b td input[type=text]{min-width:110px;padding:7px 10px;font-size:.83rem}

/* Un champ obligatoire resté vide se signale en rouge, et la marque s'efface dès
   qu'on le renseigne. C'est ce que faisait l'ancienne page en posant text-danger
   et une bordure sur chaque champ manquant. */
.stumodal-b .stu-ko,.stumodal-b .stu-ko:focus{border-color:var(--danger) !important;box-shadow:0 0 0 4px var(--tint-rg) !important}
.stumodal-b label.stu-ko-l{color:var(--danger)}

.stumodal-f{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:9px;padding:14px 18px;border-top:1px solid var(--line);background:var(--surf2)}
.stumodal-f .btn{border:0;border-radius:12px;padding:10px 16px;font:inherit;font-size:.87rem;font-weight:800;line-height:1.2;cursor:pointer;display:inline-flex;gap:8px;align-items:center;background:var(--blue);color:#fff;text-decoration:none;transition:.15s;white-space:nowrap}
@media (hover:hover){.stumodal-f .btn:hover{background:var(--blue-6);color:#fff}}
.stumodal-f .btn[disabled]{opacity:.6;cursor:progress}
.stumodal-f .btn-ghost,.stumodal-f .btn-dark{background:var(--btn-noir);color:#fff;border:0}
@media (hover:hover){.stumodal-f .btn-ghost:hover,.stumodal-f .btn-dark:hover{background:var(--btn-noir-h);color:#fff}}
.stumodal-f .btn-ko{background:var(--danger)}
@media (hover:hover){.stumodal-f .btn-ko:hover{background:#a93226}}
.stumodal-f .btn .spin{width:14px;height:14px;border:2px solid rgba(255,255,255,.45);border-top-color:#fff;border-radius:50%;display:none;animation:stuftourne .7s linear infinite}
.stumodal-f .btn.charge .spin{display:inline-block}
@keyframes stuftourne{to{transform:rotate(360deg)}}
@media(max-width:560px){.stumodal-f{justify-content:stretch}.stumodal-f .btn{flex:1 1 auto;justify-content:center}}
.docrow{display:flex;align-items:center;gap:12px;padding:12px 14px;border:1px solid var(--line);border-radius:13px;background:var(--surf2)}
.docrow-i{width:40px;height:40px;flex:none;border-radius:11px;background:var(--stu-or-tint);color:var(--stu-or-k);display:grid;place-items:center;font-size:1.15rem}
.docrow-t{flex:1;font-weight:700;font-size:.92rem;min-width:0}
.docdl{display:inline-flex;align-items:center;gap:6px;font-size:.8rem;font-weight:700;border-radius:9px;padding:8px 11px;border:1px solid var(--line);color:var(--ink2);background:var(--surf);white-space:nowrap}
@media (hover:hover){.docdl.pdf:hover{border-color:#d9534f;color:#d9534f}}@media (hover:hover){.docdl.xls:hover{border-color:var(--green);color:var(--green)}}
@media(max-width:520px){.docrow{flex-wrap:wrap}.docrow-t{flex-basis:100%}}

/* tableau personnel enrichi */
.pers-av{width:34px;height:34px;border-radius:50%;object-fit:cover;border:1px solid var(--line);background:var(--surf2);display:block}
.pers-nom{font-weight:700;color:var(--ink)}
/* Avatar cliquable → profil enseignant */
.pers-avlink{position:relative;display:inline-block;border-radius:50%;line-height:0}
.pers-avlink .pers-av{transition:box-shadow .15s,transform .15s}
.pers-avgo{position:absolute;inset-inline-end:-3px;bottom:-3px;width:18px;height:18px;border-radius:50%;background:var(--blue);color:#fff;display:grid;place-items:center;font-size:.6rem;box-shadow:0 0 0 2px var(--surf);opacity:0;transform:scale(.6);transition:opacity .15s,transform .15s}
@media (hover:hover){.pers-avlink:hover .pers-av{box-shadow:0 0 0 3px var(--bl2);transform:translateY(-1px)}}
@media (hover:hover){.pers-avlink:hover .pers-avgo{opacity:1;transform:scale(1)}}
/* Nom cliquable → profil enseignant */
.pers-link{display:inline-flex;align-items:center;gap:3px;text-decoration:none;color:inherit}
.pers-link .pers-nom{cursor:pointer}
@media (hover:hover){.pers-link:hover .pers-nom{color:var(--blue-7);text-decoration:underline}}
.pers-chev{font-size:.72rem;color:var(--ink3);transition:transform .15s,color .15s}
@media (hover:hover){.pers-link:hover .pers-chev{color:var(--blue-7);transform:translateX(2px)}}
.pers-hint{font-size:.68rem;color:var(--blue);font-weight:600;margin-top:1px;opacity:.85}
.pers-ens{max-width:300px;font-size:.85rem;line-height:1.35}
.pers-ens .more{background:var(--surf2);border:1px solid var(--line);border-radius:999px;padding:1px 8px;font-size:.7rem;font-weight:800;color:var(--ink2);margin-inline-start:4px}
.enslink{display:inline;text-align:start;background:none;border:0;padding:0;margin:0;font:inherit;color:var(--ink2);cursor:pointer}
@media (hover:hover){.enslink:hover{color:var(--blue)}}
@media (hover:hover){.enslink:hover .more{border-color:var(--blue);color:var(--blue)}}
.ensrow{display:flex;align-items:center;gap:11px;padding:11px 13px;border:1px solid var(--line);border-radius:12px;background:var(--surf)}
.ensrow-i{width:32px;height:32px;flex:none;display:grid;place-items:center;border-radius:9px;background:var(--stu-or-tint);color:var(--stu-or-k)}
.ensrow-t{font-size:.92rem;color:var(--ink);font-weight:600}
.tel{color:var(--ink2);white-space:nowrap;font-weight:600}
@media (hover:hover){.tel:hover{color:var(--blue)}}

/* ---------------- pied de page calqué sur accueil-espace-membre ---------------- */
.stu-foot{margin-top:34px}
.foot-cards{display:grid;grid-template-columns:1fr 1fr 1fr;gap:16px}
.foot-card{background:var(--cardg);border:1px solid var(--line);border-radius:18px;box-shadow:var(--sh);overflow:hidden;padding:16px}
.foot-card img{width:100%;height:150px;object-fit:cover;border-radius:12px;margin-bottom:14px;display:block}
.foot-card h5{margin:0 0 8px;font-size:1.05rem;font-weight:800;letter-spacing:-.02em;color:var(--ink)}
.foot-card p{margin:0;font-size:.9rem;line-height:1.55;color:var(--ink2)}
.foot-social{display:grid;grid-template-columns:1fr 1fr 1fr;gap:16px;margin-top:16px}
.soc-card{background:var(--cardg);border:1px solid var(--line);border-radius:18px;box-shadow:var(--sh);padding:18px;display:flex;flex-direction:column;gap:16px;justify-content:space-between}
.soc-h{display:flex;align-items:center;gap:14px;padding-bottom:14px;border-bottom:1px solid var(--line2)}
.soc-ava{width:52px;height:52px;border-radius:50%;object-fit:cover;flex:none;border:1px solid var(--line)}
.soc-h span{display:block;font-size:.82rem;color:var(--ink3);font-weight:600}
.soc-h strong{font-size:1.05rem;font-weight:800;color:var(--ink);letter-spacing:-.01em}
/* Les trois boutons du PIED (Facebook, YouTube, TikTok) : ils sont sur les 40+
   pages du shell, donc c'est là que le halo orange sous un fond de rôle se
   voyait le plus. */
.soc-btn{display:inline-flex;align-items:center;justify-content:center;gap:9px;background:var(--blue);color:#fff;font-weight:800;font-size:.9rem;padding:11px 16px;border-radius:12px;text-decoration:none;box-shadow:0 8px 20px rgba(var(--stu-role-rgb,239,127,79),.28);transition:.15s}
.soc-btn i{transition:transform .15s}
@media (hover:hover){.soc-btn:hover{background:var(--blue-6)}}
@media (hover:hover){.soc-btn:hover i{transform:translateX(3px)}}
.foot-copy{text-align:center;color:var(--ink3);font-size:.82rem;font-weight:600;padding:24px 0 10px}
@media(max-width:860px){.foot-cards,.foot-social{grid-template-columns:1fr}}

@media(max-width:900px){.content{padding:20px}}
/* Telephone : le titre passe AU-DESSUS de la rangee d'icones. Tout tenait sur une
   seule ligne, ou le titre etait ecrase entre le bouton de menu et les icones.
   Le ressort .sp prend toute la largeur et sert de saut de ligne. */
@media(max-width:640px){
  .topbar{padding:12px 14px;gap:8px;flex-wrap:wrap;row-gap:10px}
  .topbar h1{font-size:1rem}
  .topbar .meta{display:block;font-size:.74rem;margin-top:1px}
  .topbar .sp{flex:0 0 100%;height:0;margin:0}
  /* ⚠ LE BOUTON DE MENU EST LE PREMIER ENFANT DANS LES SIX SHELLS, mais ceux de
     la délégation et du département (`.dr-/.tu-burger`) ne portent pas
     `.burger` : ils partaient avec les icônes, derrière la pastille de
     périmètre. */
  .topbar .hd,.topbar>.icbtn:not(:first-child),.topbar>a{order:1}
  /* Le titre partage la rangée du bouton et passe à la ligne DANS sa boîte : en
     `flex-basis:auto`, un sous-titre long (gouvernance, Financial Hub) le
     renvoyait seul sur une rangée, et le bouton restait seul sur la sienne. */
  .topbar>.icbtn:first-child+div{flex:1 1 0;min-width:0;overflow-wrap:anywhere}

  /* ── 🔴 SUR TÉLÉPHONE, LA BARRE DÉFILE AVEC LA PAGE ─────────────────────────
   * Collante, elle gardait 125 à 239 px en haut de CHAQUE écran — 15 à 29 % de
   * 812 px (mesuré le 2026-09-18, harnais responsive, 357 pages) : ses icônes
   * passent sous le titre, et une pastille ou une recherche ajoute une rangée.
   * Aucune rangée ne pouvait partir : ni le titre, ni une commande.
   * Elle reste POSITIONNÉE (`relative`) : les menus déroulants s’y ancrent.
   *
   * Le bouton du tiroir `.burger` FLOTTE, à la place exacte qu’il occupe dans la
   * barre (12 px, 14 px) : le tiroir s’ouvre par-dessus la page, donc on peut
   * l’appeler de n’importe où. Le titre lui laisse sa place (40 + 8 px).
   * Celui du ministère porte `.burger` en plus de `.mn-burger` : son menu est
   * lui aussi un tiroir superposé (shell_ministere.php), il flotte de même.
   * ⚠ LES BOUTONS `.dr-/.tu-burger` NE FLOTTENT PAS : leur menu ne s’ouvre pas
   *   par-dessus, il s’insère EN TÊTE DE PAGE. Flottant, le bouton ouvrirait
   *   un menu hors de vue ; il reste là où son menu apparaît. */
  .topbar{position:relative}
  .topbar>.burger{position:fixed;inset-block-start:12px;inset-inline-start:14px;box-shadow:var(--sh)}
  .topbar>.burger+div{margin-inline-start:48px}

  /* ── LES MENUS DÉROULANTS, SUR TÉLÉPHONE ──────────────────────────────────
   *
   * ⚠ ILS SORTAIENT DE L'ÉCRAN. `.menu` est en `position:absolute; right:0`,
   *   donc ancré au bord droit de SON bouton (`.hd{position:relative}`). Sur
   *   grand écran les boutons sont tous à droite, l'ancrage tombe juste. Sur
   *   téléphone, `.topbar` passe à la ligne et les six boutons se répartissent
   *   sur toute la largeur : celui de gauche ouvrait un panneau de 300 px calé
   *   sur SON bord droit, qui débordait donc largement à gauche. D'où des menus
   *   « excentrés », dont une partie était inatteignable.
   *
   *   `max-width:86vw` ne corrigeait rien : il borne la LARGEUR, pas la
   *   position — un panneau de 300 px ancré à 60 px du bord gauche déborde
   *   quelle que soit sa largeur maximale.
   *
   * LA CORRECTION TIENT EN UNE LIGNE, ET C'EST `.hd{position:static}` :
   *   sans ancêtre positionné, le panneau prend `.topbar` pour référence — elle
   *   est positionnée (`relative` ici, voir plus haut). On peut alors l'étendre d'un bord
   *   à l'autre de la barre, sous elle, comme le fait n'importe quelle
   *   application mobile. Aucun JS, aucune mesure de hauteur à tenir à jour. */
  .topbar .hd{position:static}
  .topbar .hd .menu,.topbar .hd .menu.menu-sm{
    left:10px;right:10px;width:auto;max-width:none;top:calc(100% + 6px);
    /* Le panneau ne doit jamais dépasser l'écran : la liste défile à
       l'intérieur plutôt que de pousser le bas hors de vue. */
    max-height:calc(100vh - 100%);overflow:auto;
    /* `100vh` vaut l’écran barre d’adresse REPLIÉE ; `100dvh`, ce qu’on voit
       vraiment. Le navigateur qui l’ignore garde la ligne précédente. */
    max-height:calc(100dvh - 100%);
  }
  /* La zone de liste garde sa propre hauteur bornée, sinon un menu très fourni
     rendrait le pied du panneau (« Ouvrir la messagerie ») inatteignable. */
  .topbar .hd .menu .mscroll{max-height:52vh}

  /* ── LE FILET CONTRE LE DÉBORDEMENT HORIZONTAL ────────────────────────────
   * Les menus corrigés ci-dessus n'étaient pas les seuls à pouvoir sortir de
   * l'écran : les listes d'action des tableaux (.cc .dropdown-menu, 190 px
   * ancrés à droite de leur bouton) le peuvent aussi quand le bouton est près
   * du bord gauche. Un panneau qui dépasse fait défiler TOUTE LA PAGE de côté —
   * et l'on croit alors la mise en page cassée, alors qu'un seul élément
   * dépasse. On borne au niveau du document : le panneau reste peut-être
   * partiellement caché, mais la page ne bascule pas.
   *
   * ⚠ `overflow-x:clip` et non `hidden` : `hidden` sur <body> crée un contexte
   *   de défilement qui casserait `position:sticky` — les en-têtes de tableau et
   *   les sommaires collants cesseraient de suivre (la barre du haut, elle, ne
   *   colle plus sous 640 px). `clip` coupe sans rien changer au défilement. */
  html{overflow-x:clip}
  body{overflow-x:clip}
  /* Une liste d'action de tableau ne doit jamais être plus large que l'écran. */
  .cc .dropdown-menu{max-width:calc(100vw - 28px)}
}

/* ── UN TABLEAU LARGE DOIT DÉFILER EN LUI-MÊME ──────────────────────────────
 * CONSÉQUENCE NON VOULUE DU FILET CI-DESSUS. `overflow-x:clip` empêche bien la
 * page de basculer de côté, mais il ROGNE aussi ce qui dépasse — sans barre de
 * défilement, puisque `clip` n'en crée pas. Un tableau plus large que l'écran
 * ne glisse donc plus : ses colonnes de droite deviennent invisibles ET
 * inatteignables. Relevé le 2026-08-26 sur ~150 tableaux, 79 pages. Au-dessus
 * de 640px, où le clip ne s'applique pas, c'est l'autre défaut qui revient :
 * la page entière part de côté. Les deux se soignent de la même façon.
 *
 * Le tableau reçoit son propre défilement : il glisse entre ses bords pendant
 * que la page ne bouge pas. `.tbl-wrap` (plus haut) faisait déjà cela pour les
 * 120 tableaux qui l'emploient ; cette règle étend le même traitement à tous
 * les autres, sans rien à écrire dans chaque page.
 *
 * ⚠ `display:block` EST NÉCESSAIRE, ce n'est pas une préférence : `overflow`
 *   n'a AUCUN effet sur une boîte `display:table`. Les cellules continuent de
 *   s'aligner en colonnes — le tableau anonyme se reconstitue sous le bloc.
 *
 * ⚠ SOUS 900px SEULEMENT. Au-delà, `display:block` étirerait à toute la largeur
 *   les tableaux qui s'ajustent aujourd'hui à leur contenu : on corrigerait un
 *   écran cassé en en déformant un qui ne l'est pas.
 *
 * ⚠ SÉLECTEUR VOLONTAIREMENT SIMPLE. Un `:not(.tbl-wrap table)` aurait épargné
 *   les tableaux déjà protégés, mais `:not()` descendant relève des sélecteurs
 *   de niveau 4 : un navigateur qui ne le comprend pas jette LA RÈGLE ENTIÈRE.
 *   L'application sert des téléphones Android anciens ; on préfère un double
 *   conteneur défilant — sans effet visible — à une règle qui disparaît. */
@media(max-width:900px){
  .content table,.cc table{display:block;overflow-x:auto;max-width:100%}
}
@media(max-width:860px){
  .side{position:fixed;left:0;top:0;height:100vh;z-index:60;width:264px;flex-basis:264px;transform:translateX(-100%);transition:transform .22s;box-shadow:0 0 60px rgba(0,0,0,.18)}
  .side.open{transform:none}
  .main{width:100%}
  .topbar .burger{display:grid}
  .backdrop{display:none;position:fixed;inset:0;background:rgba(20,30,60,.4);z-index:50}
  .backdrop.open{display:block}
}
@media(prefers-reduced-motion:no-preference){
  /* ══ 🔴 UNE ANIMATION NE DOIT JAMAIS POUVOIR CACHER LE CONTENU ═════════════
   *
   *    CETTE RÈGLE VALAIT `opacity:0; transform:translateY(14px)` en état de
   *    DÉPART, et comptait sur l'animation pour rendre le bloc visible. L'état
   *    NON ANIMÉ d'un `.reveal` était donc : invisible. Tout ce qui empêche
   *    l'animation d'avancer laisse le bloc caché — et laisse à l'écran une
   *    BANDE VIDE de la hauteur du contenu.
   *
   *    Mesuré le 2026-09-06 : page chargée alors que l'onglet n'est pas au
   *    premier plan (`document.visibilityState === 'hidden'`), le navigateur
   *    gèle les animations — `currentTime` reste à 0 après 1,2 s, `playState`
   *    dit « running », et `opacity` vaut 0. Trois captures du propriétaire
   *    montraient exactement cela : un bandeau vide, puis le bord supérieur
   *    d'une carte qui n'était pas revenue à sa place.
   *
   * ⚠ LE CORRECTIF EST DE RETOURNER LA RÈGLE : l'état de repos est VISIBLE, et
   *   c'est l'animation qui part de l'invisible. Plus de `forwards` — la fin de
   *   l'animation rejoint l'état de base, qui est déjà le bon. Une animation qui
   *   ne tourne pas ne fait alors rien du tout, ce qui est le comportement
   *   correct d'une animation d'apparition.
   *
   * ⚠ LES QUATRE RETARDS GARDENT `backwards`, sans quoi un bloc `.d2`
   *   apparaîtrait, disparaîtrait au démarrage de son animation, puis
   *   réapparaîtrait — un clignotement pire que l'escalier qu'ils dessinent.
   *   Leur fenêtre de risque est bornée au retard lui-même (≤ 0,2 s).
   *
   * ⚠ ON NE TOUCHE PAS AU GARDE `prefers-reduced-motion` : qui demande moins de
   *   mouvement n'a toujours aucune de ces règles, donc aucun risque. */
  .reveal{animation:rise .55s cubic-bezier(.2,.75,.2,1)}
  /* ⚠ .d3 ET .d4 N'EXISTAIENT PAS, alors que quatre pages les écrivaient déjà
     (alie, nouvel-apprenant, nouveau-personnel, nouveau-parent). Le panneau
     n'était pas cassé — il apparaissait simplement en même temps que le premier,
     et l'escalier voulu s'arrêtait au deuxième. */
  .reveal.d1,.reveal.d2,.reveal.d3,.reveal.d4{animation-fill-mode:backwards}
  .reveal.d1{animation-delay:.05s}.reveal.d2{animation-delay:.1s}
  .reveal.d3{animation-delay:.15s}.reveal.d4{animation-delay:.2s}
  /* ⚠ L'ÉTAT CACHÉ VIT DANS LE `from`, PLUS DANS LA RÈGLE : c'est ce
     déplacement qui rend l'ensemble sûr. */
  @keyframes rise{from{opacity:0;transform:translateY(14px)}}
}

/* ---------------- correction de copies (kit formulaire + résultats) ---------------- */
.cc .d-none,.d-none{display:none!important}
.cc .text-danger{color:var(--danger)}.cc .text-warning{color:var(--amber)}.cc .text-success{color:var(--green)}
.cc .opacity-50{opacity:.6}.cc .opacity-75{opacity:.75}.cc .fw-semibold{font-weight:700}.cc .small{font-size:.85rem}
.cc .flex-grow-1{flex:1;min-width:0}.cc .text-nowrap{white-space:nowrap}.cc .align-middle{vertical-align:middle}
.cc-grid{display:grid;grid-template-columns:370px 1fr;gap:18px;align-items:start}
.cc .panel{padding:22px}
.cc .ch-h{margin-bottom:16px}
/* En-tête « Réglages » (panneau gauche) en bandeau orange subtil */
.cc .cc-grid>.panel:first-child{overflow:hidden}
.cc .cc-grid>.panel:first-child .ch-h{margin:-22px -22px 18px;padding:16px 20px;background:var(--grad-or);border-bottom:1px solid var(--bl2)}
.cc .mt2{margin-top:14px}
.cc .form-label{display:block;font-size:.78rem;font-weight:700;color:var(--ink2);margin-bottom:6px}
.cc .hint-inline{font-size:.72rem;color:var(--ink3);font-weight:600}
.cc .form-select,.cc .form-control{width:100%;border:1px solid var(--line);border-radius:11px;padding:10px 12px;font:inherit;font-size:.9rem;background:var(--surf);color:var(--ink);line-height:1.4}
.cc .form-select:focus,.cc .form-control:focus{outline:none;border-color:var(--blue);box-shadow:0 0 0 3px var(--bl)}
.cc textarea.form-control{resize:vertical}
.cc .input-group{display:flex;gap:8px;align-items:flex-start}
.cc .input-group .form-control{flex:1}
.cc .hint{display:block;margin-top:8px;font-size:.76rem;color:var(--ink3)}
.cc .cc-voix{display:flex;align-items:center;gap:9px}.cc .cc-voix .hint{margin:0}
.cc .cc-uploads{display:flex;gap:8px;flex-wrap:wrap}
.cc .cc-upload{cursor:pointer;flex:1 1 140px;justify-content:center}
.cc .badge{display:inline-block;padding:3px 9px;border-radius:999px;font-size:.72rem;font-weight:800}
.cc .badge.bg-success{background:var(--green-l);color:var(--green)}
.cc .badge.bg-warning{background:var(--amber-l);color:var(--amber)}
.cc .badge.bg-danger{background:var(--tint-rg);color:var(--danger)}
.cc .badge.bg-secondary,.cc .badge.bg-light{background:var(--surf2);color:var(--ink2)}
.cc .alert{border-radius:10px;padding:10px 13px;font-size:.85rem;margin-bottom:10px}
.cc .alert-warning{background:var(--amber-l);color:var(--blue-8);border:1px solid var(--bl2)}
/* Compat fragments renvoyés par les contrôleurs (Bootstrap + FontAwesome, INCHANGÉS côté serveur) */
.cc .alert-primary{background:var(--bl);color:var(--blue-8);border:1px solid var(--bl2)}
.cc .alert-secondary{background:var(--surf2);color:var(--ink2);border:1px solid var(--line)}
.cc .alert-success{background:var(--green-l);color:var(--green);border:1px solid #bfe6d3}
.cc .alert-danger{background:var(--tint-rg);color:var(--danger);border:1px solid #f3c6c2}
.cc .alert h5,.cc .alert h6{font-size:.95rem;font-weight:800;margin:0 0 6px}

/* ══ LE COMPTE RENDU D'UN CONTRÔLEUR PREND LA COULEUR DE SA FENÊTRE ══════════
 *
 * 🔴 CE QUI ÉTAIT SIGNALÉ (2026-08-26) : le bilan d'une assignation RÉUSSIE
 *    s'affichait sur fond ROSE, dans une fenêtre bleue, juste sous une ligne
 *    verte « 7 matières traitées ». Trois couleurs pour un seul message, dont
 *    une qui annonce une erreur quand tout s'est bien passé.
 *
 *    La cause est dans le contrôleur : il enveloppe son bilan dans
 *    `alert alert-danger` (send_data_multiple_matieres_eta.php:194), héritage
 *    de l'ancienne page où « alert » ne voulait dire que « encadré ». On ne
 *    touche pas au contrôleur — une vingtaine d'écrans en dépendent, et la
 *    couleur d'un panneau n'est pas une affaire de serveur.
 *
 * ⚠ ON NE REPEINT PAS TOUTES LES ALERTES D'UNE FENÊTRE, et c'est tout le point.
 *   Certaines disent vraiment quelque chose : l'avertissement avant une clôture
 *   définitive, le vert d'un succès que la page a écrit elle-même. Les aplatir
 *   toutes effacerait un sens réel pour corriger un défaut d'une seule
 *   provenance. La teinte ne s'applique donc qu'ICI — la zone où l'on déverse
 *   la réponse d'un contrôleur, et où « alert » ne veut rien dire.
 *
 * ⚠ CETTE RÈGLE EST PLACÉE APRÈS LA FAMILLE .cc .alert-*, DÉLIBÉRÉMENT.
 *   `.stu-cr .alert-danger` et `.cc .alert-danger` pèsent tous deux (0,2,0) :
 *   à spécificité égale, c'est l'ORDRE qui tranche. Remontée près du bloc des
 *   fenêtres, deux cents lignes plus haut, elle serait restée sans effet — et
 *   l'on aurait cherché la cause ailleurs.
 *
 * ⚠ HORS D'UNE FENÊTRE, --mdl-tint n'existe pas : le repli rend la teinte du
 *   RÔLE (--mh-or, que inclusions/role_utilisateur.php réémet depuis la couleur
 *   du profil connecté). Un compte rendu affiché en pleine page reste donc
 *   accordé à l'écran qui le porte. */
/* 🔴 IL NE PORTAIT QUE SA COULEUR, ET C'ÉTAIT LE DÉFAUT (2026-09-05).
 *    Cette règle ne posait que le fond, la bordure et le rayon ; le RESTE —
 *    remplissage, taille, graisse, écart sous le bloc — lui venait de
 *    `.cc .alert{padding:10px 13px;margin-bottom:10px}`, deux blocs plus haut.
 *    Or `#mk-avis`, la zone de compte rendu des dix pages du marché, est un
 *    enfant direct de `.content`, PAS de `.cc` : rien de tout cela ne
 *    l'atteignait. Mesuré dans le navigateur — `padding: 0px`, `margin: 0px`,
 *    23 px de haut, texte collé au bord gauche et aucun écart sous le bloc.
 *    C'est la capture envoyée par le propriétaire.
 *
 * ⚠ IL SE TIENT DONC DEBOUT TOUT SEUL. Un composant qui n'est lisible que
 *   dans un conteneur donné n'est pas un composant : c'est une décoration de
 *   ce conteneur. Les valeurs sont celles de `.rp-note` — même remplissage,
 *   même taille, même graisse — parce qu'un compte rendu et un encart
 *   d'information disent la même chose au même endroit de l'œil.
 *
 * ⚠ ET L'ÉCART EST SOUS LE BLOC, PAS AUTOUR. Un compte rendu s'insère AVANT
 *   ce qu'il commente ; une marge haute l'aurait décollé de ce qui vient de
 *   se produire, une marge basse le sépare de la suite.
 *
 * ⚠ PAS DE `display:flex` ICI, malgré la parenté avec `.rp-note` : cette zone
 *   reçoit des FRAGMENTS ENTIERS renvoyés par les contrôleurs — titres,
 *   listes, tableaux. Les mettre en ligne les aurait rangés côte à côte.
 *   `.rp-note` peut se le permettre, elle ne contient qu'un picto et un mot. */
.stu-cr .alert{background:var(--mdl-tint,var(--mh-or)) !important;
  border:1px solid var(--mdl-bd,var(--mh-or-b)) !important;
  color:var(--ink) !important;border-radius:14px;
  padding:12px 15px;margin:0 0 18px;font-size:.87rem;font-weight:650;line-height:1.55}
/* Le picto que `MK.dire()` place en tête : aligné sur la ligne, et il ne
   rétrécit pas quand le texte passe à la ligne suivante. */
.stu-cr .alert > i:first-child{margin-inline-end:8px;opacity:.9}
.stu-cr .alert h5,.stu-cr .alert h6,.stu-cr .alert b,.stu-cr .alert strong{color:var(--ink)}
/* Le lien et le picto prennent le TON du bandeau — le même que le sur-titre de
   la fenêtre, calibré pour être lu sur cette teinte-là (voir la note de
   .stumodal-k : un ton calibré contre la surface tombe sous 4,5:1 une fois
   posé sur une teinte de sa propre couleur). */
.stu-cr .alert a{color:var(--mdl-k,var(--mh-or-k));font-weight:700}
/* ⚠ ON NE RECOLORE QUE CE QUI EST DÉCORATIF, et la distinction est fine :
 *   `main_fa_stu` est la classe d'accent de l'ancienne interface — elle met en
 *   valeur un chiffre, jamais un verdict ; les pictogrammes `<i class="text-danger">`
 *   des listes à puces non plus. Mais `.text-danger` posé sur du TEXTE peut
 *   dire quelque chose (« 3 matières en échec ») : on le laisse rouge. D'où
 *   le sélecteur `i.text-danger` et non `.text-danger`. */
.stu-cr .alert i.text-danger,.stu-cr .alert .main_fa_stu{color:var(--mdl-k,var(--mh-or-k)) !important}
.cc .row,.cc .form-group.row{display:flex;flex-wrap:wrap;gap:10px;margin:10px 0 0}
.cc .form-group{margin-bottom:10px}
.cc [class*="col-md-6"],.cc [class*="col-sm-6"]{flex:1 1 calc(50% - 10px);min-width:210px}
/* La grille ne connaissait que les moitiés et les pleines largeurs : les
   fragments repris qui utilisent des tiers et des quarts — les fenêtres de la
   page financière, notamment — s'empilaient sur toute la largeur. */
.cc [class*="col-md-4"],.cc [class*="col-sm-4"],.cc [class*="col-lg-4"]{flex:1 1 calc(33.333% - 10px);min-width:190px}
.cc [class*="col-md-3"],.cc [class*="col-sm-3"],.cc [class*="col-lg-3"]{flex:1 1 calc(25% - 10px);min-width:165px}
.cc [class*="col-md-2"],.cc [class*="col-sm-2"],.cc [class*="col-lg-2"]{flex:1 1 calc(16.666% - 10px);min-width:120px}
.cc [class*="col-md-8"],.cc [class*="col-sm-8"],.cc [class*="col-lg-8"]{flex:1 1 calc(66.666% - 10px);min-width:230px}
.cc [class*="col-md-9"],.cc [class*="col-sm-9"],.cc [class*="col-lg-9"]{flex:1 1 calc(75% - 10px);min-width:250px}
.cc [class*="col-lg-6"]{flex:1 1 calc(50% - 10px);min-width:210px}
.cc [class*="col-lg-12"]{flex:1 1 100%;min-width:0}
@media(max-width:720px){.cc [class*="col-"]{flex:1 1 100% !important;min-width:0}}
.cc [class*="col-md-12"],.cc [class*="col-sm-12"]{flex:1 1 100%;min-width:0}
.cc .border-secondary{border-color:var(--line)!important}
.cc .col-md-6.border,.cc .col-sm-6.border,.cc .col-md-6.border-secondary,.cc .col-sm-6.border-secondary{border:1px solid var(--line);border-radius:12px;padding:12px 14px;background:var(--surf2)}
.cc .col-md-6 h6,.cc .col-sm-6 h6{display:flex;align-items:center;gap:7px}
.cc .col-md-6 ul,.cc .col-sm-6 ul{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:5px}
.cc .col-md-6 li,.cc .col-sm-6 li{font-size:.84rem;color:var(--ink2);display:flex;align-items:center;gap:8px}
.cc .font-weight-bold{font-weight:800}
.cc .main_text_stu{color:var(--ink)}
.cc .main_fa_stu{color:var(--blue)}
/* main_block_stu : les fragments renvoyés par les contrôleurs hérités s en
   servent comme d un paragraphe — <span class="main_block_stu">…</span>. Sans
   display:block, le bilan de saisie des notes (new_controle_notes_eval.php)
   s affiche d un seul tenant, ses cinq statistiques collées les unes aux
   autres. Cette classe manquait à la couche de compatibilité. */
.cc .main_block_stu{display:block;margin-bottom:8px}
.cc .mt-3{margin-top:14px}.cc .mt-2{margin-top:10px}.cc .mb-2{margin-bottom:8px}.cc .w-100{width:100%}
.cc .text-center{text-align:center}
.cc img.rounded-circle{border-radius:50%}.cc .img-2x{width:34px;height:34px;object-fit:cover}.cc .me-2{margin-inline-end:8px}
/* FontAwesome non chargé dans le shell → mappé sur bootstrap-icons */
.cc i.fa{font-family:"bootstrap-icons"!important;font-style:normal;font-weight:normal;line-height:1;display:inline-block}
.cc i.fa-book::before{content:"\f194"}
.cc i.fa-user::before{content:"\f4e1"}
.cc i.fa-phone::before{content:"\f5c1"}
.cc i.fa-whatsapp::before{content:"\f618"}
.cc .apc-mne{margin-top:4px}
.cc .btn-success{background:var(--green);box-shadow:0 8px 20px rgba(22,160,106,.25)}
@media (hover:hover){/* 🔴 UN BOUTON « CONTOUR » DEVENAIT ILLISIBLE AU SURVOL (2026-08-26).
   `.btn:hover:not(:disabled)` (l. 333) peint un fond PLEIN — `var(--blue-6)`,
   c'est-à-dire la couleur du RÔLE, puisque la famille « blue » est remappée par
   profil. Les variantes de contour, elles, ne changeaient au survol que leur
   filet et leur texte, sans jamais reprendre la main sur le fond : on obtenait
   du texte vert sur un aplat vert, mesuré à 1,0:1. Le libellé disparaissait.
   ⚠ TROIS VARIANTES SUR QUATRE ÉTAIENT CONCERNÉES. `danger` posait déjà son
     fond clair (`--tint-rg`) : c'est LUI le modèle, les trois autres l'avaient
     simplement oublié. On les complète plutôt que d'inventer autre chose.
   ⚠ ET C'EST BIEN ICI QUE CELA SE CORRIGE : 21 pages emploient ces classes.
     Le réparer page par page aurait laissé le défaut dans les vingt autres. */
.cc .btn-outline-success{background:var(--surf);border:1px solid #bfe6d3;color:var(--green);box-shadow:none}}
@media (hover:hover){.cc .btn-outline-success:hover:not(:disabled){border-color:var(--green);color:var(--ink);background:var(--green-l)}}
.cc .btn-sm{padding:8px 13px;font-size:.83rem}
.cc .btn.w-100{width:100%;justify-content:center}
.cc .input-group-text{display:inline-flex;align-items:center;padding:6px 10px;background:var(--surf2);border:1px solid var(--line);font-size:.82rem;font-weight:700;color:var(--ink2);border-radius:9px}
.cc .cc-note{width:78px;border:1px solid var(--line);border-radius:9px;padding:6px 8px;font:inherit;font-size:.9rem;text-align:center}
/* tableau des questions */
.cc table.table{width:100%;border-collapse:collapse;margin-bottom:10px}
.cc table.table th{text-align:start;font-size:.7rem;text-transform:uppercase;letter-spacing:.05em;color:var(--ink3);font-weight:800;padding:8px 10px;border-bottom:1px solid var(--line)}
.cc table.table td{padding:8px 10px;border-bottom:1px solid var(--line2);font-size:.85rem;color:var(--ink2);vertical-align:top}
/* panneau résultats + cartes */
.cc .cc-res-panel{padding:0;overflow:hidden;display:flex;flex-direction:column}
.cc .chat-head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:16px 20px;border-bottom:1px solid #d3e9fb;background:var(--tint-bl6)}
.cc .chat-head h3{margin:0;font-size:1.02rem;font-weight:800;color:var(--ink);display:flex;align-items:center;gap:9px}
.cc .chat-head .ch-t{min-width:0}
.cc .chat-head .ch-t .s{font-size:.78rem;color:#5a7a94;font-weight:600;margin-top:2px}
.cc .chat-head h3 i{color:var(--stu-or-k)}
.cc .chat-head-act{display:flex;align-items:center;gap:8px;flex:none}
.cc .cc-res-body{padding:18px 20px;min-height:200px;max-height:72vh;overflow-y:auto}
.cc .cc-card{border:1px solid var(--line);border-radius:14px;padding:14px;margin-bottom:14px;background:var(--surf);box-shadow:var(--sh)}
.cc .cc-card .cc-photo{width:110px;height:140px;object-fit:cover;border:1px solid var(--line);border-radius:10px;flex:none}
.cc .cc-card .cc-head{display:flex;gap:14px}
.cc .cc-status{margin-top:4px;font-size:.85rem}
/* ⚠ `.empty.tall` EST UNE COLONNE CENTRÉE — ce que ses NEUF appels supposaient
     déjà sans l'obtenir. Tous écrivent « <i>…</i><br>texte » : le retour à la
     ligne dit l'intention. Or `.empty` est un flex EN LIGNE, où un <br> devient
     un élément flex sans effet — le pictogramme restait donc à gauche du texte,
     collé, dans une boîte de 60 px de haut. suivi-pedagogique.php avait dû
     rétablir la colonne par un style en ligne, seul indice visible du défaut.
     Le retour à la ligne est masqué plutôt que retiré des pages : il
     redeviendrait juste si cette règle disparaissait.
   ⚠ SEULEMENT LES ENFANTS DIRECTS. suivi-pedagogique.php coupe SA PHRASE en deux
     avec un retour à la ligne, à l'intérieur d'un <span> — le masquer recollerait
     ses deux moitiés. Le sélecteur ne vise que ceux qui séparent le pictogramme
     du texte, c'est-à-dire ceux que la colonne rend inutiles. */
.cc .empty.tall{padding:60px 20px;flex-direction:column;justify-content:center;
  text-align:center;gap:12px;line-height:1.6}
.cc .empty.tall i{font-size:2rem;color:var(--ink3)}
.cc .empty.tall > br{display:none}
/* types d'épreuve : multi-sélection en grille (icône + libellé + coche) */
.cc .cc-types{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.cc .cc-type{position:relative;display:flex;align-items:center;gap:9px;text-align:start;border:1px solid var(--line);background:var(--surf);color:var(--ink2);border-radius:12px;padding:10px 12px;font:inherit;font-size:.83rem;font-weight:700;cursor:pointer;transition:.14s;min-width:0}
.cc .cc-type i{font-size:1.05rem;color:var(--ink3);flex:none;transition:.14s}
.cc .cc-type span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cc .cc-type::after{content:"\F633";font-family:"bootstrap-icons";position:absolute;inset-inline-end:9px;top:50%;transform:translateY(-50%) scale(.6);opacity:0;color:var(--blue);font-size:1rem;transition:.14s}
@media (hover:hover){.cc .cc-type:hover{border-color:var(--bl2);background:var(--bl-025)}}
.cc .cc-type.on{background:var(--bl);border-color:var(--blue);color:var(--blue-8);padding-inline-end:28px}
.cc .cc-type.on i{color:var(--stu-or-k)}
.cc .cc-type.on::after{opacity:1;transform:translateY(-50%) scale(1)}
.cc .cc-types-count{margin-top:8px;font-size:.75rem;color:var(--ink3);font-weight:600}
.cc .cc-types-count span{color:var(--blue-7);font-weight:800}
@media(max-width:520px){.cc .cc-types{grid-template-columns:1fr}}
/* exemples de barème (chips) */
.cc .cc-baremes{display:flex;flex-direction:column;gap:7px;margin-top:9px}
.cc .cc-chip{display:inline-flex;align-items:flex-start;gap:7px;text-align:start;background:var(--bl-025);border:1px solid var(--bl2);border-radius:11px;padding:8px 11px;font:inherit;font-size:.8rem;font-weight:600;color:var(--ink2);cursor:pointer;transition:.14s;line-height:1.35}
.cc .cc-chip i{color:var(--stu-or-k);flex:none;margin-top:1px}
@media (hover:hover){.cc .cc-chip:hover{background:var(--bl);border-color:var(--blue);color:var(--blue-8)}}
/* barre d'outils résultats : bilan + filtres/tri + export */
.cc .cc-toolbar{padding:14px 16px;border-bottom:1px solid var(--line);display:flex;flex-direction:column;gap:12px}
.cc .cc-bilan{display:flex;flex-wrap:wrap;gap:8px}
.cc .cc-bchip{display:inline-flex;align-items:baseline;gap:5px;background:var(--surf2);border:1px solid var(--line);border-radius:999px;padding:4px 11px;font-size:.8rem;font-weight:600;color:var(--ink2)}
.cc .cc-bchip b{font-size:1rem;font-weight:900;color:var(--ink)}
.cc .cc-bchip.g b{color:var(--green)}.cc .cc-bchip.b b{color:var(--info)}
.cc .cc-tools{display:flex;flex-wrap:wrap;align-items:center;gap:8px}
.cc .prep-filters{display:flex;flex-wrap:wrap;gap:6px;align-items:center}
.cc .prep-filters .pf{border:1px solid var(--line);background:var(--surf);color:var(--ink2);border-radius:999px;padding:6px 12px;font:inherit;font-size:.8rem;font-weight:700;cursor:pointer;transition:.14s}
@media (hover:hover){.cc .prep-filters .pf:hover{border-color:var(--blue);color:var(--blue)}}
.cc .prep-filters .pf.on{background:var(--blue);border-color:var(--blue);color:#fff}
.cc .prep-typesel{border:1px solid var(--line);background:var(--surf);color:var(--ink);border-radius:999px;padding:6px 11px;font:inherit;font-size:.8rem;font-weight:700;cursor:pointer}
.cc .cc-exp{background:var(--surf);border:1px solid var(--line);color:var(--ink2);box-shadow:none;margin-inline-start:auto}
.cc .cc-exp:first-of-type{margin-inline-start:auto}
@media (hover:hover){.cc .cc-exp:hover{border-color:var(--blue);color:var(--blue)}}
/* nom d'élève éditable */
.cc .cc-nomwrap{display:inline-flex;align-items:center;gap:6px;background:var(--surf2);border:1px solid var(--line);border-radius:999px;padding:3px 5px 3px 10px}
.cc .cc-nomwrap i{color:var(--stu-or-k)}
.cc .cc-nom{border:0;background:transparent;outline:none;font:inherit;font-size:.82rem;font-weight:700;color:var(--ink);width:150px;padding:2px 4px}
@media(max-width:900px){.cc-grid{grid-template-columns:1fr}}
/* bannière de reprise de session */
.cc .cc-resume{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:10px;background:var(--bl-025);border:1px solid var(--bl2);border-radius:12px;padding:11px 14px;margin-bottom:14px;font-size:.86rem;color:var(--ink)}
.cc .cc-resume i{color:var(--stu-or-k)}
.cc .cc-resume-act{display:flex;gap:8px}
.cc .cc-resume #ccResumeYes{background:var(--blue);border:0;color:#fff;font-weight:600}
/* rubrique « Mes corrections » (calquée sur « Mes préparations ») */
.cc .prep-toolbar{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:12px;padding:14px 16px;border-bottom:1px solid var(--line)}
/* ⚠ PLUS DE `max-width` (2026-08-28, demande du propriétaire) : le champ de
   recherche prend le reste de la largeur de sa barre. La même règle est écrite
   dans preparation-cours.php — les deux ont été corrigées ensemble. */
.cc .prep-toolbar .search{display:flex;align-items:center;gap:8px;background:var(--surf2);border:1px solid var(--line);border-radius:11px;padding:8px 12px;flex:1;min-width:200px}
.cc .prep-toolbar .search i{color:var(--stu-or-k)}
.cc .prep-toolbar .search input{border:0;background:transparent;outline:none;font:inherit;font-size:.9rem;color:var(--ink);width:100%}
.cc .prep-filters .pf-lbl,.cc .prep-typebox .pf-lbl{font-size:.66rem;font-weight:800;letter-spacing:.1em;text-transform:uppercase;color:var(--ink3);margin-inline-end:2px}
.cc .prep-typebox{display:inline-flex;align-items:center;gap:7px}
/* KPIs (calqués sur preparation-cours) */
.cc .cc-kpis{display:grid;grid-template-columns:repeat(4,1fr);gap:14px}
.cc .cc-kpis .pk{display:flex;align-items:center;gap:12px;background:var(--cardg);border:1px solid var(--line);border-radius:16px;padding:14px 16px;box-shadow:var(--sh)}
.cc .cc-kpis .pk-i{width:40px;height:40px;flex:none;border-radius:12px;display:grid;place-items:center;font-size:1.15rem;background:var(--stu-or-tint);color:var(--stu-or-k)}
.cc .cc-kpis .pk.g .pk-i{background:var(--green-l);color:var(--green)}
.cc .cc-kpis .pk.r .pk-i{background:var(--tint-rg3);color:var(--danger)}
.cc .cc-kpis .pk.b .pk-i{background:var(--stu-or-tint);color:var(--stu-or-k)}
.cc .cc-kpis .pk-t{font-size:.85rem;color:var(--ink2);line-height:1.2}
.cc .cc-kpis .pk-t b{display:block;font-size:1.5rem;font-weight:900;letter-spacing:-.04em;color:var(--ink);font-variant-numeric:tabular-nums}
@media(max-width:760px){.cc .cc-kpis{grid-template-columns:repeat(2,minmax(0,1fr))}.cc .cc-kpis .pk{min-width:0}}
/* ⚠ À 320 px, deux pistes `1fr` (= minmax(auto,1fr)) réclamaient la largeur
     d’icône + « corrigée(s) » : 147 + 141 px pour 280, tuiles sorties de 2 px
     (correction-copies, mesuré le 2026-09-18). En `minmax(0,1fr)`, le libellé
     n’aurait plus que 47 px : sous 420 px l’icône passe AU-DESSUS du chiffre,
     et le libellé retrouve la largeur de la tuile. */
@media(max-width:420px){.cc .cc-kpis .pk{flex-direction:column;align-items:flex-start;gap:8px}}
/* modale caméra */
.cccam-stage{background:#000;border-radius:12px;overflow:hidden;aspect-ratio:4/3}
.cccam-stage video{width:100%;height:100%;object-fit:cover;display:block}
.cccam-stage img{width:100%;height:100%;object-fit:contain;display:block;background:#000}
/* progression globale */
.cc .cc-prog{display:flex;align-items:center;gap:10px}
.cc .cc-prog-track{flex:1;height:8px;background:var(--surf2);border-radius:999px;overflow:hidden}
.cc .cc-prog-bar{display:block;height:100%;width:0;background:linear-gradient(90deg,var(--blue),var(--blue-6));transition:width .3s}
.cc .cc-prog-txt{font-size:.78rem;font-weight:700;color:var(--ink2);white-space:nowrap}
/* badge « à revoir » + surlignage */
.cc .badge.cc-review{background:var(--tint-rg3);color:var(--danger);display:inline-flex;align-items:center;gap:4px}
.cc .cc-card[data-review="1"]{border-inline-start:3px solid #e0a52b}
.cc .prep-filters .pf[data-v="review"].on{background:#e0a52b;border-color:#e0a52b}
/* histogramme des notes (bilan) */
.cc .cc-histo{display:flex;align-items:flex-end;gap:4px;height:40px;padding-top:14px;margin-inline-start:4px}
.cc .cc-hbar{position:relative;width:22px;background:var(--bl);border-radius:5px 5px 0 0;height:var(--h);min-height:3px}
.cc .cc-hbar.g{background:var(--green-l)}
.cc .cc-hbar b{position:absolute;top:-13px;inset-inline:0;text-align:center;font-size:.62rem;font-weight:800;color:var(--ink2)}
.cccam-thumbs{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}
.cccam-th{position:relative;width:56px;height:56px;border-radius:8px;overflow:hidden;border:1px solid var(--line)}
.cccam-th img{width:100%;height:100%;object-fit:cover;display:block}
.cccam-th b{position:absolute;inset-inline-start:2px;bottom:2px;background:rgba(0,0,0,.6);color:#fff;font-size:.6rem;font-weight:800;border-radius:5px;padding:0 5px}
.cccam-del{position:absolute;inset-inline-end:2px;top:2px;width:18px;height:18px;border:0;border-radius:6px;background:rgba(192,57,43,.92);color:#fff;font-size:.6rem;display:grid;place-items:center;cursor:pointer;padding:0;line-height:1}
@media (hover:hover){.cccam-del:hover{background:#c0392b}}
.cccam-bar{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin-top:12px}
.cccam-bar .prep-typesel{border:1px solid var(--line);background:var(--surf);color:var(--ink);border-radius:10px;padding:7px 10px;font:inherit;font-size:.82rem;font-weight:700}
/* ===== Compat : classes Bootstrap émises par le JS existant, re-mappées sur les
   tokens du shell autonome (aucune dépendance à bootstrap.css). Scopé sous .cc. ===== */
.cc .small{font-size:.82rem}
.cc .fw-semibold{font-weight:700}.cc .fw-bold{font-weight:800}
.cc .text-muted,.cc .opacity-50{color:var(--ink3)}
.cc .opacity-75{color:var(--ink2)}
.cc .text-danger{color:var(--danger)}.cc .text-success{color:var(--green)}.cc .text-warning{color:var(--amber)}
.cc .text-center{text-align:center}.cc .text-dark{color:var(--ink)}.cc .text-white{color:#fff}
.cc .text-nowrap{white-space:nowrap}
.cc .d-flex{display:flex}.cc .d-block{display:block}
.cc .justify-content-between{justify-content:space-between}.cc .align-items-center{align-items:center}
.cc .flex-wrap{flex-wrap:wrap}.cc .w-100{width:100%}
.cc .m-0{margin:0}.cc .mb-0{margin-bottom:0}.cc .mb-1{margin-bottom:.25rem}.cc .mb-2{margin-bottom:.5rem}.cc .mb-3{margin-bottom:1rem}
.cc .mt-1{margin-top:.25rem}.cc .mt-2{margin-top:.5rem}.cc .mt-3{margin-top:1rem}
.cc .me-1{margin-inline-end:.25rem}.cc .me-2{margin-inline-end:.5rem}.cc .me-3{margin-inline-end:1rem}.cc .ms-2{margin-inline-start:.5rem}
.cc .p-2{padding:.5rem}.cc .p-3{padding:1rem}.cc .ps-3{padding-inline-start:1rem}
.cc .border{border:1px solid var(--line)}.cc .rounded{border-radius:12px}
.cc .bg-light{background:var(--surf2)}
.cc ul.ps-3{padding-inline-start:1.1rem;margin:0}
/* list-group (recherche notion / résultats sémantiques) */
.cc .list-group{display:flex;flex-direction:column;gap:6px}
.cc .list-group-item{display:block;padding:10px 12px;border:1px solid var(--line);border-radius:11px;background:var(--surf2);color:var(--ink2);font-size:.9rem}
.cc a.list-group-item-action{transition:.14s;cursor:pointer}
@media (hover:hover){.cc a.list-group-item-action:hover{border-color:var(--blue);color:var(--blue-7);background:var(--bl-025)}}
/* tables générées par le JS (veille, diagnostic) */
.cc .table{width:100%;border-collapse:collapse;font-size:.9rem}
.cc .table th{background:var(--surf2);color:var(--ink3);font-size:.72rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em;text-align:start;padding:11px 14px;border-bottom:1px solid var(--line)}
.cc .table td{padding:11px 14px;border-top:1px solid var(--line2);color:var(--ink2);vertical-align:middle}
.cc .table.align-middle td{vertical-align:middle}
@media (hover:hover){.cc .table-hover tbody tr:hover td{background:var(--bl-025)}}
.cc .table-sm th,.cc .table-sm td{padding:8px 11px}
/* barre de progression (efficacité des stratégies) */
.cc .progress{height:8px;background:var(--surf2);border-radius:999px;overflow:hidden}
.cc .progress-bar{height:100%;background:var(--blue)}
.cc .progress-bar.bg-success{background:var(--green)}.cc .progress-bar.bg-warning{background:var(--amber)}.cc .progress-bar.bg-danger{background:#c0392b}
/* interrupteurs (réglages de veille) */
.cc .form-check{position:relative;padding-inline-start:2.6em;min-height:1.4rem}
.cc .form-check.form-switch .form-check-input{position:absolute;inset-inline-start:0;top:.05em;width:2.1em;height:1.15em;margin:0;-webkit-appearance:none;appearance:none;background:var(--line);border-radius:999px;cursor:pointer;transition:.15s;border:0;flex:none}
.cc .form-check-input:checked{background:var(--green)}
.cc .form-check-input::before{content:"";position:absolute;top:2px;inset-inline-start:2px;width:calc(1.15em - 4px);height:calc(1.15em - 4px);border-radius:50%;background:#fff;transition:.15s}
.cc .form-check-input:checked::before{transform:translateX(.95em)}
[dir="rtl"] .cc .form-check-input:checked::before{transform:translateX(-.95em)}
.cc .form-check-input:disabled{opacity:.5;cursor:not-allowed}
.cc .form-check-label{font-size:.9rem;color:var(--ink2);cursor:pointer}
/* badges additionnels (bg-info + variantes -subtle) */
.cc .badge.bg-info{background:var(--tint-bl);color:var(--info)}
.cc .badge.bg-danger-subtle{background:var(--tint-rg);color:var(--danger)}
.cc .badge.bg-success-subtle{background:var(--green-l);color:var(--green)}
.cc .badge.bg-warning-subtle{background:var(--amber-l);color:var(--amber)}
.cc .badge.bg-warning.text-dark,.cc .badge.bg-light.text-dark{color:var(--ink)}
/* boutons additionnels (variantes info / outline) */
/* .btn-info N'EST PAS SÉMANTIQUE ICI. Le nom vient de Bootstrap, mais sur les
   six pages du shell qui l'emploient — préparation de cours, correction de
   copies, crédits, veille, finances, fiabilité — c'est un bouton d'ACTION
   secondaire, pas un message d'information. Il gardait #00a1ff en dur, comme
   .btn-blue avant lui : même nuance -dd, la seule qui porte du blanc sur les
   dix-neuf rôles. Les vrais sémantiques (success, danger) gardent leur sens et
   ne bougent pas. */
.cc .btn-info,.cc .btn-g.btn-info{background:var(--stu-role-dd,#00a1ff);box-shadow:0 8px 20px rgba(var(--stu-role-rgb,0,161,255),.25)}
@media (hover:hover){.cc .btn-info:hover{background:var(--stu-role-d,#0090ea)}}
.cc .btn-danger{background:#c0392b;box-shadow:0 8px 20px rgba(192,57,43,.22)}
@media (hover:hover){.cc .btn-danger:hover{background:#a83224}}
.cc .btn-light{background:var(--surf);color:var(--ink2);border:1px solid var(--line);box-shadow:none}
@media (hover:hover){.cc .btn-light:hover{background:var(--surf2);color:var(--blue)}}
/* La variante contour du même bouton : bordure et texte suivent le rôle. */
.cc .btn-outline-info{background:var(--surf);border:1px solid var(--bl2,#bcdffb);color:var(--stu-role-txt,var(--info));box-shadow:none}
@media (hover:hover){/* ⚠ `--tint-bl` SUIT LE RÔLE (rgba de la teinte du profil à 15 %), tout comme
     `--stu-role-txt`, qui porte un plancher de luminance : le couple reste donc
     contrasté quel que soit le profil, au lieu de dépendre d'une couleur fixe. */
/* ⚠ LE TEXTE DE SURVOL EST NEUTRE, ET C'EST LE RÉSULTAT D'UNE MESURE, pas un
 *   parti pris. Trois teintes de rôle ont été essayées comme couleur de texte :
 *     · `--stu-role-txt` — 2,4 à 2,7:1 : il applique un PLANCHER de luminance,
 *       donc il ÉCLAIRCIT ; il est fait pour un fond SOMBRE, pas pour une teinte ;
 *     · `--stu-role-k` (la couleur lue sur fond teinté) — 3 rôles sur 19 sous AA ;
 *     · la même sur `--surf2` — encore 2 sous AA.
 *   `--ink` sur la teinte du rôle donne **12,09:1 au pire**, sur les dix-neuf
 *   rôles ET les deux thèmes. L'identité du bouton est portée par son FILET et
 *   par sa teinte de fond ; son texte n'a qu'un travail, être lu.
 *   C'est d'ailleurs ce que `.btn-outline-secondary` faisait déjà. */
.cc .btn-outline-info:hover:not(:disabled){border-color:var(--stu-role,#00a1ff);color:var(--ink);background:var(--tint-bl)}}
.cc .btn-outline-secondary{background:var(--surf);border:1px solid var(--line);color:var(--ink2);box-shadow:none}
@media (hover:hover){.cc .btn-outline-secondary:hover:not(:disabled){border-color:var(--blue);color:var(--ink);background:var(--surf2)}}
.cc .btn-outline-danger{background:var(--surf);border:1px solid #e0b4b4;color:var(--danger);box-shadow:none}
@media (hover:hover){.cc .btn-outline-danger:hover:not(:disabled){border-color:#c0392b;color:var(--ink);background:var(--tint-rg)}}
.cc .main_white_stu_text{color:#fff}
.cc .btn-light.main_white_stu_text,.cc .btn-outline-info.main_white_stu_text,.cc .btn-outline-secondary.main_white_stu_text{color:inherit}
/* petites cartes générées (flags, recos) */
.cc .border.rounded{background:var(--surf2)}
.cc .apc-people{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:14px}
.cc .apc-pcard{display:flex;align-items:center;gap:13px;background:var(--cardg);border:1px solid var(--line);border-radius:16px;padding:16px 18px;box-shadow:var(--sh)}
.cc .apc-pcard .ic{width:44px;height:44px;flex:none;border-radius:13px;display:grid;place-items:center;font-size:1.25rem;background:var(--stu-or-tint);color:var(--stu-or-k)}
.cc .apc-pcard span{display:block;font-size:.72rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em;color:var(--ink3)}
.cc .apc-pcard b{font-size:.98rem;font-weight:800;color:var(--ink)}
.cc .apc-statgrid{display:grid;grid-template-columns:auto repeat(5,1fr);gap:12px;align-items:center}
.cc .apc-ring{--pct:0;--rc:var(--green);width:104px;height:104px;flex:none;border-radius:50%;display:grid;place-items:center;position:relative;background:conic-gradient(var(--rc) calc(var(--pct)*1%),var(--surf2) 0)}
.cc .apc-ring::before{content:"";position:absolute;width:80px;height:80px;border-radius:50%;background:var(--surf)}
.cc .apc-ring b{position:relative;font-size:1.5rem;font-weight:900;color:var(--ink);line-height:1}
.cc .apc-ring b small{font-size:.8rem;font-weight:800}
.cc .apc-ring span{position:relative;font-size:.6rem;font-weight:800;text-transform:uppercase;letter-spacing:.08em;color:var(--ink3);display:block;text-align:center;margin-top:2px}
.cc .apc-ms{background:var(--cardg);border:1px solid var(--line);border-radius:14px;padding:12px 8px;text-align:center;box-shadow:var(--sh)}
.cc .apc-ms b{display:block;font-size:1.3rem;font-weight:900;color:var(--ink);letter-spacing:-.03em}
.cc .apc-ms span{font-size:.66rem;font-weight:800;text-transform:uppercase;letter-spacing:.04em;color:var(--ink3)}
.cc .apc-re{margin-top:14px}
.cc .apc-re-bar{display:flex;height:14px;border-radius:999px;overflow:hidden;background:var(--surf2);box-shadow:inset 0 0 0 1px var(--line)}
.cc .apc-re-bar .ok{background:linear-gradient(90deg,#1fa254,#27c06a)}
.cc .apc-re-bar .ko{background:linear-gradient(90deg,#e0574a,#c0392b)}
.cc .apc-re-lg{display:flex;flex-wrap:wrap;gap:8px 20px;margin-top:9px;font-size:.86rem;font-weight:700;color:var(--ink2)}
.cc .apc-re-lg .ok{color:#1e8e4e}.cc .apc-re-lg .ko{color:var(--danger)}
.cc .apc-re-lg b{color:inherit}
.cc .apc-mf{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:14px}
.cc .apc-mfcol{background:var(--cardg);border:1px solid var(--line);border-radius:14px;padding:14px 16px;box-shadow:var(--sh)}
.cc .apc-mfcol .h{font-size:.82rem;font-weight:800;margin-bottom:9px;display:flex;align-items:center;gap:7px}
.cc .apc-mfcol .h.ok{color:var(--green)}.cc .apc-mfcol .h.ko{color:var(--danger)}
.cc .apc-mfcol .chips{display:flex;flex-wrap:wrap;gap:6px}
.cc .apc-mfcol .chips span{font-size:.8rem;font-weight:700;border-radius:8px;padding:4px 10px}
.cc .apc-mfcol .chips span.ok{background:var(--green-l);color:var(--green)}
.cc .apc-mfcol .chips span.ko{background:var(--tint-rg);color:var(--danger)}
.cc .apc-mfcol.ok{border-color:var(--green-l)}.cc .apc-mfcol.ko{border-color:#f6d7d3}
.cc .apc-mrows{display:flex;flex-direction:column;gap:2px}
.cc .apc-mrow-in{display:flex;align-items:center;gap:6px;border-radius:10px;transition:.14s}
@media (hover:hover){.cc .apc-mrow-in:hover{background:var(--bl-025)}}
.cc .apc-mrow-main{flex:1;min-width:0;display:flex;align-items:center;gap:11px;padding:8px 6px;text-decoration:none}
.cc .apc-mrow-pv{flex:none;width:32px;height:32px;display:grid;place-items:center;border-radius:8px;color:var(--danger);border:1px solid var(--line);margin-inline-end:6px;transition:.14s}
@media (hover:hover){.cc .apc-mrow-pv:hover{border-color:#c0392b;background:var(--tint-rg2)}}
.cc .apc-mrow-p{flex:none;width:60px;text-align:center;border-radius:9px;padding:5px 4px;line-height:1}
.cc .apc-mrow-p b{font-size:1.06rem;font-weight:900;letter-spacing:-.03em}
.cc .apc-mrow-p small{display:block;font-size:.56rem;font-weight:800;text-transform:uppercase;letter-spacing:.03em;opacity:.9;margin-top:2px}
.cc .apc-mrow-in.ok .apc-mrow-p{background:var(--green-l);color:var(--green)}
.cc .apc-mrow-in.ko .apc-mrow-p{background:var(--tint-rg2);color:var(--danger)}
.cc .apc-mrow-n{flex:1;min-width:0;font-weight:800;color:var(--ink);font-size:.92rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cc .apc-mrow-bar{flex:none;width:64px;height:7px;border-radius:999px;background:var(--surf2);overflow:hidden}
.cc .apc-mrow-in.ok .apc-mrow-bar i{display:block;height:100%;background:linear-gradient(90deg,#27c06a,#1fa254)}
.cc .apc-mrow-in.ko .apc-mrow-bar i{display:block;height:100%;background:linear-gradient(90deg,#e0574a,#c0392b)}
/* ⚠ `minmax(0,1fr)` ET NON `1fr` (2026-08-31). Une piste `1fr` vaut
 *   `minmax(auto,1fr)` : elle ne descend pas sous la largeur MINIMALE de son
 *   contenu, et une seule piste trop exigeante élargit toute la grille — donc
 *   toute la page, que `overflow-x:clip` coupe alors à la même abscisse.
 *   Règle du dépôt depuis le 2026-08-08 ; ces pistes-là ne l'appliquaient pas.
 * ⚠ ET UN PALIER DE PLUS SOUS 560 px : trois tuiles de statistiques côte à
 *   côte sur un téléphone étroit ne laissent pas la place d'un nombre à
 *   quatre chiffres sous son intitulé. */
@media(max-width:900px){.cc .apc-statgrid{grid-template-columns:repeat(3,minmax(0,1fr))}.cc .apc-ring{grid-column:1/-1;margin:0 auto}.cc .apc-mf{grid-template-columns:minmax(0,1fr)}}
@media(max-width:560px){.cc .apc-statgrid{grid-template-columns:repeat(2,minmax(0,1fr))}}
.cc .apc-tools{display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:10px}
.cc .apc-tool{display:grid;grid-template-columns:40px 1fr;grid-template-rows:auto auto;column-gap:12px;align-items:center;background:var(--cardg);border:1px solid var(--line);border-radius:13px;padding:11px 13px;box-shadow:var(--sh);transition:transform .14s,box-shadow .14s,border-color .14s;text-decoration:none}
@media (hover:hover){.cc .apc-tool:hover{transform:translateY(-2px);box-shadow:var(--sh2);border-color:var(--grad-bl-bd)}}
.cc .apc-tool .ic{grid-row:1/3;width:40px;height:40px;flex:none;border-radius:11px;display:grid;place-items:center;font-size:1.15rem;background:var(--stu-or-tint);color:var(--stu-or-k);margin:0;transition:.14s}
@media (hover:hover){.cc .apc-tool:hover .ic{background:var(--grad-or2);color:#fff;box-shadow:0 5px 13px rgba(var(--stu-or-rgb,244,125,74),.3)}}
.cc .apc-tool b{grid-column:2;align-self:end;font-size:.92rem;font-weight:800;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cc .apc-tool small{grid-column:2;align-self:start;font-size:.76rem;color:var(--ink3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cc .apc-tool-danger .ic{background:var(--tint-rg2);color:var(--danger)}
@media (hover:hover){.cc .apc-tool-danger:hover{border-color:#e0574a}}@media (hover:hover){.cc .apc-tool-danger:hover .ic{background:linear-gradient(150deg,#e0574a,#c0392b);box-shadow:0 5px 13px rgba(192,57,43,.3)}}
.cc .apc-tool-danger .ic{background:var(--tint-rg2);color:var(--danger)}
@media (hover:hover){.cc .apc-tool-danger:hover{border-color:#e0574a}}
.cc .apc-docs{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:8px}
.cc .apc-docs a{display:flex;align-items:center;justify-content:center;gap:8px;text-align:center;font-size:.85rem;font-weight:700;color:var(--ink2);border:1px solid var(--line);border-radius:12px;padding:12px;background:var(--surf);transition:.14s}
@media (hover:hover){.cc .apc-docs a:hover{border-color:var(--blue);color:var(--blue-7);background:var(--bl-025)}}
.cc .apc-docs a i{color:var(--stu-or-k);font-size:1.05rem}
/* liste verticale (discussions sécurisées, style ancienne page) */
.cc .apc-list{display:flex;flex-direction:column;gap:6px}
.cc .apc-list a{display:flex;align-items:center;gap:11px;padding:11px 14px;border:1px solid var(--line);border-radius:11px;background:var(--surf);color:var(--ink);font-weight:700;font-size:.9rem;text-decoration:none;transition:.14s}
.cc .apc-list a::after{content:"\F285";font-family:"bootstrap-icons";margin-inline-start:auto;color:var(--ink3);font-weight:400;font-size:.85rem}
.cc .apc-list a>i{color:var(--stu-or-k);font-size:1.05rem}
@media (hover:hover){.cc .apc-list a:hover{border-color:var(--stu-role,#00a1ff);background:var(--tint-bl3);color:var(--blue-7)}}
/* en-tête : actions + recherche */
.cc .apc-hactions{margin-inline-start:auto;display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.cc .apc-search{display:flex;align-items:center;gap:7px;background:var(--surf);border:1px solid var(--line);border-radius:10px;padding:6px 11px;transition:.15s}
.cc .apc-search:focus-within{border-color:var(--stu-role,#00a1ff);box-shadow:0 0 0 3px rgba(var(--stu-role-rgb,0,161,255),.14)}
.cc .apc-search i{color:var(--ink3);font-size:.9rem}
.cc .apc-search input{border:0;background:transparent;outline:none;font:inherit;font-size:.86rem;color:var(--ink);width:210px;max-width:44vw}
/* matières & enseignants (liste enrichie) */
.cc .apc-mat{display:flex;flex-direction:column;gap:8px}
.cc .apc-matrow{display:flex;align-items:center;gap:12px;background:var(--cardg);border:1px solid var(--line);border-radius:13px;padding:11px 13px;box-shadow:var(--sh);transition:.14s}
@media (hover:hover){.cc .apc-matrow:hover{border-color:var(--grad-bl-bd);box-shadow:var(--sh2)}}
.cc .apc-matrow.warn{border-inline-start:3px solid var(--amber)}
.cc .apc-matico{width:40px;height:40px;flex:none;display:grid;place-items:center;border-radius:11px;background:var(--stu-or-tint);color:var(--stu-or-k);font-size:1.15rem}
.cc .apc-matbody{flex:1;min-width:0}
.cc .apc-matn{font-weight:800;color:var(--ink);font-size:.95rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cc .apc-matsub{display:flex;flex-wrap:wrap;gap:6px;margin-top:5px}
.cc .apc-chip{display:inline-flex;align-items:center;gap:4px;font-size:.72rem;font-weight:800;border-radius:7px;padding:3px 8px;background:var(--surf2);color:var(--ink2);text-decoration:none;white-space:nowrap}
.cc .apc-chip.ens{background:var(--tint-bl);color:var(--info2)}@media (hover:hover){.cc .apc-chip.ens:hover{background:var(--tint-bl4)}}
.cc .apc-chip.ko{background:var(--tint-rg2);color:var(--danger)}
/* enseignants en charge — l'équipe de la classe, une carte par personne.
   Se replie de trois colonnes à une : la liste des matières qu'une personne
   assure peut être longue, et doit rester lisible sur un téléphone. */
.cc .apc-ens{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:10px}
.cc .apc-enscard{display:flex;align-items:flex-start;gap:11px;background:var(--cardg);border:1px solid var(--line);border-radius:13px;padding:11px 13px;box-shadow:var(--sh);transition:.14s}
@media (hover:hover){.cc .apc-enscard:hover{border-color:var(--grad-bl-bd);box-shadow:var(--sh2)}}
.cc .apc-ensav{position:relative;width:38px;height:38px;flex:none;border-radius:50%;display:grid;place-items:center;font-weight:900;font-size:.9rem;color:#fff;background:var(--ink3);overflow:hidden;text-decoration:none}
.cc .apc-ensav img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;border-radius:50%}
.cc .apc-ensb{flex:1;min-width:0}
.cc .apc-ensn{display:block;font-weight:800;color:var(--ink);font-size:.93rem;text-decoration:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
@media (hover:hover){.cc .apc-ensn:hover{color:var(--info2)}}
.cc .apc-enschips{display:flex;flex-wrap:wrap;gap:6px;margin-top:5px}
.cc .apc-ensmats{margin-top:6px;font-size:.76rem;color:var(--ink3);line-height:1.45}
.cc .apc-matens{position:relative;width:34px;height:34px;flex:none;border-radius:50%;display:grid;place-items:center;font-weight:900;font-size:.8rem;color:#fff;background:var(--stu-role-d,#0090ea);overflow:hidden;text-decoration:none;box-shadow:0 2px 6px rgba(var(--stu-role-rgb,10,144,234),.3)}
.cc .apc-matens img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.cc .apc-matbtns{flex:none;display:flex;align-items:center;gap:5px}
.cc .apc-matacts{display:inline-flex;gap:5px}
.cc .apc-matdt{width:30px;height:30px;flex:none;display:grid;place-items:center;border:1px solid var(--line);border-radius:8px;color:var(--ink3);transition:.14s;background:var(--surf)}
@media (hover:hover){.cc .apc-matdt:hover{border-color:var(--blue);color:var(--blue-7)}}
/* élèves (liste enrichie) */
.cc .apc-eleves{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:8px}
.cc .apc-eleve{display:flex;align-items:center;gap:11px;background:var(--cardg);border:1px solid var(--line);border-radius:13px;padding:9px 12px;box-shadow:var(--sh);transition:.14s;text-decoration:none}
@media (hover:hover){.cc .apc-eleve:hover{border-color:var(--grad-bl-bd);background:var(--bl-025)}}
.cc .apc-ava{position:relative;width:38px;height:38px;flex:none;border-radius:50%;display:grid;place-items:center;font-weight:900;font-size:.9rem;color:#fff;background:var(--ink3);overflow:hidden}
.cc .apc-ava.f{background:#e0559b}.cc .apc-ava.m{background:#00a1ff}
.cc .apc-ava img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;border-radius:50%}
.cc .apc-elb{flex:1;min-width:0;display:flex;flex-direction:column}
.cc .apc-eln{font-size:.88rem;font-weight:800;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cc .apc-elm{font-size:.72rem;color:var(--ink3);font-weight:600}
.cc .apc-elc{color:var(--ink3);font-size:.9rem;flex:none}
/* évolution (graphe SVG) */
/* ===== Groupes de sections + accordéons style accueil (.grp) ===== */
/* ⚠ LES DEUX COLONNES SONT RÉTABLIES (2026-08-11). Elles avaient été retirées au
   profit du `display:block`, ce qui donnait deux sections pleine largeur
   empilées — « Accès » puis « Contacts complémentaires » — là où elles tiennent
   côte à côte et se lisent ensemble. La grille est en `auto-fit` : sur un écran
   étroit elle revient d'elle-même à une colonne, sans requête de média. */
.cc .apc-grid2{display:grid;grid-template-columns:repeat(auto-fit,minmax(340px,1fr));gap:14px;align-items:start}
.cc .apc-group{display:flex;align-items:center;gap:11px;margin:30px 0 10px;color:var(--ink);font-size:.8rem;font-weight:900;letter-spacing:.12em;text-transform:uppercase}
.cc .apc-group i{width:30px;height:30px;flex:none;display:grid;place-items:center;border-radius:9px;background:var(--grad-or2);color:#fff;font-size:1rem;letter-spacing:0;box-shadow:0 5px 13px rgba(var(--stu-or-rgb,244,125,74),.3)}
.cc .apc-group::after{content:"";flex:1;height:2px;border-radius:2px;background:linear-gradient(90deg,var(--tint-bl5),transparent)}
/* ⚠ `:first-child` SERT QUAND LE TITRE OUVRE UNE CARTE — il évite d y creuser
 *   trente pixels. Mais il s applique AUSSI quand le conteneur `.cc` suit
 *   directement une rangée de `.kpis`, et `.kpis` n a AUCUNE marge basse : le
 *   titre se collait alors aux cartes d indicateurs, à huit pixels. Constaté sur
 *   journal-des-absences.php le 2026-08-27, et le motif « .kpis puis .cc » se
 *   retrouve sur huit écrans — le corriger ici les sert tous.
 *   ⚠ On ne RETIRE pas la règle du dessus : elle reste juste dans son cas. On
   *   nomme le cas où elle ne l est pas. */
.cc .apc-group:first-child{margin-top:8px}
.kpis + .cc > .apc-group:first-child{margin-top:30px}
.cc .apc-subhead{display:flex;align-items:center;gap:8px;margin:16px 0 8px;font-size:.72rem;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:var(--ink3)}
.cc .apc-subhead.first{margin-top:2px}
.cc .apc-subhead i{color:var(--stu-or-k);font-size:.9rem}
.cc .apc-subhead::after{content:"";flex:1;height:1px;border-radius:2px;background:linear-gradient(90deg,var(--line),transparent)}
/* Accordéon calqué sur l'accordéon d'accueil (.grp) */
.cc .apc-acc{background:var(--cardg);border:1px solid var(--line);border-radius:14px;box-shadow:var(--sh);margin-bottom:8px;overflow:hidden;transition:border-color .15s,box-shadow .15s}
.cc .apc-acc[open]{border-color:var(--grad-bl-bd);box-shadow:var(--sh2)}
.cc .apc-acc>summary{list-style:none;cursor:pointer;display:flex;align-items:center;gap:12px;padding:13px 15px;font-weight:800;color:var(--ink2);font-size:.93rem;transition:.15s}
.cc .apc-acc>summary::-webkit-details-marker{display:none}
.cc .apc-acc>summary>i:first-child{width:34px;height:34px;flex:none;display:grid;place-items:center;border-radius:10px;background:var(--tint-bl);color:var(--tint-k);font-size:1.05rem;transition:.15s}
@media (hover:hover){.cc .apc-acc>summary:hover{background:var(--tint-bl3)}}
.cc .apc-acc[open]>summary{color:var(--info2);background:var(--tint-bl2);border-bottom:1px solid var(--grad-bl-bd)}
.cc .apc-acc[open]>summary>i:first-child{background:var(--grad-or2);color:#fff;box-shadow:0 5px 13px rgba(var(--stu-or-rgb,244,125,74),.35)}
.cc .apc-acc>summary .chev{margin-inline-start:auto;font-size:.85rem;color:var(--ink3);opacity:.55;transition:transform .2s,opacity .2s,color .2s}
.cc .apc-acc[open]>summary .chev{transform:rotate(180deg);opacity:1;color:var(--info2)}
.cc .apc-acc .apc-acc-b{padding:14px 15px 16px}
.cc .apc-charts{display:grid;grid-template-columns:1fr 1fr;gap:14px}
.cc .apc-charts .panel{display:flex;flex-direction:column}
.cc .apc-chart-t{display:flex;align-items:center;gap:8px;font-size:.92rem;font-weight:800;color:var(--ink);margin-bottom:8px}
@media(max-width:900px){.cc .apc-charts{grid-template-columns:minmax(0,1fr)}}
.cc .apc-seqsel{display:flex;flex-wrap:wrap;gap:8px}
.cc .apc-seq{display:inline-flex;align-items:center;gap:6px;padding:8px 14px;border-radius:999px;border:1px solid var(--line);background:var(--surf);color:var(--ink2);font-weight:800;font-size:.85rem;transition:.14s}
@media (hover:hover){.cc .apc-seq:hover{border-color:var(--blue);color:var(--blue-7)}}
.cc .apc-seq.on{background:var(--blue);border-color:var(--blue);color:#fff;box-shadow:0 4px 12px rgba(0,140,224,.28)}
.cc .apc-kdelta{display:block;margin-top:3px;font-size:.72rem;font-weight:800;letter-spacing:0}
.cc .apc-kdelta.up{color:var(--green)}.cc .apc-kdelta.down{color:var(--danger)}
.cc .apc-evo-h{display:flex;flex-wrap:wrap;gap:10px 16px;align-items:center;margin-bottom:10px;font-size:.82rem}
.cc .apc-evo-leg{display:inline-flex;align-items:center;gap:6px;color:var(--ink2);font-weight:700}
.cc .apc-evo-leg i{width:12px;height:12px;border-radius:3px;display:inline-block}
.cc .apc-evo-delta{display:inline-flex;align-items:center;gap:4px;font-weight:800;border-radius:999px;padding:2px 10px;font-size:.78rem}
.cc .apc-evo-delta.up{background:var(--green-l);color:var(--green)}
.cc .apc-evo-delta.down{background:var(--tint-rg);color:var(--danger)}
.cc .apc-evo{width:100%}
.cc .apc-evo svg{width:100%;height:220px;display:block}
.cc .apc-evo .gl{stroke:var(--line);stroke-width:1;vector-effect:non-scaling-stroke}
.cc .apc-evo text{font-family:var(--sans);font-size:10px;fill:var(--ink3)}
/* commentaires */
.cc .apc-coms{display:flex;flex-direction:column;gap:8px}
.cc .apc-com{background:var(--cardg);border:1px solid var(--line);border-inline-start:3px solid var(--blue);border-radius:12px;padding:11px 14px;box-shadow:var(--sh)}
.cc .apc-com-t{font-size:.9rem;color:var(--ink);line-height:1.5}
.cc .apc-com-d{font-size:.74rem;color:var(--ink3);margin-top:6px;display:flex;align-items:center;gap:5px}

/* ── Programme du cours : ce qui est déjà écrit, avant ce qu'on peut ajouter.
   Le modal proposait une liste de « Chapitre 1..20 » sans rapport avec le
   prévisionnel, et sans montrer les leçons déjà saisies : on écrasait sans le
   savoir. Voir a-propos-du-cours.php. */
.cc .prog-liste{display:flex;flex-direction:column;gap:6px;margin-bottom:14px;max-height:220px;overflow-y:auto}
.cc .prog-item{display:flex;gap:12px;align-items:baseline;padding:8px 12px;border:1px solid var(--line);border-radius:10px;background:var(--surf2);font-size:.86rem}
.cc .prog-item b{flex:none;font-weight:850;color:var(--blue-7);font-size:.78rem;min-width:62px}
.cc .prog-item span{color:var(--ink2);overflow:hidden;text-overflow:ellipsis}

/* Un champ dont le format est refusé (assets/js/stu-champs.js). Rouge de
   DANGER, jeton sémantique : une erreur de saisie ne change pas de sens selon
   le rôle connecté, elle ne suit donc pas la couleur du profil. */
.stu-champ-ko{border-color:var(--danger) !important;box-shadow:0 0 0 3px rgba(192,57,43,.14) !important}

/* ══ CHOIX ET ÉTAPES — l'habillage commun des formulaires de saisie ═══════════
   Demande de l'utilisateur (2026-08-04) : « un affichage plus sophistiqué pour
   les input radio et le système de pagination ».

   Les cinq pages de saisie avaient chacune sa classe de conteneur — na-oui,
   ma-radios, mp-radios, pa-radios, np-radios — et deux noms de fil d'étapes
   différents. Elles portent maintenant EN PLUS `stu-radios` et `stu-fil` : les
   règles de page restent en place, celles-ci les habillent par-dessus. Un seul
   endroit à retoucher, et rien à défaire si une page veut se singulariser. */

/* ── Les choix : de vraies pastilles cliquables ─────────────────────────────
   Un bouton radio de 13 px, sur un téléphone, se rate une fois sur trois. La
   pastille prend toute la hauteur de la ligne et reste une VRAIE <label> autour
   d'un vrai <input> : le clavier, le lecteur d'écran et la validation native
   continuent de fonctionner — on habille, on ne remplace pas. */
.stu-radios{display:flex;flex-wrap:wrap;gap:8px}
.stu-radios>label{position:relative;display:inline-flex;align-items:center;gap:9px;
  padding:9px 15px 9px 12px;border:1px solid var(--line);border-radius:11px;background:var(--surf);
  font-size:.89rem;font-weight:650;color:var(--ink2);cursor:pointer;transition:.14s;user-select:none;margin:0}
@media (hover:hover){.stu-radios>label:hover{border-color:var(--blue);color:var(--ink);background:var(--bl-025)}}
/* La coche elle-même : dessinée, mais toujours présente et focalisable. */
.stu-radios>label>input[type=radio]{appearance:none;-webkit-appearance:none;margin:0;flex:none;
  width:17px;height:17px;border:2px solid var(--ink3);border-radius:50%;background:var(--surf);
  transition:.14s;cursor:pointer}
.stu-radios>label>input[type=radio]:checked{border-color:var(--blue);border-width:5px}
.stu-radios>label:has(>input:checked){border-color:var(--blue);background:var(--bl);color:var(--blue-8);font-weight:800}
/* :has() n'existe pas partout ; la classe posée par stu-champs.js prend le
   relais nulle part ici, donc on double la mise en évidence par le focus. */
.stu-radios>label>input[type=radio]:focus-visible{outline:2px solid var(--blue);outline-offset:3px}
/* Un choix obligatoire resté vide (assets/js/stu-champs.js). Rouge SÉMANTIQUE :
   une erreur de saisie ne change pas de sens selon le rôle connecté. */
.stu-radios>label.stu-radio-ko{border-color:var(--danger);background:rgba(192,57,43,.06);color:var(--danger)}
.stu-radios>label.stu-radio-ko>input[type=radio]{border-color:var(--danger)}

/* ── Le fil des étapes ──────────────────────────────────────────────────────
   Trois états à distinguer d'un coup d'œil, et non deux : FRANCHIE (on peut y
   revenir), COURANTE, À VENIR (inatteignable). L'étape franchie porte une coche
   et devient cliquable ; celle à venir reste éteinte — proposer un lien mort
   apprend à l'utilisateur que les liens ne veulent rien dire. */
.stu-fil{counter-reset:etape}
.stu-fil li{position:relative;transition:.16s}
.stu-fil li[data-ouvrable="1"]{cursor:pointer}
@media (hover:hover){.stu-fil li[data-ouvrable="1"]:hover{background:var(--bl-025)}}
.stu-fil li[data-ouvrable="0"]{opacity:.55}
.stu-fil li .n{transition:.16s;font-variant-numeric:tabular-nums}
.stu-fil li[aria-current="step"]{font-weight:800}
/* La pastille de l'étape courante respire, celle qui est faite se tait. */
.stu-fil li[aria-current="step"] .n{box-shadow:0 0 0 4px rgba(var(--stu-role-rgb,0,161,255),.16)}
.stu-fil li[data-fait="1"] .n{box-shadow:none}
/* La jauge : elle glisse au lieu de sauter. */
.stu-jauge,.na-jauge{transition:width .32s cubic-bezier(.4,0,.2,1)}
@media (max-width:640px){
  /* Sur un téléphone, les intitulés d'étape mangent la colonne : on ne garde
     que les pastilles, alignées, et l'intitulé de l'étape COURANTE. */
  .stu-fil li{flex:1 1 auto;justify-content:center;padding:.7rem .5rem;font-size:0}
  .stu-fil li .n{font-size:.9rem}
  .stu-fil li[aria-current="step"]{font-size:.82rem;flex:2 1 auto}
}

/* ── LE FIL D'ÉTAPES, DÉTACHÉ DE L'EN-TÊTE ────────────────────────────────────
   Demande de l'utilisateur : « une pagination beaucoup mieux stylée, attrayante,
   DÉCALÉE DE L'ENTÊTE ». Le fil touchait le bandeau : deux blocs colorés collés
   se lisent comme un seul, et l'on ne voyait plus où commençait la saisie.

   Il devient une CARTE posée, avec sa propre respiration et son ombre — le même
   vocabulaire que les panneaux du shell, pour qu'il appartienne visiblement à la
   page et non au bandeau. */
.stu-fil{margin-top:22px !important;margin-bottom:26px !important;
  background:var(--cardg);border:1px solid var(--line);border-radius:16px;
  box-shadow:var(--sh);padding:6px;overflow:hidden}
/* Les bordures internes disparaissent : la carte porte déjà la sienne, et deux
   traits l'un sur l'autre font sale. */
.stu-fil li{border:0 !important;background:transparent !important;border-radius:12px !important}
.stu-fil li+li{position:relative}
/* Un chevron discret entre deux étapes, plutôt qu'un filet : il indique un SENS,
   ce qu'un trait ne fait pas. Masqué quand la barre se replie en colonne. */
.stu-fil li+li::before{content:"";position:absolute;inset-inline-start:-1px;top:50%;width:7px;height:7px;
  border-top:2px solid var(--line);border-inline-end:2px solid var(--line);
  transform:translateY(-50%) rotate(45deg);opacity:.9}
.stu-fil li[aria-current="step"]{background:var(--bl) !important;color:var(--blue-8) !important}
.stu-fil li[data-fait="1"]{color:var(--ink2) !important}
@media (hover:hover){.stu-fil li[data-fait="1"]:hover{background:var(--bl-025) !important}}
/* La jauge court SOUS la carte, sur toute sa largeur : elle mesure l'avancement
   de bout en bout, elle n'appartient à aucune étape en particulier. */
.stu-fil>.na-jauge,.stu-fil>.stu-jauge{position:absolute;inset-inline-start:0;bottom:0;height:3px;
  background:linear-gradient(90deg,var(--blue),var(--blue-7));border-radius:0 3px 3px 0;z-index:1}
.stu-fil{position:relative}
@media (max-width:640px){ .stu-fil li+li::before{display:none} }

/* ── LE RÉCAPITULATIF, GROUPÉ ─────────────────────────────────────────────────
   Vingt lignes à plat ne se relisent pas — et c'est justement le moment où l'on
   relit, juste avant d'enregistrer. Quatre à six blocs titrés, chacun sur sa
   grille, et le champ vide DIT qu'il est vide au lieu de disparaître. */
.na-rc-bloc{margin:0 0 20px}
.na-rc-bloc:last-child{margin-bottom:0}
.na-rc-bloc>h4{display:flex;align-items:center;gap:9px;margin:0 0 10px;
  font-size:.74rem;font-weight:900;letter-spacing:.1em;text-transform:uppercase;color:var(--blue-7)}
.na-rc-bloc>h4 i{font-size:.95rem;letter-spacing:0}
.na-rc-bloc>h4::after{content:"";flex:1;height:1px;background:var(--bl2)}
.na-rc-grille{display:grid;grid-template-columns:repeat(auto-fit,minmax(16rem,1fr));gap:0 1.75rem}
.na-rc-l{display:flex;justify-content:space-between;align-items:baseline;gap:1rem;
  padding:9px 0;border-bottom:1px solid var(--line2)}
.na-rc-l dt{color:var(--ink3);font-size:.82rem;font-weight:650;flex:none}
.na-rc-l dd{margin:0;font-weight:700;color:var(--ink);text-align:end;overflow-wrap:anywhere}
.na-rc-l.vide dd{font-weight:600}
.na-rc-l.vide dd span{color:var(--ink3);font-style:italic;font-weight:600}

/* ── LE COMPTE CRÉÉ ───────────────────────────────────────────────────────────
   La seule fois où un mot de passe s'affiche. Il ne se reverra pas : il lui faut
   une carte, pas une ligne de bandeau — et de quoi le copier sans le retaper. */
/* ── LE BLOC DE CONFIRMATION SUIT LE RÔLE (2026-08-11, demande du client) ─────
   Il était peint en vert fixe (--green / --green-l), la couleur du « succès ».
   Il prend désormais la famille du RÔLE connecté (--stu-role*, posée par
   inclusions/role_utilisateur.php), comme le reste de l'espace : l'écran de
   création d'un établissement n'a pas à changer de palette au dernier écran.
   ⚠ LE SENS DU SUCCÈS N'EST PAS PERDU POUR AUTANT : il est porté par l'ICÔNE
     (bi-patch-check-fill) et par le titre, pas par la couleur du cadre. On
     déplace le signal, on ne le supprime pas.
   ⚠ REPLI SUR LE VERT : ces blocs s'affichent aussi sur des pages ouvertes SANS
     session — création d'un compte depuis le portail. Aucune couche de rôle n'y
     est posée, donc aucun --stu-role* : sans second argument, la bordure serait
     tombée en « invalide » et le bloc aurait perdu son cadre. */
.na-cree{margin-top:16px;border:1px solid var(--stu-role, var(--green));border-radius:16px;
  background:var(--stu-role-tint, var(--green-l));padding:18px 20px;box-shadow:var(--sh)}
.na-cree-h{display:flex;align-items:flex-start;gap:12px;margin-bottom:14px}
.na-cree-h>i{font-size:1.5rem;color:var(--stu-role-k, var(--green));flex:none;line-height:1.2}
.na-cree-h b{display:block;font-size:1.05rem;font-weight:900;color:var(--ink);letter-spacing:-.02em}
.na-cree-h small{display:block;margin-top:2px;font-size:.82rem;color:var(--ink2)}
.na-cree-ids{display:grid;grid-template-columns:repeat(auto-fit,minmax(15rem,1fr));gap:10px}
.na-id{display:flex;align-items:center;gap:10px;background:var(--surf);
  border:1px solid var(--line);border-radius:12px;padding:10px 12px;min-width:0}
.na-id .k{flex:none;font-size:.7rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase;color:var(--ink3)}
/* Le mot de passe se lit caractère par caractère : chasse fixe, et de l'air
   entre les lettres — « xxq8TZEMh » ne se recopie pas à l'œil autrement. */
.na-id code{flex:1;min-width:0;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:.95rem;font-weight:700;color:var(--ink);letter-spacing:.04em;
  overflow-wrap:anywhere;background:none;padding:0}
.na-cp{flex:none;width:32px;height:32px;display:grid;place-items:center;cursor:pointer;
  border:1px solid var(--line);border-radius:9px;background:var(--surf2);color:var(--ink2);transition:.14s}
@media (hover:hover){.na-cp:hover{border-color:var(--blue);color:var(--blue-7);background:var(--bl-025)}}
.na-cree-n{margin:12px 0 0;font-size:.84rem;line-height:1.55;color:var(--ink2);
  display:flex;gap:8px;align-items:flex-start}
.na-cree-n i{color:var(--stu-role-k, var(--green));flex:none;margin-top:.15em}
.na-cree-a{display:flex;flex-wrap:wrap;gap:10px;margin-top:16px}

/* L'observation est un TEXTE LIBRE, pas une étiquette : dans une grille de
   colonnes étroites, elle se retrouvait pressée sur trois caractères de large et
   s'enroulait sur dix lignes. Elle prend la largeur entière et passe sous son
   intitulé plutôt qu'à côté — c'est ainsi qu'on lit un paragraphe. */
.na-rc-l.large{grid-column:1/-1;flex-direction:column;align-items:stretch;gap:4px}
.na-rc-l.large dd{text-align:start;font-weight:600;line-height:1.55;white-space:pre-line}

/* ── LES CODES D'ACCÈS, MASQUÉS PAR DÉFAUT ────────────────────────────────────
   Cette page s'affiche souvent dans un bureau, parfois devant l'élève ou un
   tiers. Les codes se révèlent d'un geste délibéré, tous ensemble.

   ⚠ ON MASQUE PAR LE STYLE, PAS EN REMPLAÇANT LA VALEUR PAR DES POINTS : le
     texte reste réel, donc sélectionnable et copiable même caché. Personne n'est
     obligé de l'afficher pour le prendre. */
.na-codes{margin-top:16px;border:1px solid var(--bl2);border-radius:14px;background:var(--bl-025);padding:14px 16px}
.na-codes-h{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:11px}
.na-codes-h>span{font-size:.72rem;font-weight:900;letter-spacing:.09em;text-transform:uppercase;color:var(--blue-7)}
.na-oeil{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--line);border-radius:9px;
  background:var(--surf);color:var(--ink2);font:inherit;font-size:.78rem;font-weight:750;
  padding:5px 11px;cursor:pointer;transition:.14s}
@media (hover:hover){.na-oeil:hover{border-color:var(--blue);color:var(--blue-7)}}
/* TROIS CODES PAR LIGNE. En auto-fit, le nombre de colonnes dépendait de la
   largeur du modal : deux ici, quatre là, et les six codes se lisaient dans un
   ordre différent selon l'écran. Trois colonnes fixes donnent deux rangées de
   trois, toujours les mêmes — on retrouve un code à sa place. En dessous de
   900 px on passe à deux, puis à une : trois colonnes de 9 rem tronqueraient
   un code de huit caractères. */
.na-codes-g{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}
@media (max-width:900px){.na-codes-g{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:560px){.na-codes-g{grid-template-columns:1fr}}
.na-codes.masque .na-id code{-webkit-text-security:disc;text-security:disc;color:var(--ink3);letter-spacing:.18em}
/* Les navigateurs sans -webkit-text-security (Firefox) : on floute. Le texte est
   toujours là, toujours copiable, simplement illisible à l'œil. */
@supports not (-webkit-text-security:disc){
  .na-codes.masque .na-id code{filter:blur(5px);user-select:all}
}
.na-codes-n{margin:11px 0 0;font-size:.79rem;line-height:1.5;color:var(--ink3)}

/* L'avertissement de doublon liste plusieurs élèves, une par ligne : sans
   white-space, les retours à la ligne s'écrasent et tout se lit d'un bloc. */
.na-dit span{white-space:pre-line}

/* ══════════════════════════════════════════════════════════════════════════════
   FORMULAIRES DE CRÉATION EN PLUSIEURS ÉTAPES  (.na-*)
   ──────────────────────────────────────────────────────────────────────────────
   Remontées ici le 2026-08-04, depuis la feuille privée de nouvel-apprenant.php.

   Elles n'ont jamais eu de raison d'être propres à l'inscription d'un élève :
   un fil d'étapes, un panneau d'étape, une grille de champs, un bandeau de
   message et un récapitulatif sont ce que demande TOUT formulaire de création en
   plusieurs étapes. nouveau-personnel.php et nouveau-parent.php écrivaient
   chacun son propre dialecte pour dire la même chose — .np-steps, .np-pane,
   .np-grid, .np-f, .np-msg d'un côté, .pa-* de l'autre — et les trois pages ne
   se ressemblaient plus.

   ⚠ LE PRÉFIXE .na- EST CONSERVÉ À DESSEIN. Le renommer aurait touché les trois
     pages, leurs scripts et leurs tests dans le même mouvement : beaucoup de
     risque pour un préfixe. Il ne veut plus dire « nouvel apprenant » mais
     « formulaire en étapes » — c'est écrit ici, une fois.
   ══════════════════════════════════════════════════════════════════════════════ */
/* 🔴 CETTE FAMILLE PARLAIT UN VOCABULAIRE DE JETONS QUE LA FEUILLE N'A PAS.
 *    --txt, --txt2 et --bd viennent de la feuille privée d'où ces règles ont été
 *    remontées ; ici ils ne sont DÉFINIS NULLE PART, et treize déclarations
 *    tombaient donc à côté sans rien dire. Ce n'est pas anodin :
 *      · `color:var(--txt2)` invalide → la couleur est HÉRITÉE : le texte
 *        secondaire s'affichait de la même encre que le principal, d'où des
 *        panneaux tout gris sans hiérarchie ;
 *      · `border:1px solid var(--bd)` invalide → c'est le RACCOURCI ENTIER qui
 *        tombe, donc `border-style:none` : le fil d'étapes n'avait aucun trait,
 *        et le filet du bloc d'aide n'a jamais été tracé.
 *    Trois alias suffisent, et ils suivent le mode sombre puisque --ink, --ink2
 *    et --line y sont redéfinis. Renommer les treize usages aurait touché trois
 *    pages et leurs tests pour le même résultat.
 * ⚠ SUR :root, pas sur un conteneur : le fil d'étapes, les panneaux et le bloc
 *   d'accueil ne partagent aucun ancêtre commun plus étroit — et personne
 *   d'autre ne définit ces trois noms (vérifié sur toutes les feuilles). */
:root{--txt:var(--ink);--txt2:var(--ink2);--bd:var(--line)}
/* Le retour : discret, mais toujours au même endroit — au-dessus du titre. */
.na-retour{display:inline-flex;align-items:center;gap:.15rem;margin:0 0 .75rem;
  padding:.35rem .7rem .35rem .45rem;border:1px solid var(--bd);border-radius:2rem;
  background:var(--surf);color:var(--txt2);text-decoration:none;font-size:.86rem;
  transition:border-color .2s,color .2s,background .2s}
@media (hover:hover){.na-retour:hover{border-color:var(--stu-role);color:var(--stu-role-txt);background:var(--stu-role-tint3)}}
.na-retour i{font-size:1.15rem}

/* La frise : trois jalons reliés par un trait qui se remplit. Le trait dit la
   progression mieux qu'un numéro — on voit d'un coup où l'on en est. */
.na-fil{display:flex;gap:0;list-style:none;padding:0;margin:0 0 1.4rem;flex-wrap:wrap;position:relative}
.na-fil li{flex:1 1 13rem;display:flex;align-items:center;gap:.7rem;padding:.85rem 1rem;
  border:1px solid var(--bd);background:var(--surf);position:relative;
  color:var(--txt2);font-size:.9rem;font-weight:500;
  transition:border-color .25s,color .25s,background .25s,box-shadow .25s}
.na-fil li:first-child{border-radius:.8rem 0 0 .8rem}
.na-fil li:last-child{border-radius:0 .8rem .8rem 0}
.na-fil li+li{border-inline-start:0}
.na-fil li .n{flex:0 0 1.85rem;height:1.85rem;display:grid;place-items:center;border-radius:50%;
  background:var(--surf2);color:var(--txt2);font-weight:700;font-size:.85rem;
  box-shadow:0 0 0 0 var(--stu-role-tint);transition:background .25s,color .25s,box-shadow .25s}
.na-fil li[aria-current="step"]{border-color:var(--stu-role);color:var(--txt);
  background:var(--stu-role-tint3);z-index:1}
.na-fil li[aria-current="step"] .n{background:var(--stu-role-d);color:#fff;
  box-shadow:0 0 0 .28rem var(--stu-role-tint)}
.na-fil li[data-fait="1"]{color:var(--txt)}
.na-fil li[data-fait="1"] .n{background:var(--ok,#1e8f57);color:#fff}
.na-fil li[data-fait="1"] .n::before{content:"\2713"}
.na-fil li[data-fait="1"] .n span{display:none}
@media(max-width:640px){
  .na-fil li,.na-fil li:first-child,.na-fil li:last-child{border-radius:.8rem;border-inline-start:1px solid var(--bd)}
  .na-fil{gap:.45rem}
}

/* Les champs verrouillés doivent SE VOIR verrouillés : le matricule de
   l'établissement et son code secret viennent de la session, ils ne se
   saisissent pas. Un champ grisé sans explication passe pour une panne. */
.na-cols input[readonly]{background:var(--surf2);color:var(--txt2);cursor:default;border-style:dashed}
.na-cade{display:flex;align-items:center;gap:.4rem;font-size:.78rem;color:var(--txt2);margin-top:.3rem}
.na-cade i{font-size:.85rem}

/* Le matricule attribué est le résultat de l'étape : on le montre comme tel. */
#AlertSecretCode_1{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;letter-spacing:.02em;
  font-weight:600;border-style:solid}
#AlertSecretCode_1.na-ok{background:rgba(30,143,87,.1);color:var(--ok,#1e8f57);
  border-color:rgba(30,143,87,.4)}
/* Le fil de progression court derrière les jalons et se remplit par tiers.
   Une barre dit l'avancement d'un coup d'œil, là où trois pastilles obligent
   à les comparer. */
.na-jauge{position:absolute;inset-inline-start:0;bottom:-1px;height:2px;width:0;border-radius:2px;
  background:var(--stu-role-d);transition:width .45s cubic-bezier(.4,0,.2,1);z-index:2}
/* Un jalon franchi se rouvre d'un clic ; un jalon non atteint reste inerte. */
.na-fil li[data-fait="1"],.na-fil li[aria-current="step"]{cursor:pointer}
@media (hover:hover){.na-fil li[data-fait="1"]:hover{background:var(--stu-role-tint3);border-color:var(--stu-role)}}
.na-fil li[data-ouvrable="0"]{cursor:not-allowed;opacity:.72}
.na-etape{display:none}
.na-etape.on{display:block}
.na-cols{display:grid;grid-template-columns:repeat(auto-fit,minmax(15rem,1fr));gap:1rem 1.15rem}
.na-cols .large{grid-column:1/-1}
.na-lab{display:block;font-size:.83rem;color:var(--txt2);margin-bottom:.35rem}
.na-lab .req{color:var(--dgr,#d9534f)}
.na-oui{display:flex;gap:1.25rem;padding-top:.4rem}
.na-oui label{display:flex;align-items:center;gap:.4rem;margin:0;color:var(--txt);font-size:.9rem;cursor:pointer}
.na-oui input{width:auto;margin:0}
.na-bas{display:flex;gap:.6rem;justify-content:flex-end;margin-top:1.35rem;flex-wrap:wrap}
/* 🔴 LA BARRE D'ACTION SEULE DANS SA CARTE ÉTAIT DÉCENTRÉE (2026-08-16).
 *   `margin-top:1.35rem` sépare les boutons DES CHAMPS QUI LES PRÉCÈDENT, à
 *   l'intérieur d'un même panneau — c'est le cas partout sauf à l'étape 1 des
 *   écrans d'établissement : inclusions/creation_etablissement.php:152 y ouvre un
 *   panneau qui ne contient QUE le bouton « Continuer ».
 *   La marge n'y sépare alors de rien : elle s'ajoute aux 24 px de padding et
 *   pousse le bouton vers le bas. Mesuré : 45,6 px au-dessus, 24 px en dessous —
 *   d'où une carte visiblement de travers, sur téléphone comme sur ordinateur.
 * ⚠ La règle porte sur la POSITION, pas sur la page : un `.na-bas` premier
 *   enfant n'a, par définition, rien au-dessus de quoi se détacher. Elle vaut
 *   donc pour tout panneau de ce genre, présent ou à venir, sans qu'aucun écran
 *   ait à s'en souvenir. */
.na-bas:first-child{margin-top:0}
.na-dit{margin-top:.9rem;padding:.75rem .95rem;border-radius:.65rem;font-size:.9rem;display:none;
  align-items:flex-start;gap:.55rem}
.na-dit.on{display:flex}
.na-dit i{font-size:1.05rem;line-height:1.35}
.na-dit.ok{background:rgba(30,143,87,.12);color:var(--ok,#1e8f57);border:1px solid rgba(30,143,87,.32)}
.na-dit.ko{background:rgba(217,83,79,.12);color:#d9534f;border:1px solid rgba(217,83,79,.32)}
/* ⚠ LE RÉCAPITULATIF N'EST PLUS UNE GRILLE, ET C'EST CE QUI SERRAIT LES
   OBSERVATIONS. Cette règle datait de l'époque où le récapitulatif était une
   liste plate de lignes : elle rangeait ses ENFANTS en colonnes de 15 rem. Depuis
   qu'il est fait de blocs titrés, ce sont les BLOCS qu'elle mettait côte à côte —
   « Observations » se retrouvait dans une colonne étroite à côté de « Personnes à
   contacter », et son texte s'enroulait sur dix lignes de dix caractères.

   Les blocs s'empilent donc sur toute la largeur ; c'est la grille INTERNE de
   chaque bloc (.na-rc-grille) qui répartit ses lignes en colonnes. */
.na-recap{display:block;margin:0}
.na-vide{color:var(--txt2);font-size:.9rem;margin:0}

/* ══ CRÉATION / MODIFICATION D'UN ÉTABLISSEMENT — le mot d'accueil (.ce-*) ════
 *
 * Ce bloc était un paragraphe suivi d'une liste à puces : le texte le plus utile
 * de la page — les identifiants ne s'affichent qu'une fois — s'y noyait, et les
 * trois contacts de l'assistance n'étaient pas des cibles qu'on tape du pouce.
 * Il porte maintenant trois étages, du plus important au plus accessoire :
 *   1. LE MOT, avec sa pastille d'icône, qui reste du texte ;
 *   2. LES REPÈRES, ce qu'on veut savoir sans lire (nombre d'étapes, rien n'est
 *      écrit avant la fin, ce qui sera demandé) ;
 *   3. L'ASSISTANCE, en cartes cliquables — un appel, un WhatsApp, un courriel.
 *
 * ⚠ AUCUNE COULEUR DE RÔLE EN DUR. --stu-role* vient de role_utilisateur.php et
 *   change avec l'espace ; --green et --amber ne servent QU'aux marques de
 *   canal (WhatsApp), jamais à porter un sens d'état. */
.ce-bloc{display:grid;gap:1.15rem}

/* 1. Le mot ── la pastille tient l'œil, le texte garde sa lisibilité. */
.ce-mot{display:flex;gap:.9rem;align-items:flex-start}
.ce-mot-ic{flex:0 0 2.6rem;height:2.6rem;border-radius:.85rem;display:grid;place-items:center;
  font-size:1.15rem;background:var(--stu-role-tint,var(--surf2));color:var(--stu-role-txt,var(--blue));
  border:1px solid var(--bd)}
.ce-intro{margin:0;color:var(--txt2);font-size:.95rem;line-height:1.65}
.ce-intro b{color:var(--txt);font-weight:700}

/* 2. Les repères ── trois colonnes qui se replient d'elles-mêmes. */
.ce-reperes{list-style:none;margin:0;padding:0;display:grid;gap:.6rem;
  grid-template-columns:repeat(auto-fit,minmax(14rem,1fr))}
.ce-reperes li{display:grid;grid-template-columns:auto 1fr;gap:.15rem .65rem;align-items:center;
  padding:.7rem .85rem;border:1px solid var(--bd);border-radius:.8rem;background:var(--surf2)}
.ce-reperes i{grid-row:1/3;font-size:1.15rem;color:var(--stu-role-txt,var(--blue))}
.ce-reperes .k{font-size:.88rem;font-weight:700;color:var(--txt);line-height:1.3}
.ce-reperes .v{font-size:.78rem;color:var(--txt2);line-height:1.35}

/* 3. L'assistance ── des cartes, pas une liste à puces. */
.ce-aide{padding-top:1rem;border-top:1px dashed var(--bd)}
.ce-aide-t{font-size:.88rem;color:var(--txt2);margin-bottom:.7rem;
  display:flex;align-items:center;gap:.45rem}
.ce-aide-t i{color:var(--stu-role-txt,var(--blue))}
.ce-aide-t b{color:var(--txt)}
.ce-contacts{display:grid;gap:.6rem;grid-template-columns:repeat(auto-fit,minmax(15rem,1fr))}
.ce-ct{display:flex;align-items:center;gap:.75rem;padding:.75rem .85rem;
  border:1px solid var(--bd);border-radius:.85rem;background:var(--surf);
  color:var(--txt);text-decoration:none;box-shadow:var(--sh);
  transition:transform .15s,box-shadow .15s,border-color .15s}
.ce-ct:focus-visible{transform:translateY(-2px);box-shadow:var(--sh2);
  border-color:var(--stu-role,var(--blue));text-decoration:none}
@media (hover:hover){.ce-ct:hover{transform:translateY(-2px);box-shadow:var(--sh2);
  border-color:var(--stu-role,var(--blue));text-decoration:none}}
.ce-ct .ic{flex:0 0 2.3rem;height:2.3rem;border-radius:.7rem;display:grid;place-items:center;
  font-size:1.1rem;background:var(--stu-role-tint,var(--surf2));color:var(--stu-role-txt,var(--blue))}
/* WhatsApp garde sa couleur : c'est la marque du canal, pas un état. */
.ce-ct.wa .ic{background:rgba(37,211,102,.14);color:#128c4a}
:root[data-theme="dark"] .ce-ct.wa .ic{color:#4fd98a}
.ce-ct .tx{min-width:0;display:grid;gap:.1rem}
.ce-ct .k{font-size:.74rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em;color:var(--txt2)}
/* ⚠ Un numéro ou une adresse ne se coupe pas : on l'ajuste plutôt que de le
     laisser déborder de sa carte sur les petits écrans. */
.ce-ct .v{font-size:.92rem;font-weight:700;color:var(--txt);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ce-ct .go{margin-inline-start:auto;flex:0 0 auto;font-size:.76rem;font-weight:700;
  color:var(--stu-role-txt,var(--blue));display:inline-flex;align-items:center;gap:.1rem;white-space:nowrap}
.ce-ct .go i{font-size:1.05rem;transition:transform .15s}
@media (hover:hover){.ce-ct:hover .go i{transform:translateX(.15rem)}}
/* Sur téléphone la mention d'action mange la largeur du numéro : la flèche suffit. */
@media (max-width:520px){
  .ce-ct .go{font-size:0}
  .ce-ct .go i{font-size:1.15rem}
}

/* Le balisage hérité ($don_inscrip_eta) reste servi aux pages non refaites :
   ces trois règles ne coûtent rien et évitent qu'elles se dégradent. */
.ce-aide ul{margin:0;padding-inline-start:1.1rem;display:grid;gap:.3rem;list-style:disc}
.ce-aide li{line-height:1.5}
.ce-aide a{color:var(--stu-role-txt,var(--blue));text-decoration:none}
@media (hover:hover){.ce-aide a:hover{text-decoration:underline}}

/* ── LES PROFILS : IDENTITÉ, REPÈRES, LISTES D'ACCÈS ─────────────────────────
   🔴 CES RÈGLES ÉTAIENT RECOPIÉES DANS CHAQUE PAGE DE PROFIL — 23 dans
   `profil-apprenant.php`, 11 dans `profil-parents.php`, pour les mêmes classes.
   Ce n'était pas une page fautive et une page saine : c'est une famille qui
   n'avait jamais été remontée ici, et que chaque profil réimplémentait. Les
   voisines `.apc-group` et `.apc-grid2` y étaient déjà : la moitié du chemin
   avait été faite.
   ⚠ UNE SEULE DIVERGENCE À TRANCHER : le survol de `.pe-list a`. L'apprenant le
     peignait en bleu, le parent en orange. On garde l'ORANGE — ce sont des
     ACTIONS (ouvrir un PDF, appeler, écrire), et la règle de marque du projet
     veut que le menu dise le rôle et l'orange dise l'action. */
.cc .pf-id{display:grid;grid-template-columns:auto 1fr;gap:16px;align-items:center}
.cc .pf-id img{width:88px;height:88px;border-radius:18px;object-fit:cover;border:1px solid var(--line);background:var(--surf2)}
.cc .pf-id .nm{font-size:1.2rem;font-weight:900;color:var(--ink);letter-spacing:-.02em}
.cc .pf-id .rl{display:inline-flex;align-items:center;gap:6px;margin-top:5px;font-size:.8rem;font-weight:800;color:var(--blue-7);background:var(--bl);border:1px solid var(--bl2);border-radius:999px;padding:3px 11px}
.cc .pf-ms{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:12px}
.cc .pf-msc{background:var(--cardg);border:1px solid var(--line);border-radius:13px;padding:11px 13px;box-shadow:var(--sh)}
.cc .pf-msc b{display:block;font-size:.95rem;font-weight:800;color:var(--ink);word-break:break-word}
.cc .pf-msc span{display:block;margin-top:2px;font-size:.7rem;font-weight:800;text-transform:uppercase;letter-spacing:.05em;color:var(--ink3)}
/* ── « ACCÈS » : DES TUILES, PAS UNE COLONNE (2026-08-11) ────────────────────
   🔴 CE BLOC ÉTAIT LE PLUS MAUVAIS DE L'ESPACE PARENT. `.pe-list` empilait ses
     liens en `flex-direction:column`, et son parent `.apc-grid2` était forcé en
     `display:block` : six actions minuscules — Fiche PDF, Excel, Mon espace,
     Rapport, Appeler, Écrire — occupaient SIX LIGNES SUR TOUTE LA LARGEUR, soit
     plus de 300 px de haut pour six mots. Une page de profil doit se lire d'un
     coup d'œil, pas se dérouler.
   On reprend l'idiome DÉJÀ dans cette feuille (`.apc-eleves`, l. 941) : une
   grille qui se remplit, des tuiles bordées, une pastille pour l'icône. Six
   actions tiennent alors sur deux rangées.
   ⚠ L'icône garde sa couleur en ligne quand la page en pose une (PDF rouge,
     Excel vert) : on ne fixe QUE la pastille, jamais le glyphe. */
.cc .pe-list{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(auto-fill,minmax(196px,1fr));gap:9px}
.cc .pe-list li{min-width:0}
.cc .pe-list a{display:flex;align-items:center;gap:10px;padding:10px 12px;border-radius:12px;text-decoration:none;color:var(--ink2);font-weight:700;font-size:.87rem;transition:.14s;
  background:var(--cardg);border:1px solid var(--line);box-shadow:var(--sh);min-width:0;overflow-wrap:anywhere}
@media (hover:hover){.cc .pe-list a:hover{background:var(--stu-or-tint3,rgba(244,125,74,.07));color:var(--stu-or-k,#b35c36);
  border-color:var(--stu-or-tint1,rgba(244,125,74,.4));transform:translateY(-1px)}}
.cc .pe-list i{width:30px;height:30px;flex:none;display:grid;place-items:center;border-radius:9px;font-size:.95rem;
  background:var(--stu-or-tint3,rgba(244,125,74,.1));color:var(--stu-or-k,#b35c36)}

/* ── Tableaux d'informations : lisibles, sans lignes de grille ───────────────
   Deux colonnes fixes (l'intitulé, puis la valeur), pas de bordure horizontale
   sur chaque cellule : le contraste de la casse et de la couleur suffit à
   séparer les lignes, et un « — » ne mérite pas un trait.
   ⚠ La largeur de l'intitulé est BORNÉE (`width:1%` + `white-space:nowrap`) :
     sans cela la valeur se retrouvait collée à droite d'un intitulé étiré, ce
     qu'on voit sur la capture — « DATE DE NAISSANCE » à gauche, la date au
     milieu de nulle part. */
.cc .pf-t{width:100%;border-collapse:separate;border-spacing:0;font-size:.88rem}
.cc .pf-t th{text-align:start;font-size:.7rem;text-transform:uppercase;letter-spacing:.05em;color:var(--ink3);
  font-weight:800;padding:9px 14px 9px 0;width:1%;white-space:nowrap;vertical-align:top}
.cc .pf-t td{padding:9px 0;color:var(--ink);font-weight:600;overflow-wrap:anywhere}
.cc .pf-t tr+tr th,.cc .pf-t tr+tr td{border-top:1px solid var(--line2)}
/* Une valeur absente s'efface au lieu de crier : elle reste lisible pour qui la
   cherche, sans peser autant qu'une information réelle. */
.cc .pf-t td.vide{color:var(--ink3);font-weight:500}

/* ── IMPRESSION DU RÉCAPITULATIF DE VÉRIFICATION (2026-08-11) ─────────────────
   Posée par window.stuImprimerRecap() : une COPIE du récapitulatif est déposée
   au premier niveau dans #stuImpression, et le reste de la page est masqué le
   temps de l'impression. Cloner plutôt que masquer les voisins évite d'avoir à
   ré-afficher toute la chaîne d'ancêtres du bloc, règle qui casserait au premier
   changement de balisage.
   ⚠ #stuImpression reste caché À L'ÉCRAN : il n'existe que pour l'imprimante. */
#stuImpression{display:none}
.stu-imp-titre{font-size:1.25rem;font-weight:900;margin:0 0 2px}
.stu-imp-date{font-size:.8rem;color:#555;margin:0 0 14px}

/* L'en-tête d'identité : qui imprime, et pour quel établissement. Un
   récapitulatif qu'on fait signer et qu'on classe doit dire d'où il vient — la
   feuille ne portait que son titre et l'heure. */
.stu-imp-eta{display:flex;align-items:center;gap:12px;padding:0 0 10px;margin:0 0 12px;
  border-bottom:2px solid #222}
.stu-imp-eta img{width:46px;height:46px;object-fit:contain;flex:none}
.stu-imp-eta .n{font-size:1rem;font-weight:900;line-height:1.2}
.stu-imp-eta .s{font-size:.76rem;color:#555;line-height:1.35;margin-top:2px}

@media print{
  /* On masque les FRÈRES de la copie, pas ses ancêtres : elle est fille directe
     de <body>, donc rien ne la contraint. */
  body.stu-en-impression > *{display:none !important}
  body.stu-en-impression #stuImpression{display:block !important}
  body.stu-en-impression{background:#fff !important}
  /* Le récapitulatif s'imprime à plat : ni ombre, ni fond, ni colonne étroite. */
  #stuImpression .na-recap{display:block;box-shadow:none;background:#fff;border:0;padding:0}

  /* ⚠ « NE COUPE AUCUN div » COÛTAIT UNE PAGE BLANCHE (corrigé le 2026-08-16).
   *   La règle portait sur TOUS les div de la copie — donc aussi sur le
   *   récapitulatif entier, qui est lui-même un div et qui dépasse la hauteur
   *   d'une page. Ne pouvant l'y tenir, le navigateur le renvoyait à la page
   *   suivante : la première ne gardait que le titre et la date, et le document
   *   comptait une page de plus. C'est exactement ce que montre l'aperçu du
   *   client — « 1/3 » vide sous le titre.
   *
   *   L'intention était de ne pas couper une LIGNE en deux. On ne vise donc que
   *   les LIGNES — une paire intitulé/valeur, un identifiant — et rien de plus
   *   haut. Le reste se répartit sur les pages, comme il doit.
   *
   * ⚠ ET SURTOUT PAS .na-rc-bloc. Rendre un bloc titré insécable ramènerait le
   *   même défaut un cran plus bas : « Responsables » ou « Coordonnées » peuvent
   *   dépasser une page selon le nombre de champs remplis, et le bloc entier
   *   serait renvoyé à la page suivante en laissant un blanc derrière lui. Un
   *   titre qui ne doit pas rester seul en bas de page se dit par break-after,
   *   pas en soudant tout son contenu. */
  #stuImpression .na-rc-l,#stuImpression dl>div,
  #stuImpression .na-id{break-inside:avoid;page-break-inside:avoid}
  /* Un titre de bloc ne reste pas seul en bas de page, son contenu à la suivante. */
  #stuImpression .na-rc-bloc>h4,#stuImpression .na-rc-bloc>:first-child{break-after:avoid;page-break-after:avoid}
  /* Deux lignes minimum de part et d'autre d'une coupure. */
  #stuImpression{orphans:3;widows:3}
  /* L'en-tête d'identité se répète en tête de chaque feuille imprimée. */
  #stuImpression .stu-imp-eta{break-inside:avoid;page-break-inside:avoid}
}

/* ══ LES ÉCRANS DE COMPTE, SUR UN TÉLÉPHONE ════════════════════════════════════
 * Demande du client (2026-08-16), captures à l'appui : sur les neuf pages de
 * création et de modification de compte — école, établissement financier,
 * personnel, apprenant, parent — les titres se lisaient mal en colonne étroite.
 *
 * ⚠ PORTÉE VOLONTAIREMENT ÉTROITE. Ces classes (.hero, .lbl, .hint-inline,
 *   .na-id) servent sur une trentaine de pages du shell. Le marqueur .cc-compte,
 *   posé sur le conteneur de ces neuf écrans seulement, cantonne la retaille à la
 *   famille visée : rien ne bouge sur les tableaux de bord ni sur les pages IA,
 *   qui n'ont pas la même densité de formulaire.
 *
 * Un seul point de rupture, 640 px — celui qu'emploient déjà le fil d'étapes
 * (.stu-fil) et .na-fil, pour que tout bascule au même moment. */
@media (max-width:640px){

  /* ── LE TITRE DE PAGE ───────────────────────────────────────────────────────
   * Le bandeau gardait ses 32 px de marge de bureau et son filigrane calibré
   * pour 900 px de large : sur un téléphone, les icônes de 5 à 6 rem passaient
   * devant le titre au lieu de l'accompagner, et le titre lui-même butait sur le
   * plancher de son clamp (4vw ne vaut que 15 px à 380 px de large).
   *
   * On resserre le cadre, on relève le plancher du titre, et on donne au
   * surtitre le poids d'une étiquette plutôt que celui d'une ligne de texte —
   * c'est lui qui dit de quel écran il s'agit.
   *
   * ⚠ TOUT EST ALIGNÉ À GAUCHE, EXPLICITEMENT — demande du client du 2026-08-16.
   *   Un titre centré sur une colonne étroite oblige l'œil à chercher le début de
   *   chaque ligne ; aligné à gauche il se lit d'un seul mouvement vertical, et
   *   c'est aussi l'axe que suivent déjà les intitulés du formulaire juste
   *   dessous. Posé sur .hero et hérité, plutôt que répété : un centrage venu
   *   d'un parent — .reveal, une classe utilitaire — ne pourrait plus passer. */
  .cc-compte .hero{padding:24px 18px 22px;border-radius:18px;text-align:start}
  .cc-compte .hero>*{text-align:start}
  .cc-compte .hero .k{display:inline-block;background:rgba(255,255,255,.18);
    border:1px solid rgba(255,255,255,.34);border-radius:999px;padding:4px 12px;
    font-size:.64rem;letter-spacing:.13em}
  .cc-compte .hero h2{margin-top:13px;font-size:clamp(1.7rem,8.2vw,2.35rem);
    letter-spacing:-.035em;line-height:1.04;text-wrap:balance;
    text-shadow:0 1px 14px rgba(0,0,0,.16)}
  /* Un filet dégradé sous le titre : il ancre le bloc à gauche et donne une base
     au bandeau, là où le texte flottait au milieu d'un aplat de couleur. Il
     s'éteint vers la droite — un trait plein se lirait comme un séparateur, or
     il souligne. */
  .cc-compte .hero h2::after{content:"";display:block;width:56px;height:4px;
    margin-top:13px;border-radius:4px;
    background:linear-gradient(90deg,rgba(255,255,255,.95),rgba(255,255,255,.22))}
  /* Un halo en haut à gauche : le bandeau n'avait de relief qu'en haut à DROITE
     (.hero::after), c'est-à-dire du côté opposé au texte. */
  .cc-compte .hero::before{content:"";position:absolute;left:-40px;top:-70px;
    width:190px;height:190px;border-radius:50%;z-index:1;pointer-events:none;
    background:radial-gradient(circle,rgba(255,255,255,.20),transparent 70%)}
  /* Le filigrane redescend au rang de fond : plus discret, et surtout plus petit
     que le texte qu'il est censé habiller. */
  .cc-compte .hero-deco i{font-size:2.4rem !important;opacity:.12 !important}
  .cc-compte .hero-deco i:nth-child(n+6){display:none}
  .cc-compte .hero::after{width:150px;height:150px;right:-60px;top:-60px}
  /* ── LES REPÈRES DU BANDEAU ─────────────────────────────────────────────────
   * Trois pastilles cerclées, chacune sur sa ligne : une centaine de pixels de
   * haut pour trois phrases de réassurance, et le formulaire repoussé sous la
   * ligne de flottaison. Elles perdent leur cadre et deviennent une petite liste
   * — même information, même ordre, trois fois moins de place. L'icône seule
   * porte l'accent : sur un aplat de couleur, un cadre de plus n'ajoute rien. */
  .cc-compte .hero .m{margin-top:14px;display:block;column-gap:0}
  .cc-compte .hero .m span{display:flex;align-items:flex-start;gap:8px;
    background:none;border:0;border-radius:0;padding:3px 0;
    font-size:.78rem;font-weight:600;line-height:1.4;color:rgba(255,255,255,.94)}
  .cc-compte .hero .m i{opacity:.85;margin-top:.12em}

  /* ── LE FIL D'ÉTAPES ────────────────────────────────────────────────────────
   * L'écran renvoyé par le client : quatre étapes empilées en une colonne de
   * trois cents pixels, avant même le premier champ. La cause est dans la base
   * de .na-fil li — « flex:1 1 13rem », soit 208 px. Sur un téléphone de 380 px,
   * une seule tient par ligne, et flex-wrap fait le reste.
   *
   * ⚠ LE SHELL AVAIT DÉJÀ PRÉVU UNE VERSION COMPACTE, ET ELLE NE S'APPLIQUAIT
   *   JAMAIS. « .stu-fil li{font-size:0} » (plus haut dans cette feuille) voulait
   *   exactement cela ; mais le fil porte les DEUX classes, et « .na-fil li »,
   *   écrit plus bas à spécificité égale, l'emportait sur la font-size. Une règle
   *   de repli invisible est pire que pas de règle : on croit le cas traité. La
   *   portée .cc-compte passe devant les deux, et le dit.
   *
   * Une seule ligne, donc : chaque étape est une pastille, et seule l'étape
   * COURANTE porte son intitulé. C'est la seule dont le nom serve — les autres
   * se lisent au numéro, et à la coche verte une fois franchies. */
  .cc-compte .na-fil{flex-wrap:nowrap;gap:4px;align-items:center;margin-bottom:1rem}
  .cc-compte .na-fil li{flex:0 0 auto;gap:0;padding:.45rem;font-size:0;
    justify-content:center;border-radius:.75rem !important}
  .cc-compte .na-fil li .n{flex:0 0 1.7rem;height:1.7rem;font-size:.82rem}
  .cc-compte .na-fil li[aria-current="step"]{flex:1 1 auto;gap:.55rem;
    padding:.45rem .75rem;font-size:.82rem;font-weight:800;justify-content:flex-start}
  /* Plus de place pour le chevron entre deux étapes : la jauge dit le sens. */
  .cc-compte .na-fil li+li::before{display:none}

  /* ── LES TITRES DE SECTION ET LEUR PRÉCISION ────────────────────────────────
   * « AVANT DE COMMENCER » et « quelques minutes, et de quoi noter » partagent
   * une même ligne flex. Sur un téléphone la précision n'avait plus la place de
   * tenir à côté de l'intitulé : elle s'enroulait sur trois lignes en gardant
   * l'alignement vertical du titre, ce qui donnait le décalage visible sur les
   * captures. Elle prend désormais sa propre ligne — demande explicite du client,
   * « chacun sur sa ligne ».
   *
   * L'ordre compte : le filet (.lbl::after) doit rester sur la ligne du titre,
   * sans quoi il se placerait entre les deux et les séparerait deux fois. */
  .cc-compte .lbl{flex-wrap:wrap;row-gap:7px;margin-bottom:12px;align-items:baseline}
  .cc-compte .lbl::after{order:2}
  /* ⚠ ELLE EST ÉCRITE EN MINUSCULES DANS LA SOURCE, ET C'EST VOULU : le .lbl qui
   *   la porte est en text-transform:uppercase, si bien qu'elle s'affichait en
   *   capitales sans qu'on ait à l'écrire. En lui rendant sa casse pour la rendre
   *   lisible sur deux lignes, on héritait d'un « quelques minutes, et de quoi
   *   noter » tout en bas de casse — le client a demandé « au moins une lettre
   *   majuscule au début ». ::first-letter la pose SANS toucher aux quelque
   *   trente pages qui écrivent ces précisions, et sans rien casser si l'une
   *   d'elles commence déjà par une majuscule. */
  .cc-compte .lbl .hint-inline{order:3;flex:1 1 100%;display:block;
    text-transform:none;letter-spacing:0;font-size:.8rem;font-weight:600;
    line-height:1.5;color:var(--ink2);
    padding:7px 11px 7px 12px;border-radius:0 10px 10px 0;
    background:var(--bl-025,rgba(0,161,255,.05));
    border-inline-start:3px solid var(--stu-role, var(--blue))}
  .cc-compte .lbl .hint-inline::first-letter{text-transform:uppercase;font-weight:800}

  /* ── LE MODAL « COMPTE CRÉÉ » ───────────────────────────────────────────────
   * Même défaut, mêmes conséquences : l'intitulé (MATRICULE, MOT DE PASSE) et sa
   * valeur se partageaient une ligne flex, avec le bouton de copie en bout. Un
   * matricule tient en dix-sept caractères et un mot de passe en dix : à cette
   * largeur ils s'enroulaient sous un intitulé resté centré verticalement — le
   * décalage que montre la capture du client.
   *
   * Trois zones plutôt qu'une rangée : l'intitulé sur sa ligne, la valeur et son
   * bouton sur la suivante. Le bouton reste À CÔTÉ de la valeur, et non sous
   * elle : c'est de cette valeur-là qu'il est le geste.
   *
   * ⚠ CETTE RÈGLE N'EST PAS PORTÉE PAR .cc-compte, ET C'EST VOLONTAIRE. La carte
   *   s'affiche dans un modal, et les modals de ce projet sont déplacés sous
   *   <body> — un ancêtre en `transform` annulait leur `position:fixed`. Ils ne
   *   sont donc plus DANS le conteneur de la page : une règle scopée sur celui-ci
   *   ne les atteindrait pas. On vise .na-cree, la carte elle-même, que seul
   *   assets/js/stu-compte-cree.js produit — la portée reste exacte. */
  .na-id{display:grid;grid-template-columns:1fr auto;
    grid-template-areas:"k k" "v b";align-items:center;row-gap:5px;column-gap:10px}
  .na-id .k{grid-area:k;font-size:.66rem;letter-spacing:.1em}
  .na-id code{grid-area:v;font-size:1rem}
  .na-id .na-cp{grid-area:b}
  /* Une seule carte d'identifiant par ligne : côte à côte, deux valeurs en chasse
     fixe se tronquaient l'une l'autre. */
  .na-cree-ids{grid-template-columns:1fr}
  /* L'en-tête de la carte : le pictogramme ne prend pas la moitié de la ligne. */
  .na-cree{padding:15px 14px}
  .na-cree-h{gap:10px}

  /* ── RÉVÉLER LES CODES D'ACCÈS ──────────────────────────────────────────────
   * Le client ne trouvait plus le bouton « Afficher » sur son téléphone. En
   * cause, l'en-tête du bloc : un titre et un bouton en space-between sur une
   * seule ligne, sans repli ni flex:none. « Codes d'accès (7) » prenait toute la
   * largeur et poussait le bouton hors du cadre — les codes restaient masqués
   * sans qu'aucun geste ne paraisse possible.
   *
   * Le bouton passe sous le titre et prend la largeur : c'est LE geste attendu à
   * cet endroit — les codes ne s'affichent qu'une fois — il n'a pas à se
   * disputer une ligne avec autre chose. */
  .na-codes{padding:13px 12px}
  .na-codes-h{flex-wrap:wrap;row-gap:9px}
  .na-codes-h>span{flex:1 1 100%}
  .na-oeil{flex:1 1 100%;justify-content:center;padding:9px 12px;font-size:.84rem}
}

/* ══ LE RENVOI VERS UN ASSISTANT ═══════════════════════════════════════════════
 * Posé dans la feuille PARTAGÉE, et c'est le point : ce motif est fait pour se
 * répéter. Relevé le 2026-08-11 — quatorze appels IA en trente jours pour près
 * de trois mille comptes : l'accès n'était ni fermé ni payant, c'est qu'aucun
 * geste de travail n'y conduisait. Chaque écran métier peut désormais amener la
 * personne AVEC sa question déjà écrite (assistants.php?a=…&q=…).
 * Le dupliquer page par page l'aurait fait diverger dès le deuxième usage. */
/* 🔴 CE BOUTON ÉTAIT ILLISIBLE POUR LES TRENTE ET UN PROFILS (2026-08-27).
     Il posait `background:var(--bl,#0f6f86)` avec du texte BLANC. Le repli
     `#0f6f86` est un bleu-vert soutenu — c'était clairement l'intention — mais
     `--bl` EXISTE : c'est la TEINTE CLAIRE du rôle (`$c['tint']`, l. 1284). Un
     repli ne s'applique jamais quand la variable est définie. On obtenait donc
     du blanc sur un pastel : **mesuré 1,23:1 au MEILLEUR des cas**, sur les
     trente et une variantes de rôle. Le client l'a décrit comme « flou ».
   ⚠ `--stu-or-btn` EST LE JETON PRÉVU POUR UN APLAT PORTANT DU BLANC — cette
     feuille le dit elle-même plus haut, et les boutons de `.alie2` l'emploient
     déjà. Il vaut 5,83:1. C'est aussi la règle du projet : le menu dit le rôle,
     l'orange dit l'ACTION — et « demander à ALIE » en est une.
   ⚠ ET LE REPLI EST UNE VRAIE COULEUR, pas un nom qui pourrait exister
     ailleurs sous un autre sens : c'est la faute exacte qu'on répare ici. */
.ai-lien{display:inline-flex;align-items:center;gap:8px;text-decoration:none;font-size:.86rem;
  font-weight:750;padding:10px 17px;border-radius:11px;background:var(--stu-or-btn,#9c502f);
  color:#fff;box-shadow:0 6px 16px rgba(var(--stu-or-rgb,244,125,74),.28);transition:.15s}
@media (hover:hover){.ai-lien:hover{background:var(--stu-or-btn-h,#7f3f24);color:#fff;transform:translateY(-1px)}}
.ai-lien:focus-visible{outline:2px solid var(--info);outline-offset:2px}
/* ⚠ Le pictogramme ne s'efface plus : sur un aplat, une opacité de 0,9 le rend
     terne sans rien gagner — c'était un reste de l'époque « fond pastel ». */
.ai-lien i{opacity:1}

/* ── LE CONTOUR ÔTÉ DOIT ÊTRE REMPLACÉ ──────────────────────────────────────
 * 🔴 Quatre champs de recherche écrivent « outline:0 » ou « outline:none » HORS
 *    de toute pseudo-classe :focus — le contour est donc supprimé EN
 *    PERMANENCE, y compris quand le champ a le focus. Quelqu'un qui navigue au
 *    clavier ne voit alors plus où il se trouve (WCAG 2.4.7).
 *
 * ⚠ NE PAS CONFONDRE AVEC LES AUTRES. Bootstrap et le reste du fichier
 *   remplacent le contour par une ombre (« outline:0 » suivi de « box-shadow »),
 *   ce qui est la bonne pratique moderne : sur 77 occurrences relevées, 73
 *   étaient dans ce cas. Seules ces quatre-là ne remplaçaient rien.
 *
 * ⚠ :focus-visible ET NON :focus — le contour n'apparaît qu'à la navigation au
 *   clavier, jamais après un clic à la souris. C'est ce que l'utilisateur
 *   attend, et c'est pourquoi ces règles avaient sans doute été écrites. */
.search input:focus-visible,
.cc .cc-nom:focus-visible,
.cc .prep-toolbar .search input:focus-visible,
.cc .apc-search input:focus-visible{
  outline:2px solid var(--blue);
  outline-offset:2px;
  border-radius:4px;
}

/* ── LES GRILLES DESCENDENT JUSQU'AUX PLUS PETITS ÉCRANS ────────────────────
 * 🔴 MESURÉ AU NAVIGATEUR LE 2026-08-27, à 320 × 568. Deux pages sur quinze
 *    glissaient de côté — gestion-administrative rendait un document de 330 px
 *    dans un écran de 320, gestion-pedagogique 342. Dix-huit grilles de
 *    l'application posent un plancher supérieur à la largeur utile.
 *
 * ⚠ D'OÙ VIENT LE DÉBORDEMENT. « repeat(auto-fit, minmax(310px, 1fr)) » n'est
 *   pas une préférence : c'est un plancher DUR. La colonne ne descend jamais
 *   sous 310 px, même s'il n'y en a que 265 de disponibles — auto-fit choisit le
 *   NOMBRE de colonnes, jamais leur largeur minimale. Sur un écran de 320 px,
 *   .content en consomme 40 en marges : il reste 265.
 *
 * ⚠ ET UNE SECONDE CAUSE, PLUS DISCRÈTE. Un élément de grille vaut
 *   « min-width:auto » par défaut : il REFUSE de rétrécir sous la largeur
 *   minimale de son contenu. C'est ce qui poussait section.gp-bloc à 322 px dans
 *   une piste de 265, alors que sa grille était déjà en une seule colonne. Le
 *   « > * { min-width:0 } » ci-dessous est donc aussi nécessaire que le premier.
 *   Le projet connaît ce remède — gestion-administrative.php:1075 l'emploie déjà
 *   sur .ga-respb — il manquait aux conteneurs de blocs.
 *
 * ⚠ POURQUOI « !important », ET POURQUOI C'EST ACCEPTABLE ICI. Ces grilles sont
 *   déclarées dans les blocs <style> des pages, qui viennent APRÈS cette feuille
 *   dans le document : sans « important », l'ordre les ferait gagner. La portée
 *   est étroite — une seule propriété, sous 420 px, sur seize sélecteurs nommés.
 *
 * ⚠ 420px ET NON 320. Un plancher de 340 déborde déjà sur un écran de 375. Le
 *   seuil couvre le plus haut plancher relevé, plus la marge de .content. Au
 *   delà, auto-fit se débrouille très bien seul. */
@media(max-width:420px){
  .cc .ga-resp,.cc .ga-codes,.cc .ga-eco,.cc .ga-duo,
  .cc .apc-grid2,.cc .apc-ens,.cc .kid,.cc .gd-list,
  .cc .lcc-sec,.cc .lcc-mat,.cc .gg-duo,.cc .sp-grid,
  .cc .crs-docs,.cc .or-afaire,.alie-groupes,.alie-comp{
    grid-template-columns:1fr!important;
  }
  .cc .ga-resp>*,.cc .ga-codes>*,.cc .ga-eco>*,.cc .ga-duo>*,
  .cc .apc-grid2>*,.cc .apc-ens>*,.cc .kid>*,.cc .gd-list>*,
  .cc .lcc-sec>*,.cc .lcc-mat>*,.cc .gg-duo>*,.cc .sp-grid>*,
  .cc .crs-docs>*,.cc .or-afaire>*,.alie-groupes>*,.alie-comp>*,
  .cc .ga-blocs>*,.cc .gp-blocs>*{
    min-width:0;
  }
}
/* ── AUCUN CHAMP SOUS 16 px SUR TÉLÉPHONE ───────────────────────────────────
 * 🔴 MESURÉ AU NAVIGATEUR LE 2026-08-27 : 113 champs distincts sous 16 px, dont
 *    68 sur la seule page de gestion pédagogique.
 *
 *    Safari sur iOS AGRANDIT la page quand on met au point un champ dont la
 *    police fait moins de 16 px — et il n'en revient jamais tout seul.
 *    L'utilisateur se retrouve sur une page zoomée qu'il doit repincer à la
 *    main, à chaque champ, sur un formulaire qui en compte parfois soixante.
 *    Ce n'est pas une préférence de style : c'est une règle du moteur.
 *
 * ⚠ SEULEMENT SOUS 640px. Le rendu de bureau ne bouge pas d'un pixel.
 * ⚠ LES CHAMPS DEVIENNENT UN PEU PLUS HAUTS sur téléphone, et c'est le prix —
 *   très inférieur à celui d'une page qu'on repince à chaque saisie.
 * ⚠ « !important » PARCE QUE LES PAGES DÉCLARENT LEUR PROPRE TAILLE dans des
 *   blocs <style> qui viennent après cette feuille. Une seule propriété, sous un
 *   seul seuil.
 * ⚠ CE QUI EST EXCLU l'est pour une raison : range, case à cocher, bouton radio
 *   et boutons n'ont pas de texte saisi, iOS ne zoome pas dessus, et leur
 *   imposer 16 px déformerait leur dessin. */
@media(max-width:640px){
  input:not([type=hidden]):not([type=range]):not([type=checkbox]):not([type=radio]):not([type=submit]):not([type=button]):not([type=reset]):not([type=color]):not([type=file]),
  select,
  textarea{
    font-size:16px!important;
  }
}

/* ── LE RACCOURCI « ALLER AU CONTENU » ──────────────────────────────────────
 * WCAG 2.4.1 : premier élément focalisable de la page, invisible tant qu'on ne
 * l'atteint pas au clavier.
 *
 * 🔴 IL VIVAIT EN « left:-9999px » DANS UN <style> DE shell_haut.php, et ce
 *    déport DÉCALAIT TOUTE LA PAGE EN ARABE (mesuré le 2026-09-12 : document de
 *    10 334 px pour une fenêtre de 334). Un débordement vers la gauche est rogné
 *    en écriture gauche-à-droite ; en `dir="rtl"` la gauche est le côté de FIN,
 *    donc DÉFILABLE. On masque par découpe : aucun débordement, dans aucun sens.
 *
 * 🔴 ET LA RÈGLE VIT ICI, PAS DANS UN SHELL. Trois shells rendent ce lien
 *    (`shell_haut.php`, `gsa_page.php`, `shell_gouvernance.php`) mais un seul
 *    portait le <style> : dans les deux autres, « Aller au contenu principal »
 *    s'affichait en clair, en haut de l'écran. Les trois chargent cette feuille.
 *
 * ⚠ `inset-inline-start` AU FOCUS, jamais `left` : le raccourci doit reparaître
 *   du côté où commence la lecture (à droite en arabe). */
.stu-skip{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);
  clip-path:inset(50%);white-space:nowrap;z-index:9999}
.stu-skip:focus{width:auto;height:auto;overflow:visible;clip:auto;clip-path:none;
  white-space:normal;inset-inline-start:8px;top:8px;background:#0f6b60;color:#fff;
  padding:10px 16px;border-radius:8px;font:600 14px/1.2 system-ui,sans-serif;
  box-shadow:0 4px 14px rgba(0,0,0,.25)}

/* ── LES CIBLES TROP PETITES POUR UN DOIGT ──────────────────────────────────
 * WCAG 2.5.8 (AA depuis 2023) demande 24 × 24 px. Sur les quinze pages
 * mesurées, trois éléments seulement y manquaient vraiment — le comptage brut
 * de « 61 sous 44 px » mélangeait la NORME (24) et le guide d'Apple (44).
 *
 * ⚠ a.stu-skip N'EST PAS DANS CETTE LISTE, et c'est délibéré : il est masqué par
 *   découpe (1 px + `clip`, voir shell_haut.php) et n'apparaît qu'au focus
 *   clavier. Ce n'est pas une cible tactile, c'est un raccourci au clavier — le
 *   mesurer comme telle était une erreur de ma première passe.
 *   (Il vivait en « left:-9999px » jusqu'au 2026-09-12 : ce déport rendait la
 *   page défilable de 10 000 px en arabe.)
 *
 * ⚠ LES LIENS EN PLEIN TEXTE SONT EXEMPTÉS par la norme elle-même, et on ne les
 *   touche donc pas : leur imposer 24 px de haut disloquerait les paragraphes. */
@media(max-width:640px){
  /* ⚠ « .dl a » AUTANT QUE « .dl » : le lien mesuré à 16×21 est un ENFANT du
       conteneur .dl, pas le conteneur lui-même. Viser la classe seule laissait
       le lien intact — c est ce qu a montré la mesure de contrôle. */
  .content .dl a,.content a.dl,.cc .dl a,.cc a.dl,#stuPreparerOffline,.profcard-off{
    min-height:26px;
    min-width:26px;
    padding-block:4px;
    /* ⚠ LA HAUTEUR NE SUFFIT PAS : le critère porte sur les DEUX côtés, et ces
         liens n'enveloppent qu'un pictogramme de 16 px. Sans « inline-flex » et
         le centrage, min-width n'a aucun effet sur un lien en ligne. */
    display:inline-flex;
    align-items:center;
    justify-content:center;
  }
  input[type=checkbox],input[type=radio]{
    min-width:24px;
    min-height:24px;
  }
}
/* ═══════════════════════════════════════════════════════════════════════════
 * SUPPORT RTL (arabe) — audit #4, 2026-08-23.
 * <html dir="rtl"> est posé par shell_haut.php via stu_ui_langue(), mais aucune
 * règle [dir=rtl] n'existait : la mise en page restait en LTR. La structure du
 * shell est en flexbox (.side + .main), dont l'axe se retourne AUTOMATIQUEMENT
 * en RTL ; ce bloc ne rattrape donc que les propriétés directionnelles ÉCRITES
 * EN DUR (alignements, positions absolues, paddings de listes, bordures d'accent).
 * Il ne s'applique qu'à l'arabe — aucun effet sur les autres langues.
 *
 * ⚠ CES RÈGLES RESTENT EN PROPRIÉTÉS PHYSIQUES, ET C'EST VOULU (2026-08-26).
 *   Le reste du fichier a été converti en propriétés logiques (margin-inline-start,
 *   text-align:start…), qui se miroitent seules en RTL et rendent à l'identique
 *   en LTR. Ici, non : à l'intérieur d'un bloc [dir=rtl], « end » désigne LA
 *   GAUCHE. Écrire « text-align:end » dans ce bloc ajouterait un second
 *   renversement à celui du bloc lui-même, et les deux s'annuleraient — le texte
 *   arabe repartait à gauche, les barres d'accent changeaient de bord.
 *   « right » nomme un bord ; « end » le calcule. Dans un bloc déjà retourné,
 *   seul le nom est sûr. Certaines de ces règles sont désormais redondantes,
 *   leur règle de base se miroitant seule (`.menu`, les jauges, `.cc-type.on`,
 *   les bordures d'accent — passées en logique le 2026-09-10) ; elles sont
 *   conservées parce que plusieurs autres corrigent du CSS Bootstrap, lui non
 *   converti. Restent physiques À DESSEIN : les décors (`.hero-deco`, `.alie-deco`,
 *   `::after` radiaux), `.gnum` (centré) et le tiroir `.side` (translateX).
 * ═══════════════════════════════════════════════════════════════════════════ */
[dir="rtl"] body,[dir="rtl"] .content{text-align:right}

/* En-têtes et cellules de tableau : le libellé revient à droite. */
[dir="rtl"] table.stu th,[dir="rtl"] .cc table.table th,[dir="rtl"] .cc .table th,
[dir="rtl"] .cc .pf-t th,[dir="rtl"] .na-rc-l.large dd{text-align:right}

/* Boutons/chips à texte aligné : idem. */
[dir="rtl"] .enslink,[dir="rtl"] .cc .cc-type,[dir="rtl"] .cc .cc-chip{text-align:right}
[dir="rtl"] .cc .cc-type.on{padding-right:12px;padding-left:28px}

/* Menu déroulant du profil : ancré à droite en LTR → à gauche en RTL. */
[dir="rtl"] .menu{right:auto;left:0}

/* Jauge de progression du fil d'Ariane : ancrée à gauche → à droite. */
[dir="rtl"] .stu-fil>.na-jauge,[dir="rtl"] .stu-fil>.stu-jauge{left:auto;right:0}

/* Retraits de listes : padding-left → padding-right. */
[dir="rtl"] .cc .ps-3,[dir="rtl"] .cc ul.ps-3,[dir="rtl"] .ce-aide ul,
[dir="rtl"] .cc .form-check{padding-left:0;padding-right:1.1rem}
[dir="rtl"] .cc .form-check{padding-right:2.6em}

/* Bordures d'accent latérales : border-left → border-right (la couleur suit le
 * sélecteur d'origine ; on ne fait que déplacer le trait de gauche à droite). */
[dir="rtl"] .cc .cc-card[data-review="1"]{border-left:0;border-right:3px solid #e0a52b}
[dir="rtl"] .cc .apc-matrow.warn{border-left:0;border-right:3px solid var(--amber)}
[dir="rtl"] .cc .apc-com{border-left:0;border-right:3px solid var(--blue)}

/* Icônes de direction (« bi-arrow-right » etc.) : on retourne l'axe horizontal
 * pour qu'une flèche « suivant » pointe dans le sens de lecture arabe. */
[dir="rtl"] .foot-card .bi-arrow-right,[dir="rtl"] .soc-btn .bi-arrow-right{transform:scaleX(-1)}

/* ══ FIN DU SUPPORT RTL ═════════════════════════════════════════════════════
 * 🔴 CE REPÈRE N'EST PAS DÉCORATIF (posé le 2026-08-27).
 *
 *    test_rtl_arabe vérifie que chaque règle de la section RTL est bornée à
 *    [dir=rtl] — une garde utile : une règle nue s'y appliquerait aux huit
 *    langues. Mais il lisait « de SUPPORT RTL jusqu'à la FIN DU FICHIER ».
 *    N'IMPORTE QUELLE règle ajoutée en bas de cette feuille le faisait donc
 *    échouer, sans avoir le moindre rapport avec l'arabe. C'est arrivé deux
 *    fois en deux jours, à deux auteurs différents.
 *
 *    Le test s'arrête désormais ici. Tout ce qui suit ce repère lui est
 *    étranger — et doit le rester : n'ajoutez pas de règle RTL après.
 * ═══════════════════════════════════════════════════════════════════════════ */

/* ══════════════════════════════════════════════════════════════════════════════
 * TÉLÉPHONE — CHAQUE CHOSE SUR SA LIGNE, ET RIEN QUI SORTE DE L'ÉCRAN
 * Demande du client du 2026-08-27, captures à l'appui.
 * ════════════════════════════════════════════════════════════════════════════ */

/* ── ① L'OUTIL D'UN EN-TÊTE PASSE SOUS SON TITRE ──────────────────────────────
 *
 * 🔴 SUR DIX ÉCRANS, LE CHAMP DE RECHERCHE ÉTAIT COUPÉ NET : « Filtrer par
 *    classe ou matiè… », « Rechercher un er… », « Recherche » amputé de sa
 *    bordure. Toujours à la même abscisse — le signe d'un conteneur plus large
 *    que l'écran, et non d'un élément isolé qui dépasse.
 *
 * 🔴 CE N'EST PAS UN MANQUE DE PLACE, C'EST UN PLANCHER. `.lbl` est un flex SANS
 *    `flex-wrap` : le titre et son outil tiennent sur une ligne quoi qu'il
 *    arrive. Et chaque boîte de recherche porte un `min-width` de 240 à 260 px —
 *    à elle seule plus large que ce qui reste après le titre sur un téléphone de
 *    360 px. Élargir n'y change rien : il faut AUTORISER LE RETOUR À LA LIGNE.
 *
 * ⚠ `overflow-x:clip`, posé plus haut sur <body>, CACHAIT le débordement sans le
 *   supprimer : le champ n'était pas seulement invisible, il était inatteignable.
 *   Un garde qui masque un symptôme rend la cause plus difficile à voir.
 *
 * ⚠ LES CLASSES SONT ÉNUMÉRÉES, ET C'EST DÉLIBÉRÉ. Un `[class*="search"]` aurait
 *   tenu en un sélecteur — mais il attrape aussi `<i class="bi bi-search">`,
 *   l'icône de loupe, qui est un enfant direct de `.lbl` sur index.php : le
 *   pictogramme aurait pris une ligne entière à lui seul. Le raccourci coûtait
 *   plus cher que la liste.
 *
 * ⚠ L'ORDRE COMPTE, SINON UN FILET RESTE SEUL SUR SA LIGNE. `.lbl::after` est le
 *   trait qui prolonge le titre ; c'est le DERNIER enfant. Sans `order`, il
 *   passerait sous l'outil devenu pleine largeur — un trait horizontal orphelin
 *   en bas du bloc. On le rappelle sur la première ligne.
 *
 * ⚠ `flex:0 0 auto` SUR `.hint-inline`, PAS `flex:1 0 100%` : une précision
 *   longue — « (30 derniers jours, mesuré) » — descend alors d'un bloc sous le
 *   titre, tandis qu'un simple compteur (« 12 ») reste à sa place. Forcer les
 *   100 % aurait donné une ligne entière à deux chiffres. */
@media(max-width:640px){
  .lbl{flex-wrap:wrap;row-gap:9px}
  .lbl::after{order:1}
  /* ⚠ `max-width:100%` EN FILET : sans lui, un rappel plus long que la ligne
   *   ne pourrait NI se replier (flex-shrink à 0) NI passer dessous — il
   *   déborderait. Borné, il descend d'abord, puis se replie à l'intérieur. */
  .lbl>.hint-inline{order:2;flex:0 0 auto;max-width:100%}

  .lbl>.apc-hactions,.lbl>.apc-search,.lbl>.gd-search,.lbl>.jga-fill,
  .lbl>.jga-search,.lbl>.pge-search,.lbl>.mn-search,.lbl>.js-search{
    order:3;flex:1 0 100%;min-width:0;margin-inline-start:0}

  /* La boîte elle-même perd son plancher et prend la largeur offerte. Un bouton
     qui l'accompagne dans `.apc-hactions` passe alors sous elle : ce conteneur
     est déjà `flex-wrap:wrap`, il n'y avait que le plancher à retirer. */
  /* ⚠ LE DOUBLE PRÉFIXE N'EST PAS UNE COQUILLE. Chaque page redéclare sa
   *   boîte (« .cc .gd-search{min-width:260px} ») dans un <style> qui vient
   *   APRÈS cette feuille : à spécificité égale, c'est elle qui gagnerait. Il
   *   faut donc peser plus lourd — d'où la variante en .cc — tout en gardant
   *   la forme nue pour les .lbl qui vivent hors de ce conteneur. */
  .lbl .apc-search,.lbl .gd-search,.lbl .jga-search,.lbl .pge-search,
  .lbl .mn-search,.lbl .js-search,.lbl .jga-fill,
  .cc .lbl .apc-search,.cc .lbl .gd-search,.cc .lbl .jga-search,.cc .lbl .pge-search,
  .cc .lbl .mn-search,.cc .lbl .js-search,.cc .lbl .jga-fill{
    min-width:0;width:100%;box-sizing:border-box}
  .lbl input[type="search"],.lbl input[type="text"]{min-width:0;width:100%}
}

/* ⚠ 16 px EXACTEMENT, ET CE N'EST PAS UN CHOIX ESTHÉTIQUE : sous ce seuil,
 *   Safari mobile ZOOME la page à la mise au point, et la laisse ensuite
 *   décalée en défilement horizontal sans que rien ne le dise. Même règle que
 *   la messagerie, et visée sur le POINTEUR : une petite fenêtre de bureau se
 *   pointe à la souris, elle n'a pas besoin de grossir. */

@media(max-width:640px) and (pointer:coarse){
  .lbl input[type="search"],.lbl input[type="text"]{font-size:16px}
}

/* ── ② LE VIDE APRÈS LA DERNIÈRE COLONNE D'UN TABLEAU ─────────────────────────
 *
 * 🔴 APRÈS « SAMEDI », L'EMPLOI DU TEMPS LAISSAIT UNE PLAGE BLANCHE que rien ne
 *    remplissait, jusqu'au bord de la carte. Capture du client du 2026-08-27.
 *
 * ⚠ C'EST LA RÈGLE DE DÉFILEMENT CI-DESSUS QUI LE PRODUIT, ET SON COMMENTAIRE
 *   AFFIRMAIT LE CONTRAIRE. Il annonçait qu'un double conteneur défilant serait
 *   « sans effet visible » pour les 120 tableaux déjà enveloppés d'un
 *   `.tbl-wrap`. C'est vrai — SAUF si le tableau porte un `min-width`, et
 *   `.cc .edt` en porte un de 720 px. Passé en `display:block`, il devient une
 *   BOÎTE de 720 px ; le tableau anonyme qui se reconstitue à l'intérieur, lui,
 *   s'ajuste à son contenu et n'en occupe que 500. D'où 220 px de vide, à droite
 *   du dernier jour.
 *
 * Un tableau déjà dans un conteneur défilant redevient donc un tableau : c'est
 * son conteneur qui glisse, comme il l'a toujours fait.
 *
 * 🔴 MA PREMIÈRE VERSION NE VISAIT QUE `.tbl-wrap`, ET LE CLIENT A REVU LE VIDE
 *    LE MÊME JOUR — sur desiderata, pas sur l'emploi du temps. J'avais corrigé
 *    LA PAGE DONT JE TENAIS LA CAPTURE, pas le défaut.
 *
 * ⚠ COMMENT ON TROUVE LE DÉNOMINATEUR : un tableau qui déclare son PROPRE
 *   `min-width` est un tableau conçu pour dépasser — donc un tableau à qui l'on
 *   a déjà donné un parent qui défile. Ce sont exactement ceux-là, et eux seuls,
 *   que `display:block` abîme : la boîte prend la largeur du plancher, le
 *   tableau intérieur celle du contenu, et l'écart se voit. Balayage du dépôt :
 *   SIX tableaux, dans CINQ conteneurs — `.tbl-wrap` (shell), `.dg-wrap`
 *   (desiderata), `.cdc-scroll` (conseil de classe), `.ed-scroll` et `.ed-tab`
 *   (composeur d'emploi du temps). Le test rebalaye la même liste : un septième
 *   écrit demain fera échouer la suite.
 *
 * ⚠ LE PRÉFIXE `.content` N'EST PAS DÉCORATIF, IL PÈSE. La règle à défaire est
 *   `.content table,.cc table` (0,1,1) ; il faut donc au moins deux classes pour
 *   la battre à coup sûr. Et c'est `.content` — non `.cc` — parce que
 *   desiderata.php ne porte AUCUN `.cc` : la règle qui l'abîmait l'atteignait
 *   par `.content`, celle qui la répare doit passer par là aussi.
 *
 * Sélecteurs de niveau 2 — compris partout, y compris des navigateurs Android
 * anciens que l'application sert. */
@media(max-width:900px){
  .content .tbl-wrap table,
  .content .dg-wrap table,
  .content .cdc-scroll table,
  .content .ed-scroll table,
  .content .ed-tab table{display:table;overflow:visible}
}

/* ── ③ LES LIGNES D'UN TABLEAU SE RESSERRENT ──────────────────────────────────
 *
 * Demande du client : « moins d'espace entre les lignes de texte qui sont dans
 * les tableaux ». Une cellule dont le texte se replie sur trois lignes —
 * « impayes, resultats, baisse » — occupait la hauteur d'une carte entière :
 * `line-height:1.5` hérité du corps de page, plus 11 à 16 px de rembourrage haut
 * ET bas. Sur un écran de bureau cette respiration se paie en pixels dont on
 * dispose ; sur un téléphone elle se paie en défilement.
 *
 * ⚠ ON NE TOUCHE QU'À L'INTERLIGNE ET AU REMBOURRAGE VERTICAL. Le rembourrage
 *   horizontal sépare les colonnes : le réduire recollerait les valeurs entre
 *   elles, ce qui rendrait le tableau moins lisible, pas plus. */
@media(max-width:640px){
  .cc table th,.cc table td,.content table th,.content table td{line-height:1.32}
  table.stu th,table.stu td{padding-top:9px;padding-bottom:9px}
  .cc .table th,.cc .table td{padding-top:8px;padding-bottom:8px}
  .cc table.table th,.cc table.table td{padding-top:7px;padding-bottom:7px}
}

/* ── ④ LES GRILLES DU SHELL, SUR UNE COLONNE ──────────────────────────────────
 * Même défaut que celui corrigé dans les pages : `.apc-grid2` pose un plancher
 * de 340 px par piste. Sur un téléphone de 360 px il reste 320 px de contenu :
 * la grille dépasse, et ses deux cartes sont coupées à la même abscisse.
 * ⚠ `auto-fit` NE SAUVE PAS : il replie les pistes VIDES, jamais celles qui
 *   portent une carte. Deux cartes = deux pistes de 340 px, quoi qu'il arrive. */
@media(max-width:640px){
  .cc .apc-grid2{grid-template-columns:minmax(0,1fr)}
}

/* ══════════════════════════════════════════════════════════════════════════
 * LES CARTES DES ESPACES LATÉRAUX — 2026-09-05
 *
 * 🔴 `.card` ET `.card-h` ÉTAIENT EMPLOYÉS PAR 31 PAGES ET DÉFINIS NULLE PART.
 *
 *    Modération, gouvernance, banque, commercial, capital, formateur : tous ces
 *    espaces écrivent `<div class="card"><div class="card-h">…`. Or aucune des
 *    feuilles qu'ils chargent — `shell.css`, `menu-lateral.css`, `fil-ariane.css`
 *    — ne portait ces sélecteurs. Les blocs sortaient donc NUS : ni fond, ni
 *    bordure, ni coin arrondi, ni ombre, ni le moindre écart entre eux. D'où
 *    l'impression, signalée par le propriétaire, de sections « trop serrées » :
 *    ce n'était pas un espacement trop court, c'était l'ABSENCE de la carte.
 *
 *    `.card` existe bien dans `main.min.css` et `stu-ui.css` — les feuilles des
 *    pages Bootstrap héritées — mais aucune page du shell ne les charge. Le
 *    défaut est resté invisible parce que le nom de la classe, lui, avait l'air
 *    parfaitement légitime. C'est le piège « classe CSS jamais définie », déjà
 *    consigné pour ce dépôt.
 *
 * ⚠ SCOPÉ À `.cc`, ET C'EST LA PRÉCAUTION QUI COMPTE. `card` est un nom très
 *   commun : le définir nu ici l'imposerait aux pages héritées qui chargent
 *   `main.min.css`, où il a déjà un sens et une apparence. Les 27 pages
 *   concernées mettent toutes leurs cartes dans un conteneur `.cc` (mesuré) ;
 *   les quatre autres — `login`, `deconnexion`, `portail-de-reconnexion`,
 *   `mes-revisions` — portent leur propre habillage et ne doivent pas bouger.
 *   La spécificité (0,2,0) l'emporte d'ailleurs sur un `.card` nu, si jamais les
 *   deux feuilles se retrouvaient sur la même page.
 *
 * ⚠ ON REPREND L'APPARENCE DE `.panel`, ON N'EN INVENTE PAS UNE QUATRIÈME. Le
 *   shell a déjà un langage de surfaces ; une carte qui ne lui ressemblerait pas
 *   ferait deux systèmes visuels dans la même application.
 *
 * ⚠ SANS PADDING PROPRE : ces pages posent elles-mêmes leur `<div style="padding:14px">`
 *   à l'intérieur, et un tableau `.tbl-wrap` doit pouvoir toucher les bords de la
 *   carte. Le padding est donc dans l'en-tête et dans le contenu, jamais sur le
 *   cadre.
 * ══════════════════════════════════════════════════════════════════════════ */
.cc .card{background:var(--cardg);border:1px solid var(--line);border-radius:20px;
  box-shadow:var(--sh);overflow:hidden}
.cc .card > .card-h{display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  padding:16px 20px;background:var(--grad-or);border-bottom:1px solid var(--line)}
.cc .card > .card-h h3{margin:0;font-size:1.02rem;font-weight:800;letter-spacing:-.02em;
  color:var(--ink);display:flex;align-items:center;gap:9px;min-width:0}
.cc .card > .card-h h3 i{color:var(--stu-or-k);flex:none}
/* Le sous-titre d'en-tête, quand la page en pose un. */
.cc .card > .card-h .s{color:var(--ink3);font-size:.84rem;font-weight:600}
/* ⚠ « À DROITE » SE DIT `margin-inline-start`, PAS `margin-left` : le shell sert
     aussi l'arabe, et un `left` en dur y renverrait l'action du mauvais côté. */
.cc .card > .card-h .card-a{margin-inline-start:auto;display:flex;gap:8px;flex-wrap:wrap}

/* ══ LE RYTHME VERTICAL ENTRE BLOCS DE PREMIER NIVEAU ═════════════════════
 * 🔴 C'EST LA SECONDE MOITIÉ DU DÉFAUT. `.sec{margin-top:28px}` donne son
 *    rythme aux pages du shell, mais les espaces latéraux n'emploient pas `.sec` :
 *    ils enchaînent des `.card` et des `.panel` directement dans `.cc`, sans
 *    aucune règle pour les séparer. Deux cartes se touchaient donc, bord à bord.
 *
 * ⚠ ON ÉNUMÈRE LES COUPLES PLUTÔT QUE D'ÉCRIRE `.cc > * + *`. Ce dernier aurait
 *   la même spécificité que `.gv-sec{margin-top:28px}` et l'aurait emporté selon
 *   l'ordre des feuilles — c'est-à-dire par accident. Ici, seules les surfaces
 *   sont visées, et les blocs qui portent déjà leur propre marge (`.gv-note`,
 *   `.kpis`, `.gv-sec`) gardent la leur.
 *
 * ⚠ 26 px, ENTRE LES 18 px DE `.sec > .panel + .panel` ET LES 28 px DE `.sec` :
 *   ces blocs sont des SECTIONS de premier niveau, pas des panneaux empilés
 *   dans une même section. */
/* ⚠ `:not(.cc-air)` REND CETTE RÈGLE ET LA SUIVANTE MUTUELLEMENT EXCLUSIVES.
 *   Les espaces qui optent pour `.cc-air` (voir plus bas) veulent un rythme
 *   UNIFORME ; sans cette exclusion les deux règles se disputeraient les mêmes
 *   couples, et c'est la spécificité qui trancherait — mesuré : `.cc > .card +
 *   .card` pèse (0,3,0), trois classes, et l'emportait sur un sélecteur
 *   générique. Se battre à coups de spécificité produit des règles que
 *   personne ne sait plus lire. Ici, chaque couple n'a qu'UNE règle possible.
 *   Pour les quatre-vingts pages qui ne portent pas `.cc-air`, le rendu est
 *   inchangé au pixel près. */
.cc:not(.cc-air) > .card + .card,
.cc:not(.cc-air) > .card + .panel,
.cc:not(.cc-air) > .panel + .card,
.cc:not(.cc-air) > .panel + .panel,
.cc:not(.cc-air) > .kpis + .card,
.cc:not(.cc-air) > .kpis + .panel,
.cc:not(.cc-air) > .card + .tbl-wrap,
.cc:not(.cc-air) > .tbl-wrap + .card{margin-top:26px}
/* Une note d'information suivie d'une surface : l'écart doit se voir aussi. */
.cc:not(.cc-air) > .md-al + .card,
.cc:not(.cc-air) > .md-al + .panel,
.cc:not(.cc-air) > .md-al + .kpis{margin-top:22px}

/* ══ ET LE PREMIER BLOC NE COLLE PLUS AU BANDEAU ══════════════════════════
 * 🔴 MESURÉ À **0 px**. Sur `moderateur.php`, le bas du `.hero` et le haut du
 *    premier bloc de `.cc` étaient à la même ordonnée : les deux se touchaient.
 *    C'est la moitié la plus visible du « trop serré » signalé — et ce n'était
 *    pas un espacement trop court, c'était l'absence totale d'espacement.
 *
 *    `.content` est une colonne flex SANS `gap`, et le rythme du shell vient de
 *    `.sec{margin-top:28px}`. Une page qui ouvre son `.cc` par autre chose qu'un
 *    `.sec` — une carte, une rangée d'indicateurs, un panneau — n'hérite donc
 *    de rien.
 *
 * ⚠ TROIS SÉLECTEURS, ET LE RAYON D'ACTION A ÉTÉ COMPTÉ AVANT D'ÉCRIRE LA RÈGLE :
 *   **18 pages**, toutes des espaces latéraux ou IT — `actionnaire`, `banque*`,
 *   `commercial*`, `documentaliste`, `formateur`, `moderateur`,
 *   `repetiteur-revenus`, `it-*`. Toutes présentaient le même collage. Aucune
 *   page d'établissement n'ouvre son `.cc` par l'une de ces trois classes ; les
 *   quatre-vingts autres commencent par une classe qui leur est propre et ne
 *   bougent pas d'un pixel.
 *
 * ⚠ 28 px, LE MÊME QUE `.sec` : ce bloc EST la première section de la page, il
 *   doit respirer comme les suivantes — pas un peu moins parce qu'il est premier. */
.cc:not(.cc-air) > .card:first-child,
.cc:not(.cc-air) > .kpis:first-child,
.cc:not(.cc-air) > .panel:first-child{margin-top:28px}

/* ══ `.cc-air` — LE RYTHME DES ESPACES COMMERCIAL ET MODÉRATION ═══════════
 * 🔴 LA RÈGLE CI-DESSUS A ÉTÉ POSÉE SUR N-1 SITES SUR N. Elle énumère trois
 *    classes — `.card`, `.kpis`, `.panel` — et **huit des quinze pages COM/MOD
 *    n'ouvrent leur `.cc` par aucune des trois** (mesuré 2026-09-05) :
 *
 *      an-bar  commercial-analytique     0 px du bandeau (aucun margin-top)
 *      rg-nav  commercial-reglages       0 px
 *      ad-al   commercial-adoption      12 px  (sa propre `margin:12px 0`)
 *      al-al   commercial-alie          12 px
 *      dv-al   commercial-devis         12 px
 *      mk-al   commercial-marche        12 px
 *      mb-al   moderateur-bareme        12 px
 *      mc-al   moderateur-credits       12 px
 *
 *    Le premier bloc de ces huit écrans collait donc au `.hero`, ou s'en
 *    approchait de moitié — c'est le « trop serré » signalé une seconde fois.
 *    Et entre sections, le même écart oscillait : 26 px entre deux `.card`,
 *    12 à 16 px dès qu'une barre ou un encart s'intercalait.
 *
 * ⚠ ON NE RALLONGE PAS L'ÉNUMÉRATION : elle redériverait au prochain écran.
 *   La portée `.cc-air` permet des sélecteurs GÉNÉRIQUES sans toucher aux
 *   quatre-vingts pages d'établissement, dont le rythme est mesuré et voulu.
 *   Une page COM future hérite du rythme du seul fait de sa classe.
 *
 * ⚠ AUCUNE BATAILLE DE SPÉCIFICITÉ N'A LIEU ICI, ET C'EST VOULU. Ma première
 *   version comptait sur `:not(script)` pour peser plus lourd que la règle
 *   énumérée ; le navigateur a tranché autrement : `.cc > .card + .card` porte
 *   TROIS classes (0,3,0) et l'emportait sur (0,2,2). Deux cartes restaient à
 *   26 px pendant que tout le reste passait à 32. La règle énumérée est donc
 *   bornée par `:not(.cc-air)` : les deux ne se rencontrent plus jamais.
 *   `:not(script)` ne sert plus qu'à ce qu'il dit — écarter les `<script>`
 *   posés en fin de `.cc`, qui prendraient une marge pour rien.
 *
 * ⚠ 32 px, UN CRAN AU-DESSUS DES 28 px DE `.sec` : ces écrans empilent des
 *   cartes de données denses (tableaux, matrices, séries), et le rythme du
 *   shell y lit serré. Le même écart en tête et entre sections — un premier
 *   bloc n'a pas de raison de respirer moins que les suivants. */
.cc.cc-air > :first-child{margin-top:32px}
.cc.cc-air > :not(script) + :not(script){margin-top:32px}
/* Sur téléphone, l'espace vertical se paie : on revient au rythme du shell. */
@media (max-width:640px){
  .cc.cc-air > :first-child{margin-top:22px}
  .cc.cc-air > :not(script) + :not(script){margin-top:22px}
}

/* ══ `.stu-al` — LA BOÎTE D'ALERTE, UNE SEULE FOIS ═══════════════════════════
 * 🔴 ELLE EXISTAIT EN **DIX COPIES**, ET ELLES AVAIENT DÉJÀ DÉRIVÉ (2026-09-05) :
 *
 *      .ad-al .an-al .mk-al .pf-al .mb-al .mc-al   rayon 11, padding 12/15
 *      .al-al .rg-al                               rayon 10, padding 11/14
 *      .dv-al                                      rayon 10, padding 10/13
 *      .md-al                                      rayon 14, padding 14/17
 *
 *    Quatre paddings, trois rayons, trois tailles de police, quatre
 *    interlignages — pour une seule et même boîte, dans un seul et même espace.
 *    Et la dérive avait déjà atteint la couleur : `.md-al.info` se bordait de
 *    `--bl2` quand les neuf autres se bordaient de `--blue`.
 *
 * ⚠ « EXACTEMENT LE MÊME STYLE » SE TIENT EN PARTAGEANT LA RÈGLE. Recopier la
 *   déclaration dans chaque page revient à parier que dix endroits changeront
 *   ensemble ; ils ne le font jamais. La onzième page écrit `class="stu-al"` et
 *   n'a plus rien à déclarer.
 *
 * ⚠ LES VALEURS RETENUES SONT CELLES DE LA MAJORITÉ (six copies sur dix), pas
 *   une moyenne inventée : c'est la forme que la plupart des écrans montrent
 *   déjà, donc celle qui change le moins de choses à l'écran. */
.cc-air .stu-al{border-radius:11px;padding:12px 15px;font-size:.86rem;
  line-height:1.6;margin:12px 0}
.cc-air .stu-al.warn{background:var(--amber-l);border:1px solid var(--amber)}
.cc-air .stu-al.info{background:var(--bl);border:1px solid var(--blue)}
/* ⚠ `var(--ok)` N'EXISTE PAS DANS CE THÈME — mesuré au navigateur : la variable
     est vide et le repli codé en dur s'appliquait donc TOUJOURS. C'est la
     variable CSS inventée, recopiée depuis `.md-al.ok`. `--green` est définie
     (#16a06a), elle porte déjà le sens « tout va bien », et elle s'accorde au
     fond `--green-l` — on la nomme au lieu de deviner. */
.cc-air .stu-al.ok{background:var(--green-l);border:1px solid var(--green)}
.cc-air .stu-al.bad{background:rgba(220,38,38,.1);border:1px solid var(--danger)}
/* Deux alertes qui se suivent ne doivent pas se coller : leurs marges fusionnent
   à 12 px, ce qui est le bon écart INTERNE — le rythme de `.cc-air` ne les
   sépare pas puisqu'elles ne sont pas des sections de premier niveau. */
.cc-air .stu-al + .stu-al{margin-top:12px}

/* ⚠ `btn-primary` EST UN NOM BOOTSTRAP QUI TRAÎNE DANS TROIS PAGES DE
 *   MODÉRATION. Plutôt que de le retirer d'un coup — et de laisser un bouton nu
 *   le jour où l'on en oublie un —, on lui donne l'apparence du bouton du shell.
 *   `.btn` porte déjà tout ; il ne reste qu'à ne pas l'annuler. */
.cc .btn.btn-primary{background:var(--blue);color:#fff}
.cc a.btn-primary:not(.btn){display:inline-flex;align-items:center;gap:9px;
  border:0;border-radius:12px;padding:12px 18px;font-weight:800;font-size:.9rem;
  background:var(--blue);color:#fff;text-decoration:none}
