#epn1-root{
  --surface-1:      #fcfcfb;
  --page:           #f9f9f7;
  --text-primary:   #0b0b0b;
  --text-secondary: #52514e;
  --text-muted:     #898781;
  --grid:           #e1e0d9;
  --baseline:       #c3c2b7;
  --border:         rgba(11,11,11,0.10);
  --series-blue:    #00b6c9;
  --series-blue-d:  #067885;
  --status-good:      #0ca30c;
  --status-good-bg:   #e8f7e8;
  --status-warning:   #fab219;
  --status-warning-bg:#fff6e2;
  --status-serious:   #ec835a;
  --status-critical:  #d03b3b;
  --status-critical-bg:#fbeaea;
  --status-neutral:   #898781;
  --status-neutral-bg:#eeede9;
  --brand-ink:  #0b1d3a;
  --brand-aqua: #00b6c9;
  /* Teintes douces des cartes "Formations XXX (tarifs)" de ⚙️ Paramètres des formations (voir
     .card-org-* plus bas) — une par organisme, pour les distinguer visuellement d'un coup d'œil
     dans une page qui les empile toutes à la suite. Choisies dans le même registre que les fonds
     --status-*-bg ci-dessus (pastel, jamais criard, texte sombre toujours lisible dessus) et
     inspirées de l'identité visuelle de chaque organisme sans en reproduire les logos/couleurs
     exactes : FFESSM (bleu marine), ANMP (bleu nuit + orange), PADI (bleu identitaire), SSI
     (turquoise). FFESSM n'ayant pas de case à cocher "actif" (toujours affichée, voir
     organismesActifs), sa teinte reste la plus neutre des quatre.
  */
  --org-ffessm-bg: #eaf2fb;
  --org-anmp-bg:   #fdf1e4;
  --org-padi-bg:   #e3f5fc;
  --org-ssi-bg:    #e6f7f3;
  --radius: 10px;
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  color: var(--text-primary);
  font-size: 14px;
  line-height: 1.45;
}
#epn1-root *{ box-sizing:border-box; }
#epn1-root button{
  font-family: inherit; font-size: 13px; cursor:pointer;
  border-radius: 8px; border: 1px solid var(--border);
  background: var(--surface-1); color: var(--text-primary);
  padding: 9px 13px; transition: background .12s ease;
}
#epn1-root button:hover{ background:#f0efec; }
#epn1-root button.primary{ background: var(--series-blue); color:#fff; border-color: var(--series-blue-d); }
#epn1-root button.primary:hover{ background: var(--series-blue-d); }
#epn1-root button.danger{ color: var(--status-critical); border-color: var(--status-critical); background:#fff; }
#epn1-root button.danger:hover{ background: var(--status-critical-bg); }
#epn1-root button.ghost{ border-color: transparent; background: transparent; }
#epn1-root button:disabled{ opacity:.45; cursor:not-allowed; }
#epn1-root input[type=text], #epn1-root input[type=date], #epn1-root input[type=tel],
#epn1-root input[type=email], #epn1-root input[type=password], #epn1-root select, #epn1-root textarea{
  font-family: inherit; font-size: 14px; color: var(--text-primary);
  background: #fff; border: 1px solid var(--baseline); border-radius: 7px;
  padding: 9px 10px; width: 100%;
}
#epn1-root textarea{ resize: vertical; min-height: 52px; }
/* Contour rouge posé directement sur un champ du socle identité (Nom/Prénom/Téléphone/Date de
   naissance/Email) tant qu'il est vide — voir IDENTITE_REQUIRED_FIELDS/updateIdentiteBadge() dans
   app.js. Placée après la règle générale ci-dessus (même spécificité : #id + tag + une classe ou
   un attribut) pour l'emporter par ordre d'apparition dans la feuille de style. */
#epn1-root input.field-missing{ border-color: var(--status-critical); box-shadow: 0 0 0 1px var(--status-critical); }
#epn1-root label{ display:block; font-size:12px; color: var(--text-secondary); margin-bottom:3px; font-weight:600; }
#epn1-root .field{ margin-bottom: 12px; }
#epn1-root h1,#epn1-root h2,#epn1-root h3{ font-weight:700; margin: 0 0 4px 0; }

/* ---- login ---- */
#epn1-login-screen{
  min-height: 60vh; display:flex; align-items:center; justify-content:center; padding: 24px 14px;
  background: var(--page);
}
.epn1-login-card{
  background: var(--surface-1); border:1px solid var(--grid); border-radius: 14px;
  padding: 28px 24px; max-width: 340px; width:100%; text-align:center;
}
.epn1-login-card h1{ font-size:22px; font-weight:800; letter-spacing:-0.02em; color:var(--brand-ink); margin-bottom:4px; display:flex; align-items:center; justify-content:center; gap:8px; }
.epn1-login-card h1 svg{ flex:0 0 auto; }
.epn1-login-sub{ font-size:12px; color:var(--text-muted); margin-bottom:20px; }
/* Logo du club (marque blanche — voir "Administration Progrezio" > Marque blanche) : masqué par
   défaut (style="display:none" en dur dans le HTML), affiché par applyClubBranding() dans app.js
   dès qu'un logo est défini. Même élément réutilisé sur l'écran de connexion et dans la sidebar. */
