/* LEKOLPAM — feuille de style unique, sans dépendance.
   ═══════════════════════════════════════════════════════════════════════════
   LOT A — CHARTE. Réécrite le 2026-08-04 sur les jetons RELEVÉS DANS LES
   MAQUETTES, pas sur une description : `docs/maquette_app.html` pour les 18
   jetons de l'application, `docs/maquette_bulletin.html` pour les 4 paliers.

   TROIS DÉCISIONS DU MAINTENEUR appliquées ici :
     · polices AUTO-HÉBERGÉES (OFL), jamais de <link> Google Fonts — la version
       locale hors-ligne perdrait la typographie du produit. Voir
       public/polices/PROVENANCE.md pour les empreintes et les licences.
     · PAPIER CLAIR, JAMAIS DE THÈME SOMBRE. Le bloc
       @media (prefers-color-scheme: dark) de la version précédente est SUPPRIMÉ,
       et <meta name="color-scheme"> retiré d'index.html.
     · la latérale segmentée par rôle vient au lot suivant : cette feuille ne
       change AUCUNE structure de DOM, seulement l'apparence.

   CE QUI N'EST PAS UNE PRÉFÉRENCE : filets fins, beaucoup de blanc, aucun
   dégradé, aucun effet. La sobriété est le parti pris. Le seul endroit où
   l'interface se permet de l'audace est la pastille d'appréciation.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ══════════════════════════════════════════════════════════════════════════
   POLICES — 10 fichiers, servis depuis /polices/.

   ⚠ IBM PLEX SANS EST UNE POLICE VARIABLE (axe wght 100 700). UNE SEULE
   déclaration par sous-jeu, portant la PLAGE de graisses et non une valeur :
   c'est elle qui rend 400, 500, 600 ET 700 disponibles sans second fichier.
   Mesuré le 2026-08-04 : l'API css2 interrogée avec des graisses discrètes
   « déroule » l'axe en quatre @font-face pointant vers le MÊME fichier — s'y
   fier fait croire à quatre fontes distinctes et télécharger quatre fois.
   Spectral et Plex Mono, elles, sont statiques (même requête en plage : 400).

   ⚠ NE PAS DEMANDER DE GRAISSE ABSENTE. Spectral n'est embarquée qu'en 400,
   600 et 700. Un `font-weight: 800` déclencherait un gras SYNTHÉTIQUE, que le
   navigateur fabrique en épaississant les traits — exactement le rendu
   approximatif que la charte cherche à éviter. C'est le défaut qu'avait
   `.marque { font-weight: 800 }` dans la version précédente.

   `font-display: swap` conservé tel que l'amont le sert : sur une connexion
   haïtienne, du texte en police de repli vaut mieux que du texte invisible.
   ══════════════════════════════════════════════════════════════════════════ */

