/* =========================================================================
   LANGAGE VISUEL SESAME AUTO — refonte du 8 août 2026.

   Ce fichier remplace la feuille reprise de SesameCaisse. Le parcours en
   quatre étapes devient un OUTIL À BARRE LATÉRALE : le gérant pourra
   bientôt quitter une étape pour aller corriger une caisse et revenir sans
   rien perdre. Voir REFONTE_GERANT.md.

   Ce qui NE change pas : le comportement. `app.js` n'a pas été touché d'une
   ligne — mêmes identifiants, mêmes classes, même mécanique `.ecran` /
   `.visible`, mêmes `data-continuer` / `data-retour` / `data-quitter`. Un
   habillage qui exige de réécrire la logique n'est plus un habillage, et
   c'est la logique qui produit le fichier de paie.

   Aucune valeur de couleur, d'espacement, de rayon ou de durée hors de
   :root.

   TROIS DIVERGENCES ASSUMÉES avec SesameCaisse, une seule raison aux trois :
   le kiosque montre UN écran à la fois, sous le pouce d'un serveur debout ;
   ceci est un outil de bureau où une barre latérale, des cartes et un fond
   coexistent à l'écran.
     1. Trois niveaux de fond au lieu d'un.
     2. Une échelle typographique plus serrée — 17px au doigt, 14px à l'œil.
     3. Une deuxième couleur d'état, --alerte, réservée au constat grave.
   ========================================================================= */

/* Inter variable, embarquée. Aucun appel à Google Fonts : l'outil doit
   s'afficher pareil sur le VPS, hors ligne, sans dépendance extérieure. La
   pile système reste en repli — si le fichier manque, la page retombe sur
   ce à quoi elle ressemblait hier, jamais sur Times. */
@font-face {
  font-family: "Inter";
  src: url("inter/inter.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

:root {
  /* Le calendrier natif, les listes déroulées et les barres de défilement
     ignorent cette feuille et suivent cette seule déclaration. */
  color-scheme: dark;

  /* ── Palette. Les trois valeurs de marque sont INCHANGÉES : prélevées
     sur le logo, rien ne justifie d'y toucher. ─────────────────────────── */
  --encre:        #FFFBDB;                   /* ivoire du logo    16.8:1   */
  --encre-2:      rgb(255 251 219 / .58);    /* texte secondaire   6.4:1   */
  --encre-3:      rgb(255 251 219 / .30);    /* DÉCOR SEUL — 2.65:1        */
  --accent:       #E0873F;                   /* action, élément actif      */

  /* Les trois niveaux de fond ajoutés pour l'outil à barre latérale. */
  --fond-page:    #121212;
  --fond:         #191919;                   /* barre latérale, panneaux   */
  --surface:      #1F1F1F;                   /* cartes posées sur la page  */
  --surface-2:    #262626;

  --bord:         rgb(255 251 219 / .10);
  --bord-fort:    rgb(255 251 219 / .18);
  --touche:       rgb(255 251 219 / .05);
  --touche-bord:  rgb(255 251 219 / .12);
  --touche-active:rgb(255 251 219 / .10);

  /* L'encre POSÉE SUR l'accent, et ce n'est pas un choix esthétique :
     ivoire sur --accent mesure 2.6:1, illisible. Le noir de fond sur
     --accent mesure 6.45:1. Un bouton orange porte donc du texte sombre,
     TOUJOURS. C'est la règle la plus facile à casser par distraction. */
  --sur-accent:   #191919;
  --accent-clair: #EDA05E;                   /* survol du bouton plein     */
  --accent-voile: rgb(224 135 63 / .14);     /* fond teinté, pas de texte  */

  /* Deuxième couleur d'état. Réservée à ce qui demande une décision : un
     signalement à traiter, une journée sans caisse. */
  --alerte:       #F2777A;
  --alerte-voile: rgb(242 119 122 / .14);

  /* TROISIÈME et dernière, ajoutée le 2026-08-14 sur demande explicite
     d'Oscar : « une petite case qui devient verte ». Cette ligne disait
     jusqu'ici « deuxième et la dernière » — la restriction était bonne, et
     c'est le propriétaire qui la lève, pas une distraction.
     Son emploi est ÉTROIT : « j'ai relu cette caisse », et rien d'autre. Ce
     n'est pas une couleur de succès à saupoudrer ; --alerte reste seule à
     dire qu'une chose demande une décision.
     7.4:1 sur le fond, et 7.0:1 pour la coche sombre posée dessus. */
  --verdict:      #7FB069;

  /* ── Typographie ─────────────────────────────────────────────────────── */
  --police: "Inter", -apple-system, system-ui, "Helvetica Neue",
            "Segoe UI", sans-serif;
  --t-grand:  28px;
  --t-titre:  20px;   /* titre de page, graisse 500                        */
  --t-sous:   15px;
  --t-saisie: 15px;
  --t-corps:  14px;
  --t-petit:  12px;

  /* Anciens noms, gardés parce que `app.js` et les blocs qu'il fabrique
     s'en servent encore. Ils pointent vers la nouvelle échelle. */
  --corps-titre: var(--t-titre);
  --corps-texte: var(--t-corps);
  --corps-petit: var(--t-petit);

  /* ── Espacements, base 4, échelle dense ──────────────────────────────── */
  --e1: 4px;  --e2: 8px;  --e3: 12px;  --e4: 16px;
  --e6: 24px; --e8: 32px; --e12: 48px;

  /* ── Géométrie ───────────────────────────────────────────────────────── */
  --r1: 8px;  --r2: 12px;  --r3: 5px;
  --barre: 248px;
  --barre-fermee: 64px;
  --point-diametre: 9px;
  --glisse: 16px;     /* amplitude du glissement entre étapes              */

  /* ── Mouvement ───────────────────────────────────────────────────────── */
  --t-vif: 120ms;
  --t-moyen: 240ms;
  --t-lent: 320ms;    /* changement d'écran                                */
  --d-panneau: 180ms; /* repli de la barre latérale                        */
  --sortie: cubic-bezier(0.2, 0, 0, 1);
  --rebond: cubic-bezier(0.34, 1.3, 0.64, 1);
}

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

/* `hidden` ne vaut que display:none dans la feuille du navigateur : la
   moindre règle d'auteur posant un display le neutralise en silence. */
[hidden] { display: none !important; }

html, body { height: 100%; overflow: hidden; overscroll-behavior: none; }

body {
  position: fixed;
  inset: 0;
  background: var(--fond-page);
  color: var(--encre);
  font-family: var(--police);
  font-size: var(--t-corps);
  line-height: 1.5;
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
  /* SesameCaisse pose ici user-select: none : bon pour un kiosque tactile,
     mauvais ici — le gérant doit pouvoir copier un nom d'employé. */
  padding: env(safe-area-inset-top) env(safe-area-inset-right)
           env(safe-area-inset-bottom) env(safe-area-inset-left);
}

button, input, select { font: inherit; color: inherit; }
button { background: none; border: none; cursor: pointer; }

/* Le halo de focus ne se retire jamais. */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ═══════════════════════════════════════════════════════════════ écrans
   Mécanique INCHANGÉE : `app.js` pose et retire `.visible`, et `.inverse`
   pour retourner le côté de repos. Seule l'amplitude a baissé — un panneau
   dans un cadre glisse moins loin qu'une page entière. */
.ecran {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  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);
}

/* ═══════════════════════════════════════════════════════════ ossature
   La barre latérale n'existe QUE pendant le parcours : au portail, la page
   est pleine. `:has()` le fait sans une ligne de JavaScript — et c'est le
   but, `app.js` n'a pas été touché. */
.app {
  position: fixed;
  inset: 0;
  display: grid;
  grid-template-columns: var(--barre) 1fr;
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--t-lent) var(--sortie),
              visibility 0s linear var(--t-lent),
              grid-template-columns var(--d-panneau) var(--sortie);
}
/* `.ecran-outil` : un écran qui vit dans le cadre sans faire partie du
   parcours en quatre étapes — les caisses, et bientôt les écarts, les
   employés, les taux. Il porte la barre latérale comme les étapes, mais il
   n'a ni rang ni points, et on y entre par la barre, pas par CONTINUER. */
body:has(.ecran-etape.visible) .app,
body:has(.ecran-outil.visible) .app {
  opacity: 1;
  visibility: visible;
  transition: opacity var(--t-lent) var(--sortie),
              grid-template-columns var(--d-panneau) var(--sortie);
}
.app.repliee { grid-template-columns: var(--barre-fermee) 1fr; }

