/* =========================================================================
   LANGAGE VISUEL SESAME — repris de SesameCaisse sans y toucher.

   Le bloc :root ci-dessous est celui de SesameCaisse, à l'identique :
   palette prélevée sur le logo, pile système, base d'espacement 4, trois
   durées, deux courbes. Les deux applications servent le même gérant pour
   le même travail ; elles doivent parler la même langue.

   Aucune valeur de couleur, d'espacement ou de durée ne doit apparaître en
   dur hors de :root.

   DEUX DIVERGENCES ASSUMÉES avec SesameCaisse, et une seule raison aux
   deux : SesameCaisse est un kiosque iPad qu'un serveur touche debout,
   SesameAuto est un outil de bureau qu'un gérant lit assis.
     1. Les verrous tactiles iOS sautent (voir body) — ici on sélectionne
        et on copie du texte.
     2. Le défilement est autorisé, mais DANS .contenu seulement — la barre
        RETOUR / CONTINUER ne doit jamais partir hors de l'écran.
   ========================================================================= */
:root {
  /* -- Palette. Fond et encre prélevés sur le logo (JPEG échantillonné). -- */
  --fond: #191919;                        /* noir du logo, 83 % des pixels  */
  --encre: #FFFBDB;                       /* ivoire du logo                 */
  --encre-2: rgb(255 251 219 / .55);      /* texte secondaire               */
  --encre-3: rgb(255 251 219 / .30);      /* texte discret (étiquettes)     */
  --touche: rgb(255 251 219 / .07);       /* surface au repos               */
  --touche-bord: rgb(255 251 219 / .12);
  --touche-active: rgb(255 251 219 / .16);
  --accent: #E0873F;                      /* SEULE couleur d'état           */

  /* -- Typographie. ------------------------------------------------------ */
  --police: -apple-system, system-ui, "Helvetica Neue", "Segoe UI", sans-serif;
  --corps-touche: 34px;
  --corps-titre: 22px;                    /* titres d'écran, graisse 300    */
  --corps-texte: 17px;                    /* jamais sous 16px sur un input  */
  --corps-petit: 14px;                    /* étiquettes, méta               */

  /* -- Espacements, base 4. ---------------------------------------------- */
  --e1: 4px;  --e2: 8px;  --e3: 12px;  --e4: 16px;
  --e6: 24px; --e8: 32px; --e12: 48px; --e16: 64px;

  /* -- Mouvement : trois durées, deux courbes. --------------------------- */
  --t-vif: 120ms;      /* enfoncement d'une touche                          */
  --t-moyen: 240ms;    /* remplissage d'un point, retour au repos           */
  --t-lent: 400ms;     /* changement d'écran, secousse                      */
  --sortie: cubic-bezier(0.2, 0, 0, 1);        /* décélération standard     */
  --rebond: cubic-bezier(0.34, 1.3, 0.64, 1);  /* léger dépassement,
                                                  réservé aux points        */

  /* -- Géométrie. --------------------------------------------------------- */
  --point-diametre: 13px;
  --glisse: 24px;      /* amplitude du glissement entre écrans              */
}

* { margin: 0; padding: 0; box-sizing: border-box; }

/* L'attribut hidden ne vaut que `display: none` dans la feuille du
   navigateur : la moindre règle d'auteur posant un display le neutralise
   en silence. Sans ceci, un `.champ { display: flex }` rend visible un
   champ que le JS croit avoir masqué. */
[hidden] { display: none !important; }

html, body {
  height: 100%;
  overflow: hidden;                       /* le défilement vit dans .contenu */
  overscroll-behavior: none;
}

body {
  position: fixed;                        /* l'écran est un cadre fixe      */
  inset: 0;
  background: var(--fond);
  color: var(--encre);
  font-family: var(--police);
  font-size: var(--corps-texte);
  font-variant-numeric: tabular-nums;
  /* DIVERGENCE 1 — SesameCaisse pose ici user-select: none,
     -webkit-touch-callout: none et touch-action: manipulation. C'est bon
     pour un kiosque qu'on tape du doigt ; ici le gérant doit pouvoir
     sélectionner un nom d'employé pour le coller ailleurs. */
  padding: env(safe-area-inset-top) env(safe-area-inset-right)
           env(safe-area-inset-bottom) env(safe-area-inset-left);
  /* Fait rendre au navigateur ses propres commandes — sélecteur de date,
     barres de défilement — en sombre. Sans cela, le calendrier natif
     s'ouvre en blanc éclatant au milieu du noir. */
  color-scheme: dark;
}