/* --- Spectral : nom d'école, titres, moyennes. Statique, 3 graisses. ------- */
@font-face {
  font-family: Spectral; font-style: normal; font-weight: 400; font-display: swap;
  src: url(/polices/spectral-400-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: Spectral; font-style: normal; font-weight: 400; font-display: swap;
  src: url(/polices/spectral-400-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: Spectral; font-style: normal; font-weight: 600; font-display: swap;
  src: url(/polices/spectral-600-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: Spectral; font-style: normal; font-weight: 600; font-display: swap;
  src: url(/polices/spectral-600-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: Spectral; font-style: normal; font-weight: 700; font-display: swap;
  src: url(/polices/spectral-700-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: Spectral; font-style: normal; font-weight: 700; font-display: swap;
  src: url(/polices/spectral-700-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* --- IBM Plex Sans : données et chiffres. VARIABLE, une par sous-jeu. ------ */
@font-face {
  font-family: 'IBM Plex Sans'; font-style: normal; font-weight: 100 700;
  font-stretch: 100%; font-display: swap;
  src: url(/polices/plex-sans-var-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Sans'; font-style: normal; font-weight: 100 700;
  font-stretch: 100%; font-display: swap;
  src: url(/polices/plex-sans-var-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* --- IBM Plex Mono : identifiants, codes, matricules. Statique. ----------- */
@font-face {
  font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 400; font-display: swap;
  src: url(/polices/plex-mono-400-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 400; font-display: swap;
  src: url(/polices/plex-mono-400-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ══════════════════════════════════════════════════════════════════════════
   JETONS CANONIQUES — recopiés LITTÉRALEMENT des maquettes.
   Ne pas « ajuster » une valeur ici sans changer la maquette d'abord : c'est
   elle qui fait foi, et deux sources qui divergent valent moins qu'une seule.
   ══════════════════════════════════════════════════════════════════════════ */
:root {
  /* Encre : un bleu profond, plus chaud qu'un marine. SEULE couleur
     institutionnelle. Paramétrable par école (schools.accent) — app.js repose
     --ink depuis MOI.school.accent à la connexion. */
  --ink:         #16324F;
  --ink-2:       #1E3D5C;
  --ink-soft:    #5A6B7E;
  --ink-faint:   #8A97A5;

  --paper:       #FCFBF8;   /* papier clair — JAMAIS de thème sombre */
  --board:       #EDECE6;   /* le sous-main sur lequel les cartes reposent */
  --card:        #FFFFFF;

  --line:        rgba(22, 50, 79, .13);   /* filets TEINTÉS d'encre, pas gris */
  --line-strong: rgba(22, 50, 79, .26);

  --ok:          #3F7D58;
  --warn:        #B0812F;
  --danger:      #B23A5B;
  --info:        #2F6E8F;
  --focus:       #2F6E8F;

  /* Présence — vocabulaire d'état de l'appel. Prêts pour le lot vie scolaire. */
  --present:     #3F7D58;
  --retard:      #B0812F;
  --absent:      #B23A5B;
  --excuse:      #2F6E8F;

  /* Fonds lavés. Valeurs des pastilles de la maquette (.pill.ok, .warn…),
     pas des opacités inventées. */
  --ok-wash:     rgba(63, 125, 88, .13);
  --warn-wash:   rgba(176, 129, 47, .14);
  --danger-wash: rgba(178, 58, 91, .12);
  --info-wash:   rgba(47, 110, 143, .12);
  --info-pale:   rgba(47, 110, 143, .08);

  /* ── Paliers d'appréciation ────────────────────────────────────────────
     La convention réelle d'Artimoun : bulletin rose, jaune, bleu ou blanc.
     Ce n'est pas de la décoration, c'est le vocabulaire d'état de TOUTE
     l'application — écolage, décision de passage, état d'accès d'une école,
     tout indicateur à quatre niveaux.
     ⚠ Les seuils et les libellés sont PARAMÉTRABLES PAR ÉCOLE et vivent en
     base (school_grading_steps). Ces quatre couleurs sont la référence
     d'Artimoun, pas une vérité imposée. */
  --tier-excellent: #B23A5B;   /* rose   ≥ 85     */
  --tier-tresbien:  #B0812F;   /* jaune  75–84,9  */
  --tier-bien:      #2F6E8F;   /* bleu   65–74,9  */
  --tier-assezbien: #6B7280;   /* blanc  50–64,9  */
  /* Palier COURANT d'un élément. Neutre par défaut ; les classes .palier-*
     ci-dessous le reposent localement, comme le bulletin le fait au :root. */
  --tier:      var(--ink-faint);
  --tier-wash: rgba(22, 50, 79, .06);

  /* Familles. Repli système explicite : si un .woff2 manque, l'écran reste
     lisible au lieu de tomber sur la serif par défaut du navigateur. */
  --serif: Spectral, Georgia, 'Times New Roman', serif;
  --sans:  'IBM Plex Sans', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --mono:  'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  /* Ombre : réservée à ce qui FLOTTE réellement au-dessus de la page — un
     dialogue, une notification. JAMAIS sur une carte : la charte interdit les
     effets, et une carte posée sur --board se détache par son filet. */
  --ombre: 0 20px 50px -28px rgba(22, 50, 79, .55);
  --ombre-legere: 0 6px 18px -10px rgba(22, 50, 79, .30);

  --r: 6px;   /* 6 px et non 10 : les maquettes tiennent entre 5 et 8 */

  /* ── Hauteur du bandeau d'environnement ────────────────────────────────
     ZÉRO EN PRODUCTION, et c'est le cas normal : /health n'y rend aucun
     bandeau, `body` ne reçoit pas `.avec-bandeau`, et toutes les règles qui
     suivent se réduisent exactement à celles de la maquette.

     ⚠ CE JETON CORRIGE UN DÉFAUT RÉEL, signalé par le mainteneur et jamais
     remonté : le bandeau est en `position: fixed; top: 0` avec `z-index: 9999`,
     la barre de menu était en `position: sticky; top: 0` avec `z-index: 5`. Le
     bandeau passait donc DEVANT et coupait les onglets à mi-hauteur. Ce n'est
     pas un défaut de z-index à inverser — le bandeau DOIT rester devant tout —
     mais un défaut de place : rien ne réservait la hauteur qu'il occupe.
     Désormais tout ce qui se colle en haut part de `--h-bandeau`, et le contenu
     est décalé d'autant. ── */
  --h-bandeau: 0px;
}

/* Plancher CSS, posé par app.js en même temps que le bandeau. Il protège même
   si la mesure JS échoue : `app.js` affine ensuite `--h-bandeau` par la hauteur
   RÉELLE de l'élément, parce que ce texte passe à deux ou trois lignes sur un
   téléphone et qu'une constante y serait fausse. */
body.avec-bandeau { --h-bandeau: 2.1rem; }

/* ══════════════════════════════════════════════════════════════════════════
   AUCUN ALIAS DE COMPATIBILITÉ, ET C'EST DÉLIBÉRÉ.

   Le plan de ce lot prévoyait de conserver les anciens noms (--fond, --carte,
   --texte, --doux, --bord, --accent, --accent-txt, --alerte) en alias, pour ne
   pas toucher aux usages de classes. Mesuré après réécriture : les huit
   n'étaient plus référencés NULLE PART — chaque usage avait été converti vers
   un jeton canonique. Vérifié aussi qu'aucun `var(--…)` n'existe dans app.js ni
   index.html, et qu'aucun jeton n'est posé par nom en JS.

   Les garder aurait été un piège : `--fond` valait --paper, alors que le fond
   de PAGE est --board. Quelqu'un l'aurait un jour employé pour un fond de page
   et obtenu du presque-blanc au lieu du beige, sans rien de cassé pour le
   signaler. Un jeton mort qui mentit sur son nom coûte plus qu'un renommage.

   ⚠ Trois anciens jetons faisaient DEUX métiers chacun. C'est la raison pour
   laquelle un alias n'aurait de toute façon pas suffi, et où regarder si un
   écart d'apparence apparaît :
     --fond   → fond de page (--board) ET fond de champ (--card)
     --accent → bouton (--ink), anneau de focus (--focus), lien (--info)
     --bord   → filet de carte (--line-strong) ET séparateur (--line)
   ══════════════════════════════════════════════════════════════════════════ */

/* Quatre jetons de présence et un fond lavé d'information sont déclarés sans
   être employés : ils viennent des maquettes et le lot « vie scolaire » les
   attend (appel P/R/A/E, pastille .p-info). Déclarés ici pour que la palette
   soit complète et relevée d'un seul endroit — ce ne sont pas des oublis. */

/* Les quatre paliers, prêts à l'emploi pour le lot suivant. Poser la classe sur
   une carte ou une ligne suffit : liseré coloré à gauche + fond lavé, ce que la
   charte prescrit comme support. Aucun élément ne les porte encore — c'est
   app.js qui les posera, à partir du palier FIGÉ dans le snapshot du bulletin,
   jamais d'un recalcul. */
.palier-excellent  { --tier: var(--tier-excellent);  --tier-wash: rgba(178, 58, 91, .10); }
.palier-tresbien   { --tier: var(--tier-tresbien);   --tier-wash: rgba(176, 129, 47, .10); }
.palier-bien       { --tier: var(--tier-bien);       --tier-wash: rgba(47, 110, 143, .10); }
.palier-assezbien  { --tier: var(--tier-assezbien);  --tier-wash: rgba(107, 114, 128, .10); }

.liseré-palier {
  border-left: 4px solid var(--tier);
  background: var(--tier-wash);
}
.pastille-palier {
  display: inline-block; padding: .1rem .55rem; border-radius: 2px;
  border: 1px solid var(--tier); background: var(--tier-wash); color: var(--tier);
  font-size: .72rem; font-weight: 600; letter-spacing: .1em; text-transform: uppercase;
}

* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; }
body {
  /* --board et NON --paper : le sous-main est beige, le papier est presque
     blanc. L'ancien --fond servait les deux, d'où cette distinction. */
  background: var(--board);
  color: var(--ink);
  font: 400 13.5px/1.45 var(--sans);
  -webkit-font-smoothing: antialiased;
}

/* Titres en Spectral. Tailles en PIXELS et non en rem : `rem` suit la racine à
   16 px, que `body { font-size }` ne déplace pas — `h1 { 1.6rem }` faisait
   25,6 px là où la maquette veut 24. Un « presque juste » typographique est le
   pire des résultats. */
h1, h2, h3 { font-family: var(--serif); font-weight: 600; margin: 0 0 .4em; line-height: 1.25; }
h1 { font-size: 24px; }
h2 { font-size: 15px; }
h3 { font-size: 14px; }
a { color: var(--info); }   /* et non --accent : un lien n'est pas un bouton */

/* ── marque et vues ── */
/* 600 et non 800 : Spectral n'est embarquée qu'en 400/600/700, et demander 800
   déclencherait un gras synthétique. */
.marque { font-family: var(--serif); font-weight: 600; font-size: 15px; letter-spacing: .02em; }
.marque.petit { font-size: 13.5px; }
.sous { color: var(--ink-soft); margin: 0 0 1.1rem; font-size: .9rem; }
/* ══════════════════════════════════════════════════════════════════════ */
/*  L'ATTRIBUT hidden DOIT VRAIMENT CACHER — et sans !important, il ne le    */
/*  faisait pas.                                                            */
/*                                                                          */
/*  MESURÉ LE 2026-08-03, à la première ouverture dans un navigateur.       */
/*  `hidden` n'a pas de règle propre dans une feuille d'auteur : son effet   */
/*  vient du display:none de la feuille par DÉFAUT du navigateur, que toute  */
/*  règle d'auteur écrase. `.vue.centre { display: grid }` a une spécificité */
/*  de 0,2,0 — elle gagnait. Résultat : `vue-motdepasse` et `vue-login`      */
/*  restaient EMPILÉES à l'écran, et le propriétaire s'est connecté 32 fois  */
/*  de suite en croyant échouer, le formulaire de connexion restant sous ses */
/*  yeux alors qu'il était déjà entré.                                       */
/*                                                                          */
/*  ⚠ !important EST ICI LE BON OUTIL, pas un contournement. `hidden` est un */
/*  ordre binaire de l'HTML : « ceci n'est pas à afficher ». Aucune règle de */
/*  mise en page ne doit pouvoir le discuter. La déclarer sans !important    */
/*  reviendrait à rendre la protection dépendante du nombre de classes du    */
/*  sélecteur voisin — c'est-à-dire à la rendre fragile par construction.    */
/*                                                                          */
/*  tests/test-css-hidden.js LIT cette feuille et échoue si la règle         */
/*  disparaît ou perd son !important. Le talon de DOM ne pouvait pas voir ce */
/*  défaut : il lit la propriété .hidden et n'applique aucun CSS.            */
/* ══════════════════════════════════════════════════════════════════════ */
[hidden] { display: none !important; }

/* Le bandeau couvrait aussi le haut de la carte de connexion, pas seulement la
   barre de menu : la marge du haut lui réserve donc sa place ici aussi. */
.vue.centre {
  min-height: 100%; display: grid; place-items: center;
  padding: calc(1.5rem + var(--h-bandeau)) 1.5rem 1.5rem;
}

/* La carte : un filet FORT et pas d'ombre. C'est le geste le plus visible de
   la charte — l'ancienne version posait `box-shadow` sur chaque carte, ce que
   « aucun effet » exclut. */
.carte {
  background: var(--card); border: 1px solid var(--line-strong);
  border-radius: var(--r); padding: 1.25rem;
}
.carte.etroite { width: min(400px, 100%); }

/* ── formulaires ── */
label {
  display: block; margin: 0 0 .8rem;
  font-size: 10.5px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-faint); font-weight: 600;
}
input, select, textarea {
  width: 100%; margin-top: .3rem; padding: .55rem .65rem;
  /* --card et NON --paper : dans la maquette un champ est BLANC sur le papier,
     pas beige. Un alias unique sur --fond aurait teinté tous les champs. */
  background: var(--card); color: var(--ink);
  border: 1px solid var(--line-strong); border-radius: 5px;
  font: 400 13px/1.4 var(--sans);
}
textarea { min-height: 90px; resize: vertical; }
input:focus-visible, select:focus-visible, textarea:focus-visible, button:focus-visible {
  outline: 2px solid var(--focus); outline-offset: 2px;
}
button {
  font: 600 12.5px/1.2 var(--sans);
  border-radius: 5px; padding: .5rem .9rem; cursor: pointer; border: 1px solid transparent;
}
.champ-motdepasse { position: relative; display: block; margin-top: .3rem; }
.champ-motdepasse input { margin-top: 0; padding-right: 2.8rem; }
.voir-motdepasse {
  position: absolute; right: .35rem; top: 50%; transform: translateY(-50%);
  width: 2.1rem; height: 2rem; padding: 0; border: 0;
  background: transparent; color: var(--ink-faint); font-size: 1rem; line-height: 1;
}
.voir-motdepasse:hover { background: var(--info-pale); color: var(--ink); }
.resultats-ecoles {
  display: grid; gap: .25rem; max-height: 190px; overflow-y: auto; margin-top: .35rem;
  border: 1px solid var(--line); border-radius: 5px; background: var(--card);
}
.resultat-ecole {
  display: grid; grid-template-columns: 7rem minmax(0,1fr); gap: .25rem .6rem;
  align-items: center; width: 100%; padding: .55rem .65rem; border: 0; border-bottom: 1px solid var(--line);
  border-radius: 0; background: transparent; color: var(--ink); text-align: left;
}
.resultat-ecole:last-child { border-bottom: 0; }
.resultat-ecole:hover { background: var(--info-pale); }
.resultat-ecole strong { color: var(--ink-2); font: 700 .78rem/1.2 var(--mono); }
.resultat-ecole span { font-size: .8rem; font-weight: 600; }
.resultat-ecole small { grid-column: 2; color: var(--ink-faint); font-size: .68rem; }
.selecteur-modules {
  margin: .35rem 0 .9rem; padding: .8rem; border: 1px solid var(--line-strong);
  border-radius: 6px; background: var(--board);
}
.selecteur-modules legend {
  padding: 0 .25rem; color: var(--ink-faint); font-size: 10.5px;
  letter-spacing: .06em; text-transform: uppercase; font-weight: 600;
}
.aide-modules { margin: .2rem 0 .7rem; color: var(--ink-faint); font-size: .78rem; line-height: 1.4; }
.grille-modules { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .45rem; max-height: 300px; overflow-y: auto; padding: .15rem; }
.choix-module {
  display: flex; align-items: flex-start; gap: .5rem; margin: 0; padding: .55rem;
  border: 1px solid var(--line); border-radius: 5px; background: var(--card);
  color: var(--ink); text-transform: none; letter-spacing: normal; cursor: pointer;
}
.choix-module:hover { border-color: var(--ink-2); background: var(--info-pale); }
.choix-module input { width: auto; flex: 0 0 auto; margin: .15rem 0 0; accent-color: var(--ink-2); }
.module-texte { display: grid; gap: .15rem; min-width: 0; }
.module-texte strong { font-size: .78rem; color: var(--ink); }
.module-texte small { color: var(--ink-faint); font-size: .68rem; line-height: 1.25; }

.primaire { background: var(--ink); color: #fff; border-color: var(--ink); width: 100%; }
.fantome { background: transparent; color: var(--ink); border-color: var(--line-strong); }
.mini { padding: .25rem .5rem; font-size: 12px; }
.rouge { color: var(--danger); border-color: var(--danger); }
/* `filter: brightness()` est un EFFET, et il délavait l'encre. Un survol se
   marque par un fond, pas par un filtre. */
button:hover { background: var(--info-pale); }
.primaire:hover { background: var(--ink-2); border-color: var(--ink-2); }
.erreur { color: var(--danger); font-size: .85rem; margin: .6rem 0 0; }

/* ══════════════════════════════════════════════════════════════════════════
   LA COQUE — lot B. Reprise de `docs/maquette_app.html`.

   REMPLACE la barre d'onglets plate. Le lot A n'avait fait que couleurs et
   polices ; la structure restait une liste d'onglets dans l'en-tête, ce que la
   charte écarte explicitement : « pas une liste plate d'onglets. Le regroupement
   par section est ce qui rend navigable une application à ~25 écrans. »

   CORRESPONDANCE avec la maquette, qui nomme en anglais là où ce dépôt nomme en
   français. Ce tableau est ce qui permet de retrouver la règle d'origine :

     .coque        ← .app          grille 238 px + contenu
     .laterale     ← .side         latérale à l'encre, collante
     .identite     ← .side .b      blason + nom + rôle
     .blason       ← .crest
     .groupe       ← .side nav .grp    intertitre de section
     .principal    ← .main
     .entete       ← .top          en-tête papier, 56 px, collant
     .pousse       ← .top .sp
     .qui          ← .top .who
     .avatar       ← .top .who .av
     .contenu      ← .content
     .entete-page  ← .pagehead     surtitre + titre de module + sous-titre
     .surtitre     ← .pagehead .eyebrow

   ⚠ `.entete-page` et NON `.entete-section` : cette dernière existe déjà et
   désigne autre chose — le titre d'une sous-section DANS un écran, rendu par le
   helper `section()` de app.js, appelé 30 fois. Les deux cohabitent : le `h1` de
   l'écran, puis les `h2` de ses sections.
   ══════════════════════════════════════════════════════════════════════════ */

.coque {
  display: grid; grid-template-columns: 238px 1fr; min-height: 100vh;
  padding-top: var(--h-bandeau);
}

/* La latérale est à l'encre : ce n'est PAS un thème sombre, c'est du bleu
   profond sur une application de papier clair. Elle se colle sous le bandeau,
   d'où le `top` et la hauteur calculée plutôt que 100vh sec. */
.laterale {
  background: var(--ink); color: #fff;
  display: flex; flex-direction: column;
  position: sticky; top: var(--h-bandeau); height: calc(100vh - var(--h-bandeau));
}

.identite {
  display: flex; align-items: center; gap: 11px;
  padding: 17px 18px; border-bottom: 1px solid rgba(255, 255, 255, .12);
}
.blason {
  flex: 0 0 auto; width: 38px; height: 38px; border-radius: 9px;
  background: rgba(255, 255, 255, .16);
  display: flex; align-items: center; justify-content: center;
  font-family: var(--serif); font-weight: 700; font-size: 18px;
}
/* ⚠ `min-width: 0` — C'EST LA CORRECTION DU TRONQUAGE. Dans la maquette,
   « Opérateur LEKOLPAM » sortait coupé faute de place : l'enfant d'un conteneur
   flex a `min-width: auto` par défaut, donc il refuse de rétrécir sous la
   largeur de son contenu et débordait des ~153 px restants après le blason et
   les marges.
   ⚠ ET SURTOUT : le nom PASSE À LA LIGNE, il ne se coupe jamais. Pas
   d'`text-overflow: ellipsis` ici — amputer le nom propre d'une école est de la
   même famille que lui montrer le nom de son fournisseur. Le cas de référence
   n'est pas inventé : « Institution Mixte Artimoun », l'école du gabarit de
   bulletin. `overflow-wrap: anywhere` est le dernier recours pour un mot unique
   plus large que la colonne. */
.identite > div { min-width: 0; }
.identite .nom {
  font-family: var(--serif); font-weight: 600; font-size: 13.5px; line-height: 1.15;
  overflow-wrap: anywhere;
}
.identite .role {
  font-size: 9px; color: rgba(255, 255, 255, .6);
  text-transform: uppercase; letter-spacing: .1em; margin-top: 2px;
  overflow-wrap: anywhere;
}

.laterale nav { flex: 1; overflow-y: auto; padding: 10px 0; }

/* L'intertitre de section. Il n'est PAS interactif : c'est une étiquette. */
.groupe {
  font-size: 9px; letter-spacing: .15em; text-transform: uppercase;
  color: rgba(255, 255, 255, .4); padding: 12px 18px 5px; font-weight: 600;
}

/* Les entrées de navigation sont des BOUTONS et non des liens : la navigation
   est côté client, il n'y a pas d'URL à donner à un href. L'état courant se
   marque par `aria-current="page"` — un attribut que les lecteurs d'écran
   annoncent, pas une classe qui ne dit rien à personne. */
.laterale nav button {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 8px 18px; border: none; border-left: 3px solid transparent;
  border-radius: 0; background: transparent; color: rgba(255, 255, 255, .82);
  font: 400 12.5px/1.3 var(--sans); text-align: left; cursor: pointer;
}
.laterale nav button:hover { background: rgba(255, 255, 255, .07); color: #fff; }
.laterale nav button[aria-current="page"] {
  background: rgba(255, 255, 255, .12); color: #fff;
  border-left-color: #fff; font-weight: 600;
}
.laterale nav button:focus-visible { outline: 2px solid #fff; outline-offset: -2px; }

.laterale .deconnexion {
  margin: 10px 14px 14px; padding: 9px;
  background: rgba(255, 255, 255, .1); border: none; color: #fff;
  border-radius: 5px; font: 600 12.5px/1.2 var(--sans); cursor: pointer;
}
.laterale .deconnexion:hover { background: rgba(255, 255, 255, .18); }

/* `min-width: 0` ici aussi : sans lui, un tableau large dans le contenu
   élargirait la colonne de grille au lieu de défiler dans sa propre boîte. */
.principal { display: flex; flex-direction: column; min-width: 0; }

.entete {
  background: var(--paper); border-bottom: 1px solid var(--line-strong);
  min-height: 56px; display: flex; align-items: center; gap: 12px;
  padding: 0 22px; position: sticky; top: var(--h-bandeau); z-index: 5;
}
/* Le nom de l'école apparaît AUSSI ici, et ce n'est pas une redondance
   d'inattention : sous 860 px la latérale devient une bande d'onglets et son
   bloc d'identité disparaît. L'en-tête est alors le seul endroit qui porte le
   nom. C'est la raison pour laquelle la maquette le duplique. */
.entete .ecole { font-family: var(--serif); font-weight: 600; font-size: 15px; overflow-wrap: anywhere; }
.pousse { flex: 1; }
.qui { display: flex; align-items: center; gap: 9px; min-width: 0; }
.qui .nom { font-weight: 600; font-size: 12.5px; }
.qui .role { font-size: 10.5px; color: var(--ink-faint); }
.avatar {
  flex: 0 0 auto; width: 32px; height: 32px; border-radius: 50%;
  background: var(--ink); color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-weight: 600; font-size: 13px;
}

.contenu { padding: 24px 26px 60px; max-width: 1120px; width: 100%; }

/* En-tête de module. PUREMENT ADDITIF : aucune vue ne rendait de titre de page
   — vérifié, zéro `el('h1')` dans app.js — donc rien à retirer ailleurs. */
.entete-page {
  margin-bottom: 16px; display: flex; align-items: flex-end;
  justify-content: space-between; gap: 14px; flex-wrap: wrap;
}
.surtitre {
  font-size: 10px; letter-spacing: .15em; text-transform: uppercase;
  color: var(--info); font-weight: 600;
}
.entete-page h1 { margin: 3px 0 2px; }
.entete-page p { margin: 0; color: var(--ink-soft); }

/* ── Téléphone. Beaucoup de directions haïtiennes travaillent au téléphone, et
   la latérale de 238 px n'y tient pas. La maquette la replie en bande d'onglets
   horizontale et MASQUE les intertitres : les garder tripleraît la hauteur de la
   bande avant d'atteindre le contenu. Les sections restent dans l'ordre, seuls
   leurs titres s'effacent. ── */
@media (max-width: 860px) {
  .coque { grid-template-columns: 1fr; }
  .laterale { position: static; height: auto; }
  .laterale nav { display: flex; flex-wrap: wrap; padding: 6px; }
  .groupe { display: none; }
  .laterale nav button { width: auto; border-left: none; border-radius: 5px; }
  .laterale nav button[aria-current="page"] { border-left: none; }
  .laterale .deconnexion { margin: 8px; }
  .contenu { padding: 18px 14px 48px; }
}

/* ── tuiles de chiffres ── */
.tuiles { display: grid; gap: .8rem; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); margin-bottom: 1.2rem; }
/* Liseré d'encre de 3 px à gauche : c'est la signature du KPI dans la maquette
   (.kpi::before). `overflow: hidden` pour qu'il suive le rayon du coin. */
.tuile {
  background: var(--card); border: 1px solid var(--line-strong);
  border-radius: var(--r); padding: .85rem 1rem;
  position: relative; overflow: hidden;
}
.tuile::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0;
  width: 3px; background: var(--ink);
}
.tuile .n { font-family: var(--serif); font-weight: 700; font-size: 26px; font-variant-numeric: tabular-nums; }
.tuile .l { font-size: 10px; color: var(--ink-faint); font-weight: 600; text-transform: uppercase; letter-spacing: .1em; }

/* ── tableaux : ils débordent sur téléphone, donc ils défilent SEULS ── */
.tableau-boite { overflow-x: auto; border: 1px solid var(--line-strong); border-radius: var(--r); background: var(--card); }
table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
th, td { text-align: left; padding: .6rem .85rem; border-bottom: 1px solid var(--line); white-space: nowrap; }
th {
  font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: .07em;
  color: var(--ink-faint); background: var(--paper);
  border-bottom: 1px solid var(--line-strong);
}
tr:last-child td { border-bottom: 0; }
tbody tr:hover { background: rgba(22, 50, 79, .03); }
td.nombre { text-align: right; font-variant-numeric: tabular-nums; }
.vide { padding: 1.5rem; text-align: center; color: var(--ink-faint); }

/* ── pastilles d'état ── */
/* Fond lavé + texte coloré, comme .pill dans la maquette : un contour seul se
   lit mal à 10 px. */
.pastille {
  display: inline-block; padding: .15rem .55rem; border-radius: 20px;
  font-size: 10px; font-weight: 600; letter-spacing: .02em; border: 1px solid transparent;
}
.p-active  { color: var(--ok);     background: var(--ok-wash); }
.p-partial { color: var(--warn);   background: var(--warn-wash); }
.p-blocked { color: var(--danger); background: var(--danger-wash); }
.p-neutre  { color: var(--ink-soft); background: rgba(22, 50, 79, .07); }

.section { margin-bottom: 1.6rem; }
.entete-section { display: flex; align-items: center; gap: .7rem; margin-bottom: .6rem; flex-wrap: wrap; }
.entete-section h2 { margin: 0; }
.actions { display: flex; gap: .4rem; flex-wrap: wrap; }
.grille2 { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.mods { display: flex; gap: .3rem; flex-wrap: wrap; }
.mods span { font-size: 10px; border: 1px solid var(--line); border-radius: 4px; padding: .1rem .4rem; color: var(--ink-faint); }
/* Identifiants provisoires : du MONO, parce que ce sont des codes à recopier
   sans se tromper — c'est précisément l'emploi que la charte donne à Plex Mono. */
pre.identifiants {
  background: var(--paper); border: 1px dashed var(--info); border-radius: 5px;
  padding: .7rem; font: 400 12.5px/1.6 var(--mono);
  white-space: pre-wrap; word-break: break-word;
}

/* ── dialogue ── */
/* L'ombre est LÉGITIME ici : un dialogue flotte réellement au-dessus de la page. */
dialog {
  border: 1px solid var(--line-strong); border-radius: var(--r);
  background: var(--paper); color: var(--ink);
  padding: 1.1rem; width: min(460px, 94vw); box-shadow: var(--ombre);
}
dialog::backdrop { background: rgba(18, 40, 62, .40); }
dialog menu { display: flex; gap: .5rem; justify-content: flex-end; margin: 1rem 0 0; padding: 0; }
dialog menu button { width: auto; }

/* ── dossier d’inscription élève ── */
.champ-dossier {
  display: grid; gap: .28rem; margin: .55rem 0; color: var(--ink-soft);
  font-size: 11px; font-weight: 600;
}
.champ-dossier input, .champ-dossier select, .champ-dossier textarea {
  width: 100%; box-sizing: border-box; font-weight: 400; color: var(--ink);
}
.champ-dossier textarea { min-height: 72px; resize: vertical; }
.champ-dossier input[type="file"] { padding: .35rem; background: var(--card); }
#dlg-champs > .faible { margin: .2rem 0 .8rem; }
#dlg-champs > .avert { margin: .6rem 0; }
#dlg-champs pre.identifiants { max-height: 220px; overflow: auto; }

/* ── notifications ── */
.toasts { position: fixed; right: 1rem; bottom: 1rem; display: grid; gap: .5rem; z-index: 50; }
.toast {
  background: var(--card); border: 1px solid var(--line-strong); border-left: 4px solid var(--ok);
  border-radius: 5px; padding: .6rem .8rem; box-shadow: var(--ombre-legere);
  max-width: 340px; font-size: 12.5px;
}
.toast.mauvais { border-left-color: var(--danger); }

.chargement { color: var(--ink-faint); padding: 1.5rem; text-align: center; }

/* Champ en LECTURE SEULE (code d'école) : il doit se voir comme non modifiable,
   sinon l'opérateur essaie de le corriger et ne comprend pas pourquoi rien ne
   change. Le contraste reste suffisant pour être lu — un champ grisé illisible
   serait une régression d'accessibilité, pas une indication. */
label.lecture-seule input[readonly] {
  background: var(--board);
  color: var(--ink);
  cursor: default;
  font-family: var(--mono);
  letter-spacing: .06em;
}

/* ==================================================================== */
/*                    LOT C — NOTES ET BULLETINS                        */
/* ==================================================================== */

/* Barre de filtres (classe / étape / modèle). Elle se replie sur les petits
   écrans : ces trois sélecteurs côte à côte débordent sur un téléphone, et
   beaucoup de directions haïtiennes travaillent au téléphone. */
.barre-filtres {
  display: flex; flex-wrap: wrap; gap: .75rem 1rem; align-items: center;
  margin: 0 0 1rem; padding: .6rem .8rem;
  background: var(--paper); border: 1px solid var(--line-strong); border-radius: var(--r);
}
.barre-filtres .filtre {
  display: flex; flex-direction: column; gap: .15rem;
  font-size: 10px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-faint); font-weight: 600;
}
.barre-filtres .filtre select { font-size: 13px; font-weight: 400; padding: .3rem .45rem; text-transform: none; letter-spacing: 0; color: var(--ink); }

/* Avertissement : ni une erreur (rien n'a échoué) ni une note discrète (il faut
   le lire). Un ton distinct des deux, sinon il se confond avec l'un ou l'autre. */
.avertissement {
  margin: .6rem 0; padding: .5rem .7rem;
  background: var(--warn-wash); border-left: 3px solid var(--warn);
  font-size: 12.5px; color: var(--ink);
}

/* Bandeaux de contrôle du composeur. Ils REPRODUISENT ce que la base vérifie,
   pour que la direction le sache AVANT de cliquer sur Publier — le serveur
   reste l'arbitre et son refus s'affiche tel quel s'ils divergent. */
.bandeau {
  margin: .7rem 0 0; padding: .55rem .8rem; border-radius: 5px;
  font-size: 12.5px; font-weight: 600;
}
.bandeau-ok { background: var(--ok-wash); color: var(--ok); border: 1px solid rgba(63, 125, 88, .35); }
.bandeau-alerte { background: var(--danger-wash); color: var(--danger); border: 1px solid rgba(178, 58, 91, .35); }

/* Mention : la couleur vient du PALIER figé dans le bulletin, jamais recalculée.
   La bordure garantit qu'un palier blanc reste visible sur fond blanc. */
.mention-boite { display: flex; align-items: center; gap: .5rem; margin: .5rem 0; }
.mention-note { font-family: var(--serif); font-size: 22px; font-weight: 700; font-variant-numeric: tabular-nums; }
.mention-puce {
  display: inline-block; width: 1rem; height: 1rem; border-radius: 3px;
  border: 1px solid var(--line-strong); vertical-align: -2px; margin-right: .35rem;
}

/* Grille de saisie. En-têtes collants : une classe de 40 élèves fait perdre de
   vue le nom des matières dès qu'on défile.
   ⚠ L'en-tête est à l'ENCRE, texte blanc — comme `table.grade thead th` dans la
   maquette. L'ancienne version le posait sur `#fff` en dur, ce qui, sous le
   thème sombre désormais supprimé, donnait du gris clair sur blanc. */
.grille-notes { min-width: 100%; }
.grille-notes thead th {
  position: sticky; top: 0; z-index: 1;
  background: var(--ink); color: #fff; border-bottom: none;
  text-align: center; letter-spacing: .04em;
}
.grille-notes td { padding: .2rem .3rem; }
.grille-notes input.note {
  width: 4.5rem; padding: .2rem .3rem; text-align: right;
  font-variant-numeric: tabular-nums;
}
.grille-notes td.calculee { color: var(--ink-faint); text-align: center; font-style: italic; }

/* Bulletin ouvert : entièrement rendu depuis le snapshot. Largeur limitée pour
   rester lisible, et impression sans les habillages d'écran. */
.bulletin { max-width: 46rem; }
.bulletin h3 { margin: 0 0 .2rem; }
@media print {
  /* ⚠ Mis à jour au lot B : `.barre` et `.onglets` n'existent plus, la coque les
     remplace. Une règle d'impression qui cite des classes mortes ne masque
     RIEN — et on ne s'en aperçoit qu'en imprimant. */
  .barre-filtres, .actions, .avertissement, .toasts,
  .laterale, .entete, .entete-page, .bandeau-env { display: none; }
  /* La grille de la coque n'a plus de sens sur du papier : une seule colonne. */
  .coque { display: block; padding-top: 0; }
  .contenu { padding: 0; max-width: none; }
  .bulletin { max-width: none; }
  /* Un document imprimé sort sur du papier réel : pas de sous-main, pas
     d'ombre, et des filets qui ne dépendent pas du rendu des fonds. */
  body { background: #fff; }
  dialog { box-shadow: none; border: none; }
}

/* ⚠ BANDEAU D'ENVIRONNEMENT — n'apparaît QUE si /health rend un `bandeau`, donc
   jamais en production. Fixé en haut, sur toute la largeur, dans une couleur
   qu'aucun autre élément n'emploie : il doit être impossible de le manquer, et
   impossible de confondre deux fenêtres. `position: fixed` et un `z-index` élevé
   pour qu'aucune vue ne passe devant.

   ⚠ IL RECOUVRAIT LA BARRE DE MENU, et les onglets sortaient coupés à
   mi-hauteur derrière lui. Signalé par le mainteneur ; le message n'est jamais
   parti, donc le défaut a vécu. Corrigé au lot B, et PAS en baissant son
   `z-index` : il doit rester devant tout, c'est sa fonction. Ce qui manquait,
   c'est que personne ne réservait sa hauteur. Voir `--h-bandeau` en tête de
   feuille — la coque décale son contenu d'autant et tout ce qui se colle en haut
   part de là. Sa hauteur est MESURÉE par app.js, pas supposée : ce texte passe à
   deux ou trois lignes sur un téléphone.
   ⚠ Ce violet est VOLONTAIREMENT hors charte, et c'est la seule exception de la
   feuille. Le remplacer par un jeton le rendrait confondable avec un état
   métier — exactement ce qu'il ne doit jamais être. */
.bandeau-env {
  position: fixed; top: 0; left: 0; right: 0; z-index: 9999;
  background: #7B1FA2; color: #fff; text-align: center;
  font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  padding: .35rem .5rem; font-size: .8rem;
  box-shadow: 0 2px 6px rgba(0, 0, 0, .35);
}

/* --- T3 : photo d'identité de la fiche élève ------------------------------
   ⚠ Dimensions FIXES et `object-fit: cover`. Une photo d'identité arrive dans
   tous les formats ; la laisser dicter la mise en page ferait sauter la fiche
   d'un élève à l'autre. On cadre, on ne déforme pas. */
.photo-fiche { display: flex; align-items: center; gap: .6rem; margin-bottom: .8rem; }
.photo-eleve { width: 84px; height: 108px; object-fit: cover; border-radius: var(--r);
  border: 1px solid var(--line); background: var(--board); }
.photo-vide { width: 84px; height: 108px; display: grid; place-items: center;
  border: 1px dashed var(--line); border-radius: var(--r); color: var(--muted);
  font-size: .75rem; text-align: center; background: var(--board); }

/* --- T10 : les tuiles de chiffres du tableau de bord ----------------------
   ⚠ `auto-fit` et `minmax` plutôt qu'un nombre fixe de colonnes : une
   directrice regarde parfois sur un téléphone, et une grille à six colonnes y
   rendrait chaque chiffre illisible. */
.tuiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: .6rem; margin: .6rem 0; }
.tuile { border: 1px solid var(--line); border-radius: var(--r); padding: .7rem .8rem;
  background: var(--paper); display: flex; flex-direction: column; gap: .15rem; }
.tuile-valeur { font-size: 1.5rem; font-weight: 600; color: var(--ink); line-height: 1.1; }
.tuile-libelle { font-size: .78rem; color: var(--muted); }
/* Un document produit garde ses retours à la ligne : c'est un texte officiel,
   pas un paragraphe que le navigateur peut ré-agencer. */
.document { white-space: pre-wrap; font-family: inherit; background: var(--board);
  border: 1px solid var(--line); border-radius: var(--r); padding: 1rem; line-height: 1.6; }

/* ══════════════════════════════════════════════════════════════════════════
   REFONTE DE RÉFÉRENCE — connexion et console opérateur
   Ces règles s’ajoutent aux composants métier existants. Elles reprennent la
   composition vérifiée dans les captures : carte claire centrée sur bleu nuit
   avant connexion, puis console vert sarcelle pour le périmètre opérateur.
   ══════════════════════════════════════════════════════════════════════════ */

#vue-login {
  min-height: 100vh;
  background:
    radial-gradient(circle at 50% 42%, rgba(32, 71, 107, .24) 0, rgba(10, 34, 57, 0) 42%),
    #071d31;
}
#vue-login .carte.etroite {
  width: min(360px, calc(100vw - 40px));
  min-height: 378px;
  padding: 29px 28px 21px;
  border: 0;
  border-radius: 10px;
  box-shadow: 0 22px 55px rgba(0, 0, 0, .24);
  text-align: center;
}
.login-sceau {
  width: 46px;
  height: 46px;
  margin: 0 auto 14px;
  display: grid;
  place-items: center;
  border-radius: 10px;
  background: #163d60;
  color: #fff;
  font: 600 21px/1 var(--serif);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.12);
}
#vue-login .marque {
  margin: 0;
  color: #24364a;
  font-size: 18px;
  letter-spacing: 0;
}
#vue-login #hote {
  margin: 4px 0 18px;
  color: #a0a8b1;
  font-size: 11px;
}
#vue-login label { text-align: left; margin-bottom: 11px; }
#vue-login input {
  height: 38px;
  margin-top: 5px;
  border-color: #d8dce2;
  color: #26374a;
}
#vue-login input::placeholder { color: #a5adb6; }
#vue-login .primaire {
  margin-top: 3px;
  height: 39px;
  border-radius: 4px;
  background: #163d60;
  border-color: #163d60;
}
#vue-login .primaire:hover { background: #102f4b; border-color: #102f4b; }
.login-aide {
  margin: 14px 0 0;
  color: #a0a8b1;
  font-size: 10px;
  line-height: 1.45;
}

/* La console opérateur se distingue de l’espace école, comme dans les captures. */
body.operateur { --ink: #0f766e; --ink-2: #0b5d58; --focus: #0f766e; }
body.operateur .laterale { background: #0f766e; }
body.operateur .entete { background: #fffefa; }
body.operateur .contenu { max-width: 1240px; }
body.operateur .surtitre { color: #0f766e; }
body.operateur .avatar { background: #0f766e; }
body.operateur .laterale nav button[aria-current="page"] {
  background: rgba(255,255,255,.16);
  border-left-color: rgba(255,255,255,.86);
}

/* Gabarit « fonctionnalités du module » de la console de référence. */
.catalogue-fonctions {
  overflow: hidden;
  padding: 0;
  border-color: rgba(15,118,110,.24);
}
.catalogue-fonctions .ligne-fonction {
  display: grid;
  grid-template-columns: 34px minmax(0,1fr) auto;
  align-items: center;
  gap: 8px;
  min-height: 54px;
  padding: 9px 14px;
  border-top: 1px solid rgba(15,118,110,.14);
}
.catalogue-fonctions .ligne-fonction:first-of-type { border-top: 0; }
.catalogue-fonctions .numero-fonction {
  color: #98a5a5;
  font: 400 11px/1 var(--mono);
}
.catalogue-fonctions .libelle-fonction { color: #277c75; font-size: 12.5px; }
.catalogue-fonctions .ouvrir-fonction {
  min-width: 51px;
  padding: 6px 8px;
  background: #fff;
  color: #0f766e;
  border: 1px solid #0f766e;
  border-radius: 5px;
  font-size: 11px;
}
.catalogue-fonctions .ouvrir-fonction:hover { background: rgba(15,118,110,.08); }
.compteur-module {
  display: inline-grid;
  place-items: center;
  min-width: 24px;
  height: 20px;
  padding: 0 6px;
  border-radius: 20px;
  background: rgba(15,118,110,.10);
  color: #277c75;
  font: 600 10px/1 var(--sans);
}

@media (max-width: 620px) {
  .grille-modules { grid-template-columns: 1fr; max-height: 260px; }
  .resultat-ecole { grid-template-columns: 1fr; }
  .resultat-ecole small { grid-column: 1; }


  #vue-login .carte.etroite { min-height: 0; padding: 26px 22px 20px; }
  .catalogue-fonctions .ligne-fonction { grid-template-columns: 28px minmax(0,1fr); }
  .catalogue-fonctions .ouvrir-fonction { grid-column: 2; justify-self: start; }
}