/* Le portail passe AU-DESSUS : les deux occupent tout le cadre. */
.ecran-portail { z-index: 2; }

/* ══════════════════════════════════════════════════════ barre latérale */
.barre {
  background: var(--fond);
  border-right: 1px solid var(--bord);
  display: flex;
  flex-direction: column;
  padding: var(--e3);
  gap: var(--e2);
  overflow: hidden;
}

.tete-barre {
  position: relative;                /* ancre le logo une fois replié     */
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e2);
  padding: var(--e2) var(--e2) var(--e3);
  min-height: 48px;
}

/* Replié, le logo QUITTE LE FLUX. L'effacer en opacité ne suffit pas : ses
   103 px continueraient d'occuper la rangée, le bouton resterait poussé
   hors des 64 px visibles, et `overflow: hidden` le couperait — on pourrait
   replier la barre sans plus jamais la rouvrir. */
.app.repliee .tete-barre { justify-content: center; }
.app.repliee .tete-barre .logo {
  position: absolute;
  left: var(--e2);
  opacity: 0;
  pointer-events: none;
}

.replier {
  flex: none;
  display: grid; place-items: center;
  width: 28px; height: 28px;
  border-radius: var(--r3);
  color: var(--encre-2);
  transition: background var(--t-vif) var(--sortie),
              color var(--t-vif) var(--sortie);
}
.replier:hover { background: var(--touche); color: var(--encre); }
.replier svg { transition: transform var(--d-panneau) var(--sortie); }
.app.repliee .replier svg { transform: rotate(180deg); }

.groupe-nav { display: flex; flex-direction: column; gap: 2px; padding: var(--e2) 0; }
.groupe-nav + .groupe-nav { border-top: 1px solid var(--bord); }

.item-nav {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--e3);
  height: 38px;
  padding: 0 var(--e3);
  border-radius: var(--r1);
  color: var(--encre-2);
  font-size: var(--t-corps);
  white-space: nowrap;
  text-align: left;
  transition: background var(--t-vif) var(--sortie),
              color var(--t-vif) var(--sortie);
}
.item-nav:hover { background: var(--touche); color: var(--encre); }
.item-nav.actif { background: var(--touche-active); color: var(--encre); }
/* La pastille survit au repli : barre fermée, on sait encore où on est. */
.item-nav.actif::before {
  content: "";
  position: absolute;
  left: 0; top: 50%;
  width: 3px; height: 18px;
  border-radius: 0 2px 2px 0;
  background: var(--accent);
  transform: translateY(-50%);
}
.item-nav svg { flex: none; width: 18px; height: 18px; }

/* Le libellé s'efface en transform+opacity : une largeur animée ferait
   sauter le texte pendant tout le repli. */
.item-nav .libelle, .pied-barre .qui {
  transition: opacity var(--t-vif) var(--sortie),
              transform var(--d-panneau) var(--sortie);
}
.app.repliee .item-nav .libelle,
.app.repliee .pied-barre .qui {
  opacity: 0;
  transform: translateX(-6px);
  pointer-events: none;
}

.pied-barre {
  margin-top: auto;
  border-top: 1px solid var(--bord);
  padding-top: var(--e3);
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.pied-barre .qui {
  padding: 0 var(--e3) var(--e1);
  font-size: var(--t-petit);
  color: var(--encre-2);
  white-space: nowrap;
}

/* ══════════════════════════════════════════════════════ zone de droite */
.zone { display: flex; flex-direction: column; min-width: 0; position: relative; }
/* Les quatre étapes s'empilent ici : `.ecran` est en position absolue et
   c'est cette boîte qui lui sert de cadre. */
.pile-ecrans { position: relative; flex: 1; min-height: 0; }

/* ════════════════════════════════════════════════════════════════ logo */
.logo { height: 15px; width: auto; opacity: .95; }
.ecran-portail .logo { height: 26px; opacity: 1; }

/* ══════════════════════════════════════════════════════════ en-tête de page
   Titre en haut à gauche, rang de l'étape en dessous. Elle ne défile
   jamais : le contenu défile sous elle. */
.entete-page {
  flex: none;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--e4);
  padding: var(--e4) var(--e6);
  border-bottom: 1px solid var(--bord);
}
.titre-ecran {
  font-size: var(--t-titre);
  font-weight: 500;
  letter-spacing: -.01em;
  color: var(--encre);
}

/* En-tête d'étape : le rang et les points, en RANGÉE désormais — ils
   tiennent sous le titre au lieu d'occuper le haut de l'écran. */
.entete-etape {
  display: flex;
  align-items: center;
  gap: var(--e3);
  margin-top: var(--e1);
}
.rang-etape {
  font-size: var(--t-petit);
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--encre-2);
}

/* ══════════════════════════════════════════════════════════════ points
   Les points de SesameCaisse, en indicateur d'étape : pleins pour ce qui
   est fait, cerclés d'accent pour l'étape en cours. `app.js` pose
   `.rempli` et `.courant` — noms conservés. */
.points { display: flex; gap: var(--e2); }
.point {
  width: var(--point-diametre);
  height: var(--point-diametre);
  border-radius: 50%;
  border: 1.5px solid var(--encre-3);
  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-2);
  border-color: var(--encre-2);
  animation: gonfle var(--t-moyen) var(--rebond);
}
.point.courant { border-color: var(--accent); background: transparent; animation: none; }
@keyframes gonfle { from { transform: scale(.6); } to { transform: scale(1); } }

/* ═════════════════════════════════════════════════════════════ contenu */
.contenu {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: var(--e6);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--e4);
}
/* La revue liste trente noms : elle se lit du haut, en pleine largeur. */
.contenu-revue { align-items: stretch; }
/* `.contenu` aligne ses enfants au début, donc un bloc s'y rétrécirait à son
   contenu : d'où la largeur imposée. Mais elle s'appliquait AUSSI aux
   boutons, et REMPLIR sortait en barre orange de 820 px de large. Un bouton
   garde sa largeur intrinsèque — sa taille est ce qui dit s'il est l'action
   de l'écran ou un geste secondaire, et un bouton étiré ne le dit plus. */
.contenu > * { max-width: 820px; width: 100%; }
.contenu > .bouton, .contenu > .attente { width: auto; }

/* `.contenu-large` — pour les écrans qui portent un TABLEAU.
   Les 820 px ci-dessus servent la lecture : au-delà, une ligne de texte
   devient pénible à suivre d'une fin de ligne au début de la suivante. Un
   tableau n'a pas cette contrainte — ses colonnes sont courtes et alignées —
   et le plafond n'y laissait qu'un vide à droite : 84 px à 1200, mais 324 à
   1440 et 564 à 1680. Sur un écran large, la liste des caisses paraissait
   tassée dans un coin.

   1400 px et non « aucune limite » : au-delà, l'œil perd la ligne entre le
   nom du serveur, tout à gauche, et ses signalements, tout à droite. */
.contenu-large > * { max-width: 1400px; }
/* La prose garde SA largeur de lecture même dans un conteneur large. */
.contenu-large > .sous-titre { max-width: 72ch; }

/* Le détail d'une caisse, entre les deux. C'est un FORMULAIRE — donc pas les
   1400 px d'un tableau, où l'œil perdrait la ligne entre l'intitulé et son
   champ. Mais deux rubans lisibles prennent 400 px à eux seuls, et les
   820 px de lecture ne laissaient plus rien à la fiche : tout était tassé.
   1060 laisse ~620 px à la colonne d'informations. */
.contenu-detail > * { max-width: 1060px; }

.sous-titre {
  font-size: var(--t-corps);
  color: var(--encre-2);
  max-width: 72ch;
}

/* ══════════════════════════════════════════════════ barre de navigation
   RETOUR à gauche, CONTINUER à droite. Elle ne part JAMAIS hors de
   l'écran — c'est pour cela que le défilement vit dans .contenu. */
.barre-navigation {
  flex: none;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--e4);
  padding: var(--e3) var(--e6);
  border-top: 1px solid var(--bord);
  background: var(--fond-page);
}
.barre-navigation .seul { margin-left: auto; }

/* ═════════════════════════════════════════════════════════════ boutons
   UN bouton plein par écran, et il est orange. */
