/* ------------------------------------------------------------------
   Janguinou — coquille responsive partagée.
   Téléphone / tablette : la barre latérale devient un TIROIR (☰) et une
   barre de navigation basse apparaît. Rien n'est jamais supprimé.
   ------------------------------------------------------------------ */

/* --- Barre latérale : tiroir sous 1024px --- */
@media (max-width: 1023px) {
  /* TAILWIND GAGNAIT LA MISE.
     La barre laterale porte la classe utilitaire `z-10`. `.z-10` et
     `.jg-aside` ont EXACTEMENT la meme specificite, et la feuille de Tailwind
     est injectee apres la notre : le tiroir se retrouvait au plan 10, donc
     DERRIERE le voile (55), DERRIERE la barre du bas (45) et DERRIERE le
     bouton (50). D'ou l'ecran de la capture : la croix posee sur le logo, et
     la barre du bas qui coupe « Log Out » en deux.
     Mesure : `getComputedStyle(aside).zIndex` renvoyait « 10 ».
     On ne discute pas la specificite avec un utilitaire — on impose. */
  .jg-aside {
    position: fixed !important;
    top: 0; left: 0; bottom: 0;
    z-index: 60 !important;
    transform: translateX(-100%) !important;
    transition: transform .25s ease;
    box-shadow: 0 0 40px rgba(0, 0, 0, .18);
    /* `overflow-hidden` de Tailwind coupait net les dernieres entrees au lieu
       de les laisser defiler : un menu dont on ne peut pas atteindre le bas
       est un menu incomplet. */
    overflow-y: auto !important;
    overscroll-behavior: contain;
  }
  .jg-aside.ouvert { transform: translateX(0) !important; }

  /* UNE SEULE NAVIGATION A LA FOIS.
     Le tiroir ouvert, la barre du bas n'aide plus : elle repete les memes
     entrees et mange les dernieres lignes du menu. */
  body.jg-tiroir-ouvert .jg-nav-basse { display: none !important; }

  /* LE BOUTON DE FERMETURE NE S'ASSOIT PAS SUR LE LOGO.
     Ouvert, il passe dans le coin haut-droit du tiroir — la ou l'on ferme,
     et non par-dessus le nom de l'application. */
  body.jg-tiroir-ouvert .jg-burger {
    left: auto;
    right: calc(100vw - min(84vw, 300px) + 14px);
    z-index: 65;
  }

  /* UNE FENÊTRE OUVERTE PREND L'ÉCRAN.
     Le ☰ est fixé en haut à gauche au même niveau d'empilement que les
     modales (50) et, ajouté au corps de page en dernier, il passait DEVANT :
     sur téléphone il s'asseyait sur le titre de la fenêtre (« Test
     calendrier » se lisait « st calendrier »). Une modale est modale : le
     menu n'a rien à y faire tant qu'elle est là.
     Le voile du tiroir n'est PAS concerné — il porte `jg-voile`, pas les
     classes `fixed inset-0` — et le ☰ reste donc la croix du tiroir. */
  body:has(div.fixed.inset-0:not(.hidden)) .jg-burger { display: none !important; }

  .jg-burger {
    position: fixed; top: 14px; left: 14px; z-index: 50;
    width: 44px; height: 44px; border-radius: 12px;
    background: #fff; border: 1px solid #e5e7eb;
    display: flex; align-items: center; justify-content: center;
    font-size: 22px; color: #374151; box-shadow: 0 2px 8px rgba(0,0,0,.08);
  }
  .jg-voile {
    position: fixed; inset: 0; background: rgba(15, 23, 42, .45);
    z-index: 55; opacity: 0; pointer-events: none; transition: opacity .25s;
  }
  .jg-voile.visible { opacity: 1; pointer-events: auto; }

  /* L'en-tête laisse la place au bouton ☰ */
  header { padding-left: 68px !important; }

  /* Barre de navigation basse */
  .jg-nav-basse {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 45;
    display: flex; background: #fff; border-top: 1px solid #e5e7eb;
    padding: 6px 4px calc(6px + env(safe-area-inset-bottom));
    box-shadow: 0 -2px 12px rgba(0,0,0,.06);
  }
  .jg-nav-basse a {
    flex: 1; display: flex; flex-direction: column; align-items: center;
    gap: 2px; padding: 6px 2px; text-decoration: none;
    color: #9ca3af; font-size: 10px; font-weight: 600;
  }
  .jg-nav-basse a i { font-size: 22px; }
  .jg-nav-basse a.actif { color: #4c1d95; }

  /* Le contenu ne passe pas sous la barre basse */
  main { padding-bottom: 84px !important; }

  /* Confort tactile : cibles plus grandes, tableaux défilables */
  button, .btn-status { min-height: 40px; }
  table { display: block; overflow-x: auto; white-space: nowrap; }
}

@media (min-width: 1024px) {
  .jg-burger, .jg-voile, .jg-nav-basse { display: none !important; }
}

/* ==================================================================
   UN MENU DÉROULANT NE SORT PAS DE SON CADRE.
   ==================================================================

   Un `<select>` se dimensionne sur son option la PLUS LONGUE, et rien ne
   l'arrête. « Astou Camara (Mathématiques, Physique-Chimie, SVT) » réclamait
   457 px dans une carte de 343 : mesuré le 01/09/2026, **143 px hors du
   cadre** sur l'écran du surveillant. Le défaut ne se voyait qu'au Lycée de
   Thiès — ailleurs les professeurs n'enseignent qu'une matière et le menu
   restait court. Une trentaine de menus de l'application sont remplis par le
   réseau et couraient le même risque : noms de professeurs, libellés de
   classes, noms d'établissements.

   Deux règles, et le problème ne peut plus se reproduire :
     • le menu ne dépasse jamais la largeur qu'on lui donne ;
     • l'étiquette qui le porte a le droit de RÉTRÉCIR — dans une rangée
       `flex`, un élément refuse par défaut de descendre sous la taille de son
       contenu (`min-width: auto`), et c'est lui qui poussait alors le cadre.
   Le texte trop long est coupé par le navigateur, comme il sait le faire.
   ------------------------------------------------------------------ */
select { max-width: 100%; }
label:has(> select) { min-width: 0; }

/* --- Squelettes de chargement (jamais de spinner) --- */
.jg-skel {
  height: 56px; margin-bottom: 10px; border-radius: 12px;
  background: linear-gradient(90deg, #f1f5f9 25%, #e2e8f0 37%, #f1f5f9 63%);
  background-size: 400% 100%;
  animation: jg-skel 1.3s ease-in-out infinite;
}
@keyframes jg-skel { 0% { background-position: 100% 50% } 100% { background-position: 0 50% } }

/* --- État vide (au lieu de fausses données) --- */
.jg-vide {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 8px; padding: 28px 16px; color: #94a3b8; text-align: center;
}
.jg-vide i { font-size: 32px; }
.jg-vide p { font-size: 13px; }

/* --- Notifications toast --- */
.jg-toasts { position: fixed; right: 16px; bottom: 16px; z-index: 80; display: flex; flex-direction: column; gap: 8px; }
.jg-toast {
  background: #0f172a; color: #fff; padding: 12px 16px; border-radius: 12px;
  font-size: 13px; max-width: 340px; box-shadow: 0 8px 24px rgba(0,0,0,.2);
  animation: jg-toast-in .25s ease;
}
.jg-toast.ok { background: #047857; }
.jg-toast.erreur { background: #b91c1c; }
@keyframes jg-toast-in { from { opacity: 0; transform: translateY(8px) } to { opacity: 1; transform: none } }
@media (max-width: 1023px) { .jg-toasts { bottom: 84px; left: 16px; right: 16px; } }

/* --- Centre de notifications --- */
.jg-pastille {
  position: absolute; top: -4px; right: -4px; min-width: 18px; height: 18px;
  padding: 0 4px; border-radius: 9px; background: #dc2626; color: #fff;
  font-size: 10px; font-weight: 700; display: flex; align-items: center; justify-content: center;
  border: 2px solid #fff;
}
.jg-panneau {
  position: fixed; top: 72px; right: 16px; width: 340px; max-height: 70vh; overflow-y: auto;
  background: #fff; border: 1px solid #e5e7eb; border-radius: 16px; z-index: 70;
  box-shadow: 0 12px 40px rgba(0,0,0,.14);
}
.jg-panneau-tete { display: flex; justify-content: space-between; align-items: center; padding: 14px 16px; border-bottom: 1px solid #f1f5f9; }
.jg-panneau-tete button { font-size: 11px; font-weight: 600; color: #4c1d95; }
.jg-notif { padding: 12px 16px; border-bottom: 1px solid #f8fafc; }
.jg-notif.non-lue { background: #faf5ff; }
.jg-notif strong { display: block; font-size: 13px; color: #111827; }
.jg-notif p { font-size: 12px; color: #6b7280; margin-top: 2px; }
.jg-notif span { font-size: 10px; color: #9ca3af; }
@media (max-width: 1023px) { .jg-panneau { left: 12px; right: 12px; width: auto; } }

/* --- Indicateur de fraîcheur des données --- */
#jg-maj { font-size: 11px; color: #94a3b8; }

/* --- Bandeau hors connexion / synchronisation --- */
.jg-bandeau-hors-ligne {
  position: fixed; left: 0; right: 0; top: 0; z-index: 75;
  display: none; align-items: center; justify-content: center; gap: 10px;
  padding: 8px 16px; font-size: 12px; font-weight: 600;
  background: #1e293b; color: #fff;
  box-shadow: 0 2px 10px rgba(0, 0, 0, .15);
}
.jg-bandeau-hors-ligne.coupe { background: #b45309; }
.jg-bandeau-hors-ligne i { font-size: 16px; }
.jg-bandeau-hors-ligne button {
  padding: 4px 10px; border-radius: 8px; font-size: 11px; font-weight: 700;
  background: rgba(255, 255, 255, .18); color: #fff;
}
.jg-bandeau-hors-ligne button:hover { background: rgba(255, 255, 255, .3); }
/* Le bandeau ne doit pas masquer l'en-tête */
body:has(.jg-bandeau-hors-ligne[style*="flex"]) header { margin-top: 34px; }
@media (max-width: 1023px) {
  .jg-bandeau-hors-ligne { font-size: 11px; padding: 6px 12px; }
  body:has(.jg-bandeau-hors-ligne[style*="flex"]) .jg-burger { top: 48px; }
}

/* ---- NOUVEAUTÉS : l'onglet clignote et porte le nombre ----
   Règle produit : dès qu'une notification ou un message arrive, l'entrée de
   menu concernée clignote et affiche le compteur. Vaut pour tous les profils,
   et tout particulièrement pour WaxChat. */
@keyframes jg-clignote {
  0%, 100% { opacity: 1; }
  50%      { opacity: .45; }
}
.jg-clignote {
  animation: jg-clignote 1.1s ease-in-out infinite;
}
.jg-pastille {
  position: absolute;
  top: -4px;
  right: -4px;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 9999px;
  background: #dc2626;
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  line-height: 18px;
  text-align: center;
  box-shadow: 0 0 0 2px #fff;
}
/* Dans un menu latéral, la pastille se pose à droite de l'intitulé. */
nav a .jg-pastille {
  position: static;
  margin-left: auto;
  box-shadow: none;
}
/* Respect des préférences système : pas d'animation si l'utilisateur la refuse. */
@media (prefers-reduced-motion: reduce) {
  .jg-clignote { animation: none; }
}

/* ============================== IMPRESSION ==============================
   ON N'IMPRIME QUE LE DOCUMENT, PAS L'APPLICATION — ET ON L'IMPRIME EN ENTIER.

   Deux pièges, tombés l'un après l'autre :

   1. `window.print()` sortait la page entière : barre latérale, menu, boutons,
      champs de filtre. Le rapport arrivait noyé au milieu de l'interface.

   2. Masquer le reste puis poser la zone en `position: absolute` réglait le
      premier point mais en créait un pire : UN ÉLÉMENT POSITIONNÉ EN ABSOLU NE
      SE PAGINE PAS. Le navigateur imprimait la première feuille et coupait
      tout le reste — le rapport sortait amputé.

   La zone est donc DÉPLACÉE en enfant direct de <body>, dans le flux normal :
   là, elle se répartit sur autant de feuilles qu'il faut. `JG.imprimer` la
   remet exactement où elle était une fois l'impression terminée. */
@media print {
  @page { margin: 14mm 12mm; }

  /* LA CAUSE PRINCIPALE DE L'IMPRESSION TRONQUÉE.

     Toutes les pages de l'application ouvrent sur
     `<body class="h-screen flex overflow-hidden">` : le corps de page est
     bridé à la hauteur de l'écran et masque tout ce qui dépasse. C'est
     indispensable à l'écran — la barre latérale reste fixe, seule la zone
     centrale défile — mais sur une imprimante, cela COUPE le document à la
     hauteur d'un écran, quelle que soit sa longueur réelle.

     Sur le papier, il n'y a pas d'écran : la hauteur est libre, rien ne
     déborde, et le corps redevient un bloc ordinaire — sinon le document,
     devenu élément d'un conteneur flex, se ferait encore comprimer. */
  html, body {
    height: auto !important;
    min-height: 0 !important;
    max-height: none !important;
    overflow: visible !important;
    display: block !important;
  }

  /* Tout ce qui n'est pas le document sort de la feuille. `display: none` et
     non `visibility: hidden` : un élément seulement invisible occupe encore sa
     place et laisse des pages blanches. */
  body.jg-impression > * { display: none !important; }

  body.jg-impression > .jg-print {
    display: block !important;
    position: static !important;   /* EN FLUX : c'est ce qui permet la pagination */
    width: auto !important;
    height: auto !important;
    max-height: none !important;
    overflow: visible !important;
    margin: 0 !important;
    padding: 0 !important;
    background: #fff !important;
  }

  /* Aucun conteneur intérieur ne doit brider la hauteur : une zone à
     défilement ne s'imprimerait que sur sa hauteur visible. */
  body.jg-impression .jg-print * {
    overflow: visible !important;
    max-height: none !important;
  }

  /* LES COULEURS DOIVENT SORTIR. Sans cela, le navigateur supprime les fonds
     et les aplats : les barres du diagramme, les pastilles de verdict et les
     jauges s'impriment en blanc — le rapport perd tout ce qu'il explique. */
  body.jg-impression .jg-print,
  body.jg-impression .jg-print * {
    -webkit-print-color-adjust: exact !important;
    print-color-adjust: exact !important;
  }

  /* NE PAS COUPER — MAIS SEULEMENT CE QUI PEUT TENIR SUR UNE FEUILLE.
     `break-inside: avoid` sur un tableau de deux pages est une consigne
     impossible : le navigateur la contourne comme il peut, en laissant une
     page blanche ou en tronquant. `JG.imprimer` mesure donc chaque bloc et ne
     pose `jg-insecable` que sur ceux qui tiennent réellement. */
  body.jg-impression .jg-print .jg-insecable,
  body.jg-impression .jg-print svg { break-inside: avoid; page-break-inside: avoid; }

  /* Une LIGNE de tableau ne se coupe jamais en deux, et l'en-tête se répète en
     haut de chaque feuille : sans lui, les colonnes n'ont plus de nom. */
  body.jg-impression .jg-print tr { break-inside: avoid; page-break-inside: avoid; }
  body.jg-impression .jg-print thead { display: table-header-group; }
  body.jg-impression .jg-print tfoot { display: table-footer-group; }

  /* Pas de titre seul en bas de page, ni de ligne isolée en haut. */
  body.jg-impression .jg-print h3 { break-after: avoid; page-break-after: avoid; }
  body.jg-impression .jg-print p { orphans: 3; widows: 3; }

  /* À l'écran, les diagrammes larges défilent horizontalement. Sur le papier,
     rien ne défile : on les laisse se réduire à la largeur de la feuille. */
  body.jg-impression .jg-print .overflow-x-auto { overflow: visible !important; }
  body.jg-impression .jg-print svg { min-width: 0 !important; max-width: 100% !important; }

  /* Les commandes n'ont rien à faire sur une feuille de papier. */
  body.jg-impression .jg-print button,
  body.jg-impression .jg-print select,
  body.jg-impression .jg-print input,
  body.jg-impression .jg-sans-impression { display: none !important; }

  /* L'en-tête du document imprimé — caché à l'écran, visible sur le papier. */
  body.jg-impression .jg-print .jg-entete-impression { display: block !important; }
}

/* L'en-tête n'existe QUE sur le papier. */
.jg-entete-impression { display: none; }

/* ============ L'EMPLOI DU TEMPS EN GRILLE ============
   Ces styles vivaient dans `surveillant.html`. Le parent et l'élève lisent
   désormais la MÊME grille : les styles montent donc dans la feuille commune,
   sinon le tableau sortirait sans bordures ni couleurs sur leurs pages.

   Un cours de deux heures s'écrit d'un seul tenant, sans trait au milieu —
   c'est le `rowspan` qui le fait, et c'est pourquoi les bordures portent sur
   la CELLULE et non sur la ligne. */
table.jg-edt { border-collapse: collapse; table-layout: fixed; width: 100%; }
table.jg-edt th, table.jg-edt td { border: 1px solid #e5e7eb; padding: 0; }
.jg-edt-h { background: #1e3a8a; color: #fff; font-size: 12px;
            font-weight: 700; padding: 8px 6px; }
.jg-edt-heure { background: #f8fafc; color: #475569; font-size: 11px;
                font-weight: 600; text-align: center; padding: 6px;
                white-space: nowrap; }
.jg-edt-cours { vertical-align: top; padding: 6px 8px; }
.jg-edt-vide { background: #fff; }
.jg-edt-pause { text-align: center; font-size: 11px; font-weight: 700;
                padding: 5px; }

/* LA GRILLE DES ÉPREUVES SE LIT DE PLUS LOIN.
   Celle des cours porte cinq à six lignes par jour : elle doit tenir dans un
   écran, d'où sa petite fonte. Une composition, c'est deux ou trois créneaux
   par jour — la même table paraissait alors minuscule, avec des cellules
   perdues dans du vide. On lui donne des cases hautes, une fonte lisible et
   une largeur de colonne minimale : c'est un tableau qu'on affiche au mur et
   qu'on lit debout. */
table.jg-edt-epreuves { table-layout: auto; }
table.jg-edt-epreuves .jg-edt-h { font-size: 13px; padding: 11px 10px; }
table.jg-edt-epreuves .jg-edt-heure { font-size: 13px; padding: 14px 10px; }
table.jg-edt-epreuves .jg-edt-cours { padding: 12px 14px; min-width: 190px; }
table.jg-edt-epreuves td, table.jg-edt-epreuves th { height: 76px; }
table.jg-edt-epreuves .jg-edt-vide { min-width: 190px; }

/* ==================================================================
   L'EN-TÊTE NE DÉBORDE PLUS — ET PAS SEULEMENT SUR TÉLÉPHONE.
   ==================================================================
   Mesuré le 22/08/2026 sur un écran de 1280×800, donc un portable
   ordinaire : l'en-tête du directeur réclamait 1058 px pour 1024
   disponibles. Le nom de l'école, écrasé à 87 px, était coupé au
   milieu — « École : [SIM] Collè… ». À 334 px de large, il manquait
   675 px.

   LA CAUSE : un conteneur `flex` dont aucun enfant n'a le droit de
   rétrécir. En CSS, un élément flex a `min-width: auto` par défaut :
   il refuse de descendre sous la largeur de son contenu, et pousse
   tout le reste dehors plutôt que de se laisser tronquer. Le champ de
   recherche, lui, est fixé à 288 px et ne cède rien.

   LA RÈGLE : ce qui peut se tronquer se tronque, ce qui est essentiel
   reste. Le nom de l'école se réduit avec des points de suspension et
   garde son infobulle ; le libellé du rôle disparaît sous 640 px — il
   est déjà rappelé dans le tiroir. Rien n'est perdu, rien ne déborde.
   ------------------------------------------------------------------ */
header { min-width: 0; column-gap: 12px; }
/* LA COMPRESSION DOIT TRAVERSER TOUTE LA CHAINE.
   `min-width: 0` sur les enfants directs ne suffit pas : le nom de l'ecole
   est enveloppe dans un `div.flex.flex-col` intermediaire qui, lui, gardait
   `min-width: auto` et refusait de descendre sous la largeur de son texte.
   L'ellipse ne se declenchait donc jamais et le nom debordait de 24 px sur
   son voisin. Un seul maillon rigide suffit a bloquer toute la chaine. */
header > div,
header > div > div { min-width: 0; }

/* Le nom de l'établissement : une ligne, tronquée proprement.
   LA CLASSE, PAS L'IDENTIFIANT. Chaque page nomme son école autrement —
   `jg-etab` chez le directeur, `el-etab` chez l'élève, et ainsi de suite.
   Une règle sur `#jg-etab` ne servait donc qu'à une page sur onze ; les
   autres laissaient le nom passer à la ligne et se faire couper par le
   bouton ☰. C'est `app.js` qui pose `.jg-etab-nom` sur le bon élément,
   quel que soit son identifiant. */
.jg-etab-nom, #jg-etab {
  display: block;
  max-width: 34vw;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ==================================================================
   L'EN-TÊTE SE PARTAGE UNE LARGEUR, IL NE SE LA DISPUTE PAS
   ==================================================================
   Trois tentatives ont échoué avant celle-ci, et elles se ressemblent :
   fixer 190 px au champ de recherche, puis 140, puis le rendre
   élastique. La première réduisait le débordement, la deuxième aussi,
   la troisième l'a AGGRAVÉ — `flex: 1 1 auto` autorise à GRANDIR, et
   le champ est passé de 140 à 184 px.

   La leçon : tant que chacun réclame sa largeur, l'un des trois finit
   dehors. On donne donc à chaque élément une PART de l'écran, et la
   somme des parts tient par construction. Mesuré à 820 px : 246 + 130
   + 88 + 213 + espacements = 754 pour 820 disponibles.
   ------------------------------------------------------------------ */
@media (max-width: 1399px) {
  /* 190 px laissait 15 px de trop a 1100. Le champ reste utilisable a 150 —
     « Bineta Mb » tient — et c'est le seul element dont la largeur ne porte
     aucune information. */
  header input[type="text"] { width: 150px !important; }
}

/* POURQUOI 1399 ET NON 1279.
   Mesure : l'en-tete complet — nom de l'ecole, badge de role, recherche,
   cloche, avatar, nom du compte, selecteur — reclame 1111 px. La barre
   laterale en prend 256. Il faut donc une fenetre d'au moins 1367 px pour
   tout montrer ; en dessous, 87 px se peignaient les uns sur les autres a
   1280 — sur un portable ordinaire.
   Ce qui disparait ne manque a personne : le badge « Directeur
   d'etablissement » repete ce que dit le selecteur juste a cote, et le nom
   du compte repete l'avatar et le tiroir. */
@media (max-width: 1023px) {
  .jg-etab-nom, #jg-etab { max-width: 30vw; }
  header input[type="text"] { width: 130px !important; }
  .jg-profil-boite { max-width: 26vw; }
}
/* UN SEUL POINT DE RUPTURE, CELUI DE LA MISE EN PAGE.
   Le libelle du role et le nom du compte disparaissaient a 900 px, alors
   que le tiroir apparait a 1023. Entre les deux — une tablette de 912 px,
   par exemple — l'en-tete gardait tous ses elements dans l'espace reduit
   d'une mise en page mobile : 77 px de debordement. Deux seuils pour une
   meme question donnent toujours une bande ou personne n'a raison. */
/* LA BARRE LATERALE MANGE 256 PIXELS, ET LE CSS NE LE SAIT PAS.
   Une media query mesure la FENETRE, pas la place qui reste a l'en-tete.
   A 1100 px de fenetre, l'en-tete n'en a que 844 — soit l'etroitesse d'une
   tablette — mais toutes les regles « tablette » s'etaient deja arretees a
   1023. Resultat : 109 px de debordement en pleine mise en page de bureau,
   exactement le defaut d'origine. On decale donc le seuil de la largeur de
   la barre : ce qui s'efface sur tablette s'efface jusqu'a 1279 px. */
@media (max-width: 1399px) {
  header .jg-role-libelle { display: none !important; }
  /* Tout ce qui accompagne l'avatar — le nom, surtout — s'efface. L'avatar
     reste : il suffit a se reconnaitre, et l'identite complete est dans le
     tiroir. */
  .jg-avatar-groupe > *:not(.jg-avatar) { display: none !important; }
  .jg-identite-nom { display: none !important; }
  .jg-profil-boite { max-width: 24vw; }
}
@media (max-width: 640px) {
  .jg-etab-nom, #jg-etab { max-width: 52vw; font-size: 12px; padding: 2px 8px; }
  header input[type="text"],
  header .jg-recherche { display: none; }
  header { height: 60px !important; padding-right: 12px !important; }
}

/* ==================================================================
   TÉLÉPHONE ET TABLETTE — le confort tactile
   ==================================================================
   Trois choses qu'un doigt exige et qu'une souris pardonne : des
   cibles d'au moins 44 px (recommandation d'Apple, reprise par
   Android), un champ de saisie à 16 px au moins — en dessous, iOS
   ZOOME automatiquement à la mise au point et l'écran part de
   travers — et le respect des zones sûres, sinon la barre basse passe
   sous la barre gestuelle de l'iPhone.
   ------------------------------------------------------------------ */
@media (max-width: 1023px) {
  /* iOS zoome sur tout champ dont le texte fait moins de 16 px. */
  input, select, textarea { font-size: 16px !important; }

  /* Cibles tactiles. `.jg-compact` permet d'y échapper là où une
     grille de boutons serrés est volontaire (une grille horaire). */
  button:not(.jg-compact),
  a.jg-nav-basse-lien,
  select { min-height: 44px; }

  /* Les grilles de bureau retombent sur une colonne. */
  .grid-cols-3, .grid-cols-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 640px) {
  .grid-cols-2, .grid-cols-3, .grid-cols-4 { grid-template-columns: minmax(0, 1fr); }
  /* Les cartes respirent moins : l'écran est étroit. */
  .p-8 { padding: 16px !important; }
  .p-6 { padding: 14px !important; }
  .px-8 { padding-left: 14px !important; padding-right: 14px !important; }

  /* LA BARRE DU BAS MANGEAIT LA FIN DE CHAQUE PAGE.
     Ces trois lignes ci-dessus emploient le raccourci `padding`, en
     `!important`, sur une classe. `.p-8` pèse (0,1,0) ; `main` pèse (0,0,1).
     La règle « le contenu ne passe pas sous la barre basse » — écrite plus
     haut en `main { padding-bottom: 84px !important }` — était donc écrasée
     par un raccourci qui remettait 16 px en bas, et le dernier bloc de chaque
     écran disparaissait derrière les 64 px de la barre de navigation.
     Mesuré sur l'espace surveillant : `padding-bottom` calculé = 16 px, et
     « Ce que les analyses ont trouvé » coupé en deux.
     Toutes les pages étaient concernées : leur `<main>` porte `p-8`.
     On la réaffirme avec assez de poids pour tenir : `main[class]` vaut
     (0,1,1). */
  main[class] { padding-bottom: 84px !important; }
}

/* LE BOUTON ☰ A BESOIN DE SA PLACE, ET IL LA PERDAIT.
   `header` porte la classe `px-8`. La règle ci-dessus, écrite pour les
   cartes, s'appliquait donc aussi à l'en-tête et ramenait son retrait
   gauche à 14 px — un sélecteur de classe l'emporte sur un sélecteur
   d'élément, même avec `!important` des deux côtés. Le bouton se
   superposait alors au nom de l'école. On réserve la place explicitement,
   avec la même spécificité. */
@media (max-width: 1023px) {
  header.px-8, header { padding-left: 68px !important; }
}

/* LES TABLEAUX NE CASSENT PAS LA PAGE. `display:block` sur `table`
   perdait l'alignement des colonnes ; on enveloppe plutôt. */
@media (max-width: 1023px) {
  table { display: table; white-space: normal; }
  .jg-table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .jg-table-scroll table { min-width: 560px; }
}

/* Zones sûres iOS (encoche, barre gestuelle). */
@supports (padding: max(0px)) {
  .jg-nav-basse { padding-bottom: max(6px, env(safe-area-inset-bottom)); }
  .jg-aside {
    padding-top: env(safe-area-inset-top);
    padding-bottom: max(12px, env(safe-area-inset-bottom));
  }
}

/* La barre basse n'apparaît que sur téléphone : une tablette a la
   place d'afficher le tiroir sans sacrifier le bas de l'écran. */
@media (min-width: 768px) { .jg-nav-basse { display: none !important; } }
@media (min-width: 768px) and (max-width: 1023px) { main { padding-bottom: 24px !important; } }

/* Le tiroir ne dépasse jamais l'écran d'un téléphone. */
@media (max-width: 1023px) {
  .jg-aside { width: min(84vw, 300px) !important; max-width: 300px; }
}

/* Le sélecteur de rôle : il cède avant le nom de l'école. */
.jg-profil-boite { max-width: 42vw; }
#jg-profil-actif { display: inline-block; max-width: 100%; overflow: hidden;
                   text-overflow: ellipsis; white-space: nowrap; }
/* 34vw laissait la pastille reclamer 279 px a 820 : plus large que la part
   qu'on venait de lui donner juste au-dessus, et ecrite APRES, donc elle
   gagnait. Deux regles pour la meme chose, la seconde annulant la
   premiere — le genre de contradiction qui se voit a l'ecran et jamais
   dans le code. */
/* (fusionne dans le bloc <= 1023 px) */
@media (max-width: 640px) {
  /* Plus que les icônes : le nom du rôle est dans la liste et dans le tiroir. */
  #jg-profil-actif { display: none; }
  .jg-profil-boite { max-width: none; }
  #jg-profil-liste { width: min(88vw, 320px) !important; }
}

/* ==================================================================
   PERSONNE NE SE PEINT SUR SON VOISIN
   ==================================================================
   Un groupe de l'en-tête qui déborde ne pousse pas les autres : il les
   TRAVERSE, et le premier fond opaque rencontré le recouvre. C'est ce
   qui donnait « Ousse… / Diagn… » à moitié caché sous la pastille de
   rôle : la cloche et l'avatar réclamaient 68 px dans 48 disponibles.

   On rend donc chaque groupe compressible (`min-width: 0`), et on
   décide de l'ordre dans lequel les choses cèdent — au lieu de laisser
   le navigateur les empiler.
   ------------------------------------------------------------------ */
header > div { min-width: 0; }

/* Ordre de sacrifice, du moins utile au plus utile :
   1. le champ de recherche      (< 640 px, déjà)
   2. le libellé du rôle         (< 900 px, déjà)
   3. le NOM du compte           (< 900 px)
   4. l'AVATAR                   (< 420 px)
   La cloche, elle, ne part jamais : une alerte non vue est perdue. */
/* (fusionne dans le bloc <= 1023 px ci-dessus) */
/* 360 px, MESURE — PAS DEVINE. A 375 px l'avatar tient sans gener personne :
   le cacher la aurait ete une perte gratuite. A 334 px il ne tient plus, et la
   cloche debordait sous la pastille de role. Le seuil est donc pose entre les
   deux, la ou la mesure le place. */
@media (max-width: 360px) {
  .jg-avatar { display: none !important; }
  .jg-avatar-groupe { gap: 0 !important; }
  /* Le nom de l'école se serre aussi : c'est lui qui a le plus de place. */
  .jg-etab-nom, #jg-etab { max-width: 44vw; }
}

/* La pastille de role s'efface des qu'un selecteur de role est present :
   il porte la meme information, et l'etablissement en plus. */
header.jg-a-selecteur .jg-role-libelle { display: none !important; }


/* Le bouton « revenir en haut » : discret, et absent tant qu'il ne sert pas. */
.jg-haut {
  position: fixed; right: 18px; bottom: 22px; z-index: 40;
  width: 44px; height: 44px; border-radius: 999px;
  background: #4c1d95; color: #fff; border: none;
  display: flex; align-items: center; justify-content: center;
  font-size: 20px; box-shadow: 0 4px 14px rgba(0, 0, 0, .18);
  opacity: 0; pointer-events: none; transform: translateY(8px);
  transition: opacity .2s ease, transform .2s ease;
}
.jg-haut.visible { opacity: 1; pointer-events: auto; transform: none; }
.jg-haut:hover { background: #5b21b6; }

/* Sur telephone, la barre de navigation basse occupe deja le bas : on se
   pose au-dessus d'elle plutot que par-dessus un onglet. */
@media (max-width: 767px) {
  .jg-haut { bottom: calc(76px + env(safe-area-inset-bottom)); right: 14px; }
}
@media (prefers-reduced-motion: reduce) {
  .jg-haut { transition: none; }
}

/* LE DOCUMENT À IMPRIMER VIT DANS LA PAGE, mais hors de l'écran : il n'a rien
   à y faire tant qu'on ne l'imprime pas. La règle d'impression le remet en
   flux (`body.jg-impression > .jg-print { position: static !important }`). */
.jg-doc-temporaire {
  position: absolute; left: -10000px; top: 0; width: 760px;
}

/* LA PASTILLE QUI DIT « ÇA SE CHANGE ».
   Sans elle, la photo de profil était une fonction que personne ne pouvait
   découvrir : rien n'indiquait que l'avatar se clique, et un `title` ne se
   lit pas sur un téléphone. */
.jg-photo-badge {
  position: absolute; right: -3px; bottom: -3px;
  width: 16px; height: 16px; border-radius: 50%;
  background: #0f172a; color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-size: 10px; line-height: 1; border: 2px solid #fff;
  pointer-events: none;
}