.club-logo-img{ height:15px; width:auto; max-width:60px; vertical-align:-3px; margin-right:5px; border-radius:2px; object-fit:contain; }
.epn1-login-card .field{ text-align:left; }
.epn1-login-error{ background: var(--status-critical-bg); color:#9b2323; border-radius:8px; padding:8px 10px; font-size:12.5px; margin-bottom:12px; }

/* ---- layout ---- */
#app{ display:flex; min-height: 70vh; position:relative; background:var(--page); border-radius:12px; overflow:hidden; border:1px solid var(--grid); }
#btn-sidebar-toggle{ display:none; position:absolute; top:10px; left:10px; z-index:30; font-size:18px; padding:6px 11px; }
#sidebar-scrim{ display:none; position:fixed; inset:0; background:rgba(11,11,11,.4); z-index:15; }

#sidebar{
  width: 280px; flex: 0 0 280px; background: var(--surface-1);
  border-right: 1px solid var(--grid); display:flex; flex-direction:column;
}
#sidebar-header{ padding: 16px 16px 10px; border-bottom:1px solid var(--grid); }
#sidebar-header h1{ font-size:16px; font-weight:800; letter-spacing:-0.02em; color:var(--brand-ink); display:flex; align-items:center; gap:6px; }
#sidebar-header h1 svg{ flex:0 0 auto; }
#sidebar-header .sub{ font-size:11.5px; color:var(--text-muted); }
#sidebar-actions{ padding: 10px 12px; display:flex; flex-direction:column; gap:6px; border-bottom:1px solid var(--grid); }
#search{ padding: 10px 12px 6px; }
#fiche-list{ flex:1; overflow-y:auto; padding: 4px 8px 12px; max-height: 40vh; }
.fiche-item{ position:relative; padding: 9px 10px; border-radius: 8px; cursor:pointer; margin-bottom:3px; border: 1px solid transparent; }
.fiche-item:hover{ background:#f0efec; }
.fiche-item.active{ background: #E6F8FA; border-color: var(--series-blue); }
.fiche-item .name{ font-weight:600; font-size:13px; }
.fiche-item .meta{ font-size:11px; color:var(--text-muted); margin-top:1px; }
.fiche-item .prog-mini{ height:4px; border-radius:2px; background:var(--grid); margin-top:6px; overflow:hidden; }
/* Petit point rouge signalant une fiche dont le socle identité (Nom/Prénom/Téléphone/Date de
   naissance/Email) est incomplet — voir ficheInfosIncomplete() dans app.js. Positionné en haut à
   droite de l'entrée ; le liseré blanc (box-shadow) le détache du fond, y compris sur le fond
   bleu pâle de l'entrée active. */
.fiche-incomplete-dot{ position:absolute; top:6px; right:7px; width:8px; height:8px; border-radius:50%; background:var(--status-critical); box-shadow:0 0 0 2px #fff; }
.fiche-item.active .fiche-incomplete-dot{ box-shadow:0 0 0 2px #E6F8FA; }
.fiche-item .prog-mini > div{ height:100%; background: var(--status-good); }
#sidebar-footer{ padding: 10px 12px; border-top:1px solid var(--grid); display:flex; flex-direction:column; gap:6px; }
#whoami{ font-size:11.5px; color:var(--text-muted); padding: 2px 2px 4px; }
#online-now{ font-size:11px; color:var(--text-muted); padding: 0 2px 4px; line-height:1.5; }
#empty-list{ padding: 20px 12px; color: var(--text-muted); font-size:12.5px; text-align:center; }

#main{ flex:1; display:flex; flex-direction:column; min-width:0; }
#main-empty{ flex:1; display:flex; flex-direction:column; align-items:center; justify-content:center; color:var(--text-muted); text-align:center; padding:20px 20px 18vh; }
#main-empty-brand{ display:flex; flex-direction:column; align-items:center; flex:none; margin-bottom:30px; }
#main-empty-brand img{ width:min(220px, 70%); height:auto; }
#main-empty-brand p{ font-size:15px; font-weight:600; margin:0 0 10px; }
#main-empty-content{ display:flex; flex-direction:column; align-items:center; }
#fiche-view{ flex:1; display:none; flex-direction:column; min-height:0; }
#fiche-topbar{
  padding: 14px 20px; border-bottom:1px solid var(--grid); background: var(--surface-1);
  display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap;
}
#fiche-topbar .title{ display:flex; align-items:center; gap:10px; }
#fiche-topbar .title h2{ font-size:16px; }
#fiche-topbar .title .sub{ font-size:12px; color:var(--text-muted); }
#fiche-topbar .actions{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.fiche-avatar{ width:34px; height:34px; border-radius:50%; object-fit:cover; flex:none; border:1px solid var(--grid); cursor:pointer; }

.photo-row{ display:flex; align-items:center; gap:14px; margin-bottom:14px; }
.photo-thumb-wrap{ width:64px; height:64px; border-radius:50%; overflow:hidden; flex:none; background:var(--page); display:flex; align-items:center; justify-content:center; border:1px solid var(--grid); }
.photo-thumb{ width:100%; height:100%; object-fit:cover; display:block; cursor:pointer; }
.photo-placeholder{ font-size:26px; color:var(--text-muted); line-height:1; }
.photo-actions{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; }

/* Bannière publicitaire (upload : "🛠️ Administration Progrezio → Identité du club",
   #admin-panel-marque) — même famille visuelle que .photo-thumb-wrap ci-dessus, mais large et
   basse (ratio ~6:1) plutôt que ronde : c'est un bandeau, pas un avatar. object-fit:contain (pas
   cover) pour ne jamais rogner l'image envoyée, quel que soit son ratio réel — un bandeau recadré
   au mauvais endroit serait pire qu'une image simplement contenue avec un peu de vide de part et
   d'autre. Le cadre en tirets + placeholder ne servent qu'à signaler un emplacement vide côté
   upload ; voir .banner-display-wrap ci-dessous pour la variante "affichage" sur chaque fiche. */
.banner-preview-wrap{ width:100%; max-width:480px; aspect-ratio:6/1; border-radius:var(--radius); overflow:hidden; background:var(--page); display:flex; align-items:center; justify-content:center; border:1px dashed var(--grid); }
.banner-preview{ width:100%; height:100%; object-fit:contain; display:block; }
.banner-placeholder{ font-size:13px; color:var(--text-muted); }

/* Carte d'affichage de la bannière (#card-resume-banniere, en bas de l'onglet "Résumé", lecture
   seule) — EN PLEIN FOND PERDU EN LARGEUR (demande explicite et répétée du club de ne jamais
   avoir le moindre vide sur les côtés, comme une vraie bannière publicitaire de site web) MAIS À
   RATIO FIXE (aspect-ratio:6/1, même ratio que EPN1_Resume_Pdf::BANNER_H côté PDF — pour ne pas
   laisser une image "carrée" envahir toute la carte). aspect-ratio (et non une hauteur fixe en
   px) pour que la case affichée soit TOUJOURS un vrai 6:1 quelle que soit la largeur réelle de la
   carte — c'est ce qui garantit qu'une image envoyée exactement au format conseillé dans l'UI
   (1200×200, soit 6:1) s'affiche pile, sans le moindre recadrage, plutôt que d'être rognée dans
   une case qui ne serait pas vraiment 6:1. Contrairement à .card (padding:16px 18px), cette carte
   annule son propre padding pour que l'image touche les quatre bords ; overflow:hidden pour que
   les coins restent bien arrondis (le rayon vient de .card) plutôt que de déborder du cadre —
   c'est aussi ce qui, combiné à object-fit:cover ci-dessous, rogne visuellement l'éventuel
   débordement d'une image dont le ratio réel s'écarterait du 6:1 conseillé. Contrairement à
   .banner-preview-wrap ci-dessus (object-fit:contain, qui centre l'image SANS la rogner dans une
   case de ratio fixe côté upload), ici c'est bien object-fit:cover : toute image dont le ratio
   réel ne correspond pas exactement à 6:1 est recadrée (centrée), jamais étirée ni laissée avec
   du vide. La carte entière est masquée tant qu'aucune bannière n'est configurée (voir
   renderResumeBannerCard() dans app.js), donc pas besoin de signaler visuellement un emplacement
   vide. */
/* margin-top:auto : dans la colonne flex #tab-resume.active (voir juste au-dessus), pousse cette
   carte tout en bas de la zone visible plutôt que de la laisser juste après le contenu qui la
   précède — demande explicite du club, "en bas de page" comme sur le PDF (voir
   EPN1_Resume_Pdf::BANNER_H). */
#card-resume-banniere{ padding:0; overflow:hidden; margin-top:auto; }
.banner-display-wrap{ width:100%; aspect-ratio:6/1; display:block; }
.banner-display-wrap .banner-preview{ width:100%; height:100%; object-fit:cover; display:block; }

/* Pour un <input type="file"> déclenché par un bouton (le vrai input reste caché, un
   .click() JS sur lui ouvre le sélecteur — voir btn-photo-upload/btn-import-json dans app.js).
   "display:none" fonctionne pour ça sur ordinateur, mais plusieurs navigateurs mobiles (Safari
   iOS notamment) refusent silencieusement d'ouvrir le sélecteur de fichier/appareil photo sur un
   input totalement retiré du rendu — ce qui donnait "rien ne se passe" au clic sur mobile.
   Cette classe le garde techniquement "affiché" (1px, hors champ de vision) au lieu de le
   supprimer du rendu, ce qui rétablit le comportement sur mobile sans rien changer visuellement. */
.visually-hidden-file{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0;
}

/* "Bouton" pour un input file — en réalité un <label for="…">, pas un <button>+.click() JS. Un
   <label> associé à son input ouvre nativement le sélecteur de fichier au clic/tap, sans passer
   par un appel .click() scripté sur l'input — plus robuste que "bouton + onclick { input.click() }"
   sur certains navigateurs mobiles (constaté sur Chrome Android : le clic scripté sur un input
   file caché pouvait être silencieusement ignoré, sans aucune erreur ni sélecteur visible). Comme
   #epn1-root définit "label{ display:block; font-size:12px; … }" pour les libellés de champ, cette
   classe doit réécrire explicitement l'apparence pour ressembler à #epn1-root button ci-dessus. */
.file-btn{
  display:inline-block; text-align:center; font-family:inherit; font-size:13px; font-weight:400;
  cursor:pointer; border-radius:8px; border:1px solid var(--border);
  background:var(--surface-1); color:var(--text-primary); padding:9px 13px;
  margin-bottom:0; transition:background .12s ease;
}
.file-btn:hover{ background:#f0efec; }
.file-btn.is-busy{ opacity:.45; pointer-events:none; }

/* Un seul bouton visible ("📷 Ajouter une photo") ouvre un petit menu à deux choix — "Prendre
   une photo" (avec capture="environment", ouvre directement l'appareil photo sur mobile) et
   "Depuis la galerie" (sans capture) — plutôt que deux boutons séparés en permanence. Les deux
   restent des <label for="…"> natifs (voir .file-btn plus haut et la note dans app.js) : ce menu
   ne fait que les regrouper visuellement, il ne remplace pas leur déclenchement natif par du JS. */
.photo-menu-wrap{ position:relative; display:inline-block; }
.photo-menu{
  position:absolute; top:calc(100% + 4px); left:0; z-index:5; display:none;
  flex-direction:column; gap:2px; min-width:190px; padding:4px;
  background:var(--surface-1); border:1px solid var(--grid); border-radius:8px;
  box-shadow:0 4px 14px rgba(11,11,11,.14);
}
.photo-menu.open{ display:flex; }
.photo-menu-item{
  display:block; margin:0; padding:8px 10px; border-radius:6px; cursor:pointer;
  font-size:13px; font-weight:400; color:var(--text-primary); white-space:nowrap;
}
.photo-menu-item:hover{ background:#f0efec; }
.save-status{ font-size:11.5px; color:var(--text-muted); white-space:nowrap; }
.save-status.saving{ color: var(--series-blue); }
.save-status.saved{ color: var(--status-good); }
.save-status.error{ color: var(--status-critical); }
.save-status.dirty{ color: var(--status-warning); font-weight:600; }
#btn-save-fiche{ position:relative; }
#btn-save-fiche.dirty{ background: var(--status-warning); border-color:#c98500; color:#3a2600; animation: epn1-pulse 1.6s ease-in-out infinite; }
@keyframes epn1-pulse{ 0%,100%{ box-shadow:0 0 0 0 rgba(250,178,25,.5);} 50%{ box-shadow:0 0 0 6px rgba(250,178,25,0);} }

#tabs{ display:flex; gap:2px; padding: 4px 14px 0; background: var(--surface-1); border-bottom:1px solid var(--grid); overflow-x:auto; -webkit-overflow-scrolling:touch; }
#epn1-root .tab-btn{
  border:none; border-radius: 8px 8px 0 0; background:transparent; padding: 12px 13px; font-weight:600;
  color: var(--text-secondary); border-bottom: 3px solid transparent; white-space:nowrap; font-size:13px;
}
#epn1-root .tab-btn:hover{ background: #f4f3f0; }
#epn1-root .tab-btn.active{
  color: var(--series-blue-d); background: rgba(0,182,201,0.12); border-bottom-color: var(--series-blue);
}
#epn1-root .tab-btn.active:hover{ background: rgba(0,182,201,0.16); }

#tab-content{ flex:1; overflow-y:auto; padding: 16px 16px 60px; }
.panel{ display:none; max-width: 900px; }
.panel.active{ display:block; }
/* Onglet "Résumé" (#tab-resume) uniquement : colonne flex de hauteur >= la zone visible de
   #tab-content (min-height:100%, résolu car #tab-content a une hauteur bornée — c'est tout
   l'intérêt de son overflow-y:auto, voir plus haut) plutôt qu'un simple bloc, pour que
   #card-resume-banniere (margin-top:auto, voir plus bas) puisse être poussée tout en bas de la
   zone visible — demande explicite du club — même quand le contenu au-dessus (jauge + 3
   catégories) est court et ne remplit pas l'écran. Si le contenu déborde (beaucoup de
   compétences), la colonne grandit naturellement au-delà de cette hauteur minimale et la
   bannière suit juste en dessous, sans vide artificiel ni chevauchement. Spécificité
   #tab-resume.active volontairement supérieure à .panel.active ci-dessus pour l'emporter sans
   !important. */
#tab-resume.active{ display:flex; flex-direction:column; min-height:100%; }

/* ---- Administration Progrezio — écran plein cadre (remplace #sidebar + #main, voir
   showAdminPage()/hideAdminPage() dans app.js), volontairement construit sur EXACTEMENT le même
   trio topbar/onglets/cartes que la fiche elle-même (#fiche-topbar, #tabs/.tab-btn,
   #tab-content/.card) plutôt que d'inventer un nouveau style : classes distinctes
   (.admin-tab-btn/.admin-panel) pour que le JS de bascule des onglets de la fiche
   (querySelectorAll('.tab-btn')) n'interfère pas avec celui de cette page, mais mêmes règles
   visuelles ci-dessous. ============================================================ */
#admin-page, #params-page, #seance-fiches-page{ flex:1; display:none; flex-direction:column; min-width:0; }
#admin-topbar, #params-topbar, #seance-fiches-topbar{
  padding: 14px 20px; border-bottom:1px solid var(--grid); background: var(--surface-1);
  display:flex; align-items:center; gap:14px;
}
#admin-topbar .title h2, #params-topbar .title h2, #seance-fiches-topbar .title h2{ font-size:16px; margin:0; }
/* Bouton "Enregistrer les paramètres" — remonté dans la topbar (à côté de "← Retour") plutôt que
   tout en bas de la page, qui peut être longue une fois les 4 catalogues (FFESSM/ANMP/PADI/SSI) et
   les prérequis affichés : il reste ainsi visible sans avoir à tout faire défiler. Seule #params-topbar
   a un bloc .actions pour l'instant (ni #admin-topbar ni #seance-fiches-topbar n'en ont besoin), donc
   la règle est scopée à elle seule plutôt qu'ajoutée à la règle partagée ci-dessus, pour ne pas changer
   la disposition (actuellement bouton+titre collés à gauche) des deux autres topbars. */
#params-topbar{ flex-wrap:wrap; }
#params-topbar .actions{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-left:auto; }
#admin-tabs{ display:flex; gap:2px; padding: 4px 14px 0; background: var(--surface-1); border-bottom:1px solid var(--grid); overflow-x:auto; -webkit-overflow-scrolling:touch; }
#epn1-root .admin-tab-btn{
  border:none; border-radius: 8px 8px 0 0; background:transparent; padding: 12px 13px; font-weight:600;
  color: var(--text-secondary); border-bottom: 3px solid transparent; white-space:nowrap; font-size:13px;
}
#epn1-root .admin-tab-btn:hover{ background: #f4f3f0; }
#epn1-root .admin-tab-btn.active{
  color: var(--series-blue-d); background: rgba(0,182,201,0.12); border-bottom-color: var(--series-blue);
}
#epn1-root .admin-tab-btn.active:hover{ background: rgba(0,182,201,0.16); }
#admin-tab-content, #params-content, #seance-fiches-content{ flex:1; overflow-y:auto; padding: 16px 16px 60px; }
#params-content, #seance-fiches-content{ max-width: 900px; }
.admin-panel{ display:none; max-width: 900px; }
.admin-panel.active{ display:block; }