.bouton {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--e2);
  height: 36px;
  padding: 0 var(--e4);
  border-radius: var(--r1);
  font-size: var(--t-corps);
  font-weight: 500;
  letter-spacing: .02em;
  white-space: nowrap;
  transition: background var(--t-vif) var(--sortie),
              border-color var(--t-vif) var(--sortie),
              transform var(--t-vif) var(--sortie),
              opacity var(--t-vif) var(--sortie);
}
.bouton:active:not(:disabled) { transform: translateY(1px); }
/* Un bouton hors service reste LISIBLE : on doit pouvoir lire ce qu'on ne
   peut pas encore faire. D'où .35 et non .2. */
.bouton:disabled { opacity: .35; cursor: not-allowed; transform: none; }

/* ⚠ --sur-accent, jamais --encre. Voir le commentaire de la variable. */
.bouton-principal { background: var(--accent); color: var(--sur-accent); }
.bouton-principal:hover:not(:disabled) { background: var(--accent-clair); }

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

/* QUITTER vit maintenant dans le pied de la barre latérale, plus en haut à
   droite de chaque écran. La classe reste : `app.js` cherche
   [data-quitter], pas une classe, mais le portail n'a pas de barre. */
.lien-discret {
  font-size: var(--t-petit);
  color: var(--encre-2);
  padding: var(--e2) var(--e3);
  border-radius: var(--r1);
  text-align: left;
}
.lien-discret:hover { color: var(--encre); background: var(--touche); }

/* ══════════════════════════════════════════════════════════════ champs */
.champ { display: flex; flex-direction: column; gap: var(--e1); }
.etiquette { font-size: var(--t-petit); color: var(--encre-2); }

.saisie-texte {
  height: 36px;
  padding: 0 var(--e3);
  background: var(--fond-page);
  border: 1px solid var(--bord);
  border-radius: var(--r1);
  font-size: var(--t-saisie);
  color: var(--encre);
  width: 100%;
  /* Sans ceci, un input[type=date] impose sa largeur intrinsèque (icône de
     calendrier comprise) et déforme la rangée qui le contient. */
  min-width: 0;
  transition: border-color var(--t-vif) var(--sortie),
              background var(--t-vif) var(--sortie);
}
.saisie-texte::placeholder { color: var(--encre-3); }
.saisie-texte:hover { border-color: var(--bord-fort); }
.saisie-texte:focus { outline: none; border-color: var(--accent); }

/* L'ICÔNE DU CALENDRIER NATIF — et surtout, ce qu'il ne faut plus lui faire.

   Elle portait `filter: invert(1)`, écrit quand cette feuille ne déclarait pas
   encore `color-scheme`. Depuis, `:root` dit `color-scheme: dark` (voir tout
   en haut) : le navigateur dessine DÉJÀ le calendrier en clair, et l'inverser
   le repeignait en noir sur un champ noir. Invisible, donc — un champ de date
   dont on ne voit pas le bouton se clique au jugé.

   Rien à inverser, donc. L'opacité seule suffit à le tenir au rang d'un
   élément secondaire, et il redevient franc au survol. */
.saisie-texte[type="date"]::-webkit-calendar-picker-indicator {
  opacity: .75; cursor: pointer;
}
.saisie-texte[type="date"]::-webkit-calendar-picker-indicator:hover { opacity: 1; }

/* Dans une colonne de valeurs alignées à droite, la date l'est aussi — et son
   icône passe alors DEVANT le texte. Laissée à sa place, elle se glisserait
   entre la date et le bord droit, seule chose de la colonne à ne pas finir au
   même endroit. `order` la déplace, `margin-right: auto` pousse la date au
   bout ; ni l'un ni l'autre n'a d'effet là où le navigateur ne connaît pas ce
   pseudo-élément, et le champ reste alors ce qu'il est aujourd'hui.

   Porté par `.au-bout` et non par `[type="date"]` : les deux dates de
   « Choisir une autre période », dans Contrôles, sont des champs de
   formulaire ordinaires et gardent leur alignement à gauche. */
.saisie-texte.au-bout[type="date"]::-webkit-calendar-picker-indicator {
  order: -1;
  margin-left: 0;
  margin-right: auto;
}

.sous-champ { font-size: var(--t-petit); color: var(--encre-2); min-height: 1.3em; }
.sous-champ.alerte { color: var(--alerte); }

.rangee-champs { display: flex; gap: var(--e4); max-width: 440px; }
.rangee-champs > .champ { flex: 1 1 0; min-width: 0; }
@media (max-width: 560px) {
  .rangee-champs { flex-direction: column; gap: var(--e3); }
}

/* ═════════════════════════════════════════════════════════════ attente
   Les points de l'en-tête, réemployés. Pas un tourniquet inventé pour
   l'occasion : le gérant a déjà appris à les lire. */
.attente { display: flex; align-items: center; gap: var(--e3); }
.attente .points { gap: var(--e2); }
.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-2); border-color: var(--encre-2); }
}

/* ════════════════════════════════════════════════════════ état du réseau
   L'écran ne se vide jamais : il dit ce qui a raté et laisse ce qui était
   là. `app.js` pose `.visible` pour l'afficher. */
.bandeau-echec {
  display: none;
  align-items: center;
  gap: var(--e3);
  padding: var(--e3) var(--e4);
  border: 1px solid var(--alerte-voile);
  background: var(--alerte-voile);
  border-radius: var(--r1);
  color: var(--alerte);
  font-size: var(--t-corps);
}
.bandeau-echec.visible { display: flex; }

/* ═════════════════════════════════════════════════════════════ portail */
.ecran-portail {
  align-items: center;
  justify-content: center;
  gap: var(--e6);
  padding: var(--e8);
  background: var(--fond-page);
}
.ecran-portail .sous-titre { text-align: center; }
.carte-portail {
  width: 100%;
  max-width: 340px;
  background: var(--surface);
  border: 1px solid var(--bord);
  border-radius: var(--r2);
  padding: var(--e6);
  display: flex;
  flex-direction: column;
  gap: var(--e4);
}
.ecran-portail .bandeau-echec { max-width: 340px; }
.mention-portail { font-size: var(--t-petit); color: var(--encre-2); text-align: center; }

/* Bouton de texte, sans cadre : « Retour » depuis le portail vers l'accueil.
   Il ne doit pas concurrencer ENTRER, qui est l'action de l'écran. */
.bouton-fantome { color: var(--encre-2); height: 32px; padding: 0 var(--e3); }
.bouton-fantome:hover { color: var(--encre); background: var(--touche); }

/* Le champ de mot de passe et sa bascule voir/masquer. La bascule est POSÉE
   SUR le champ, dont le rembourrage droit lui fait la place : sans ce
   rembourrage, un mot de passe long passerait sous l'icône. */
.champ-avec-bascule { position: relative; display: flex; }
.champ-avec-bascule .saisie-texte { padding-right: var(--e8); }
.bascule-visibilite {
  position: absolute;
  right: var(--e1);
  top: 50%;
  transform: translateY(-50%);
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: var(--r3);
  color: var(--encre-2);
  transition: color var(--t-vif) var(--sortie),
              background var(--t-vif) var(--sortie);
}
.bascule-visibilite:hover { color: var(--encre); background: var(--touche); }

/* L'erreur d'un champ vit SOUS lui, et n'occupe aucune place au repos : une
   ligne réservée en permanence ferait sauter la carte à chaque échec. */
.erreur-champ { font-size: var(--t-petit); color: var(--alerte); }
.erreur-champ:empty { display: none; }

/* ═══════════════════════════════════════════════════════════════ cartes */
.carte {
  background: var(--surface);
  border: 1px solid var(--bord);
  border-radius: var(--r2);
  padding: var(--e4);
}

/* ═══════════════════════════════════════════════════════════════ la revue
   Un seul propos : que ce qui cloche se voie avant ce qui va bien.
   Le vocabulaire est celui d'`app.js`, qui fabrique ces blocs — .bloc-flags,
   .liste-flags, li.grave. Ne pas renommer sans rouvrir app.js. */
.bloc-flags {
  display: flex;
  flex-direction: column;
  gap: var(--e3);
  padding: var(--e4);
  background: var(--surface);
  border: 1px solid var(--bord);
  border-radius: var(--r2);
}
.bloc-flags + .bloc-flags { margin-top: var(--e3); }
.bloc-flags h2 {
  font-size: var(--t-sous);
  font-weight: 500;
  color: var(--encre);
}
/* Le groupe « à traiter » : la seule chose de cet écran qui demande une
   décision. Un filet vertical, pas un cadre entier — un cadre coloré autour
   de tout finit par ne plus rien distinguer. */