/* ================================================================ écrans
   Transitions directionnelles : l'écran entrant glisse de --glisse depuis
   la droite en avançant dans le parcours, depuis la gauche en revenant.
   Le JS pose .inverse sur un écran pour retourner son côté de repos. */
.ecran {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--e8);
  padding: var(--e8);
  opacity: 0;
  visibility: hidden;
  transform: translateX(var(--glisse));
  transition: opacity var(--t-lent) var(--sortie),
              transform var(--t-lent) var(--sortie),
              visibility 0s linear var(--t-lent);
}
.ecran.inverse { transform: translateX(calc(-1 * var(--glisse))); }
.ecran.visible {
  opacity: 1;
  visibility: visible;
  transform: translateX(0);
  transition: opacity var(--t-lent) var(--sortie),
              transform var(--t-lent) var(--sortie);
}

/* Écran d'étape : titre en haut, contenu au centre, navigation en bas. */
.ecran-etape {
  justify-content: flex-start;
  gap: var(--e6);
}
/* DIVERGENCE 2 — `overflow-y: auto` et `min-height: 0` : c'est ici, et
   nulle part ailleurs, que la page défile. La revue listera trente
   employés ; le titre au-dessus et la barre de navigation en dessous
   doivent rester en place pendant qu'on la parcourt. Même modèle que
   l'écran gérant de SesameCaisse. */
.ecran-etape .contenu {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  width: 100%;
  max-width: 900px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--e6);
  /* `safe` n'est pas un ornement. Un contenu centré qui déborde de son
     conteneur déborde des DEUX côtés : le haut passe au-dessus du point de
     défilement zéro, et rien ne permet d'y remonter — sur l'écran de
     vérification, le sous-titre et la première rangée devenaient
     inatteignables. `safe center` centre tant que ça tient, et retombe sur
     un alignement en haut dès que ça déborde. La ligne sans `safe` reste
     pour les navigateurs qui ignorent le mot-clé : ils la gardent, les
     autres la remplacent. */
  justify-content: center;
  justify-content: safe center;
  padding-block: var(--e2);
}

.titre-ecran {
  font-size: var(--corps-titre);
  font-weight: 300;
  letter-spacing: .08em;
  color: var(--encre);
  text-align: center;
}
.sous-titre {
  font-size: var(--corps-texte);
  font-weight: 300;
  color: var(--encre-2);
  text-align: center;
  max-width: 60ch;
  line-height: 1.5;
}

/* ================================================================== logo */
.logo {
  height: 30px;
  width: auto;
  color: var(--encre);
  margin-bottom: var(--e6);
}
/* Sur le portail, le logo est la seule chose à regarder : il respire. */
.ecran-portail .logo {
  height: 42px;
  margin-bottom: var(--e2);
}
/* En haut d'une étape, il n'est plus qu'une signature. */
.ecran-etape .logo {
  height: 22px;
  margin-bottom: 0;
  opacity: .8;
}

/* ================================================================ points
   Les quatre points du code de SesameCaisse, réemployés en indicateur
   d'étape : mêmes formes, même remplissage, même gonflement. Le parcours
   compte quatre étapes, comme le code comptait quatre chiffres. */
.points { display: flex; gap: var(--e6); }
.point {
  width: var(--point-diametre);
  height: var(--point-diametre);
  border-radius: 50%;
  border: 1.5px solid var(--encre-2);
  background: transparent;
  transition: background var(--t-moyen) var(--sortie),
              border-color var(--t-moyen) var(--sortie),
              transform var(--t-moyen) var(--rebond);
}
.point.rempli {
  background: var(--encre);
  border-color: var(--encre);
  animation: gonfle var(--t-moyen) var(--rebond);
}
/* L'étape en cours : cerclée d'accent, pas encore pleine. C'est la seule
   marque qui distingue « j'y suis » de « c'est fait ». */
.point.courant {
  border-color: var(--accent);
  background: transparent;
  animation: none;
}
@keyframes gonfle { from { transform: scale(.6); } to { transform: scale(1); } }