/* ---- "🖼️ Fiches de séance" : une ligne par formation, un emplacement (miniature + bouton) par
   plongée prévue (voir renderSeanceFichesPage() dans app.js). Pas de <table> : le nombre
   d'emplacements varie d'une formation à l'autre. ---- */
.seance-formation-row{ display:flex; flex-wrap:wrap; align-items:flex-start; gap:14px; padding:14px 0; border-bottom:1px solid var(--grid); }
.seance-formation-row:last-child{ border-bottom:none; }
.seance-formation-name{ flex:0 0 170px; font-weight:600; font-size:13px; padding-top:6px; }
.seance-slots{ flex:1 1 auto; display:flex; flex-wrap:wrap; gap:10px; min-width:0; }
.seance-slot{ width:92px; text-align:center; }
.seance-slot-label{ font-size:11px; color: var(--text-secondary); margin-bottom:4px; }
.seance-slot-thumb-wrap{ position:relative; width:92px; height:92px; border:1px solid var(--grid); border-radius:8px; overflow:hidden; background:var(--surface-1); display:flex; align-items:center; justify-content:center; }
.seance-slot-thumb-wrap img{ width:100%; height:100%; object-fit:cover; cursor:pointer; }
.seance-slot-empty{ font-size:22px; color: var(--text-secondary); opacity:0.5; }
.seance-slot-actions{ display:flex; justify-content:center; gap:4px; margin-top:4px; }
.seance-slot-actions .file-btn, .seance-slot-actions button{ font-size:11px; padding:2px 6px; }
.seance-no-plongees{ color: var(--text-secondary); font-style:italic; font-size:13px; padding:10px 0; }

/* ---- Visionneuse plein écran d'une fiche de séance (voir openSeanceFicheViewer() dans app.js).
   Pincer pour zoomer, double-tap pour zoomer/dézoomer d'un cran, glisser pour déplacer une fois
   zoomée ; touch-action:none empêche le navigateur de capter ces gestes pour son propre zoom/scroll
   de page, condition pour que notre gestion tactile (voir initSeanceFicheViewerGestures() dans
   app.js) fonctionne à la fois en portrait et en paysage. ---- */
#seance-fiche-viewer{
  position:fixed; inset:0; z-index:9000; background:rgba(20,20,20,0.92);
  display:flex; align-items:center; justify-content:center; cursor:zoom-out; padding:24px;
  touch-action:none; overflow:hidden;
}
#seance-fiche-viewer img{
  max-width:100%; max-height:100%; border-radius:6px; box-shadow:0 8px 32px rgba(0,0,0,0.5);
  cursor:default; touch-action:none; transform-origin:center center; will-change:transform;
}
#epn1-root #close-seance-fiche-viewer{
  position:fixed; top:14px; right:16px; z-index:9001; float:none;
  background:rgba(20,20,20,0.55); border-color:rgba(255,255,255,0.35); color:#fff;
}

/* ---- "Suivi des élèves" (Administration Progrezio) — colonnes triables au clic, voir
   renderAdminSuiviTable()/sortAdminSuiviBy() dans app.js. ---- */
th[data-sort]{ cursor:pointer; user-select:none; white-space:nowrap; }
th[data-sort]:hover{ color: var(--text-primary); }
th[data-sort].sorted{ color: var(--series-blue-d); }
#admin-suivi-tbody tr{ cursor:pointer; }
#admin-suivi-tbody tr:hover{ background:#f4f3f0; }