.bloc-flags.majeur { border-left: 2px solid var(--alerte); }
.bloc-flags.majeur > h2 { color: var(--alerte); }

.liste-flags { display: flex; flex-direction: column; gap: var(--e2); list-style: none; }
.liste-flags li {
  font-size: var(--t-corps);
  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(--alerte); font-weight: 600; }

.titre-flag {
  display: inline-block;
  padding: 1px var(--e2);
  margin-right: var(--e1);
  border-radius: var(--r3);
  background: var(--touche);
  font-size: var(--t-petit);
  font-weight: 500;
  color: var(--encre-2);
}
.liste-flags li.grave .titre-flag { background: var(--alerte-voile); color: var(--alerte); }
/* 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(--t-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(--t-corps); color: var(--encre-2); }

/* ─────────────────────────────────────────────────────────── les chiffres
   Ils confirment que l'extraction a tourné ; ils n'alertent pas. */
.bloc-recap {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e4) var(--e8);
  padding: var(--e4);
  margin-top: var(--e2);
  border-top: 1px solid var(--bord);
}
.ligne-recap { display: flex; flex-direction: column; gap: 0; }
.recap-etiquette { font-size: var(--t-petit); color: var(--encre-2); order: 2; }
.recap-valeur {
  font-size: var(--t-grand);
  font-weight: 500;
  line-height: 1.1;
  letter-spacing: -.02em;
  color: var(--encre);
  order: 1;
}

/* ─────────────────────────────────────────────────────────────── le roster */
.bloc-section { padding-top: var(--e4); }
.titre-section {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--e4);
  font-size: var(--t-petit);
  font-weight: 500;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--encre-2);
  padding-bottom: var(--e2);
  border-bottom: 1px solid var(--bord);
}
/* text-transform: none, sinon la capitalisation du titre remonte jusqu'à
   l'unité et affiche « 92 H 40 ». */
.total-section { color: var(--encre); letter-spacing: 0; 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;
  border-bottom: 1px solid var(--bord);
  font-size: var(--t-corps);
}
.ligne-employe:last-child { border-bottom: none; }
.nom-employe { color: var(--encre); }
.meta-employe { color: var(--encre-2); font-size: var(--t-petit); }

/* ────────────────────────────────────────────────────────────── les écartés
   Repliés — c'est pourtant là que se trouve « un employé absent du roster ». */