/* En-tête d'étape : le rang chiffré au-dessus des points, discret. */
.entete-etape {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--e3);
}
.rang-etape {
  font-size: var(--corps-petit);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--encre-3);
}

/* =============================================================== boutons */
.bouton {
  font-family: inherit;
  font-size: var(--corps-texte);
  font-weight: 400;
  letter-spacing: .06em;
  border-radius: 999px;
  padding: var(--e4) var(--e8);
  min-height: 56px;
  min-width: 160px;
  cursor: pointer;
  transition: background var(--t-vif) var(--sortie),
              transform var(--t-vif) var(--sortie),
              opacity var(--t-vif) var(--sortie);
}
.bouton:active { transform: scale(.97); }
/* Un bouton hors service reste LISIBLE : on doit pouvoir lire ce qu'on ne
   peut pas encore faire. D'où l'opacité à .35 et non à .2. */
.bouton:disabled {
  opacity: .35;
  cursor: not-allowed;
  transform: none;
}

.bouton-principal {
  background: var(--encre);
  color: var(--fond);
  border: 1px solid var(--encre);
}
.bouton-principal:active:not(:disabled) { opacity: .85; }

.bouton-secondaire {
  background: transparent;
  color: var(--encre);
  border: 1px solid var(--touche-bord);
}
.bouton-secondaire:active:not(:disabled) { background: var(--touche-active); }

/* Le clavier doit voir où il est. SesameCaisse s'en passait — un kiosque
   tactile n'a pas de touche Tab. */
.bouton:focus-visible,
.saisie-texte:focus-visible,
.lien-discret:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Lien discret (déconnexion) : du texte, pas un bouton. */
.lien-discret {
  position: absolute;
  top: var(--e6);
  right: var(--e8);
  background: none;
  border: none;
  font-family: inherit;
  font-size: var(--corps-petit);
  letter-spacing: .06em;
  color: var(--encre-3);
  cursor: pointer;
  padding: var(--e2) var(--e3);
}
.lien-discret:hover,
.lien-discret:active { color: var(--encre); }

/* Barre de navigation d'étape : RETOUR à gauche, CONTINUER à droite. */
.barre-navigation {
  width: 100%;
  max-width: 900px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--e6);
}
.barre-navigation .seul { margin-left: auto; }

/* ================================================================ champs */
.champ {
  display: flex;
  flex-direction: column;
  gap: var(--e1);
}
.etiquette {
  font-size: var(--corps-petit);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--encre-3);
}

.saisie-texte {
  font-family: inherit;
  font-size: var(--corps-texte);
  font-variant-numeric: tabular-nums;
  color: var(--encre);
  background: var(--touche);
  border: 1px solid var(--touche-bord);
  border-radius: var(--e3);
  padding: var(--e3) var(--e4);
  min-height: 52px;
  width: 100%;
  transition: border-color var(--t-vif) var(--sortie),
              background var(--t-vif) var(--sortie);
}
.saisie-texte::placeholder { color: var(--encre-3); }
.saisie-texte:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--touche-active);
}

/* Le sélecteur natif pose son icône de calendrier en sombre sur du sombre.
   L'inverser la rend visible sans avoir à dessiner la nôtre. */
.saisie-texte[type="date"]::-webkit-calendar-picker-indicator {
  filter: invert(1);
  opacity: .55;
  cursor: pointer;
}
.saisie-texte[type="date"]::-webkit-calendar-picker-indicator:hover { opacity: 1; }

.sous-champ {
  font-size: var(--corps-petit);
  color: var(--encre-2);
  padding-left: var(--e1);
  min-height: 1.3em;
}
.sous-champ.alerte { color: var(--accent); }

/* Les deux dates côte à côte, repliées l'une sous l'autre en portrait. */
.rangee-champs {
  display: flex;
  gap: var(--e6);
  width: 100%;
  max-width: 620px;
}
.rangee-champs > .champ { flex: 1 1 0; min-width: 0; }
@media (max-width: 560px) {
  .rangee-champs { flex-direction: column; gap: var(--e4); }
}

/* ================================================================ attente
   Les points de l'en-tête, réemployés en attente. Pas un tourniquet
   inventé pour l'occasion : le gérant a déjà appris à les lire. */