.card{ background: var(--surface-1); border:1px solid var(--grid); border-radius: var(--radius); padding: 16px 18px; margin-bottom:16px; }
.card h3{ font-size:13px; text-transform:uppercase; letter-spacing:.02em; color:var(--text-secondary); margin-bottom:12px; }
/* La pastille "⚠ Informations incomplètes" (#badge-identite-incomplete, voir app-shell.php et
   ficheInfosIncomplete()/updateIdentiteBadge() dans app.js) hérite du h3 parent — on annule ici
   la majuscule forcée et l'espacement de lettres pour qu'elle reste lisible comme les autres
   .status-pill de l'appli. */
.card h3 .status-pill{ text-transform:none; letter-spacing:normal; margin-left:8px; vertical-align:middle; }
/* Cartes "Formations XXX (tarifs)" de ⚙️ Paramètres des formations — voir #ffessm-tarifs-card /
   #anmp-tarifs-card / #padi-tarifs-card / #ssi-tarifs-card dans app-shell.php et les variables
   --org-*-bg définies plus haut. Une teinte douce par organisme, appliquée UNIQUEMENT à ces
   4 cartes (pas d'effet ailleurs) pour repérer immédiatement à quel organisme appartient chaque
   bloc dans une page qui les empile tous à la suite. */
.card-org-ffessm{ background: var(--org-ffessm-bg); }
.card-org-anmp{ background: var(--org-anmp-bg); }
.card-org-padi{ background: var(--org-padi-bg); }
.card-org-ssi{ background: var(--org-ssi-bg); }
/* Logo de l'organisme, avant le titre "Formations XXX (tarifs)" — absent du DOM si le fichier
   assets/organismes/<organisme>.png n'a pas été fourni (voir epn1_organisme_logo_url() côté PHP),
   jamais un espace vide réservé pour rien. */
.card h3 .org-logo{ height:20px; width:auto; margin-right:8px; vertical-align:middle; border-radius:3px; }
.grid2{ display:grid; grid-template-columns: 1fr 1fr; gap: 0 20px; }
.grid3{ display:grid; grid-template-columns: 1fr 1fr 1fr; gap: 0 20px; }
.grid4{ display:grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 0 16px; }
@media (max-width: 640px){ .grid2, .grid3, .grid4{ grid-template-columns:1fr; } }
@media (max-width: 900px){ .grid4{ grid-template-columns: 1fr 1fr; } }

/* Historique des formations déjà certifiées sur cette fiche (#cert-historique, carte
   "Certifications" — voir renderCertificationCard() dans app.js et EPN1_Data::start_next_formation()
   côté serveur). Une ligne par formation enchaînée, la plus ancienne en premier. */
.cert-histo-row{ display:flex; flex-wrap:wrap; align-items:center; gap:4px 14px; padding:8px 0; border-top:1px solid var(--grid); font-size:13px; color:var(--text-secondary); }
.cert-histo-row:first-child{ border-top:1px solid var(--grid); margin-top:10px; }
.cert-histo-row .niveau{ font-weight:600; color:var(--text-primary); }
/* "📄 PDF" (ouvre la fiche de validation du cycle archivé, voir fetchAndOpenPdf() dans app.js) —
   poussé à droite de la ligne, en retrait visuel (petit et discret) par rapport aux informations
   du cycle elles-mêmes. */
.cert-histo-row .cert-histo-pdf{ margin-left:auto; font-size:12px; padding:2px 10px; }