.ecartes { margin-top: var(--e4); }
.ecartes > summary, .journal > summary {
  font-size: var(--t-petit);
  color: var(--encre-2);
  cursor: pointer;
  padding: var(--e3) 0;
  border-top: 1px solid var(--bord);
}
.ecartes > summary:hover, .journal > summary:hover { color: var(--encre); }
.groupe-ecart { padding: var(--e3) 0; }
.groupe-ecart > h3 {
  font-size: var(--t-petit);
  font-weight: 500;
  color: var(--encre-2);
  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 voulues. */
.groupe-ecart.majeur > h3 { color: var(--alerte); }
.groupe-ecart li {
  list-style: none;
  font-size: var(--t-corps);
  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 {
  padding: var(--e3) var(--e4);
  border: 1px solid var(--bord);
  border-radius: var(--r1);
  font-size: var(--t-corps);
  color: var(--encre-2);
}
/* Quatre semaines change la structure du fichier produit — ça se souligne. */
.mention-mode.appuyee {
  border-color: var(--accent);
  background: var(--accent-voile);
  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. */
.journal pre {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: var(--t-petit);
  line-height: 1.6;
  color: var(--encre-2);
  background: var(--fond);
  border: 1px solid var(--bord);
  border-radius: var(--r1);
  padding: var(--e3);
  max-height: 320px;
  overflow: auto;
  white-space: pre-wrap;
  word-break: break-word;
}

/* ═════════════════════════════════════════════════════════════ tableaux
   La liste des caisses. Les colonnes d'argent sont alignées à droite et en
   chiffres tabulaires (`font-variant-numeric` sur body) : deux montants
   l'un sous l'autre doivent se comparer d'un coup d'œil. */
.tableau { display: flex; flex-direction: column; }
.entete-tableau, .ligne-tableau {
  display: grid;
  /* Serveur · Date · Quart · Ventes · Tips · Cash · Tip outs · Signalements.
     La colonne Quart est arrivée le 2026-08-18, contre la date : elle doit
     tenir « MIDI + SOIR » d'un seul tenant, sinon un double s'abrège en
     « MIDI + … » et ne dit plus rien de plus qu'une caisse simple.
     Les autres tableaux du gérant ne passent PAS par là : chacun redéfinit
     ses colonnes (.tableau-ecarts, .tableau-employes, .tableau-generations),
     donc cette règle de base est celle de la liste des caisses, et d'elle
     seule. */
  grid-template-columns: 1.3fr .85fr .8fr .75fr .7fr .7fr .8fr 1.1fr;
  gap: var(--e3);
  align-items: center;
  padding: var(--e3) var(--e4);
}
.entete-tableau {
  font-size: var(--t-petit);
  color: var(--encre-2);
  text-transform: uppercase;
  letter-spacing: .04em;
  border-bottom: 1px solid var(--bord);
}
.ligne-tableau {
  border-bottom: 1px solid var(--bord);
  cursor: pointer;
  text-align: left;
  width: 100%;
  transition: background var(--t-vif) var(--sortie);
}
.ligne-tableau:last-child { border-bottom: none; }
.ligne-tableau:hover { background: var(--surface-2); }
.au-bout { text-align: right; }

/* Le quart est un REPÈRE, pas une valeur : il se lit du coin de l'œil pour
   distinguer deux caisses du même serveur le même jour. En petites capitales
   espacées et dans l'encre secondaire, il se distingue de la date sans lui
   disputer l'attention — la colonne voisine reste ce qu'on lit d'abord. */
.quart-caisse {
  font-size: var(--t-petit);
  letter-spacing: .06em;
  color: var(--encre-2);
}

/* Aucune cellule ne passe à la ligne : un nom long rendrait sa rangée plus
   haute que ses voisines, ce qui se lit comme un défaut dans une liste
   dense. Le nom complet reste en `title` sur la rangée. */
.ligne-tableau > span, .entete-tableau > span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Une caisse annulée reste VISIBLE et barrée. Non-négociable 7 : c'est ce
   qui rend une annulation remarquable sans dépendre de personne, ni de
   l'idée d'aller ouvrir un autre écran. */
.ligne-tableau.annulee { color: var(--encre-2); }
.ligne-tableau.annulee .nom-serveur { text-decoration: line-through; }

/* ══════════════════════════════════════════════════════════════ badges */
.badge {
  display: inline-flex;
  align-items: center;
  padding: 2px var(--e2);
  border-radius: var(--r3);
  font-size: var(--t-petit);
  font-weight: 500;
  white-space: nowrap;
}
/* Les trois niveaux, et ils doivent se distinguer AU COUP D'ŒIL : c'est le
   seul travail de cet écran. `champs.js` traduit les sévérités de l'API
   (« high »/« med »/« low ») vers ces trois noms — voir la table GRAVITES,
   dont l'absence rendait tous les signalements gris.

   Les sélecteurs doublés `.titre-flag.badge-*` ne sont pas une redondance :
   `.titre-flag` porte sa propre couleur, et sans eux la couleur de sévérité
   dépendrait de l'ordre des règles dans ce fichier — c'est-à-dire de rien
   du tout. */
.badge-haute,   .titre-flag.badge-haute   { background: var(--alerte-voile); color: var(--alerte); }
.badge-moyenne, .titre-flag.badge-moyenne { background: var(--accent-voile); color: var(--accent); }
.badge-basse,   .titre-flag.badge-basse   { background: var(--touche);       color: var(--encre-2); }
.badges { display: flex; flex-wrap: wrap; gap: var(--e1); }
.ligne-tableau .badges { flex-wrap: nowrap; overflow: hidden; }
/* Les badges portent depuis le 2026-08-11 un intitulé français et non plus le
   code de la base : « Cash manquant » est plus long que ce que la colonne
   promet toujours. Dans une rangée, c'est l'INTITULÉ qui cède, jamais le
   « +2 » — un intitulé abrégé se voit et le détail est à un clic, tandis
   qu'un compte de signalements poussé hors du cadre disparaîtrait sans que
   rien ne le dise. `display: block` est nécessaire : sur un `inline-flex`,
   `text-overflow` n'a aucun effet, le texte serait coupé net au milieu d'une
   lettre. */
.ligne-tableau .badges > .badge:first-child {
  display: block;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ligne-tableau .badges > .badge + .badge { flex: none; }

/* ═══════════════════════════════════════════════ détail d'une caisse
   STRUCTURE reprise de sesamecaisse/CLAUDE.md, inchangée : rangée haute
   pleine largeur (les deux rubans à côté de l'identité de la caisse), puis
   ce que le serveur a DÉCIDÉ par quart, puis corrections et signalements, et
   l'annulation EN DERNIER — c'est le geste le plus lourd de l'écran.

   Un champ paraît à EXACTEMENT un endroit. Deux contrôles portant la même
   clé laisseraient corriger celui qui n'est pas enregistré. Plusieurs de ces
   règles ont été payées par de vrais bogues : on restyle, on ne réorganise
   pas. */
/* Les blocs du détail RESPIRENT. Ils se touchaient — « Informations » finissait
   quatre pixels au-dessus du quart — et l'écran se lisait comme un seul pavé
   dont on ne distinguait plus les parties. */
#corps-caisse {
  display: flex;
  flex-direction: column;
  gap: var(--e6);
}
#corps-caisse > .carte, .rangee-detail > .carte { padding: var(--e6); }

/* `stretch` et non `flex-start` : les rubans prennent TOUTE la hauteur de la
   fiche à côté d'eux. Alignés en haut, ils s'arrêtaient à mi-carte et
   laissaient un vide sous eux — de la place perdue là où il faut justement
   de la surface pour lire un ruban. */
.rangee-detail { display: flex; gap: var(--e6); align-items: stretch; }
.rangee-detail > .colonne-infos { flex: 1; min-width: 0; }

/* ── Les rubans ──────────────────────────────────────────────────────────
   C'EST LA PIÈCE À CONVICTION DE L'ÉCRAN : on vient ici pour comparer un
   chiffre saisi à ce qui est imprimé sur le papier.

   Ils faisaient 150×200 en `object-fit: cover`, c'est-à-dire qu'un ruban de
   366×720 était ROGNÉ EN SON MILIEU — ni l'en-tête ni les totaux n'étaient
   visibles. Une vignette qui ne se lit pas occupe de la place sans rendre le
   service qu'on attend d'elle.

   Désormais : le rapport d'un ruban (1:2), `contain` pour ne rien couper, et
   un clic pour l'ouvrir en grand. `aspect-ratio` est posé sur le CADRE, donc
   avant que l'image n'arrive : la page ne sursaute pas au chargement. */
/* 480 px pour les deux rubans, soit un peu moins de la moitié des 1060 de
   l'écran. La fiche perd de la largeur et y gagne : ses intitulés et ses
   montants se rapprochent, donc se lisent ensemble au lieu d'être séparés
   par du vide. */
.rubans { display: flex; gap: var(--e4); flex: none; width: 480px; }
.ruban-colonne {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--e2);
}
/* Le cadre prend la hauteur restante de la colonne, laquelle est étirée à
   celle de la fiche. Pas d'`aspect-ratio` : c'est la carte voisine qui donne
   la hauteur, et elle la donne AVANT que l'image n'arrive — la page ne
   sursaute donc pas davantage qu'avec un rapport figé. */
.ruban {
  flex: 1;
  min-height: 260px;
  border-radius: var(--r1);
  border: 1px solid var(--bord);
  background: var(--fond-page);
  display: grid; place-items: center;
  overflow: hidden;
  color: var(--encre-2);
  font-size: var(--t-petit);
  text-align: center;
  padding: var(--e2);
}
.ruban img {
  width: 100%; height: 100%;
  /* `contain` et non `cover` : un ruban se lit en entier ou ne sert à rien. */
  object-fit: contain;
  display: block;
}
.ruban-lisible {
  cursor: zoom-in;
  padding: 0;
  transition: border-color var(--t-vif) var(--sortie);
}
.ruban-lisible:hover { border-color: var(--accent); }
.ruban-absent { border-style: dashed; }
.ruban-nom {
  font-size: var(--t-petit);
  color: var(--encre-2);
  text-align: center;
}

/* Sous 1240 px, les rubans passent SOUS la fiche.
   Côte à côte, ils lui prenaient 480 px fixes : à 1100 px de fenêtre il ne
   lui restait que 300 px, dont 54 pour la colonne des intitulés — « Cash en
   enveloppe » s'y repliait sur trois lignes. Une fiche illisible pour garder
   deux photos au même rang n'est pas un compromis, c'est une perte sèche.
   Empilés, ils gardent leur rapport de ruban puisque plus aucune carte
   voisine ne leur donne de hauteur. */
@media (max-width: 1240px) {
  .rangee-detail { flex-direction: column; }
  .rubans { width: 100%; max-width: 420px; }
  .ruban { min-height: 0; aspect-ratio: 1 / 2; }
}

/* Le ruban en grand. Sans lui, il faudrait zoomer le navigateur entier pour
   lire une ligne de chiffres — et perdre de vue le champ qu'on vérifie. */
.voile-ruban {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  padding: var(--e8);
  background: rgb(0 0 0 / .82);
  cursor: zoom-out;
  animation: cal-apparait var(--t-moyen) var(--sortie);
}
.voile-ruban img {
  max-width: min(96vw, 900px);
  max-height: 94vh;
  object-fit: contain;
  border-radius: var(--r1);
  box-shadow: 0 24px 64px rgb(0 0 0 / .6);
}
.fermer-ruban {
  position: fixed;
  top: var(--e4); right: var(--e4);
  width: 36px; height: 36px;
  border-radius: 50%;
  background: var(--surface);
  border: 1px solid var(--bord-fort);
  color: var(--encre);
  font-size: 15px;
  cursor: pointer;
}
.fermer-ruban:hover { background: var(--surface-2); }

.champs-detail { display: flex; flex-direction: column; }
/* 180px et non 150 : « 100 000,00 » touchait les bords de son champ.
   Les rangées respirent (e3 au lieu de e2) — c'est une fiche qu'on relit,
   pas un tableau qu'on balaie. */
.rangee-champ {
  display: grid;
  grid-template-columns: 1fr 180px;
  gap: var(--e4);
  align-items: center;
  padding: var(--e3) 0;
  border-bottom: 1px solid var(--bord);
}
.rangee-champ:last-child { border-bottom: none; }
.rangee-champ .intitule { font-size: var(--t-corps); color: var(--encre-2); }
.rangee-champ .saisie-texte { height: 32px; }
/* Un <select> et un <input type=date> imposent leur largeur intrinsèque —
   flèche de menu, icône de calendrier comprises — et élargiraient leur piste
   de grille, si bien que leur rangée sortirait plus large que les autres. */
.rangee-champ select.saisie-texte, .rangee-champ input[type="date"] { min-width: 0; }

/* UNE SEULE GRILLE POUR TOUT LE QUART.
   Chaque groupe avait la sienne, souple : deux champs de ventes se
   répartissaient sur deux colonnes, trois tip outs sur trois, et rien ne
   s'alignait d'une rangée à l'autre. Trois colonnes fixes mettent tous les
   montants sur les mêmes verticales — c'est ce qui fait qu'une colonne de
   chiffres se lit d'un coup d'œil au lieu de se déchiffrer. */
.grille-quart {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--e4) var(--e6);
  max-width: 660px;
}
/* Les cases se serrent : elles se lisent en liste, pas en tableau. */
.grille-quart.presences { gap: var(--e2) var(--e6); }

/* L'intertitre qui nomme chaque groupe. Les trois répondaient à trois
   questions différentes sans qu'aucun libellé ne le dise. */
.sous-titre-bloc {
  font-size: var(--t-petit);
  font-weight: 500;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--encre-2);
  margin: var(--e6) 0 var(--e3);
}
.sous-titre-bloc:first-of-type { margin-top: var(--e4); }


/* La valeur calculée s'affiche SOUS la valeur saisie. Une saisie humaine est
   gelée et ne se recalcule plus (non-négociable 6b) ; c'est cet écart-là qui
   doit rester lisible, sans quoi il passerait pour un bug. */
.calcule { font-size: var(--t-petit); color: var(--encre-2); margin-top: 2px; }
.calcule.ecart { color: var(--accent); }

/* Une case à cocher masquée mais focusable doit être positionnée CONTRE son
   propre libellé. Sans `position: relative` ici, son bloc conteneur devient
   le premier ancêtre positionné, et le navigateur, en la révélant au focus,
   fait glisser tout le panneau. Corrigé une fois dans le kiosque ; la même
   règle vaut ici dès le premier jour. */