.attente {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--e3);
}
.attente .points { gap: var(--e4); }
.attente .point { animation: respire 1.2s var(--sortie) infinite; }
.attente .point:nth-child(2) { animation-delay: .2s; }
.attente .point:nth-child(3) { animation-delay: .4s; }
@keyframes respire {
  0%, 100% { background: transparent; }
  50% { background: var(--encre); border-color: var(--encre); }
}

/* ======================================================== état du réseau
   Bandeau d'échec : l'écran ne se vide jamais, il dit ce qui a raté et
   laisse ce qui était là. */
.bandeau-echec {
  display: none;
  align-items: center;
  gap: var(--e6);
  padding: var(--e3) var(--e6);
  border: 1px solid var(--touche-bord);
  border-radius: var(--e3);
  color: var(--encre-2);
  font-size: var(--corps-texte);
}
.bandeau-echec.visible { display: flex; }

/* Message discret, réservé aux pannes — jamais de texte d'aide ici. */
.etat-reseau {
  min-height: 1.4em;
  font-size: var(--corps-petit);
  color: var(--encre-2);
  text-align: center;
}

/* ============================================================== portail */
.ecran-portail .champ { width: 100%; max-width: 320px; }

.mention-portail {
  font-size: var(--corps-petit);
  color: var(--encre-3);
  letter-spacing: .06em;
  text-align: center;
}

/* ============================================================== la revue
   L'écran principal. Tout ce qui suit sert un seul propos : que ce qui
   cloche se voie avant ce qui va bien.

   .contenu-revue déroge au centrage vertical des autres écrans — une liste
   de trente noms se lit du haut, pas du milieu. */
.ecran-etape .contenu-revue {
  justify-content: flex-start;
  align-items: stretch;
  max-width: 720px;
}

/* ------------------------------------------------------- signalements
   Le vocabulaire de la vue gérant de SesameCaisse, repris tel quel :
   .bloc-flags, .liste-flags, li.grave. La distinction avertissement /
   information y était déjà résolue ; on ne la réinvente pas. */
.bloc-flags {
  display: flex;
  flex-direction: column;
  gap: var(--e3);
  width: 100%;
  padding: var(--e4) var(--e6);
  border: 1px solid var(--touche-bord);
  border-radius: var(--e4);
}
.bloc-flags + .bloc-flags { margin-top: var(--e4); }
.bloc-flags h2 {
  font-size: var(--corps-petit);
  font-weight: 400;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--encre-3);
}
/* Le groupe « à traiter » : son titre porte l'accent, et son cadre aussi.
   C'est la seule chose de cet écran qui demande une décision. */
.bloc-flags.majeur { border-color: var(--accent); }
.bloc-flags.majeur > h2 { color: var(--accent); }

.liste-flags {
  display: flex;
  flex-direction: column;
  gap: var(--e2);
  list-style: none;
}
.liste-flags li {
  font-size: var(--corps-texte);
  line-height: 1.5;
  color: var(--encre-2);
  padding-left: var(--e4);
  position: relative;
}
.liste-flags li::before {
  content: "—";
  position: absolute;
  left: 0;
  color: var(--encre-3);
}
/* Un signalement grave se voit, mais ne bloque rien : c'est le gérant qui
   décide, pas la feuille de style. */
.liste-flags li.grave { color: var(--encre); }
.liste-flags li.grave::before { content: "!"; color: var(--accent); }

.titre-flag {
  font-size: var(--corps-petit);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--encre-3);
}
/* Un repli veut dire qu'un motif de signalements.py n'a pas mordu et que
   le message anglais est rendu brut. Le dire, plutôt que de le laisser
   passer pour une phrase française bizarre. */
.mention-repli { color: var(--accent); font-size: var(--corps-petit); }

/* Aucun signalement : un bloc qui le DIT. Une zone vide se lirait comme un
   écran qui n'a pas fini de charger. */
.bloc-serein { border-style: dashed; }
.texte-serein { font-size: var(--corps-texte); color: var(--encre-2); }

/* ------------------------------------------------------- les chiffres
   Ils confirment que l'extraction a tourné ; ils n'alertent pas. D'où le
   traitement le plus discret de l'écran. */