.toggle-row{ display:flex; align-items:center; justify-content:space-between; padding:9px 0; border-bottom:1px solid var(--grid); }
.toggle-row:last-child{ border-bottom:none; }
.toggle-row .lbl{ font-size:13px; }
.switch{ position:relative; width:42px; height:24px; flex:0 0 auto; }
.switch input{ opacity:0; width:0; height:0; }
.switch .slider{ position:absolute; inset:0; background: var(--baseline); border-radius:24px; cursor:pointer; transition:.15s; }
.switch .slider:before{ content:""; position:absolute; width:18px; height:18px; left:3px; top:3px; background:#fff; border-radius:50%; transition:.15s; }
.switch input:checked + .slider{ background: var(--status-good); }
.switch input:checked + .slider:before{ transform: translateX(18px); }

.stat-row{ display:grid; grid-template-columns: repeat(4, 1fr); gap:10px; margin-bottom:14px; }
@media (max-width: 720px){ .stat-row{ grid-template-columns: repeat(2,1fr); } }
.stat-tile{ background:var(--surface-1); border:1px solid var(--grid); border-radius: var(--radius); padding:12px 14px; }
.stat-tile .val{ font-size:20px; font-weight:700; font-variant-numeric: tabular-nums; }
.stat-tile .lbl{ font-size:11.5px; color:var(--text-secondary); display:flex; align-items:center; gap:5px; margin-top:2px; }
.dot{ width:9px; height:9px; border-radius:50%; flex:0 0 auto; }
.dot.neutral{ background:var(--status-neutral); }
.dot.critical{ background:var(--status-critical); }
.dot.warning{ background:var(--status-warning); }
.dot.good{ background:var(--status-good); }
.dot.blue{ background:var(--series-blue); }

.stack-bar{ display:flex; height:14px; border-radius:7px; overflow:hidden; background:var(--grid); margin-bottom:8px; }
.stack-bar > div{ height:100%; }
.stack-bar > div + div{ margin-left:2px; }
/* Zones cliquables du chenillard (filtre des compétences par statut, voir renderSuiviStats /
   renderSuiviTab dans app.js) : léger effet au survol + anneau blanc sur le segment actif pour
   qu'il reste repérable même très fin (statut peu représenté). */
.stack-bar-seg{ cursor:pointer; transition:opacity .1s; }
.stack-bar-seg:hover{ opacity:.8; }
.stack-bar-seg.active{ box-shadow: inset 0 0 0 2px #fff, 0 0 0 2px rgba(0,0,0,.55); }
.stack-legend{ display:flex; flex-wrap:wrap; gap:12px; font-size:11.5px; color:var(--text-secondary); margin-bottom:16px; }
.stack-legend .item{ display:flex; align-items:center; gap:5px; cursor:pointer; padding:2px 6px; border-radius:20px; }
.stack-legend .item:hover{ background:var(--surface-1); }
.stack-legend .item.active{ background:var(--surface-2, #eef2f5); color:var(--text-primary); font-weight:700; }
.stack-legend .item.all{ font-weight:600; border-right:1px solid var(--grid); padding-right:12px; margin-right:2px; }
.suivi-filter-badge{ display:inline-flex; align-items:center; gap:4px; margin-left:10px; padding:2px 10px; border-radius:20px; font-size:11.5px; font-weight:600; background:var(--surface-1); border:1px solid var(--grid); cursor:pointer; vertical-align:middle; }
.suivi-filter-badge:hover{ background:var(--status-critical-bg); color:#9b2323; }
.suivi-filter-empty{ padding:16px; text-align:center; color:var(--text-secondary); font-size:13px; }

.status-pill{ display:inline-flex; align-items:center; gap:5px; padding:3px 9px; border-radius:20px; font-size:11.5px; font-weight:600; white-space:nowrap; }
.status-pill.neutral{ background:var(--status-neutral-bg); color:#5b5a56; }
.status-pill.critical{ background:var(--status-critical-bg); color:#9b2323; }
.status-pill.warning{ background:var(--status-warning-bg); color:#8a5b00; }
.status-pill.good{ background:var(--status-good-bg); color:#0a6e0a; }
/* #epn1-root en préfixe pour l'emporter sur "#epn1-root select{ font-size:14px; padding:9px 10px;
   width:100%; }" (règle générale des champs, plus spécifique qu'un simple ".status-pill") — sans
   ça, ce menu déroulant restait à la taille d'un champ de formulaire standard au lieu de la
   pastille compacte visée. "width:auto" + "flex:0 0 auto" l'empêchent aussi de s'étirer sur toute
   la largeur restante dans "entry-head" (flex). */
#epn1-root select.status-select{
  font-weight:600; font-size:11.5px; border-radius:20px; padding:3px 22px 3px 9px;
  width:auto; flex:0 0 auto;
}
/* Compétence "Acquis" verrouillée (voir renderSuiviTab() dans app.js) : on garde la pastille
   pleinement lisible (le grisé par défaut d'un <select disabled> l'aurait rendue terne, à
   l'opposé de l'effet "validé" recherché) et un curseur qui indique que ce n'est plus modifiable. */
#epn1-root select.status-select:disabled{
  opacity:1; -webkit-text-fill-color:inherit; cursor:not-allowed;
}

/* ---- prérequis (card-prerequis) ---- */
.prereq-list{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:7px; }
.prereq-row{ display:flex; align-items:center; gap:8px; font-size:12.5px; line-height:1.4; flex-wrap:wrap; }
.prereq-row b{ flex:0 0 auto; }
.prereq-note{ color:var(--text-secondary); }
.prereq-source{ font-size:11px; color:var(--text-muted); margin:10px 0 0; line-height:1.4; word-break:break-word; }

/* ---- card-based entries (journal + suivi), mobile-friendly ---- */
.entry-card{ border:1px solid var(--grid); border-radius:10px; padding:12px 14px; margin-bottom:10px; background:#fff; }
.entry-card .entry-head{ display:flex; align-items:center; justify-content:space-between; gap:8px; margin-bottom:8px; }
.entry-card .entry-group{ font-size:11px; text-transform:uppercase; letter-spacing:.02em; color:var(--text-muted); font-weight:700; }
.entry-card .entry-title{ font-size:13.5px; font-weight:600; }
.entry-card .entry-fields{ display:grid; grid-template-columns: 1fr 1fr; gap:8px 12px; }
.entry-card .entry-fields.single{ grid-template-columns: 1fr; }
@media (max-width:520px){ .entry-card .entry-fields{ grid-template-columns:1fr; } }
.entry-card .field{ margin-bottom:0; }
.entry-card .row-del{ flex:0 0 auto; }
/* Regroupe le titre "Séance N" et le bouton d'accès à sa fiche de proposition de séance (si
   fournie) pour que ce bouton reste collé au nom plutôt que plaqué à l'extrémité droite de la
   carte — le bouton supprimer garde sa place à droite grâce au space-between de .entry-head. */
.entry-title-wrap{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; }

/* Ouvre en plein écran la fiche de proposition de séance (voir "🖼️ Fiches de séance" et
   seanceFicheImage() dans app.js), juste à droite du nom de la séance : une petite miniature de
   l'image accolée au texte "Proposition de séance". Zone de clic généreuse (min-height 40px)
   pour rester facilement touchable au doigt sur téléphone. */
.entry-seance-fiche-btn{ flex:0 0 auto; display:flex; align-items:center; gap:6px; min-height:40px; font-size:13px; padding:6px 12px 6px 6px; border:1px solid var(--grid); color: var(--accent, #00b6c9); }
.entry-seance-fiche-thumb{ flex:0 0 auto; width:28px; height:28px; object-fit:cover; border-radius:5px; }

/* Emplacements des Adventure Dives PADI AOW/SSI AOWD (5) et ADV (3) — carte "Identité &
   formation", voir renderAowSpecialitesRow()/renderAdvSpecialitesRow() dans app.js et
   #row-aow-specialites/#row-adv-specialites dans app-shell.php. Grille à 3 colonnes fixes (plutôt
   qu'un simple flex-wrap) pour que les 5 emplacements AOW se répartissent toujours 3 sur la
   première ligne puis 2 sur la seconde, quelle que soit la largeur de la carte — demande explicite
   du club ; les 3 emplacements ADV tiennent alors sur une seule ligne. Repasse à 1 colonne sur
   téléphone (voir le @media plus bas), comme .grid2/.grid3/.grid4. Version 4.59.2 : la miniature de
   fiche de séance par emplacement a été retirée de cette carte (elle reste visible dans le Journal
   des séances et dans "🖼️ Fiches de séance" — voir renderJournalTab()/renderSeanceFormationRow()),
   sur demande du club. */
.aow-specialites-row{ display:grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap:10px 16px; margin:2px 0 16px; }
.aow-slot{ display:flex; align-items:center; gap:6px; min-width:0; }
.aow-slot-fixed{ background: var(--surface-2, #f4f3f0); border:1px solid var(--grid); border-radius:8px; padding:6px 10px; }
.aow-slot-label{ font-size:12.5px; color:var(--text-secondary); white-space:nowrap; }
.aow-slot-select{ flex:1 1 auto; min-width:0; }
.aow-slot-select:disabled{ opacity:.65; cursor:not-allowed; }
.aow-slot-locked{ font-size:13px; cursor:help; }
.aow-slot .aow-fiche-btn{ padding:2px; border:none; }
@media (max-width: 640px){ .aow-specialites-row{ grid-template-columns:1fr; } }

.helptext{ font-size:12px; color:var(--text-muted); margin-top:6px; }
.banner{ background:#E6F8FA; border:1px solid #BFEDF2; color:#067885; border-radius:8px; padding:10px 14px; font-size:12.5px; margin-bottom:16px; }

/* container-type:inline-size fait de .table-wrap une "container query" : le bascule­ment en
   cartes empilées ci-dessous réagit à la largeur RÉELLEMENT DISPONIBLE pour ce tableau (celle
   de sa carte, une fois la nav/barre latérale déduite), pas à la largeur de toute la fenêtre.
   Sans ça, un tableau à colonnes fixes (catalogues de tarifs notamment) pouvait rester coincé
   en mode "tableau classique" avec un ascenseur horizontal gênant sur une fenêtre pourtant pas
   si étroite (ex. 800px), simplement parce que 800px > 640px alors que la carte, elle, n'offre
   que 550px de large une fois la nav retirée. */
.table-wrap{ overflow-x:auto; container-type: inline-size; }
table{ width:100%; border-collapse: collapse; font-size:12.5px; }
/* Sans ceci, une table dimensionne ses colonnes selon leur CONTENU (table-layout:auto, la valeur
   par défaut) : une URL longue tapée dans "Lien PDF" (⚙️ Paramètres → catalogue des formations)
   revendique alors toute la place qu'elle veut et écrase les largeurs pourtant fixées en px sur
   "Plongées"/"Coût (€)", qui se retrouvent rognées. table-layout:fixed force le navigateur à
   respecter les largeurs déclarées sur <th> ; l'URL, elle, défile simplement dans son propre champ
   au lieu de déborder sur le reste de la ligne. */
.fixed-cols-table{ table-layout:fixed; }
.fixed-cols-table td input, .fixed-cols-table td select{ width:100%; box-sizing:border-box; min-width:0; }
/* LA vraie cause de l'ascenseur horizontal des tableaux de tarifs (catalogues FFESSM/ANMP/
   PADI/SSI) : le bouton "🗑️" hérite du padding standard de tout bouton de l'outil (9px 13px,
   voir #epn1-root button plus haut) — largeur naturelle ~50px — alors que sa colonne ne fait
   que 32px de large (<th style="width:32px;"> dans app-shell.php). En table-layout:fixed, cette
   largeur de colonne est imposée mais RIEN n'empêche le contenu d'un <td> de déborder dessus :
   le bouton débordait donc sur ~18px, à TOUTE largeur de fenêtre — ce n'était pas un problème de
   fenêtre trop étroite, le bouton ne rentrait tout simplement jamais dans sa colonne. */
.fixed-cols-table td.catalog-row-actions{ padding:4px 2px; text-align:center; }
.fixed-cols-table td.catalog-row-actions button{ width:100%; min-width:0; box-sizing:border-box; padding:4px 0; font-size:13px; line-height:1; }

/* Poignée de glisser-déposer (⠿) pour réordonner les formations d'un catalogue — voir
   wireCatalogRowDrag() dans app.js. Un <span>, jamais un <button> : pas de padding standard de
   bouton à contenir dans une colonne étroite (voir le commentaire juste au-dessus sur la vraie
   cause de l'ancien ascenseur horizontal — même piège à éviter ici). touch-action:none UNIQUEMENT
   sur la poignée, pas sur toute la ligne : le reste de la carte/ligne garde un défilement tactile
   normal. */
.fixed-cols-table td.catalog-row-drag{ padding:4px 2px; text-align:center; width:28px; }
.drag-handle{ display:inline-flex; align-items:center; justify-content:center; width:100%; min-height:32px; box-sizing:border-box; touch-action:none; cursor:grab; color:var(--text-muted); font-size:16px; user-select:none; -webkit-user-select:none; }
.drag-handle:active{ cursor:grabbing; }
tr.row-dragging{ opacity:.5; background:var(--surface-2, #f4f3f0); }

/* ---- Moniteurs (Administration Progrezio) — une CARTE par moniteur plutôt qu'une ligne de
   tableau : Nom/Prénom/FFESSM/PADI/SSI/ANMP/Actions ne tiennent pas sur une seule ligne sans
   écraser au moins une colonne (ANMP a fini invisible avec l'ancien tableau à largeurs fixes) ou
   sans imposer un ascenseur horizontal peu pratique sur ordinateur. Une carte flex-wrap se
   redistribue naturellement sur plusieurs lignes selon la largeur de l'écran, sans jamais rogner
   ni cacher un champ — même principe que .entry-card (journal/suivi), réutilisé ici. */
.admin-moniteur-card{ border:1px solid var(--grid); border-radius:10px; padding:14px 16px; margin-bottom:12px; background:#fff; }
/* Compte technique (m.isHidden, voir is_hidden côté PHP/EPN1_DB::install()) — toujours en dernier
   dans la liste (voir renderAdminMoniteursList() dans app.js), visiblement grisé/désactivé plutôt
   que masqué : l'Administratif doit voir que ce compte existe (transparence envers le club), mais
   comprendre au premier coup d'œil qu'il n'est pas modifiable ici (voir aussi le bandeau
   .am-hidden-badge ci-dessous) — la vraie barrière reste le rejet serveur sur les routes PUT/DELETE
   /moniteurs/{id} correspondantes, ceci n'est qu'un signal visuel cohérent avec ce rejet. */
.admin-moniteur-card--hidden{ opacity:.6; background:var(--page); filter:grayscale(.4); }
.admin-moniteur-card--hidden .am-hidden-badge{ font-size:12px; font-weight:600; color:var(--text-secondary); margin-bottom:10px; }
.admin-moniteur-card .am-top-row{ display:flex; flex-wrap:wrap; gap:12px; align-items:flex-end; }
.admin-moniteur-card .am-names{ display:flex; flex-wrap:wrap; gap:12px; flex:1 1 280px; }
.admin-moniteur-card .am-names .field{ flex:1 1 130px; min-width:120px; margin-bottom:0; }
/* Sous le Nom/Prénom (et non plus à côté, où le switch se retrouvait écrasé contre les champs
   sur les largeurs de carte les plus courantes) — voir am-top-row ci-dessus. Le sélecteur doit
   être préfixé par #epn1-root : la règle générique "#epn1-root label{ display:block; … }" (voir
   plus haut dans ce fichier) l'emportait sinon sur notre "display:flex" — un id bat toujours deux
   classes en spécificité CSS — ce qui effondrait la mise en page du switch (positionnement absolu
   pensé pour un conteneur flex) et faisait apparaître son curseur rond par-dessus le texte. */
/* am-roles-row : Administratif et Moniteur sont deux cases indépendantes (voir
   renderAdminMoniteursList() dans app.js) — côte à côte plutôt qu'empilées, comme les autres
   rangées de cette carte (am-quals-row, am-actions-row). */
.admin-moniteur-card .am-roles-row{ display:flex; flex-wrap:wrap; gap:16px; }
#epn1-root .admin-moniteur-card .am-admin-toggle{ display:flex; align-items:center; gap:8px; font-size:12.5px; color:var(--text-secondary); font-weight:600; white-space:nowrap; margin-top:10px; }
.admin-moniteur-card .am-quals-row{ display:flex; flex-wrap:wrap; gap:14px; margin-top:14px; padding-top:14px; border-top:1px dashed var(--grid); }
.admin-moniteur-card .am-quals-row .field{ flex:1 1 150px; min-width:150px; margin-bottom:0; }
.admin-moniteur-card .am-org-field label{ display:flex; align-items:center; gap:6px; font-size:12.5px; color:var(--text-primary); font-weight:600; margin-bottom:4px; }
.admin-moniteur-card .am-org-field input[type="text"]{ width:100%; box-sizing:border-box; }
.admin-moniteur-card .am-actions-row{ display:flex; flex-wrap:wrap; gap:8px; align-items:center; margin-top:14px; padding-top:14px; border-top:1px dashed var(--grid); }
.admin-moniteur-card .am-actions-row input[type="password"]{ flex:1 1 150px; min-width:120px; }
/* La case "Lien PDF" reste volontairement étroite (120px) : on ne lit jamais l'URL entière au repos,
   seulement de quoi confirmer qu'un lien est bien renseigné — un clic dedans permet de la parcourir
   au clavier. L'ellipse évite une troncature brutale en plein milieu d'un mot. */
.fixed-cols-table td input{ text-overflow:ellipsis; }
th{ text-align:left; font-size:11px; text-transform:uppercase; letter-spacing:.02em; color:var(--text-muted); font-weight:600; padding:6px 8px; border-bottom:1px solid var(--baseline); }
td{ padding:5px 8px; border-bottom:1px solid var(--grid); vertical-align:top; }
tr:last-child td{ border-bottom:none; }
td input, td select{ border-color: transparent; background:transparent; padding:4px 5px; }
td input:hover, td select:hover{ border-color: var(--grid); background:#fff; }
td input:focus, td select:focus{ border-color: var(--series-blue); background:#fff; outline:none; }
.group-cell{ font-weight:600; color:var(--text-secondary); }

.modal-backdrop{ position:fixed; inset:0; background:rgba(11,11,11,.45); display:none; align-items:center; justify-content:center; z-index:1000; padding:16px; }
.modal-backdrop.open{ display:flex; }
.modal{ background:#fff; border-radius:12px; max-width:640px; width:100%; max-height:85vh; overflow-y:auto; padding:22px 24px; }
.modal h2{ font-size:16px; margin-bottom:14px; }
.modal-close{ float:right; }

.modal.photo-modal{ max-width:min(90vw, 640px); text-align:center; }
.modal.photo-modal img{ display:block; max-width:100%; max-height:70vh; margin:8px auto 0; border-radius:8px; }
.photo-modal-actions{ margin-top:14px; }
/* ---- "Mode d'emploi" — full guide, laid out like the app itself: a fixed
   vertical nav with the Progrezio brand, and a scrollable content column.
   ============================================================ */
.modal.help-shell{
  max-width:1100px; width:95vw; height:88vh; max-height:88vh;
  padding:0; display:flex; overflow:hidden; position:relative;
}
.modal.help-shell .modal-close{ position:absolute; top:12px; right:14px; z-index:2; float:none; }

.help-nav{
  width:250px; flex:0 0 250px; background:var(--page);
  border-right:1px solid var(--grid); display:flex; flex-direction:column;
  overflow-y:auto; padding:18px 14px;
}
.help-brand{ display:flex; align-items:center; gap:7px; padding-bottom:14px; margin-bottom:10px; border-bottom:1px solid var(--grid); }
.help-brand-name{ font-size:15px; font-weight:800; letter-spacing:-0.02em; color:var(--brand-ink); }
.help-brand-sub{ font-size:10.5px; color:var(--text-muted); margin-top:1px; }
.help-navlabel{ font-size:10px; font-weight:700; letter-spacing:.08em; text-transform:uppercase; color:var(--text-muted); margin:4px 0 6px; }
.help-nav nav{ display:flex; flex-direction:column; gap:2px; }
.help-nav nav a{ display:block; padding:7px 9px; border-radius:7px; font-size:12px; color:var(--text-secondary); text-decoration:none; border:1px solid transparent; }
.help-nav nav a:hover{ background:#f0efec; }
.help-nav nav a.active{ background:#E6F8FA; border-color:var(--series-blue); color:var(--series-blue-d); font-weight:600; }
.help-nav-foot{ margin-top:auto; padding-top:14px; font-size:10.5px; color:var(--text-muted); line-height:1.4; }

.help-content{ position:relative; flex:1; min-width:0; overflow-y:auto; padding:30px 34px 60vh; }
.help-content section{ max-width:680px; padding:26px 0; border-top:1px solid var(--grid); scroll-margin-top:16px; }
.help-content section:first-of-type{ border-top:none; padding-top:0; }
.help-content .sec-num{ font-size:10.5px; font-weight:700; color:var(--brand-aqua); letter-spacing:.04em; }
.help-content h3.mh-section{ font-size:17px; font-weight:700; letter-spacing:-0.01em; color:var(--text-primary); margin:2px 0 12px; }
.help-content h4.mh-sub{ font-size:12.5px; margin:16px 0 6px; color:var(--text-secondary); }
.help-content ul.mh-list{ margin:0 0 10px; padding:0; list-style:none; display:flex; flex-direction:column; gap:6px; }
.help-content ul.mh-list li{ font-size:12.5px; line-height:1.5; }
.help-content ul.mh-list b{ font-weight:700; }
.help-content .callout{ background:var(--surface-1); border:1px solid var(--grid); border-left:3px solid var(--brand-aqua); border-radius:8px; padding:11px 13px; font-size:12.5px; margin:0 0 12px; }
.help-content .callout b{ color:var(--brand-ink); }
/* Texte justifié pour le mode d'emploi (paragraphes de section, listes, encadrés) et le rappel
   rapide — "text-align-last:left" évite qu'une dernière ligne courte (souvent le cas des <li>,
   qui tiennent parfois sur une seule ligne) ne s'étire de façon disgracieuse d'un bord à l'autre.
   Les cellules de tableau (<td>) sont volontairement exclues : sur une colonne étroite, quelques
   mots par ligne suffisent à créer de grands "couloirs" blancs disgracieux une fois justifiés. */
.help-content p, .help-content li, .help-content .callout,
#modal-rappel p { text-align: justify; text-align-last: left; }

@media (max-width: 760px){
  .modal.help-shell{ flex-direction:column; width:96vw; height:92vh; }
  .help-nav{ width:100%; flex:0 0 auto; max-height:150px; border-right:none; border-bottom:1px solid var(--grid); }
}

footer.epn1-foot{ font-size:11px; color:var(--text-muted); text-align:center; padding:10px; }

/* ---- date range ("du … au …") ---- */
.date-range{ display:flex; align-items:center; gap:6px; }
.date-range input[type=date]{ flex:1 1 0; min-width:0; }
.date-range-lbl{ font-size:12px; color:var(--text-secondary); flex:0 0 auto; }

/* ---- indicatif pays + numéro ("Téléphone") ---- */
.tel-group{ display:flex; align-items:center; gap:6px; }
.tel-group select{ flex:0 0 auto; max-width:132px; }
.tel-group input[type=tel]{ flex:1 1 0; min-width:0; }

/* Ligne "Téléphone / Date de naissance / Lieu de naissance" (card-coordonnees) : la date de
   naissance a toujours le même format (jj/mm/aaaa), donc pas besoin d'une colonne aussi large
   que les autres ; on redonne cette place au téléphone (indicatif + numéro complet visible). */
.grid3.grid-coordonnees-tel{ grid-template-columns: 1.7fr 130px 1fr; }
@media (max-width: 640px){ .grid3.grid-coordonnees-tel{ grid-template-columns:1fr; } }

/* ---- collapsible "détails" (suivi rows: date / moniteur / observation) ---- */
.entry-details-toggle{ background:none; border:none; padding:0; font-size:12px; color:var(--accent, #00b6c9); cursor:pointer; margin-bottom:0; }
.entry-details-toggle:hover{ text-decoration:underline; }
.entry-details{ display:none; margin-top:8px; }
.entry-details.open{ display:block; }
.entry-details .entry-fields{ margin-top:8px; }

/* ---- Paramètres → Prérequis admin list ---- */
.prereq-admin-list{ max-height:420px; overflow-y:auto; border:1px solid var(--grid); border-radius:10px; padding:8px; }
.prereq-admin-card{ border:1px solid var(--grid); border-radius:8px; padding:8px 10px; margin-bottom:8px; background:#fff; }
.prereq-admin-card:last-child{ margin-bottom:0; }
.prereq-admin-head{ display:flex; align-items:center; justify-content:space-between; gap:8px; cursor:pointer; }
.prereq-admin-name{ font-size:13px; font-weight:600; }
.prereq-admin-badges{ display:flex; gap:6px; flex-wrap:wrap; }
.prereq-admin-fields{ display:none; grid-template-columns:1fr 1fr; gap:8px 12px; margin-top:10px; }
.prereq-admin-fields.open{ display:grid; }
.prereq-admin-fields .field{ margin-bottom:0; }
@media (max-width:520px){ .prereq-admin-fields{ grid-template-columns:1fr; } }

/* ---- responsive tables (Administration Progrezio : Moniteurs, Historique, catalogues de
   tarifs) ----
   Sous 640px DE LARGEUR DE CONTENEUR (voir container-type sur .table-wrap ci-dessus — pas
   640px de fenêtre), la table classique (colonnes serrées) devient illisible ou impose un
   ascenseur horizontal : on la transforme en une liste de cartes empilées, une carte par
   ligne, avec le nom de colonne affiché devant chaque valeur (via data-label posé côté JS). */
@container (max-width: 640px){
  table.responsive-table thead{ display:none; }
  table.responsive-table, table.responsive-table tbody, table.responsive-table tr, table.responsive-table td{ display:block; width:100%; }
  /* Neutralise le min-width posé pour le mode tableau classique (voir #admin-moniteurs-table
     ci-dessus) — en carte empilée, chaque champ prend déjà toute la largeur disponible tout
     seul, un min-width figé forcerait un défilement horizontal inutile sur téléphone. */
  table.responsive-table{ min-width:0; }
  /* box-sizing:border-box est indispensable ici : la ligne a width:100% ET un padding
     horizontal (8px 10px) — sans border-box, ce padding s'ajoute à la largeur au lieu
     d'être prélevé dessus, et chaque carte déborde de 20px hors de son conteneur (c'était
     la cause d'un ascenseur horizontal résiduel, même une fois la table passée en cartes). */
  table.responsive-table tr{ box-sizing:border-box; border:1px solid var(--grid); border-radius:10px; padding:8px 10px; margin-bottom:10px; background:#fff; }
  table.responsive-table tr:last-child{ margin-bottom:0; }
  table.responsive-table td{ border-bottom:none; padding:6px 0; }
  table.responsive-table td:not(:last-child){ border-bottom:1px dashed var(--grid); }
  table.responsive-table td::before{ content:attr(data-label); display:block; font-size:10.5px; font-weight:700; text-transform:uppercase; letter-spacing:.03em; color:var(--text-muted); margin-bottom:4px; }
  /* Nom/Prénom : le libellé passe AU-DESSUS du champ (au lieu d'à côté) pour que le champ
     garde toute la largeur de la carte — sinon un nom long reste invisible dans un champ
     trop étroit, coincé à côté de son étiquette. */
  table.responsive-table td input[type="text"], table.responsive-table td input[type="password"], table.responsive-table td select{ display:block; width:100%; box-sizing:border-box; border-color:var(--grid); background:#fff; }
  /* Administratif / Actions : contenu compact, le libellé peut rester à côté. */
  table.responsive-table td:has(.switch){ display:flex; align-items:center; gap:8px; }
  table.responsive-table td:has(.switch)::before{ margin-bottom:0; }
  table.responsive-table td.admin-mon-actions{ display:flex; flex-wrap:wrap; gap:8px; align-items:center; }
  table.responsive-table td.admin-mon-actions::before{ flex:0 0 100%; margin-bottom:2px; }
  table.responsive-table td.admin-mon-actions input[type="password"]{ width:auto; flex:1 1 130px; min-width:0; }
  /* Catalogues de tarifs (⚙️ Paramètres des formations — voir renderCatalogTable() dans app.js) :
     dernière colonne "🗑️" sans data-label (rien à nommer, un bouton se suffit à lui-même) — pas
     d'étiquette vide au-dessus. Le bouton passe pleine largeur et son texte "Supprimer" (masqué en
     table classique, voir .catalog-row-actions-label plus bas) redevient visible, plus confortable
     à toucher au doigt qu'une icône seule une fois la ligne empilée en carte. */
  table.responsive-table td.catalog-row-actions::before{ content:none; margin-bottom:0; }
  table.responsive-table td.catalog-row-actions button{ width:100%; }
  table.responsive-table .catalog-row-actions-label{ display:inline; }
  /* Poignée "⠿" (voir wireCatalogRowDrag() dans app.js) : pas d'étiquette au-dessus (rien à
     nommer), plus grande et plus haute qu'en tableau classique pour rester facilement saisissable
     au doigt une fois la ligne empilée en carte. */
  table.responsive-table td.catalog-row-drag{ padding:0 0 6px; }
  table.responsive-table td.catalog-row-drag::before{ content:none; }
  table.responsive-table .drag-handle{ min-height:40px; font-size:20px; }
}
/* Bouton "🗑️" des catalogues de tarifs : icône seule en tableau classique (colonne étroite, voir
   <th style="width:32px;"> dans app-shell.php) — le texte "Supprimer" n'apparaît qu'en carte
   empilée sous 640px (voir .catalog-row-actions-label ci-dessus). */
.catalog-row-actions-label{ display:none; }

/* ---- mobile layout ---- */
@media (max-width: 860px){
  #btn-sidebar-toggle{ display:block; }
  #app{ min-height: 80vh; }
  #sidebar{
    position:fixed; top:0; left:0; bottom:0; z-index:20; width:82vw; max-width:320px;
    transform: translateX(-100%); transition: transform .2s ease; box-shadow: 2px 0 16px rgba(0,0,0,.2);
  }
  #app.sidebar-open #sidebar{ transform: translateX(0); }
  #app.sidebar-open #sidebar-scrim{ display:block; }
  #fiche-topbar{ padding-left:52px; }
  #tab-content{ padding: 14px 12px 60px; }

  /* ---- Sidebar mobile plus compacte : sur téléphone, en Administratif, les 6 boutons
     d'action + le tri/filtre + le pied de page (export/import/déconnexion) empilés sur une
     seule colonne ne laissaient plus qu'1 ou 2 fiches visibles dans #fiche-list avant de devoir
     défiler. Regroupés en grilles à 2 colonnes ci-dessous (DOM inchangé, donc aucun impact sur
     les tests qui ciblent ces boutons par id/classe) : ça libère largement plus de hauteur pour
     la liste, qui n'est de toute façon plus bornée par le max-height 40vh hérité du desktop
     (pensé pour éviter une sidebar plus haute que la fiche à côté — non pertinent ici, la
     sidebar mobile est un tiroir plein écran). ---- */
  #fiche-list{ max-height:none; }
  #sidebar-actions{
    display:grid; grid-template-columns:1fr 1fr; gap:5px 8px; padding:8px 12px;
  }
  #sidebar-actions #btn-new-fiche{ grid-column: 1 / -1; }
  #sidebar-actions button{
    padding:8px 6px; font-size:11.5px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  }
  #search{ display:grid; grid-template-columns:1fr 1fr; gap:5px 8px; padding:8px 12px 5px; }
  #search #search-input{ grid-column: 1 / -1; }
  #search select{ margin-top:0; font-size:12.5px; padding:8px 6px; }
  #sidebar-footer{ padding:8px 12px; gap:5px; }
  #sidebar-footer #btn-export-all, #sidebar-footer #btn-import-json{
    display:inline-flex; align-items:center; justify-content:center;
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis; font-size:11.5px; padding:8px 6px;
  }
  #sidebar-footer{ display:grid; grid-template-columns:1fr 1fr; grid-template-areas:"whoami whoami" "online online" "export import" "logout logout"; }
  #whoami{ grid-area:whoami; padding-bottom:0; }
  #online-now{ grid-area:online; padding-bottom:0; }
  #btn-export-all{ grid-area:export; }
  #btn-import-json{ grid-area:import; }
  #btn-logout{ grid-area:logout; }
  /* Sur smartphone, "Suivi" est consulté bien plus souvent que "Journal des séances" en usage
     courant (sur le terrain) : on l'avance juste après "Fiche & suivi admin". Uniquement l'ordre
     visuel des onglets change (propriété flex "order") — le DOM et le data-tab ciblé par le JS
     restent identiques, donc aucun impact sur la logique d'affichage des panneaux. */
  #tabs{ display:flex; }
  #tabs .tab-btn[data-tab="tab-fiche"]{ order:1; }
  #tabs .tab-btn[data-tab="tab-suivi"]{ order:2; }
  /* "Résumé" juste après "Suivi" (order:3), le reste décalé d'un cran — voir aussi le nouvel
     onglet lui-même, #tab-resume ci-dessous. */
  #tabs .tab-btn[data-tab="tab-resume"]{ order:3; }
  #tabs .tab-btn[data-tab="tab-journal"]{ order:4; }
  #tabs .tab-btn[data-tab="tab-recap"]{ order:5; }
}

/* En-tête d'impression (logo + nom du club) : invisible à l'écran, réservée au PDF/impression —
   voir #print-letterhead dans app-shell.php. */
#print-letterhead{ display:none; }

/* ---- Onglet "Résumé" — vue d'ensemble en un coup d'œil (jauge + 3 catégories), pensée pour
   reprendre exactement le vocabulaire visuel de la fiche PDF envoyée par mail après chaque séance
   (voir class-epn1-resume-pdf.php et renderResumeTab() dans app.js) : mêmes couleurs, même jauge
   circulaire — construits une fois, utilisés aux deux endroits. Contrairement à "Suivi" (grille
   complète des 4 statuts, exhaustive), "Résumé" simplifie volontairement à 3 catégories
   (Validées / En cours / À revoir, "À revoir" regroupant "Pas abordé" + "Non acquis") pour rester
   lisible d'un coup d'œil ; le détail précis reste dans "Suivi", juste à côté. */
.resume-gauge-card{ display:flex; align-items:center; gap:26px; }
.resume-gauge-ring{ position:relative; width:112px; height:112px; flex:none; }
.resume-gauge-ring svg{ transform:rotate(-90deg); }
.resume-gauge-ring .resume-gauge-pct{
  position:absolute; inset:0; display:flex; flex-direction:column; align-items:center; justify-content:center;
}
.resume-gauge-ring .resume-gauge-pct .val{ font-size:26px; font-weight:800; color:var(--brand-ink); line-height:1; }
.resume-gauge-ring .resume-gauge-pct .lbl{ font-size:10px; color:var(--text-muted); margin-top:2px; }
/* flex-wrap : sur mobile (largeur d'écran étroite), les 3 items ("Validée"/"En cours"/"À revoir")
   ne tiennent pas sur une seule ligne à côté du texte d'intro qui les précède — sans wrap, le
   dernier item ("À revoir") débordait hors de l'écran et se retrouvait coupé. */
.resume-legend{ display:flex; flex-wrap:wrap; gap:8px 20px; margin-top:12px; }
.resume-legend .item{ display:flex; align-items:center; gap:7px; font-size:12.5px; color:var(--text-secondary); }
.resume-legend .dot{ width:11px; height:11px; }
.resume-section{ border-radius:var(--radius); padding:14px 18px; margin-bottom:12px; }
.resume-section.good{ background:var(--status-good-bg); }
.resume-section.warning{ background:var(--status-warning-bg); }
.resume-section.neutral{ background:var(--status-neutral-bg); }
.resume-section-head{ display:flex; align-items:center; gap:8px; margin-bottom:10px; }
.resume-section-head .title{ font-size:14px; font-weight:700; }
.resume-section.good .title{ color:#0a6e0a; }
.resume-section.warning .title{ color:#8a5b00; }
.resume-section.neutral .title{ color:#5b5a56; }
.resume-section-head .count{ font-size:11.5px; font-weight:700; background:#fff; padding:2px 8px; border-radius:10px; margin-left:auto; }
.resume-section.good .count{ color:#0a6e0a; }
.resume-section.warning .count{ color:#8a5b00; }
.resume-section.neutral .count{ color:#5b5a56; }
.resume-item-row{ display:flex; align-items:center; gap:9px; font-size:13px; padding:3px 0; }
.resume-item-row .marker{ width:8px; height:8px; border-radius:50%; flex:none; }
.resume-section.good .resume-item-row{ color:var(--text-primary); }
.resume-section.warning .resume-item-row .marker{ background:var(--status-warning); }
.resume-section.neutral .resume-item-row .marker{ border:1.5px dashed #b9b7b0; background:transparent; }
.resume-item-more{ font-size:12px; color:var(--text-muted); font-style:italic; margin-top:2px; }
.resume-empty{ padding:20px; text-align:center; color:var(--text-secondary); font-size:13px; }

@media print{
  * { -webkit-print-color-adjust: exact !important; print-color-adjust: exact !important; color-adjust: exact !important; }
  #epn1-login-screen, #btn-sidebar-toggle, #sidebar, #tabs, #fiche-topbar .actions, #admin-page, #params-page, #seance-fiches-page, #seance-fiche-viewer{ display:none !important; }
  .no-print{ display:none !important; }
  /* La coquille de l'appli (#app/#main/#fiche-view/#tab-content) est un empilement flexbox avec
     défilement interne (#tab-content{overflow-y:auto}) pour garder la barre latérale/l'en-tête
     fixes à l'écran — nécessaire en usage normal, mais un vrai problème à l'impression : les
     unités "vh" s'y calculent par rapport à UNE page imprimée (pas à l'écran), donc
     "#app{min-height:70vh}" ne fait plus qu'une fraction de page, et son "overflow:hidden" coupe
     alors tout le reste (compétences, synthèse…) au lieu de le renvoyer sur les pages suivantes —
     symptôme observé : une seule carte visible, suivie d'un grand vide. On repasse donc toute la
     coquille en flux normal (bloc, hauteur automatique, pas de recadrage) le temps de l'impression.
  */
  #app, #main, #fiche-view, #tab-content{
    display:block !important; overflow:visible !important;
    height:auto !important; min-height:0 !important; max-height:none !important;
  }
  #app{ border:none !important; border-radius:0 !important; }
  #tab-content{ padding:0 !important; }
  .panel{ display:block !important; max-width:none; page-break-after: always; break-after: page; }
  .panel:last-of-type{ page-break-after: auto !important; break-after: auto !important; }
  /* Les compétences repliées (▸ Date & moniteur) et un éventuel filtre "Suivi" actif ne doivent
     jamais faire disparaître d'information à l'impression : on force tout à s'ouvrir et on
     masque le bouton de repli lui-même (voir aussi le clic sur #btn-print dans app.js, qui lève
     temporairement suiviFilter avant window.print()). */
  .entry-details{ display:block !important; }
  .entry-details-toggle{ display:none !important; }
  .suivi-filter-badge, .suivi-filter-empty{ display:none !important; }
  .card, .entry-card, .stat-tile{ page-break-inside: avoid; break-inside: avoid; }
  #print-letterhead{
    display:flex !important; align-items:center; gap:14px;
    padding-bottom:14px; margin-bottom:16px; border-bottom:2px solid #000;
  }
  #print-letterhead .club-logo-img{ height:52px; width:auto; max-width:180px; object-fit:contain; }
  #print-letterhead .club-name-text{ font-size:19px; font-weight:700; color:#000; }
}