.case {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--e2);
  cursor: pointer;
  font-size: var(--t-corps);
  padding: var(--e1) 0;
}
.case input { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
.case .marque {
  flex: none;
  width: 18px; height: 18px;
  border: 1px solid var(--bord-fort);
  border-radius: var(--r3);
  display: grid; place-items: center;
  transition: background var(--t-vif) var(--sortie),
              border-color var(--t-vif) var(--sortie);
}
.case input:checked + .marque { background: var(--accent); border-color: var(--accent); }
.case input:checked + .marque::after {
  content: "";
  width: 9px; height: 5px;
  border-left: 2px solid var(--sur-accent);
  border-bottom: 2px solid var(--sur-accent);
  transform: rotate(-45deg) translate(1px, -1px);
}
.case input:focus-visible + .marque { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Le bloc d'annulation, en dernier, et il se distingue sans crier. */
.bloc-annulation { border-color: var(--alerte-voile); }
.bloc-annulation h2 { color: var(--alerte); }

/* Un cadre vide se confond avec un chargement qui n'a pas abouti : ce qui
   est vide le DIT. Même règle que le kiosque applique à une caisse sans
   photo. */
.vide { color: var(--encre-2); font-size: var(--t-corps); }

.barre-outils {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e3);
  flex-wrap: wrap;
}

/* ═══════════════════════════════════════════════════════ vue d'ensemble
   DEUX colonnes et non `auto-fit` : à 1440px, `auto-fit` en produisait trois
   et la quatrième carte restait seule sur sa rangée — une asymétrie qui se
   lit comme un oubli. Quatre cartes veulent 2×2. */
.grille-ensemble {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--e4);
  align-items: start;
}
@media (max-width: 1100px) {
  .grille-ensemble { grid-template-columns: minmax(0, 1fr); }
}

.compte {
  font-size: var(--t-grand);
  font-weight: 500;
  line-height: 1.1;
  letter-spacing: -.02em;
}
.compte.en-alerte { color: var(--alerte); }
.compte-note { font-size: var(--t-petit); color: var(--encre-2); margin-top: 2px; }

.resume { display: flex; flex-direction: column; gap: var(--e1); margin-top: var(--e3); }
.ligne-resume {
  display: flex; align-items: center; gap: var(--e2);
  padding: var(--e2);
  border-radius: var(--r3);
  font-size: var(--t-corps);
  transition: background var(--t-vif) var(--sortie);
}
.ligne-resume:hover { background: var(--touche); }
.ligne-resume .nom {
  flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ligne-resume .val { color: var(--encre-2); font-size: var(--t-petit); }

.tete-carte .voir {
  font-size: var(--t-petit);
  color: var(--encre-2);
  display: inline-flex; align-items: center; gap: var(--e1);
  transition: color var(--t-vif) var(--sortie);
}
.tete-carte .voir:hover { color: var(--accent); }

/* ═══════════════════════════════════════════════ écarts et employés
   Chaque liste a sa géométrie. Les colonnes d'argent restent à droite et en
   chiffres tabulaires : deux montants l'un sous l'autre doivent se comparer
   d'un coup d'œil. */
.tableau-ecarts { grid-template-columns: 1.4fr .8fr .8fr .9fr .9fr .5fr; }
.tableau-employes { grid-template-columns: .6fr 1.6fr 1.2fr .9fr 1.4fr; }
.tableau-ecarts .case, .tableau-employes .badges { justify-self: start; }

/* ══════════════════════════════════ le rappel des caisses TAKE OUT

   Elles n'écrivent RIEN dans le classeur : leurs pourboires se répartissent
   à la main, moitié Suite moitié Cuisine. Ce bloc est la seule trace que le
   gérant en aura, puisque le classeur n'en dira rien.

   L'avertissement est ÉCRIT, pas seulement teinté : la couleur ne dit rien à
   qui la distingue mal, et celle-ci porte une consigne qu'on ne peut pas
   deviner. `--alerte` reste juste ici — c'est bien une chose qui demande une
   action, et elle ne se fera pas toute seule. */
.rappel-take-out:empty { display: none; }
.bloc-take-out {
  margin-top: var(--e4);
  padding: var(--e4);
  background: var(--alerte-voile);
  border: 1px solid var(--alerte);
  border-radius: var(--r2);
}
.avis-take-out {
  font-size: var(--t-corps);
  font-weight: 500;
  color: var(--alerte);
  margin-bottom: var(--e1);
}

/* Le débordement réutilise le bloc du take out : même gabarit, même
   `--alerte`. Les deux disent la même chose au gérant — « ceci ne sera pas
   dans le classeur, occupe-t'en » — et deux traitements visuels pour un
   seul message n'apprendraient rien de plus. La liste des noms, elle, doit
   se lire un par un : pas de puce décorative, du chiffre tabulaire pour que
   deux noms voisins s'alignent. */
.liste-debordement {
  margin: var(--e2) 0 var(--e3) var(--e4);
  padding: 0;
  list-style: none;
}
.liste-debordement li {
  padding: 2px 0;
  font-weight: 500;
}
.liste-debordement li::before {
  content: "• ";
  color: var(--alerte);
}
.bloc-debordement + .bloc-take-out,
.bloc-debordement .avis-take-out:not(:first-child) { margin-top: var(--e4); }

.tableau-take-out { margin-top: var(--e3); }
.entete-take-out, .ligne-take-out {
  display: grid;
  grid-template-columns: 1.1fr .7fr 1fr 1fr 1fr;
  gap: var(--e3);
  align-items: center;
  padding: var(--e2) 0;
  font-size: var(--t-corps);
}
.entete-take-out {
  font-size: var(--t-petit);
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--encre-2);
  border-bottom: 1px solid var(--bord);
}
.ligne-take-out + .ligne-take-out { border-top: 1px solid var(--bord); }


/* ═══════════════════════════════════ le type d'une fiche : serveur ou poste

   Trois choix exclusifs, donc de VRAIS boutons radio — le clavier les
   parcourt, le lecteur d'écran les annonce comme un groupe. Le style de
   segments ne change que l'apparence : la case native est masquée sans
   jamais quitter le document, sinon elle cesserait d'exister pour le
   clavier. */
.groupe-type { border: none; padding: 0; margin: 0; }
.groupe-type legend { padding: 0; }
.segments {
  display: flex;
  gap: var(--e1);
  padding: 3px;
  background: var(--touche);
  border: 1px solid var(--bord);
  border-radius: var(--r1);
}
.segment { flex: 1; }
.segment input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}
.segment span {
  display: block;
  padding: var(--e2) var(--e3);
  border-radius: calc(var(--r1) - 3px);
  text-align: center;
  font-size: var(--t-corps);
  color: var(--encre-2);
  cursor: pointer;
  transition: background var(--t-vif) var(--sortie),
              color var(--t-vif) var(--sortie);
}
.segment:hover span { color: var(--encre); }
.segment input:checked + span {
  background: var(--surface-2);
  color: var(--encre);
  font-weight: 500;
}
/* Le foyer se voit sur l'ÉTIQUETTE, la case native étant invisible : sans
   cette règle, tabuler dans le groupe ne montrerait rien du tout. */