.bloc-recap {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e2) var(--e8);
  width: 100%;
  padding: var(--e4) var(--e6);
  margin-top: var(--e6);
  border-top: 1px solid var(--touche-bord);
}
.ligne-recap { display: flex; align-items: baseline; gap: var(--e2); }
.recap-etiquette {
  font-size: var(--corps-petit);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--encre-3);
}
.recap-valeur { font-size: var(--corps-titre); font-weight: 300; color: var(--encre); }

/* ----------------------------------------------------------- le roster */
.bloc-section { width: 100%; padding-top: var(--e6); }
.titre-section {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--e4);
  font-size: var(--corps-petit);
  font-weight: 400;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--encre-3);
  padding-bottom: var(--e2);
  border-bottom: 1px solid var(--touche-bord);
}
/* text-transform: none, sinon la capitalisation du titre de section
   remonte jusqu'à l'unité et affiche « 92 H 40 ». */
.total-section {
  color: var(--encre-2);
  letter-spacing: .04em;
  text-transform: none;
}
.liste-employes { list-style: none; }
.ligne-employe {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--e4);
  padding: var(--e2) 0;
  font-size: var(--corps-texte);
}
.nom-employe { color: var(--encre); }
.meta-employe { color: var(--encre-2); font-size: var(--corps-petit); }

/* --------------------------------------------------------- les écartés
   Repliés — le vocabulaire .anciens-employes de SesameCaisse. Streamlit
   n'en montrait que le nombre ; c'est pourtant là que se trouve
   « un employé absent du roster ». */
.ecartes { width: 100%; margin-top: var(--e8); }
.ecartes > summary {
  font-size: var(--corps-petit);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--encre-3);
  cursor: pointer;
  padding: var(--e3) 0;
  border-top: 1px solid var(--touche-bord);
}
.ecartes > summary:hover { color: var(--encre-2); }
.groupe-ecart { padding: var(--e3) 0; }
.groupe-ecart > h3 {
  font-size: var(--corps-petit);
  font-weight: 400;
  letter-spacing: .08em;
  color: var(--encre-3);
  padding-bottom: var(--e1);
}
/* « Aucun punch » est le seul motif qui appelle une décision : l'employé
   était à l'horaire et n'a pas poinçonné. Les autres sont des exclusions
   voulues. */
.groupe-ecart.majeur > h3 { color: var(--accent); }
.groupe-ecart li {
  list-style: none;
  font-size: var(--corps-texte);
  color: var(--encre-2);
  padding: var(--e1) 0;
}
.groupe-ecart.majeur li { color: var(--encre); }

/* ============================================================ le fichier */

/* Le mode deux/quatre semaines. fill_pourb le devine seul au-delà de 14
   jours et n'en dit rien : ce texte est le seul endroit où le gérant
   apprend quels onglets vont être écrits. */
.mention-mode {
  width: 100%;
  max-width: 620px;
  padding: var(--e3) var(--e6);
  border: 1px solid var(--touche-bord);
  border-radius: var(--e3);
  font-size: var(--corps-texte);
  color: var(--encre-2);
  text-align: center;
}
/* Quatre semaines change la structure du fichier produit — ça se souligne. */
.mention-mode.appuyee { border-color: var(--accent); color: var(--encre); }

/* Le journal brut de fill_pourb : anglais, parsemé d'émojis, cité tel quel.
   C'est une pièce à conviction, pas de la copie d'interface — d'où le
   monospace et le repli par défaut. */
.journal { width: 100%; max-width: 620px; }
.journal > summary {
  font-size: var(--corps-petit);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--encre-3);
  cursor: pointer;
  padding: var(--e3) 0;
  border-top: 1px solid var(--touche-bord);
}
.journal > summary:hover { color: var(--encre-2); }
.journal pre {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: var(--corps-petit);
  line-height: 1.6;
  color: var(--encre-2);
  background: var(--touche);
  border-radius: var(--e3);
  padding: var(--e4);
  max-height: 320px;
  overflow: auto;
  white-space: pre-wrap;
  word-break: break-word;
}

/* ===================================================== mouvement réduit */
@media (prefers-reduced-motion: reduce) {
  .point.rempli { animation: none; }
  /* L'attente garde une animation : c'est la SEULE chose qui distingue
     « ça travaille » de « c'est figé ». On la ralentit au lieu de la
     couper. */
  .attente .point { animation-duration: 2.4s; }
  .ecran, .ecran.visible, .point, .bouton, .saisie-texte {
    transition-duration: 1ms;
  }
}