.segment input:focus-visible + span {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* Une fiche de poste porte sa marque dans la liste. Ton d'accent et non
   d'alerte : ce n'est pas un problème, c'est une nature. */
.badge-poste { background: var(--accent-voile); color: var(--accent); }


/* ═══════════════════════════════════════════ les caisses déjà relues

   Un pense-bête de gérant, rangé dans son navigateur — pas un état de la
   caisse. Voir champs.js.

   La case est SŒUR de la rangée et non son enfant : la rangée est un bouton,
   et un bouton dans un bouton n'est pas du HTML valide. La grille du parent
   tient les deux colonnes alignées, en-tête compris. */
.rangee-caisse {
  display: grid;
  grid-template-columns: var(--colonne-coche, 44px) minmax(0, 1fr);
  align-items: center;
}
.rangee-caisse > .ligne-tableau { padding-left: 0; }
.rangee-caisse > .entete-tableau { padding-left: 0; }
.entete-rangee { border-bottom: 1px solid var(--bord); }
.entete-rangee > .entete-tableau { border-bottom: none; }

.case-verifiee {
  /* 28px de côté dans une gouttière de 44 : la cible du clic dépasse le
     dessin, ce qui évite de rater la case en visant vite. */
  width: 28px;
  height: 28px;
  margin: 0 auto;
  display: grid;
  place-items: center;
  border: 1.5px solid var(--bord-fort);
  border-radius: 50%;
  background: transparent;
  color: transparent;              /* la coche n'existe qu'une fois cochée */
  cursor: pointer;
  transition: background var(--t-vif) var(--sortie),
              border-color var(--t-vif) var(--sortie),
              color var(--t-vif) var(--sortie);
}
.case-verifiee svg { width: 16px; height: 16px; display: block; }
.case-verifiee:hover:not(:disabled) { border-color: var(--encre-2); }
.case-verifiee:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
/* Le vert du verdict. Il ne porte JAMAIS l'information seul : la coche est
   dessinée par-dessus, et le détail écrit le mot en toutes lettres. */
.case-verifiee.cochee {
  background: var(--verdict);
  border-color: var(--verdict);
  color: var(--fond-page);
}
.case-verifiee:disabled { opacity: .25; cursor: default; }

.case-large { width: 34px; height: 34px; }
.case-large svg { width: 20px; height: 20px; }

/* Une rangée relue s'efface d'un cran — assez pour que l'œil saute aux
   autres, pas au point de la rendre illisible : on doit pouvoir revenir
   dessus. */
.ligne-tableau.verifiee .nom-serveur { color: var(--encre-2); }

/* La barre du haut : où j'en suis, et les deux gestes qui raccourcissent la
   boucle de relecture. */
.barre-relecture {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--e3);
  margin-bottom: var(--e4);
}
.barre-relecture:empty { display: none; }
.compte-relecture { font-size: var(--t-corps); color: var(--encre-2); }
.compte-relecture.tout-fait { color: var(--verdict); }
.gestes-relecture { display: flex; align-items: center; gap: var(--e2); }
/* Le filtre actif se voit : un bouton qui a changé la liste sans le dire
   fait chercher la caisse manquante ailleurs. */
.bouton.bouton-actif {
  border-color: var(--accent);
  color: var(--accent);
}

/* « Validé ? » — en haut du détail, avant les chiffres. */
.bloc-verification {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e3);
  padding: var(--e3) 0 var(--e4);
  border-bottom: 1px solid var(--bord);
  margin-bottom: var(--e4);
}
.etiquette-verification { font-size: var(--t-petit); color: var(--encre-2); }
.geste-verification { display: flex; align-items: center; gap: var(--e3); }
.mot-verification { font-size: var(--t-corps); color: var(--encre-2); }

/* Les flèches de l'en-tête du détail : la caisse voisine sans repasser par
   la liste. */
.entete-avec-fleches { display: flex; align-items: center; gap: var(--e4); }
.fleches-caisse { display: flex; gap: var(--e1); }
.bouton-fleche {
  width: 36px;
  height: 36px;
  display: grid;
  place-items: center;
  border: 1px solid var(--bord);
  border-radius: var(--r1);
  background: transparent;
  color: var(--encre-2);
  cursor: pointer;
  transition: background var(--t-vif) var(--sortie),
              border-color var(--t-vif) var(--sortie),
              color var(--t-vif) var(--sortie);
}
.bouton-fleche:hover:not(:disabled) {
  background: var(--touche);
  border-color: var(--bord-fort);
  color: var(--encre);
}
.bouton-fleche:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
/* Comme les flèches de pagination : en bout de course elles restent VISIBLES
   et cessent de répondre, sinon l'en-tête se réorganise à chaque bord. */
.bouton-fleche:disabled { opacity: .3; cursor: default; }


/* ═══════════════════════════════════════════════════════════ pagination
   Vingt caisses par page. Le repère à gauche — « 21-40 sur 137 » — vient
   AVANT les boutons : c'est lui qui empêche de croire qu'une liste coupée à
   vingt est une liste de vingt.

   Vide quand il n'y a qu'une page : la règle est en JS, mais `:empty` fait
   aussi disparaître la marge, sans quoi le bas du tableau flotterait sans
   raison. */
.pagination {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--e3);
  margin-top: var(--e4);
}
.pagination:empty { display: none; }
.compte-pages { font-size: var(--t-petit); color: var(--encre-2); }
.boutons-pages { display: flex; align-items: center; gap: var(--e1); }

/* 36px de côté : au-dessus de la cible confortable pour un pointeur, et
   assez large pour un numéro à trois chiffres sans que la barre ne saute de
   largeur en passant de 9 à 10. */
.page-bouton {
  min-width: 36px;
  height: 36px;
  padding: 0 var(--e2);
  border: 1px solid var(--bord);
  border-radius: var(--r1);
  background: transparent;
  color: var(--encre-2);
  font-size: var(--t-petit);
  font-family: inherit;
  cursor: pointer;
  transition: background var(--t-vif) var(--sortie),
              border-color var(--t-vif) var(--sortie),
              color var(--t-vif) var(--sortie);
}
.page-bouton:hover:not(:disabled) {
  background: var(--touche);
  border-color: var(--bord-fort);
  color: var(--encre);
}
.page-bouton:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
/* Une flèche en bout de course reste VISIBLE et cesse de répondre : la
   retirer ferait bouger toute la barre d'un cran à chaque extrémité. */
.page-bouton:disabled { opacity: .35; cursor: default; }
.page-courante {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--fond-page);
  font-weight: 500;
}
.page-courante:hover { background: var(--accent); color: var(--fond-page); }
.page-saut {
  padding: 0 var(--e1);
  color: var(--encre-3);
  font-size: var(--t-petit);
  user-select: none;
}

/* L'unité vit SOUS son intitulé, jamais flottante à côté du champ. */
.rangee-champ .intitule .aide { display: block; }
label.rangee-champ { cursor: pointer; }

/* ══════════════════════════════════════════════════════════════ modale
   Le voile laisse voir la liste derrière : le gérant crée une fiche en ayant
   sous les yeux celles qui existent déjà. */
.fond-modale {
  position: absolute;
  inset: 0;
  z-index: 5;
  display: grid;
  place-items: center;
  padding: var(--e6);
  background: rgb(18 18 18 / .72);
}
.formulaire-employe {
  width: 100%;
  max-width: 420px;
  max-height: 100%;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: var(--e4);
}

/* Aucun constat : 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; }

/* ═════════════════════════════════════════════════════════ calendrier
   Le choix de la période de paie : un bouton, et deux mois en surimpression.

   Les champs date natifs vivent toujours derrière, masqués — ils restent la
   source de vérité et c'est `calendrier.js` qui les remplit. `display: none`
   et non `visibility` : un champ invisible mais focusable piégerait la
   tabulation dans un contrôle que personne ne voit. */
.champs-natifs { display: none; }

.bloc-periode { position: relative; max-width: 420px; }

.declencheur-periode {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e3);
  width: 100%;
  height: 38px;
  padding: 0 var(--e3);
  background: var(--fond-page);
  border: 1px solid var(--bord);
  border-radius: var(--r1);
  font-size: var(--t-saisie);
  color: var(--encre);
  text-align: left;
  transition: border-color var(--t-vif) var(--sortie),
              background var(--t-vif) var(--sortie);
}
.declencheur-periode:hover { border-color: var(--bord-fort); }
.declencheur-periode[aria-expanded="true"] { border-color: var(--accent); }
.declencheur-periode .icone { flex: none; color: var(--encre-2); }
.declencheur-periode:hover .icone { color: var(--encre); }
/* Aucune période choisie : le libellé s'efface pour se lire comme une
   invitation, pas comme une valeur. */
#etiquette-periode.vide { color: var(--encre-2); }

/* Le panneau. Il monte de 6 px en apparaissant — assez pour se lire comme
   une apparition, trop peu pour être un glissement. Aucun rebond : sur de
   l'informationnel, le dépassement se lit comme du négligé. */
.panneau-calendrier {
  position: absolute;
  top: calc(100% + var(--e2));
  left: 0;
  z-index: 30;
  padding: var(--e4);
  background: var(--fond);
  border: 1px solid var(--bord-fort);
  border-radius: var(--r2);
  box-shadow: 0 12px 32px rgb(0 0 0 / .45);
  animation: cal-apparait var(--t-moyen) var(--sortie);
}
@keyframes cal-apparait {
  from { opacity: 0; transform: translateY(-6px) scale(.98); }
  to   { opacity: 1; transform: none; }
}

/* Les flèches encadrent les grilles, à hauteur des noms de mois. */
.cal-corps { display: flex; align-items: flex-start; gap: var(--e2); }
.cal-fleche {
  display: grid; place-items: center;
  width: 28px; height: 28px;
  border-radius: var(--r3);
  font-size: 18px;
  line-height: 1;
  color: var(--encre-2);
  transition: background var(--t-vif) var(--sortie), color var(--t-vif) var(--sortie);
}
.cal-fleche:hover { background: var(--touche); color: var(--encre); }

.cal-mois { display: flex; gap: var(--e6); }
.cal-nom-mois {
  font-size: var(--t-petit);
  font-weight: 500;
  color: var(--encre);
  text-align: center;
  text-transform: capitalize;
  margin-bottom: var(--e2);
}

/* 7 colonnes de 34 px : la grille ne bouge pas d'un mois à l'autre, parce
   qu'on peint toujours 42 cases, débordements compris. */
.cal-grille {
  display: grid;
  grid-template-columns: repeat(7, 34px);
  gap: 2px 0;
}
.cal-jour-semaine {
  display: grid; place-items: center;
  height: 26px;
  font-size: var(--t-petit);
  color: var(--encre-3);
}
/* Samedi et dimanche, à peine marqués : assez pour repérer une fin de
   semaine du coin de l'œil, pas assez pour concurrencer la sélection. */
.cal-jour-semaine[data-rang="5"], .cal-jour-semaine[data-rang="6"] {
  color: var(--encre-2);
}

.cal-jour {
  position: relative;
  display: grid; place-items: center;
  height: 32px;
  font-size: var(--t-corps);
  font-variant-numeric: tabular-nums;
  color: var(--encre);
  border-radius: var(--r1);
  transition: background var(--t-vif) var(--sortie), color var(--t-vif) var(--sortie);
}
.cal-jour:hover { background: var(--touche-active); }
.cal-jour.dehors { color: var(--encre-3); }

/* AUJOURD'HUI : un point sous le chiffre, jamais un fond. Un fond entrerait
   en concurrence avec la sélection, et on ne saurait plus lequel des deux
   on regarde. */
.cal-jour.aujourdhui::after {
  content: "";
  position: absolute;
  bottom: 4px; left: 50%;
  width: 3px; height: 3px;
  border-radius: 50%;
  background: var(--accent);
  transform: translateX(-50%);
  transition: background var(--t-vif) var(--sortie);
}

/* LA PLAGE. Le milieu est plat et teinté, les extrémités sont pleines et
   arrondies vers l'extérieur seulement : c'est ce qui fait lire une barre
   continue plutôt que quatorze pastilles alignées.

   Le rayon nul sur le bord intérieur est ce qui soude les cases entre
   elles ; sans lui, une encoche apparaît à chaque jour. */
.cal-jour.dans-la-plage {
  background: var(--accent-voile);
  border-radius: 0;
  color: var(--encre);
}
.cal-jour.dans-la-plage.dehors { color: var(--encre-2); }
.cal-jour.bord-debut { border-radius: var(--r1) 0 0 var(--r1); }
.cal-jour.bord-fin { border-radius: 0 var(--r1) var(--r1) 0; }
.cal-jour.bord-debut.bord-fin { border-radius: var(--r1); }
.cal-jour.bord-debut, .cal-jour.bord-fin {
  background: var(--accent);
  color: var(--sur-accent);
  font-weight: 500;
}
/* Sur une extrémité pleine, le point d'aujourd'hui passe en sombre — sinon
   il disparaîtrait dans l'orange. */
.cal-jour.aujourdhui.bord-debut::after,
.cal-jour.aujourdhui.bord-fin::after { background: var(--sur-accent); }

/* Les 2 px d'écart entre rangées laisseraient la barre se rompre d'une
   semaine à l'autre : on la prolonge derrière l'interligne. */
.cal-jour.dans-la-plage::before {
  content: "";
  position: absolute;
  inset: -1px 0;
  z-index: -1;
  background: var(--accent-voile);
}
.cal-jour.bord-debut::before { left: 0; border-radius: var(--r1) 0 0 var(--r1); }
.cal-jour.bord-fin::before { right: 0; border-radius: 0 var(--r1) var(--r1) 0; }

.cal-pied {
  margin-top: var(--e3);
  padding-top: var(--e3);
  border-top: 1px solid var(--bord);
  font-size: var(--t-petit);
  color: var(--encre-2);
}

@media (max-width: 640px) {
  .cal-mois { flex-direction: column; gap: var(--e4); }
}

/* ═══════════════════════════════════════════════════════ mouvement réduit
   L'attente GARDE son animation, ralentie et non coupée : c'est la seule
   chose qui distingue « ça travaille » de « c'est figé ». Une règle
   générale `animation: none` la supprimerait — c'est exactement l'erreur
   que cette exception existe pour empêcher. */
@media (prefers-reduced-motion: reduce) {
  .point.rempli { animation: none; }
  .attente .point { animation-duration: 2.4s; }
  .ecran, .ecran.visible, .app, .point, .bouton, .saisie-texte,
  .item-nav, .replier svg, .item-nav .libelle {
    transition-duration: 1ms;
  }
}

/* ═════════════════════════════════════════════════ période de paie — accueil

   Un seul geste au repos, et il doit se voir depuis l'autre bout de la
   pièce ; sous lui, la mémoire de ce qui a déjà été remis.

   Le bouton ordinaire (`.bouton`, hauteur 36) pesait exactement autant
   qu'ACTUALISER. Ici il ne pèse plus pareil que rien. Le libellé n'est pas
   en capitales : « Générer une nouvelle période de paie » fait trente-cinq
   caractères, et une phrase entière en capitales se déchiffre au lieu de se
   lire. Les capitales restent aux verbes courts des boutons secondaires. */

.bloc-geste {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--e2);
  padding: var(--e6) 0 var(--e4);
}

.bouton-geste {
  font-family: inherit;
  font-size: var(--t-titre);
  font-weight: 500;
  background: var(--accent);
  /* Encre SOMBRE sur l'accent, toujours : ivoire sur cet orange mesure
     2.6:1. C'est la règle que :root signale comme la plus facile à casser
     par distraction, et un bouton de cette taille la casserait en grand. */
  color: var(--sur-accent);
  border: none;
  border-radius: var(--r2);
  padding: var(--e4) var(--e6);
  min-height: 76px;
  width: min(560px, 100%);
  cursor: pointer;
  transition: background var(--t-vif) var(--sortie),
              transform var(--t-vif) var(--sortie);
}
.bouton-geste:hover { background: var(--accent-clair); }
.bouton-geste:active { transform: translateY(1px); }
.bouton-geste:focus-visible {
  outline: 2px solid var(--encre);
  outline-offset: 3px;
}
.bloc-historique {
  display: flex;
  flex-direction: column;
  gap: var(--e3);
}
.tete-historique > h2 {
  font-size: var(--t-petit);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--encre-2);
}
.tete-historique > .sous-titre { margin-top: 2px; }

/* Trois colonnes seulement, et la dernière calée à droite : période et heure
   disent QUOI et QUAND et se lisent ensemble ; le contenu du classeur ferme
   la rangée. */
.tableau-generations {
  grid-template-columns: minmax(0, 220px) minmax(0, 200px) minmax(0, 1fr);
}
/* Les rangées sont des <button> — c'est un vrai contrôle, atteignable au
   clavier. Sans cette remise à zéro elles hériteraient de la police et du
   fond que le navigateur donne aux boutons. */
button.ligne-tableau {
  font: inherit;
  color: inherit;
  background: none;
  border: none;
  border-bottom: 1px solid var(--bord);
}
button.ligne-tableau:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
/* Un classeur d'un mégaoctet ne part pas instantanément : la rangée dit
   qu'elle a été entendue, sinon elle se fait cliquer une seconde fois. */
.ligne-tableau.en-cours { opacity: .5; }

.periode-generation { color: var(--encre); }
.instant-generation { color: var(--encre-2); font-size: var(--t-corps); }
/* « Heures seulement » n'est pas une erreur — c'est un classeur réellement
   remis. Il porte donc l'encre secondaire et non la couleur d'alerte, qui
   est réservée à ce qui demande une décision. Mais il se distingue, pour
   qu'on ne le renvoie pas en le croyant complet. */
.contenu-complet { color: var(--encre-2); font-size: var(--t-corps); }
.contenu-partiel {
  color: var(--accent);
  font-size: var(--t-corps);
}
