/* ---- base.css ---- */
/*
 * Composants de référence (bouton principal, champ de formulaire, carte,
 * lien), construits uniquement à partir des variables de tokens.css. Voir
 * docs/design-system.md pour les règles d'usage des couleurs.
 */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
}

/* Focus clavier visible sur tout élément interactif (audit qualité, issue
   #60, WCAG 2.2 §1) : filet de sécurité au jeton --action déjà utilisé par
   .champ input:focus ci-dessous — les composants qui définissent déjà leur
   propre focus (formulaires, onglets) l'emportent par spécificité, celui-ci
   couvre les liens, boutons et cartes qui n'en avaient pas. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
  outline: var(--epaisseur-focus) solid var(--action);
  outline-offset: var(--epaisseur-trait);
}

/* Lien d'évitement « Aller au contenu » (layout.tsx) : hors écran tant
   qu'il n'a pas le focus clavier, puis posé en haut à gauche au-dessus de
   tout (audit accessibilité du 25/09, WCAG 2.4.1). */
.lien-evitement {
  position: absolute;
  left: var(--espacement-petit);
  top: calc(var(--grille-base) * -20);
  z-index: 1000;
  padding: var(--espacement-petit) var(--bouton-padding-horizontal);
  border-radius: var(--rayon);
  background: var(--surface);
  color: var(--texte);
  font-weight: var(--graisse-semibold);
  text-decoration: none;
}
.lien-evitement:focus {
  top: var(--espacement-petit);
}
/* Cible du lien d'évitement (tabindex="-1" posé par layout.tsx) : le focus
   y est déplacé pour la lecture, pas pour une action — pas d'anneau autour
   de toute la page. */
main[tabindex='-1']:focus {
  outline: none;
}

/* Focus clavier des choix dont le vrai champ est masqué (radio en
   `opacity: 0` ou .visuellement-cache sous une tuile, champ fichier dans un
   label-bouton) : l'anneau se pose sur la tuile ou le bouton qui porte
   l'état, sinon le focus clavier était invisible (audit accessibilité du
   25/09, WCAG 2.4.7). */
.role:has(input:focus-visible),
.choix-carte:has(input:focus-visible),
.tunnel-mobile__option:has(input:focus-visible),
.option:has(input:focus-visible),
.ball-e-tuile:has(input:focus-visible),
.choix-groupe label:has(input:focus-visible),
label.bouton:has(input:focus-visible),
.champ:has(> input[type='file']:focus-visible) label.bouton[for],
.champ-lien:has(.champ-lien-champ:focus-visible) {
  outline: var(--epaisseur-focus) solid var(--action);
  outline-offset: var(--epaisseur-trait);
}

body {
  margin: 0;
  background: var(--fond);
  color: var(--texte);
  font-family: var(--police-texte);
  font-weight: var(--graisse-regular);
  font-size: var(--taille-texte-1);
  line-height: 1.5;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  font-family: var(--police-titres);
  font-weight: var(--graisse-titre);
  letter-spacing: var(--interlettrage-titre);
  margin: 0 0 var(--grille-base) 0;
}

.titre-1 {
  font-size: var(--taille-titre-1);
}
.titre-2 {
  font-size: var(--taille-titre-2);
}
.titre-3 {
  font-size: var(--taille-titre-3);
}
.titre-4 {
  font-size: var(--taille-titre-4);
}

.texte-corps {
  color: var(--texte);
  font-size: var(--taille-texte-1);
}

.texte-secondaire {
  color: var(--texte-secondaire);
  font-size: var(--taille-texte-2);
}

/* Valeur chiffrée (prix, tensions, compteurs, dates de tableau) : chasse
   fixe, plus facile à comparer d'une ligne à l'autre qu'en Inter. */
.nombre {
  font-family: var(--police-nombre);
  font-variant-numeric: tabular-nums;
}

/* Valeur de base biffée à côté de la valeur effective, quand une réduction
   de certification s'applique (tarification effective, issue #65). */
.valeur-barree {
  text-decoration: line-through;
  color: var(--texte-secondaire);
}

/* Gabarit d'écran mobile d'abord (marges verticales, largeur héritée de
   .page — voir plus bas). */
.ecran {
  padding-block: var(--espacement-section);
  display: flex;
  flex-direction: column;
  gap: var(--espacement-section);
  min-height: 100vh;
}

/* Logo : dessin vectoriel (monogramme double-S + mot-symbole), jamais
   recomposé en texte (docs/design-system.md). */
.logo {
  display: block;
  height: 28px;
  width: auto;
}
.logo--petit {
  height: 22px;
}
/* Pleine taille, centrée : écrans inscription et connexion. */
.logo--pleine {
  height: auto;
  width: 180px;
  margin-inline: auto;
}

/* Monogramme discret de la page d'erreur (remplace l'ancien tamis). */
.erreur-monogramme {
  width: 64px;
  height: auto;
  opacity: 0.35;
}

/* Champ de formulaire : étiquette toujours visible, aide et erreur. */
.champ {
  display: flex;
  flex-direction: column;
  gap: var(--grille-base);
  margin-bottom: var(--grille-base);
}
.champ label {
  font-family: var(--police-texte);
  font-weight: var(--graisse-semibold);
  font-size: var(--taille-texte-2);
  color: var(--texte);
}
.champ input,
.champ select,
.champ textarea {
  font-family: var(--police-texte);
  font-size: var(--taille-texte-1);
  color: var(--texte);
  background: var(--surface);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
  padding: var(--espacement-champ-vertical) var(--espacement-champ-horizontal);
  min-height: var(--cible-tactile-min);
  width: 100%;
}
.champ textarea {
  resize: vertical;
}
.champ input::placeholder,
.champ select::placeholder,
.champ textarea::placeholder {
  color: var(--texte-secondaire);
}
.champ input:focus,
.champ select:focus,
.champ textarea:focus {
  outline: var(--epaisseur-focus) solid var(--action);
  outline-offset: var(--epaisseur-trait);
}
.champ .aide {
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
  margin: 0;
}

/* Groupe de choix radio/case à cocher (ex. créneau horaire vs journée
   entière) : distinct de .champ pour ne pas hériter du style des champs
   texte, qui déformerait les cases natives. */
.choix-groupe {
  display: flex;
  flex-direction: column;
  gap: var(--grille-base);
  margin-bottom: var(--grille-base);
}
.choix-groupe label {
  display: flex;
  align-items: center;
  gap: var(--espacement-petit);
  font-family: var(--police-texte);
  font-size: var(--taille-texte-1);
  color: var(--texte);
}

/* Interrupteur/bascule (issue n°173, ex-scopé à .adm-shell--app) : sorti de
   bureau.css par la consolidation de l'issue n°183 pour devenir le seul
   composant de bascule de l'app — auparavant deux styles coexistaient
   (celui-ci en bureau, une case nue stylée par accent-color pour
   admin/site, admin/parametres, admin/recette via .champ--interrupteur,
   retirée). Une case à cocher réelle et accessible (.visuellement-cache,
   interface.css), stylée en bascule plutôt que pilotée en JS — voir
   docs/design-system.md, section Composants. */
.interrupteur {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 12px 0;
  border-top: var(--epaisseur-trait) solid var(--trait);
  font-size: var(--taille-texte-1);
  cursor: pointer;
}
.interrupteur:first-of-type {
  border-top: 0;
}
/* Un interrupteur qui suit un champ garde son filet haut, même s'il est le
   premier label de son conteneur (fiche fournisseur, fiche produit…) : placée
   après :first-of-type, à spécificité égale la dernière règle gagne. */
.champ + .interrupteur {
  border-top: var(--epaisseur-trait) solid var(--trait);
}
.interrupteur small {
  display: block;
  color: var(--texte-secondaire);
  font-size: var(--taille-texte-2);
}
.bascule {
  width: 40px;
  height: 24px;
  border-radius: var(--rayon-pastille);
  background: var(--trait);
  position: relative;
  flex: none;
}
.bascule::after {
  content: '';
  position: absolute;
  top: 3px;
  left: 3px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--surface);
}
.interrupteur input:checked + .bascule {
  background: var(--accent-marque-encre);
}
.interrupteur input:checked + .bascule::after {
  left: 19px;
}
.interrupteur input:focus-visible + .bascule {
  outline: var(--epaisseur-focus) solid var(--texte);
  outline-offset: 2px;
}

/* Bouton principal (aplat marine, un seul par écran) et secondaire. Pleine
   largeur et confort tactile 18 px sur mobile ; taille au contenu et
   padding 10/16 à partir du palier bureau (voir @media plus bas). */
.bouton {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: var(--cible-tactile-min);
  padding: var(--bouton-padding-vertical) var(--marge-ecran);
  border-radius: var(--rayon);
  border: none;
  font-family: var(--police-titres);
  font-weight: var(--graisse-titre);
  font-size: var(--taille-titre-4);
  cursor: pointer;
}
.bouton--principal {
  background: var(--action);
  color: var(--sur-action);
}
.bouton--secondaire {
  background: var(--surface);
  color: var(--texte);
  border: var(--epaisseur-trait) solid var(--trait);
}
/* Action destructive (suppression de compte) : seul cas d'usage de
   --erreur hors formulaire, décidé sur l'issue #28 pour marquer une action
   irréversible. */
.bouton--danger {
  color: var(--erreur);
  border-color: var(--erreur);
}
.bouton:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

/* Carte et carte de mise en avant (une seule par écran) : blanc, filet
   --trait, rayon unique, sans ombre. */
.carte {
  background: var(--surface);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
  padding: var(--carte-padding-vertical) var(--carte-padding-horizontal);
}
.carte--mise-en-avant {
  background: var(--surface-haute);
  color: var(--sur-action);
  border: var(--epaisseur-trait) solid var(--accent-marque);
  border-radius: var(--rayon);
  padding: var(--carte-padding-vertical) var(--carte-padding-horizontal);
}
/* Carte cliquable (ex. carte « Mes raquettes », carte raquette de la liste). */
.carte--lien {
  display: flex;
  flex-direction: column;
  gap: var(--grille-base);
  text-decoration: none;
  color: inherit;
}

/* Liste de cartes empilées. */
.liste-cartes {
  display: flex;
  flex-direction: column;
  gap: var(--espacement-petit);
}

/* Carte Leaflet (issue #61, /commander vue Carte et /relais/carte) : hauteur
   fixe requise par la bibliothèque, largeur pleine, mêmes formes que .carte
   (rayon, trait) pour rester cohérente avec le reste de l'écran. */
.cartographie {
  height: var(--hauteur-carte);
  border-radius: var(--rayon);
  border: var(--epaisseur-trait) solid var(--trait);
  overflow: hidden;
}

/* Carte de commande de /cordeur/commandes (issue #54) : même empilement que
   .carte--lien (statut, contenu, action rapide), sans les styles de lien —
   la carte elle-même n'est plus cliquable, seuls le nom du joueur et
   l'action rapide le sont (un <a>/<form> ne peut pas contenir de contenu
   interactif imbriqué). */
.carte--commande {
  display: flex;
  flex-direction: column;
  gap: var(--grille-base);
}

/* Ligne de disponibilité (créneau, congé) avec bouton de retrait. */
.ligne-disponibilite {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--espacement-petit);
}
.ligne-disponibilite form {
  margin: 0;
}
.bouton--discret {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--cible-tactile-min);
  padding: 0;
  border: none;
  background: none;
  color: var(--texte-secondaire);
  font-family: var(--police-texte);
  font-weight: var(--graisse-semibold);
  font-size: var(--taille-texte-2);
  text-decoration: underline;
  cursor: pointer;
}

/* Étiquette de sport (tennis, badminton, squash…). */
.etiquette-sport {
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  padding: var(--grille-base) var(--espacement-petit);
  border-radius: var(--rayon-etiquette);
  background: transparent;
  border: var(--epaisseur-trait) solid var(--trait);
  color: var(--texte-secondaire);
  font-family: var(--police-texte);
  font-weight: var(--graisse-semibold);
  font-size: var(--taille-texte-4);
  text-transform: uppercase;
  letter-spacing: var(--interlettrage-etiquette);
}

/* Étiquette de statut de commande : état, donc vert sans aplat — contour et
   texte, jamais de fond plein (réservé au bouton principal). Capitales
   11 px. */
.etiquette-statut {
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  padding: var(--grille-base) var(--espacement-petit);
  border-radius: var(--rayon-etiquette);
  background: transparent;
  border: var(--epaisseur-trait) solid var(--action);
  color: var(--action);
  font-family: var(--police-texte);
  font-weight: var(--graisse-semibold);
  font-size: var(--taille-texte-4);
  text-transform: uppercase;
  letter-spacing: var(--interlettrage-etiquette);
}
/* Sur la carte de mise en avant (fond marine), --action n'a plus de contraste
   avec le fond : seul cas où le vert fluo sert d'accent (--accent-marque),
   réservé aux fonds marine. */
.carte--mise-en-avant .etiquette-statut {
  border-color: var(--accent-marque);
  color: var(--accent-marque);
}

/* Lien. */
.lien {
  color: var(--action);
  font-weight: var(--graisse-semibold);
  text-decoration: underline;
}
/* Lien de ligne de tableau dense (issue #56, listes admin) : avatar-initiale
   puis nom, sur une seule ligne cliquable. */
.lien--ligne {
  display: inline-flex;
  align-items: center;
  gap: var(--espacement-petit);
}

/* Encart d'erreur de formulaire, annoncé via role="alert". */
.encart {
  border-radius: var(--rayon);
  padding: var(--espacement-champ-vertical) var(--marge-ecran);
  font-size: var(--taille-texte-2);
}
.encart--erreur {
  background: color-mix(in srgb, var(--erreur) 15%, var(--surface));
  border: var(--epaisseur-trait) solid var(--erreur);
  color: var(--erreur);
  font-weight: var(--graisse-medium);
}
.encart--erreur[hidden] {
  display: none;
}
.encart--info {
  background: color-mix(in srgb, var(--action) 6%, var(--surface));
  border: var(--epaisseur-trait) solid var(--trait);
  color: var(--texte);
}

/* Liste de /notifications (issue #50) : chaque ligne est un formulaire POST
   sans JS (marque lue puis redirige, voir POST /notifications/:id/ouvrir),
   dont le bouton est stylé comme une carte cliquable. */
.notif-liste {
  display: flex;
  flex-direction: column;
  gap: var(--espacement-petit);
}
.notif-liste form {
  margin: 0;
}
.notif-carte {
  display: flex;
  flex-direction: column;
  gap: var(--grille-base);
  width: 100%;
  text-align: left;
  font: inherit;
  color: inherit;
  cursor: pointer;
}
.notif-titre {
  margin: 0;
  font-size: var(--taille-texte-1);
  font-weight: var(--graisse-medium);
}
.notif-titre--non-lue {
  font-weight: var(--graisse-titre);
}
.notif-corps {
  margin: 0;
  font-size: var(--taille-texte-2);
  color: var(--texte-secondaire);
}
.notif-date {
  margin: 0;
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
}

/* Chronologie verticale des événements d'une commande (accepter, poser,
   prête, récupérée…). Pastille verte (état, jamais cliquable — voir
   docs/design-system.md) reliée par un trait. */
.chronologie {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}
.chronologie-etape {
  display: flex;
  gap: var(--espacement-petit);
}
.chronologie-repere {
  display: flex;
  flex-direction: column;
  align-items: center;
  flex-shrink: 0;
}
.chronologie-pastille {
  width: calc(var(--grille-base) * 3);
  height: calc(var(--grille-base) * 3);
  border-radius: var(--rayon-pastille);
  background: var(--action);
  flex-shrink: 0;
}
/* Sur la carte de mise en avant (fond marine), même raison que
   .etiquette-statut ci-dessus : --accent-marque plutôt que --action. */
.carte--mise-en-avant .chronologie-pastille {
  background: var(--accent-marque);
}
.chronologie-trait {
  flex: 1;
  width: var(--epaisseur-trait);
  min-height: var(--grille-base);
  background: var(--trait);
  margin: var(--grille-base) 0;
}
.chronologie-contenu {
  padding-bottom: var(--espacement-section);
  display: flex;
  flex-direction: column;
  gap: var(--grille-base);
}
.chronologie-etape:last-child .chronologie-contenu {
  padding-bottom: 0;
}
.chronologie-libelle {
  font-family: var(--police-texte);
  font-weight: var(--graisse-semibold);
  font-size: var(--taille-texte-1);
  color: var(--texte);
  margin: 0;
}
.chronologie-date {
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
  margin: 0;
}
.chronologie-motif {
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
  font-style: italic;
  margin: 0;
}

/* Étape courante (issue #52, /poses/:id) : contour plutôt qu'aplat, pour
   distinguer le dernier événement écrit (statut courant) du passé révolu. */
.chronologie-pastille--courante {
  background: transparent;
  border: var(--epaisseur-focus) solid var(--action);
}
.carte--mise-en-avant .chronologie-pastille--courante {
  border-color: var(--accent-marque);
}
/* Étapes à venir (issue #52) : grisées, jamais d'événement encore écrit. */
.chronologie-pastille--future {
  background: var(--trait);
}
.chronologie-etape--future .chronologie-libelle {
  color: var(--texte-secondaire);
  font-weight: var(--graisse-regular);
}

/* Groupe d'actions de transition (bouton principal + actions secondaires,
   éventuellement avec un champ de motif). */
.actions-commande {
  display: flex;
  flex-direction: column;
  gap: var(--espacement-petit);
}
/* Paire de décision (accepter/refuser, valider/rejeter…) : voir
   .actions-paire (boutons.css, issue n°249) — remplace cette variante
   .actions-commande--decision, dont le `flex-direction: column` hérité
   ci-dessus empilait les deux boutons plutôt que de les garder sur une
   ligne (retour de Vincent du 24/09, corrige le correctif du 23/09 posé ici
   même, issue n°244). */

/* Rangée de boutons (formulaires côte à côte, ex. Admin → Site, Admin →
   Médiathèque, fil de messages). Retour recette du 15/09 (issue n°207,
   « pas la première fois ») : la classe n'avait plus de règle de base ici
   — seuls des correctifs scopés à une page (.page-admin-site .actions-ligne
   et consorts, voir ecrans.css/mediatheque.css/lcs.css) supposaient un
   `display: flex` qui n'existait nulle part, donc les boutons s'empilaient
   sans espace. */
.actions-ligne {
  display: flex;
  flex-wrap: wrap;
  gap: var(--espacement-petit);
}

/* Note en étoiles (issue #20) : caractères ★/☆, jamais d'image. Texte marine,
   cas autorisé par la hiérarchie du vert (texte, hors aplat — voir
   docs/design-system.md). */
.note-etoiles {
  color: var(--action);
  font-size: var(--taille-titre-3);
  margin: 0;
}

/* Groupe de boutons radio « 1 à 5 étoiles » du formulaire de notation :
   variante horizontale de .choix-groupe. */
.note-etoiles-choix {
  flex-direction: row;
  align-items: center;
  gap: var(--espacement-petit);
}
.note-etoiles-choix label {
  flex-direction: column;
  gap: var(--grille-base);
}

/* Fil de messages d'une commande (issue #24) : bulles alignées à droite pour
   les miens (fond plus clair), à gauche sur carte pour ceux de l'autre
   partie. */
.fil-messages {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--espacement-petit);
}
.bulle {
  display: flex;
  flex-direction: column;
  gap: var(--grille-base);
  align-self: flex-start;
  max-width: 85%;
  background: var(--surface);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
  padding: var(--espacement-champ-vertical) var(--espacement-champ-horizontal);
}
.bulle--moi {
  align-self: flex-end;
  background: var(--surface-haute);
  color: var(--sur-action);
  border-color: var(--surface-haute);
}
.bulle-texte {
  font-size: var(--taille-texte-1);
  margin: 0;
  white-space: pre-wrap;
}
.bulle-meta {
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
  margin: 0;
}
.bulle--moi .bulle-meta {
  color: var(--texte-sur-sombre);
}

.pied-de-page {
  text-align: center;
  color: var(--texte-secondaire);
  font-size: var(--taille-texte-4);
  padding: var(--marge-ecran) 0;
}

/* Toast de confirmation (`?ok=`, remplace l'ancien encart de /compte) :
   pilule marine en bas de l'écran, masquée après 4 s par le script inline
   qui l'accompagne (seul JS toléré, voir docs/design-system.md). N'existe
   dans le DOM que lorsqu'il y a quelque chose à confirmer. */
.toast {
  position: fixed;
  left: 50%;
  bottom: var(--espacement-section);
  transform: translateX(-50%);
  background: var(--surface-haute);
  color: var(--sur-action);
  border-radius: var(--rayon-pastille);
  padding: var(--espacement-petit) var(--espacement-section);
  font-family: var(--police-texte);
  font-weight: var(--graisse-semibold);
  font-size: var(--taille-texte-2);
  box-shadow: none;
  z-index: 10;
}

/* Aide contextuelle « ? » près d'un titre de page : panneau natif
   <details>/<summary>, sans JavaScript. */
.aide {
  display: inline-block;
}
.aide-bouton {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: var(--rayon-pastille);
  border: var(--epaisseur-trait) solid var(--trait);
  color: var(--texte-secondaire);
  background: var(--surface);
  cursor: pointer;
  list-style: none;
}
.aide-bouton::-webkit-details-marker {
  display: none;
}
.aide-panel {
  margin-top: var(--grille-base);
  background: var(--surface);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
  padding: var(--espacement-champ-vertical) var(--espacement-champ-horizontal);
  font-size: var(--taille-texte-2);
  color: var(--texte-secondaire);
  max-width: var(--largeur-formulaire);
}

/* Conteneur de page unique : porte la largeur et le centrage des écrans
   sans barre latérale (inscription, connexion, erreur, récapitulatif
   imprimable). `.contenu` (voir plus bas) reste `display: contents` sur ces
   écrans : le gabarit ne change pas pour eux. */
.page {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  margin: 0 auto;
  padding-inline: var(--marge-ecran);
}
.contenu {
  display: contents;
}

/* Écrans connectés seulement (voir .lateral et .nav-basse ci-dessous) :
   réserve la hauteur de la navigation basse mobile sous le palier bureau
   pour qu'elle ne masque jamais le bas du contenu — réinitialisé dans le
   @media du palier bureau, où .nav-basse disparaît au profit de .lateral. */
.page--connectee {
  padding-bottom: calc(var(--hauteur-nav-basse) + env(safe-area-inset-bottom));
}

/* Navigation basse mobile (issue #58) : barre fixe en bas d'écran, pendant
   de la barre latérale sous le palier bureau — voir navigation-basse.tsx.
   Masquée à partir de 900 px (voir @media plus bas), où .lateral prend le
   relais. */
.nav-basse {
  display: flex;
  align-items: flex-end;
  justify-content: space-around;
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 10;
  height: var(--hauteur-nav-basse);
  padding-bottom: env(safe-area-inset-bottom);
  background: var(--surface);
  border-top: var(--epaisseur-trait) solid var(--trait);
}
.nav-basse-onglet {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--grille-base);
  flex: 1;
  height: 100%;
  color: var(--texte-secondaire);
  font-family: var(--police-texte);
  font-weight: var(--graisse-medium);
  font-size: var(--taille-texte-4);
  text-decoration: none;
}
.nav-basse-icone-conteneur {
  position: relative;
  display: inline-flex;
}
.nav-basse-icone {
  width: var(--taille-icone-nav-basse);
  height: var(--taille-icone-nav-basse);
  flex-shrink: 0;
}
.nav-basse-onglet--actif {
  color: var(--action);
  font-weight: var(--graisse-semibold);
}
/* Onglet central (Corder côté joueur, Scanner côté cordeur — issue n°140,
   maquettes du 09/09) : rond menthe remonté au-dessus de la barre, icône
   encre — même disque que .geste-compteur, jamais la couleur seule (icône +
   graisse du libellé à l'état actif). */
.nav-basse-onglet--central .nav-basse-icone-conteneur {
  align-items: center;
  justify-content: center;
  width: var(--cible-tactile-min);
  height: var(--cible-tactile-min);
  margin-top: calc(var(--decalage-bouton-commander) * -1);
  border-radius: var(--rayon-pastille);
  background: var(--accent-marque);
  border: var(--liseret-bouton-commander) solid var(--surface);
  box-shadow: var(--ombre-bouton-central); /* anneau gris + ombre : un bouton posé sur la barre */
}
.nav-basse-onglet--central .nav-basse-icone {
  color: var(--texte);
}
.nav-basse-onglet--central .nav-basse-libelle {
  color: var(--texte);
  font-weight: var(--graisse-semibold);
}
/* Pastille de non-lus (issue #50) sur l'onglet Accueil, même jeton que
   .lateral-lien-compteur côté barre latérale. */
.nav-basse-pastille {
  position: absolute;
  top: calc(var(--grille-base) * -1);
  right: calc(var(--grille-base) * -2);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: calc(var(--grille-base) * 4);
  height: calc(var(--grille-base) * 4);
  padding: 0 var(--grille-base);
  border-radius: var(--rayon-pastille);
  background: var(--action);
  color: var(--sur-action);
  font-family: var(--police-nombre);
  font-size: var(--taille-texte-4);
  font-weight: var(--graisse-semibold);
}

/* Compteur de non-lus (issue #50), sur l'entrée « Notifications » de la
   barre latérale et sur le lien mobile équivalent (voir Layout). Poussé à
   droite de la ligne par le flex du lien parent (margin-left: auto). */
.lateral-lien-compteur {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: calc(var(--grille-base) * 5);
  height: calc(var(--grille-base) * 5);
  padding: 0 var(--grille-base);
  margin-left: auto;
  border-radius: var(--rayon-pastille);
  background: var(--action);
  color: var(--sur-action);
  font-family: var(--police-nombre);
  font-size: var(--taille-texte-4);
  font-weight: var(--graisse-semibold);
}

/* Utilitaires de visibilité par palier (ex. tableau bureau / cartes mobile
   de /admin/cordeurs, /cordeur/commandes, et tableau de bord de l'accueil ;
   /poses est en cartes à tous les paliers depuis l'issue n°155). Pas de
   règle .seulement-mobile hors @media : en
   dessous du palier bureau, l'élément garde l'affichage donné par ses
   propres classes (ex. .liste-cartes) — une règle "display: revert" ici
   entrerait en conflit de spécificité égale avec ces classes. */
.seulement-bureau {
  display: none;
}
/* Sous le palier bureau, .seulement-bureau doit gagner contre les règles plus
   spécifiques des composants (.champ label, .carte h2…) — constaté le 12/09
   sur /admin/mediatheque mobile : libellé et titre « bureau » affichés en
   double. Utilitaire d'affichage : !important est légitime ici. */
@media (max-width: 899px) {
  .seulement-bureau {
    display: none !important;
  }
}

/* En-tête de page : titre à gauche, action principale à droite à partir du
   palier bureau (voir @media plus bas). Sous 900 px, l'action est un bouton
   pleine largeur sous le titre (comportement par défaut de .bouton et de la
   disposition en colonne ci-dessous). */
.entete-page {
  display: flex;
  flex-direction: column;
  gap: var(--espacement-petit);
}
.entete-page form {
  margin: 0;
}
/* Titre + aide contextuelle (« ? »), regroupés qu'ils soient seuls (accueil,
   commander) ou dans un .entete-page (disponibilités, commandes reçues). */
.entete-page-titre {
  display: flex;
  align-items: center;
  gap: var(--espacement-petit);
}
.entete-page-titre h1 {
  margin: 0;
}
/* Sous-titre phrase complète (.sous-titre, pas une bulle d'aide) : il
   s'empile sous le titre au lieu de partager sa ligne (retour Vincent du
   29/09, /cordeur/livraison ; même règle que /relais/cordeurs, issue
   n°211). Exclut le cas où une .aide-bulle est aussi présente : elle reste
   accolée au titre. */
.entete-page-titre:has(> .sous-titre):not(:has(> .aide-bulle)) {
  flex-direction: column;
  align-items: flex-start;
}
/* Retour au-dessus de l'en-tête (<EnTetePage retour=…>, entete-page.tsx,
   issue n°248) : rapproché de .entete-page par une marge négative — le gap de
   .ecran (--espacement-section) resterait trop grand entre un fil d'Ariane et
   le titre qu'il précède. */
.entete-page-retour {
  gap: 4px;
  margin-bottom: calc(var(--espacement-petit) - var(--espacement-section));
}
/* Sans taille explicite, le SVG du picto (viewBox seul) remplit son
   conteneur : vu en capture e2e sur la fiche fournisseur (issue n°266). */
.entete-page-retour svg {
  width: var(--taille-icone-retour);
  height: var(--taille-icone-retour);
  flex: none;
}

/* Tableau bureau (listes /admin/cordeurs, /cordeur/commandes, /raquettes, et
   « Dernières poses » de l'accueil) : en-tête capitales, lignes séparées par
   un filet, ligne survolée sur le papier.
   Défilement horizontal propre au tableau lui-même (pas de div d'habillage
   à ajouter partout) : un tableau à plusieurs colonnes ne rétrécit jamais
   sous sa largeur de contenu — sans ceci, les colonnes de droite débordent
   hors de l'écran sur mobile, invisibles et impossibles à atteindre (retour
   Vincent du 17/09, /admin/centrale/produits/:id, tableau des variantes). */
.tableau {
  display: block;
  width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border-collapse: collapse;
}
.tableau th,
.tableau td {
  text-align: left;
  padding: var(--espacement-petit) var(--espacement-champ-horizontal);
  border-bottom: var(--epaisseur-trait) solid var(--trait);
  font-size: var(--taille-texte-2);
}
.tableau th {
  font-family: var(--police-texte);
  font-weight: var(--graisse-semibold);
  color: var(--texte-secondaire);
  text-transform: uppercase;
  font-size: var(--taille-texte-4);
  letter-spacing: var(--interlettrage-etiquette);
}
.tableau tbody tr:hover {
  background: var(--fond);
}

/* Rangée de compteurs du tableau de bord (accueil, à partir du palier
   bureau). */
.compteurs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--espacement-petit);
}
.compteur {
  background: var(--surface);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
  padding: var(--carte-padding-vertical) var(--carte-padding-horizontal);
  display: flex;
  flex-direction: column;
  gap: var(--grille-base);
}
.compteur-valeur {
  font-family: var(--police-nombre);
  font-variant-numeric: tabular-nums;
  font-size: var(--taille-titre-1);
  color: var(--texte);
}
.compteur-libelle {
  font-size: var(--taille-texte-2);
  color: var(--texte-secondaire);
}
/* Compteur cliquable (issue #56, /admin) : mêmes jetons que .compteur, en
   lien plutôt qu'en div — contour marine au survol, seul indice visuel
   au-delà du curseur (jamais la couleur seule sur le contenu). */
.compteur--lien {
  text-decoration: none;
  color: inherit;
}
.compteur--lien:hover {
  border-color: var(--action);
}

/* Onglets de filtre (issue #54, /cordeur/commandes ?statut=) : liens
   horizontaux, défilables plutôt qu'empilés sur mobile — même logique
   d'état que le lien de nav latérale (graisse + couleur, jamais la couleur
   seule). */
.onglets {
  display: flex;
  gap: var(--espacement-petit);
  overflow-x: auto;
  border-bottom: var(--epaisseur-trait) solid var(--trait);
}
.onglet {
  padding: var(--espacement-petit) var(--grille-base);
  color: var(--texte-secondaire);
  text-decoration: none;
  font-size: var(--taille-texte-2);
  white-space: nowrap;
  border-bottom: calc(var(--epaisseur-trait) * 2) solid transparent;
}
.onglet--actif {
  color: var(--action);
  font-weight: var(--graisse-semibold);
  border-bottom-color: var(--action);
}

/* Pagination (issue #63, /admin/commandes) : liens précédent/suivant de part
   et d'autre du numéro de page, même espacement que .onglets. */
.pagination {
  display: flex;
  align-items: center;
  gap: var(--espacement-champ-horizontal);
}

/* Pagination en pilules numérotées (issue n°187, /admin/raquettes) : à poser
   dans .pagination ci-dessus. Pilules 40px, page courante en encre — même
   grammaire que .seg/.filtre (lcs.css), en composant à part car dédiée aux
   numéros plutôt qu'à un choix de filtre. Voir /qa/design. */
.pagination-pages {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--grille-base);
}
.pagination-page {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 40px;
  height: 40px;
  padding: 0 var(--grille-base);
  border-radius: var(--rayon-pastille);
  border: var(--epaisseur-trait) solid var(--trait);
  color: var(--texte);
  text-decoration: none;
  font-weight: var(--graisse-semibold);
  font-variant-numeric: tabular-nums;
}
.pagination-page:hover {
  border-color: var(--texte);
}
.pagination-page--active {
  background: var(--texte);
  border-color: var(--texte);
  color: var(--surface);
}
.pagination-ellipsis {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 40px;
  height: 40px;
  color: var(--texte-secondaire);
}

/* Liste compacte (issue n°187, /admin/raquettes) : lignes ~56px, alternative
   dense à .liste-cartes pour les catalogues à fort volume (100+ entrées) —
   bascule .filtres Liste/Blocs juste au-dessus, mémorisée par cookie. */
.liste-lignes {
  display: flex;
  flex-direction: column;
  border-top: var(--epaisseur-trait) solid var(--trait);
}
.ligne-compacte {
  display: flex;
  align-items: center;
  gap: var(--espacement-petit);
  min-height: 56px;
  padding: var(--grille-base) 0;
  border-bottom: var(--epaisseur-trait) solid var(--trait);
}
.ligne-compacte-principal {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
}
.ligne-compacte-titre {
  font-size: var(--taille-texte-1);
  font-weight: var(--graisse-forte);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ligne-compacte-sous {
  font-size: var(--taille-texte-2);
}
.ligne-compacte form {
  margin: 0;
}

/* Élément poussé à l'extrémité d'un parent en colonne flex (.ecran) sans lui
   dédier un conteneur — ex. compteur de résultats aligné à droite au-dessus
   d'une liste paginée (issue n°187). */
.aligne-fin {
  align-self: flex-end;
}

/* Page détail (/poses/:id, /cordeur/commandes/:id, /commander/:stringerId) :
   colonne principale (récap, chronologie, messages) puis colonne latérale
   (actions, notation, re-commande), empilées en mobile, réparties en grille
   à partir du palier bureau (voir @media plus bas). */
.detail-mise-en-page {
  display: flex;
  flex-direction: column;
  gap: var(--espacement-section);
}
.detail-colonne-principale,
.detail-colonne-laterale {
  display: flex;
  flex-direction: column;
  gap: var(--espacement-section);
}

/* Vue semaine de /cordeur/disponibilites (issue #54) : grille 7 colonnes à
   partir du palier bureau (les 14 prochains jours se répartissent alors sur
   deux lignes) ; liste par jour en dessous (voir .seulement-mobile,
   @media plus bas). Chaque jour est une .carte-atelier (issue n°145, mêmes
   statuts à sens que les cartes « à faire/prêtes » de l'accueil cordeur) :
   voir ecrans.css.*/
.semaine {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: var(--espacement-petit);
}

/* Étapes numérotées du parcours commande (issue #52, src/web/etapes.tsx) :
   1 Cordeur · 2 Créneau · 3 Confirmation. Séparateur « · » entre étapes via
   ::before plutôt qu'un gap uniforme, pour garder le numéro collé à son
   libellé. */
.etapes {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
}
.etape {
  display: flex;
  align-items: center;
  gap: var(--grille-base);
}
.etape + .etape::before {
  content: '·';
  margin: 0 var(--espacement-petit);
  color: var(--trait);
}
.etape-numero {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: calc(var(--grille-base) * 5);
  height: calc(var(--grille-base) * 5);
  border-radius: var(--rayon-pastille);
  border: var(--epaisseur-trait) solid var(--trait);
  font-family: var(--police-nombre);
  font-size: var(--taille-texte-4);
  flex-shrink: 0;
}
.etape--fait {
  color: var(--texte);
}
/* Menthe = fait/ok (issue n°140) : une étape franchie du tunnel de
   réservation porte le même sens que partout ailleurs. */
.etape--fait .etape-numero {
  background: var(--accent-marque);
  border-color: var(--accent-marque);
  color: var(--texte);
}
.etape--actif {
  color: var(--texte);
  font-weight: var(--graisse-semibold);
}
.etape--actif .etape-numero {
  border-color: var(--action);
  color: var(--action);
}

/* Avatar rond à initiale (issue #52) : fond marine, initiale en vert fluo —
   seul cas hors carte de mise en avant où le vert sert d'accent, la surface
   étant elle-même marine (même contraste que --surface-haute). */
.avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: calc(var(--grille-base) * 11);
  height: calc(var(--grille-base) * 11);
  border-radius: var(--rayon-pastille);
  background: var(--surface-haute);
  color: var(--accent-marque);
  font-family: var(--police-titres);
  font-weight: var(--graisse-titre);
  font-size: var(--taille-titre-3);
  flex-shrink: 0;
}
.avatar--grand {
  width: calc(var(--grille-base) * 16);
  height: calc(var(--grille-base) * 16);
  font-size: var(--taille-titre-1);
}
/* Avatar de ligne de tableau dense (issue #56, listes admin) : plus petit que
   le format par défaut, pour ne pas alourdir une ligne déjà chargée en
   colonnes. */
.avatar--petit {
  width: calc(var(--grille-base) * 8);
  height: calc(var(--grille-base) * 8);
  font-size: var(--taille-texte-2);
}

/* Grille de créneaux groupée par jour (issue #52,
   /commander/:stringerId) : un groupe par jour, boutons-créneaux larges à
   l'intérieur. */
.creneaux-jour {
  display: flex;
  flex-direction: column;
  gap: var(--espacement-petit);
}
.creneaux-jour-titre {
  font-family: var(--police-texte);
  font-weight: var(--graisse-semibold);
  font-size: var(--taille-texte-2);
  color: var(--texte-secondaire);
  margin: 0;
}
.creneaux-grille {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(calc(var(--grille-base) * 40), 1fr));
  gap: var(--espacement-petit);
}
.creneau {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: var(--cible-tactile-min);
  padding: var(--espacement-champ-vertical) var(--espacement-champ-horizontal);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
  background: var(--surface);
  color: var(--texte);
  text-align: center;
  text-decoration: none;
  font-family: var(--police-texte);
  font-size: var(--taille-texte-2);
}
a.creneau:hover {
  background: var(--fond);
}

/* Carte de choix sélectionnable (issue #52, cordage de
   /commander/:stringerId/nouvelle) : variante de .choix-groupe où chaque
   option est encadrée, le contour marine indiquant la sélection en plus de
   la case native (jamais la couleur seule). */
.choix-carte {
  display: flex;
  align-items: center;
  gap: var(--espacement-petit);
  padding: var(--espacement-champ-vertical) var(--espacement-champ-horizontal);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
  background: var(--surface);
}
.choix-carte:has(input:checked) {
  border-color: var(--action);
}
.choix-carte input {
  accent-color: var(--action);
}

/* Chip de catalogue (issue #52, « nom · prix »). */
.chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--grille-base);
}
.chip {
  display: inline-flex;
  align-items: center;
  padding: var(--grille-base) var(--espacement-petit);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon-etiquette);
  background: var(--surface);
  color: var(--texte);
  font-size: var(--taille-texte-3);
}

/* État vide (composant <EtatVide>, src/web/etat-vide.tsx, issue n°246) :
   médaillon d'icône du système au-dessus du titre, menthe voile — remplace
   l'ancienne illustration en pointillés et le monogramme qui bat de LCS
   (interdits par la règle du composant : jamais d'illustration ni de
   mascotte hors Ball-E). */
.etat-vide-icone {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--taille-badge-etat-vide);
  height: var(--taille-badge-etat-vide);
  margin: 0 auto;
  border-radius: var(--rayon-pastille);
  background: var(--accent-marque-voile);
  color: var(--accent-marque-encre);
}
.etat-vide-icone svg {
  width: var(--taille-icone-etat-vide);
  height: var(--taille-icone-etat-vide);
}
.etat-vide-titre {
  font-weight: var(--graisse-semibold);
}
.etat-vide {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--espacement-petit);
  padding: var(--espacement-section) 0;
  text-align: center;
  color: var(--texte-secondaire);
}

/* Bandeau d'état de /poses/:id (issue #52) : reprend .carte--mise-en-avant,
   statut en grand puis phrase d'aide contextuelle (domain/orders/format.
   statusHelpText) en --texte-sur-sombre, même jeton que la méta des bulles
   de messages sur fond marine. */
.bandeau-etat {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--grille-base);
}
.bandeau-etat-statut {
  font-family: var(--police-titres);
  font-weight: var(--graisse-titre);
  font-size: var(--taille-titre-2);
  margin: var(--grille-base) 0 0 0;
}
.bandeau-etat-aide {
  color: var(--texte-secondaire);
  font-size: var(--taille-texte-2);
  margin: var(--grille-base) 0 0 0;
}
.bandeau-etat-action {
  margin-top: var(--espacement-petit);
}

/* Contour coloré par statut des cartes de /poses (issue #52) : seule
   dérogation à « état = marine sans aplat » (docs/design-system.md), décidée
   sur cette issue pour distinguer les statuts au premier coup d'œil dans une
   liste dense — le texte de l'étiquette de statut reste inchangé. */
.carte--statut-attente {
  border-color: var(--attention);
}
.carte--statut-action {
  border-color: var(--action);
}
.carte--statut-ok {
  border-color: var(--ok);
}
.carte--statut-inactif {
  border-color: var(--texte-secondaire);
}

/* Vitrine publique (issue #58, `/`, visiteur non connecté) : même conteneur
   centré que inscription/connexion (Layout sans prop `nav`, voir .page
   ci-dessus). */
.vitrine-entete {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--espacement-petit);
}
.vitrine-entete-liens {
  display: flex;
  gap: var(--espacement-petit);
}

.vitrine-heros {
  display: flex;
  flex-direction: column-reverse;
  align-items: center;
  gap: var(--espacement-section);
  text-align: center;
}
/* Composition CSS sobre du héros (pas de photo, docs/design-system.md) :
   monogramme seul sur un disque marine — même filet --accent-marque que le
   logo. */
.vitrine-heros-visuel {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: var(--taille-visuel-heros);
  height: var(--taille-visuel-heros);
  border-radius: var(--rayon-pastille);
  background: var(--surface-haute);
  border: var(--epaisseur-trait) solid var(--accent-marque);
}
.logo--monogramme {
  height: var(--taille-logo-monogramme);
  width: auto;
}

.vitrine-pied {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--espacement-petit) var(--espacement-section);
  padding-top: var(--espacement-section);
  border-top: var(--epaisseur-trait) solid var(--trait);
}

/* Palier tablette (--palier-tablette, dupliqué ici : voir tokens.css). */
@media (min-width: 600px) {
  .page {
    max-width: var(--largeur-colonne-tablette);
  }

  .liste-cartes {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
  }
  /* Chronologie, messages, avis : restent en colonne malgré la grille
     ci-dessus (contenu séquentiel, pas des cartes indépendantes). */
  .liste-cartes.liste-cartes--fil {
    display: flex;
    flex-direction: column;
  }

  /* Formulaires alignés à gauche, jamais centrés (sauf formulaires
     « boutons » ci-dessous, qui restent pleine largeur de leur parent). */
  form,
  .formulaire {
    max-width: var(--largeur-formulaire);
    margin-inline: 0;
  }
  .carte--formulaire {
    max-width: var(--largeur-formulaire);
  }
  .actions-commande form,
  .ligne-disponibilite form {
    max-width: none;
  }

  .cartographie {
    height: var(--hauteur-carte-tablette);
  }
}

/* Palier bureau (--palier-bureau, dupliqué ici : voir tokens.css). */
@media (min-width: 900px) {
  .page {
    max-width: var(--largeur-contenu-bureau);
  }

  /* Le tiroir (navigation.tsx) affiche déjà son propre logo en permanence
     à partir de ce palier — celui posé en tête de chaque page (<Logo />,
     pensé pour le mobile sans tiroir visible) devient redondant. Ne
     touche pas --pleine (connexion/inscription) ni --monogramme (héros
     vitrine), volontairement affichés quel que soit le palier. */
  main.ecran > .logo {
    display: none;
  }

  .bouton {
    width: auto;
    min-height: 0;
    padding: var(--bouton-padding-vertical-bureau) var(--bouton-padding-horizontal-bureau);
  }

  .liste-cartes {
    grid-template-columns: repeat(3, 1fr);
  }

  /* Détail (récap/chronologie/messages + actions/notation/re-commande) :
     deux colonnes à partir du palier bureau, empilées en dessous. */
  .detail-mise-en-page {
    display: grid;
    grid-template-columns: 2fr 1fr;
    align-items: start;
    gap: var(--espacement-section);
  }

  .entete-page {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: var(--espacement-section);
  }
  .entete-page h1 {
    flex: 1;
  }

  .seulement-mobile {
    display: none !important;
  }
  .seulement-bureau {
    display: revert;
  }

  /* Structure bureau : barre latérale fixe (largeur --largeur-laterale) +
     zone de contenu, à la place du conteneur unique centré ci-dessus.
     `.page--connectee` seulement : les écrans sans nav (inscription,
     connexion, erreur, récapitulatif imprimable) gardent le conteneur
     centré. */
  .page--connectee {
    display: grid;
    grid-template-columns: var(--largeur-laterale) 1fr;
    max-width: none;
    width: 100%;
    margin: 0;
    padding-inline: 0;
    padding-bottom: 0;
  }
  .page--connectee .contenu {
    display: block;
    max-width: var(--largeur-contenu-bureau);
    padding: var(--espacement-section) var(--contenu-padding-bureau);
  }

  .nav-basse {
    display: none;
  }

  .vitrine-heros {
    flex-direction: row;
    text-align: left;
  }
  /* Le logo de la barre latérale remplace celui affiché en tête de .ecran
     par chaque écran (mobile d'abord) : on masque ce second logo une fois
     la barre latérale visible, sans dupliquer la logique de rendu des
     pages. */
  .page--connectee .ecran > .logo {
    display: none;
  }
}

/* Récapitulatif de pose imprimable (/poses/:id/recu, issue #32) : la page
   n'a pas de nav, mais boutons, liens, toast et aide n'ont pas leur place
   sur le papier. Couleurs : voir la redéfinition des jetons dans @media
   print de tokens.css (marine sur blanc, décision 003) — body récupère
   automatiquement --fond une fois les jetons redéfinis. */
@media print {
  .bouton,
  .lien,
  .toast,
  .aide {
    display: none;
  }
}

/* Battement du monogramme (écran de lancement et états vides — décision du
   07/09, infini depuis le 12/09 sur demande de Vincent). Défini ici et non
   dans splash.css : l'état vide bat aussi sur les pages sans splash
   (recette, design system), qui ne chargent pas cette feuille. */
@keyframes splash-pulsation {
  0%,
  100% {
    box-shadow: 0 0 0 0 var(--halo-marque);
    transform: scale(1);
  }
  45% {
    box-shadow: 0 0 0 44px var(--halo-marque-fin);
    transform: scale(1.05);
  }
  60% {
    box-shadow: 0 0 0 0 var(--halo-marque);
    transform: scale(1);
  }
  75% {
    box-shadow: 0 0 0 26px var(--halo-marque-fin);
    transform: scale(1.03);
  }
}

/* ---- interface.css ---- */
/*
 * Couche d'interface Stringerz — écrite à la main (Claude, 02/09/2026), sur
 * le modèle de LCS Shop (même société, même famille visuelle). Chargée après
 * base.css : elle redéfinit la structure mobile (en-tête compact, cartes
 * denses, recherche sur une ligne, boutons plus bas) sans toucher aux
 * composants du moteur. Aucune valeur en dur : tout passe par tokens.css.
 */

/* ---------- Base : densité et lisibilité ---------- */

body {
  line-height: var(--interligne);
  -webkit-font-smoothing: antialiased;
}

.ecran {
  gap: var(--espacement-section);
  padding-top: var(--espacement-section);
}

p {
  margin: 0;
}

/* Libellé présent pour les lecteurs d'écran, invisible à l'œil. */
.visuellement-cache {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- Barre du haut mobile (connectée, < 900 px, issue n°189) ---------- */
/* Rendue par <BarreHaute> (barre-haute.tsx), Layout la pose à la place de
   .topbar pour .adm-shell--app (joueur/cordeur) sous le palier bureau — voir
   layout.tsx et lcs.css. Monogramme repris de EnteteSite (.monogramme,
   bureau.css) : même fond marine (--surface-haute), taille propre à cette
   barre. Cloche et avatar réutilisent .bellbtn/.belldot/.avatar (lcs.css,
   base.css), agrandis ici à la taille de cible tactile de la maquette
   (docs/maquettes/2026-09-12/entete-mobile.html) sans changer leur apparence
   ailleurs dans l'app (sélecteurs composés, jamais .bellbtn ou .avatar nus).
   Fond opaque (retour Vincent 13/09, issue n°194 : « la barre flotte ») — le
   contenu qui défile dessous ne doit plus se voir en transparence. Le flou
   translucide de --fond-translucide reste réservé à <EnteteSite> sur bureau
   (bureau.css), qui n'a pas ce défaut. */

.barre-haute {
  display: flex;
  align-items: center;
  gap: 12px;
  height: var(--hauteur-barre-haute);
  padding: 0 var(--marge-ecran);
  background: var(--fond);
  border-bottom: var(--epaisseur-trait) solid var(--trait);
  position: sticky;
  top: 0;
  z-index: 20;
}
.barre-haute .monogramme {
  width: 30px;
  height: 30px;
  border-radius: 8px;
  background: var(--surface-haute);
  display: grid;
  place-items: center;
  flex-shrink: 0;
}
.barre-haute .monogramme img {
  width: 17px;
  height: 17px;
  display: block;
}
.barre-haute-retour {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  border-radius: var(--rayon);
  border: var(--epaisseur-trait) solid var(--trait);
  background: var(--surface);
  color: var(--texte);
  text-decoration: none;
}
.barre-haute-retour svg {
  width: 20px;
  height: 20px;
}
.barre-haute-titre {
  flex: 1;
  min-width: 0;
  font-size: var(--taille-titre-3);
  font-weight: var(--graisse-forte);
  letter-spacing: -0.01em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.barre-haute .bellbtn {
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  border-radius: var(--rayon);
}
.barre-haute .bellbtn svg {
  width: 20px;
  height: 20px;
}
.barre-haute .avatar {
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  font-size: var(--taille-texte-2);
}
/* Prénom à côté de l'avatar (issue n°212) : tronqué plutôt que de pousser le
   titre de rubrique, seul élément de la barre en flex: 1. */
.barre-haute-nom {
  flex-shrink: 0;
  max-width: 72px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--taille-texte-4);
  color: var(--texte-secondaire);
}

@media (min-width: 900px) {
  .barre-haute {
    display: none;
  }
}

/* ---------- Titres de page ---------- */

.entete-page-titre {
  display: flex;
  align-items: center;
  gap: var(--espacement-petit);
}
.titre-1 {
  margin: 0;
  line-height: 1.15;
}
.titre-2 {
  margin: 0 0 var(--espacement-petit);
}
@media (max-width: 899px) {
  .entete-page-titre .aide {
    display: none;
  }
  /* Plus de titre en double sous <BarreHaute> (retour Vincent 13/09, issue
     n°194) : elle porte déjà le nom de la rubrique, le h1 de la page redit la
     même information (« Commandes » / « Commandes », « Mon atelier » /
     « Mon atelier »...). Scopé à .adm-shell--app, seul contexte où
     <BarreHaute> remplace .topbar (layout.tsx) : admin et relais gardent leur
     .pagehead/.entete-page intacts, sans BarreHaute pour faire doublon.
     .pagehead-titre-salut (accueil) est exclu : la barre n'y porte pas de
     titre, « Salut {prénom} » reste seul repère. Ce qui suit le h1 masqué
     (action, sous-titre) remonte à sa place. Quatre pages posent leur h1 hors
     de .pagehead/.entete-page (cordeur.tsx, pose-detail.tsx,
     commander-cordeur.tsx, raquette-formulaire.tsx en édition) : ciblées par
     leur conteneur propre plutôt qu'une classe supplémentaire, pour ne pas
     faire bouger leur balisage. La fiche cordeur est exclue (retour Vincent
     13/09) : le nom du cordeur à côté de son prix est l'identité de la fiche,
     pas une rubrique — il reste, même si la barre l'affiche aussi. L'assistant d'ajout (raquette-formulaire.tsx,
     mode « nouvelle ») n'est pas concerné : son h1 (#assistant-titre,
     aria-live) change de texte à chaque étape (raquette-assistant.js), ce
     n'est pas un doublon du titre générique de la barre.

     Passe en-têtes de page (issue n°248) : `display: none` retire l'élément
     de l'arbre d'accessibilité, pas seulement de l'écran — un lecteur
     d'écran ne voit plus aucun h1 sur ces pages en mobile (cause la plus
     probable du « sans titre principal » relevé par l'audit du 23/09,
     `/compte/notifications` et `/poses/:id` vérifiés). Corrigé par la
     technique déjà utilisée pour le contenu réservé aux lecteurs d'écran
     (.visuellement-cache ci-dessus) plutôt qu'une nouvelle : le titre reste
     dans l'arbre d'accessibilité, seulement retiré de l'écran — mécanisme
     unique documenté dans docs/design-system.md, « En-tête de page ». */
  .adm-shell--app .pagehead h1:not(.pagehead-titre-salut),
  .adm-shell--app .entete-page h1,
  .adm-shell--app .entete-page-titre h1,
  .adm-shell--app .ecran-atelier > h1,
  .adm-shell--app .entete-suivi-titre,
  .adm-shell--app .ecran-raquette-edition > h1 {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
}

/* ---------- Boutons ---------- */

.bouton {
  min-height: var(--cible-tactile-min);
  padding: var(--bouton-padding-vertical) var(--bouton-padding-horizontal);
  font-size: var(--taille-titre-4);
  border-radius: var(--rayon);
  transition: background-color 120ms ease, transform 80ms ease;
}
.bouton:active {
  transform: scale(0.985);
}
.bouton--principal {
  background: var(--action);
  color: var(--sur-action);
}
.bouton--principal:hover,
.bouton--principal:focus-visible {
  background: var(--action-pressee);
}
.bouton--secondaire {
  background: var(--surface);
  color: var(--texte);
  border: var(--epaisseur-trait) solid var(--trait);
}
.bouton--secondaire:hover {
  background: var(--surface-douce);
}
.bouton--compact {
  min-height: var(--cible-tactile-min);
  padding: 6px 12px;
  font-size: var(--taille-texte-2);
  width: auto;
}
.lien {
  color: var(--action);
}

/* ---------- Cartes ---------- */

.carte {
  padding: var(--carte-padding-vertical) var(--carte-padding-horizontal);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
  box-shadow: var(--ombre-carte);
  background: var(--surface);
}
.carte--lien {
  gap: var(--grille-base);
}
.carte--lien .titre-3 {
  margin: 0;
}
.liste-cartes {
  gap: var(--espacement-petit);
}
@media (max-width: 599px) {
  .liste-cartes--deux {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--espacement-petit);
  }
}

/* ---------- Ligne de compte (listes admin mobile — issue n°185) ----------
   Remplace .liste-cartes/.carte sous 900px pour les listes de comptes
   admin (cordeurs, joueurs, relais) : une ligne dense (~72px) séparée par
   un trait, pas une carte avec filet + ombre — le refus de recette du
   12/09 portait sur l'espace perdu par les cartes (230px pour 5 infos). */
.liste-lignes-compte {
  list-style: none;
  margin: 0;
  padding: 0;
}
.ligne-compte {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--espacement-champ-horizontal);
  min-height: 72px;
  padding: var(--espacement-petit) 0;
  border-bottom: var(--epaisseur-trait) solid var(--trait);
  text-decoration: none;
  color: inherit;
}
.ligne-compte:last-child {
  border-bottom: 0;
}
.ligne-compte-corps {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.ligne-compte-nom {
  font-size: var(--taille-texte-1);
  font-weight: var(--graisse-forte);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ligne-compte-sous-titre {
  font-size: var(--taille-texte-2);
  color: var(--texte-secondaire);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ligne-compte-meta {
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
}
.ligne-compte-droite {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 4px;
  text-align: right;
}
.ligne-compte-valeur {
  font-size: var(--taille-texte-2);
  color: var(--texte-secondaire);
  white-space: nowrap;
}

/* ---------- Familles du tableau de bord admin, liste dense mobile ----------
   Retour Vincent du 23/09 (issue n°247) : sous 900px, .liste-cartes empile
   des cartes hautes (titre + description sur deux lignes) pour un simple
   lien de rubrique — remplacée ici par une ligne par rubrique (icône +
   titre + description sur une seule ligne tronquée), .seulement-mobile en
   regard de .liste-cartes.seulement-bureau (admin.tsx). Plus dense que
   .ligne-compte (72px, deux lignes de texte) : une seule ligne de texte,
   hauteur ramenée à --cible-tactile-min — ce n'est pas une liste de
   comptes, pas de colonne de droite. */

/* ---------- Recherche sur une ligne ---------- */

.recherche {
  display: flex;
  gap: var(--espacement-petit);
  align-items: center;
  margin: 0;
}
.recherche input {
  flex: 1;
  min-width: 0;
  font-family: var(--police-texte);
  font-size: var(--taille-texte-1);
  color: var(--texte);
  background: var(--surface);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
  padding: var(--espacement-champ-vertical) var(--espacement-champ-horizontal);
  min-height: var(--cible-tactile-min);
}
.recherche input:focus {
  outline: var(--epaisseur-focus) solid var(--action);
  outline-offset: var(--epaisseur-trait);
}
.recherche .bouton {
  width: auto;
  flex: 0 0 auto;
}
.barre-outils {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--espacement-petit);
}
.barre-outils .onglets {
  margin: 0;
}

/* ---------- Carte cordeur (liste /commander) ---------- */

.carte-cordeur {
  display: grid;
  grid-template-columns: var(--taille-avatar) 1fr;
  grid-template-areas:
    'avatar identite'
    'infos infos'
    'action action';
  column-gap: var(--espacement-champ-vertical);
  row-gap: var(--espacement-petit);
  align-items: center;
  text-decoration: none;
  color: inherit;
}
.carte-cordeur .avatar {
  grid-area: avatar;
  width: var(--taille-avatar);
  height: var(--taille-avatar);
  font-size: var(--taille-titre-3);
}
.carte-cordeur-identite {
  grid-area: identite;
  min-width: 0;
}
.carte-cordeur-identite .titre-3 {
  margin: 0;
  line-height: 1.2;
}
.carte-cordeur-identite .texte-secondaire {
  font-size: var(--taille-texte-3);
}
.carte-cordeur-infos {
  grid-area: infos;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--grille-base) var(--espacement-petit);
  font-size: var(--taille-texte-2);
  color: var(--texte-secondaire);
}
.carte-cordeur-infos .prix {
  font-family: var(--police-nombre);
  font-weight: var(--graisse-semibold);
  color: var(--texte);
}
.carte-cordeur-infos .note-etoiles {
  margin: 0;
  font-size: var(--taille-texte-2);
}
.carte-cordeur-infos .chip {
  font-size: var(--taille-texte-4);
}
.carte-cordeur .bouton {
  grid-area: action;
}

/* ---------- Étiquettes et toast (LCS) ---------- */

.etiquette-nouveau {
  display: inline-flex;
  align-items: center;
  padding: 2px var(--espacement-petit);
  border-radius: var(--rayon-etiquette);
  background: var(--attention-fond);
  color: var(--attention-texte);
  font-family: var(--police-texte);
  font-weight: var(--graisse-semibold);
  font-size: var(--taille-texte-4);
  text-transform: uppercase;
  letter-spacing: var(--interlettrage-etiquette);
}
.toast {
  background: var(--ok-fond);
  color: var(--ok);
  border: var(--epaisseur-trait) solid color-mix(in srgb, var(--ok) 25%, var(--surface));
}

/* ---------- Formulaires ---------- */

.champ {
  gap: 6px;
  margin-bottom: var(--espacement-petit);
}
.champ label {
  font-size: var(--taille-texte-2);
}
.champ input,
.champ select,
.champ textarea {
  border-radius: var(--rayon);
  padding: var(--espacement-champ-vertical) var(--espacement-champ-horizontal);
}
.champ input:focus,
.champ select:focus,
.champ textarea:focus {
  outline-color: var(--action);
}

/* ---------- Navigation basse ---------- */

.nav-basse {
  border-top: var(--epaisseur-trait) solid var(--trait);
  background: var(--surface);
}
.nav-basse a[aria-current='page'] {
  color: var(--action);
}

/* ---------- Geste rond double-S ---------- */

.geste {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--taille-geste);
  height: var(--taille-geste);
  border-radius: var(--rayon-pastille);
  background: var(--surface-haute);
  box-shadow: var(--ombre-carte);
  transition: transform 120ms ease;
}
.geste img {
  width: 56%;
  height: 56%;
}
.geste:active {
  transform: scale(0.96);
}

/* ---------- Accueil mobile : geste + tuiles ---------- */
.accueil-geste {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--espacement-petit);
  padding: var(--espacement-section) var(--marge-ecran);
  text-decoration: none;
  color: inherit;
  text-align: center;
}
.accueil-geste .geste {
  width: calc(var(--taille-geste) * 1.5);
  height: calc(var(--taille-geste) * 1.5);
  position: relative;
}
.geste-compteur {
  position: absolute;
  top: 0;
  right: 0;
  min-width: 28px;
  height: 28px;
  padding: 0 8px;
  border-radius: var(--rayon-pastille);
  background: var(--accent-marque);
  color: var(--surface-haute);
  font-family: var(--police-nombre);
  font-weight: var(--graisse-semibold);
  font-size: var(--taille-texte-2);
  line-height: 28px;
  text-align: center;
}
.tuiles {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--espacement-petit);
}
.tuile {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-height: 84px;
  padding: var(--carte-padding-vertical) var(--carte-padding-horizontal);
  background: var(--surface);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
  box-shadow: var(--ombre-carte);
  text-decoration: none;
  color: inherit;
}
.tuile:active {
  background: var(--surface-douce);
}
.tuile-valeur {
  font-family: var(--police-nombre);
  font-weight: var(--graisse-semibold);
  font-size: var(--taille-titre-1);
  line-height: 1.1;
  color: var(--action);
}

/* ---------- Pastille de langue FR | EN (connexion, Mon compte ; issue n°121) ---------- */

/* Pastille de langue FR | EN (même dessin que la vitrine, sur fond clair). */
.langue-pastille {
  display: inline-flex;
  align-self: center;
  padding: 3px;
  border-radius: var(--rayon-pastille);
  border: var(--epaisseur-trait) solid var(--trait);
  gap: 2px;
}
.langue-pastille-choix {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 34px;
  height: 26px;
  border-radius: var(--rayon-pastille);
  font-size: var(--taille-texte-3);
  font-weight: var(--graisse-semibold);
  letter-spacing: var(--interlettrage-etiquette);
  color: var(--texte-secondaire);
  text-decoration: none;
}
.langue-pastille-choix--actif {
  background: var(--surface-haute);
  color: var(--sur-action);
}


/* ---------- Grand visuel de raquette (Hugeicons, 07/09) ---------- */

.raquette-visuel {
  color: var(--texte);
}
.raquette-visuel .picto-tete {
  fill: var(--accent-marque-voile);
}
.raquette-visuel {
  stroke-width: 1.75;
}

/* ---------- États vides : phrase d'aide sous le message (07/09) ---------- */

.etat-vide-aide {
  max-width: 34ch;
  text-align: center;
  margin: 0 0 var(--espacement-petit);
}

/* ---------- Invitation à installer l'app (installer.js, 08/09) ---------- */

.installer-app {
  position: fixed;
  left: var(--marge-ecran);
  right: var(--marge-ecran);
  bottom: calc(var(--marge-ecran) + env(safe-area-inset-bottom));
  /* Au-dessus de .chat-bulle et .recette-bulle (toutes deux z-index 200,
     même coin bas-droit) : sans ça, elles passent devant le bouton « × »
     de cette bannière, qui devient impossible à fermer (retour Vincent,
     14/09). Bug préexistant, pas introduit par le scan relais. */
  z-index: 210;
  display: flex;
  align-items: center;
  gap: var(--espacement-champ-horizontal);
  padding: var(--carte-padding-vertical) var(--carte-padding-horizontal);
  background: var(--surface-haute);
  color: var(--fond);
  border-radius: var(--rayon);
  border: var(--epaisseur-trait) solid var(--filet-sur-sombre);
  border-left: 4px solid var(--accent-marque);
  box-shadow: var(--ombre-vitrine-forte);
  transform: translateY(calc(100% + 24px));
  opacity: 0;
  transition: transform 300ms cubic-bezier(0.2, 0.8, 0.2, 1), opacity 300ms ease;
}
.installer-app--visible {
  transform: none;
  opacity: 1;
}
.installer-app-icone {
  width: 44px;
  height: 44px;
  border-radius: 10px;
  flex: 0 0 44px;
}
.installer-app-corps {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1;
  font-size: var(--taille-texte-2);
  line-height: 1.35;
}
.installer-app-corps span {
  opacity: 0.8;
}
.installer-app-fermer {
  background: none;
  border: 0;
  color: inherit;
  font-size: var(--taille-titre-1);
  line-height: 1;
  padding: 6px;
  cursor: pointer;
  opacity: 0.7;
}
@media (min-width: 900px) {
  .installer-app {
    left: auto;
    right: 24px;
    bottom: 24px;
    max-width: 440px;
  }
}

/* Barre du bas visible (joueur, cordeur — issue n°140) : la bannière ne doit
   pas se superposer aux onglets. */
@media (max-width: 899px) {
  body:has(.adm-shell--app) .installer-app {
    bottom: calc(var(--hauteur-nav-basse) + var(--marge-ecran) + env(safe-area-inset-bottom));
  }
}

/* Personnifier — liste des comptes (refonte du 12/09, retour Vincent : le
   tableau débordait sur mobile et cachait « Voir comme »). Une carte par
   compte, cliquable en entier ; filtres par rôle en pilules. */
.comptes-recherche { display: grid; gap: var(--grille-base); margin-bottom: var(--espacement-section); }
.comptes-filtres { display: flex; flex-wrap: wrap; gap: 6px; }
/* Favoris (issue n°197) : section à part au-dessus du reste de la liste. */
.comptes-section-titre { margin: var(--espacement-section) 0 var(--grille-base); }
.comptes-section-titre:first-child { margin-top: 0; }
.comptes-liste { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.comptes-liste + .comptes-liste { margin-top: var(--espacement-section); }
.comptes-item { position: relative; }
.comptes-forme { margin: 0; }
.comptes-carte {
  display: grid;
  grid-template-columns: 40px 1fr auto;
  grid-template-areas: 'avatar texte action' 'avatar roles action';
  gap: 4px 12px;
  align-items: center;
  width: 100%;
  padding: 12px 14px;
  background: var(--surface);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
  text-align: left;
  font: inherit;
  color: var(--texte);
  cursor: pointer;
}
.comptes-carte:hover { border-color: var(--texte); }
.comptes-avatar { grid-area: avatar; width: 40px; height: 40px; border-radius: 50%; background: var(--surface-haute); color: var(--accent-marque); font-weight: var(--graisse-bold); font-size: var(--taille-texte-2); display: grid; place-items: center; }
.comptes-texte { grid-area: texte; min-width: 0; display: flex; flex-direction: column; }
.comptes-nom { font-weight: var(--graisse-bold); display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.comptes-demo { font-size: var(--taille-texte-4); }
.comptes-email { color: var(--texte-secondaire); font-size: var(--taille-texte-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.comptes-roles { grid-area: roles; display: flex; gap: 6px; flex-wrap: wrap; }
.comptes-action { grid-area: action; font-weight: var(--graisse-semibold); color: var(--accent-marque-encre); white-space: nowrap; }
.comptes-note { margin-top: var(--grille-base); font-size: var(--taille-texte-2); }
@media (max-width: 480px) {
  .comptes-carte { grid-template-columns: 40px 1fr; grid-template-areas: 'avatar texte' 'avatar roles' 'action action'; }
  .comptes-action { justify-self: end; }
}
/* Pastille de rôle dans la liste (la coquille admin ne charge pas .pastille de bureau.css). */
.comptes-roles .pastille { display: inline-flex; align-items: center; padding: 3px 9px; border-radius: var(--rayon-pastille); font-size: var(--taille-texte-3); font-weight: var(--graisse-semibold); background: var(--accent-marque-voile); color: var(--accent-marque-encre); }
.comptes-roles .pastille--encre { background: var(--action); color: var(--sur-action); }
.comptes-roles .pastille--attente { background: var(--attention-fond); color: var(--attention-texte); }
.comptes-roles .pastille--neutre, .comptes-demo { background: var(--fond); color: var(--texte-secondaire); border: var(--epaisseur-trait) solid var(--trait); }

/* Étoile d'épinglage (issue n°197) : formulaire séparé de celui de la carte
   (jamais un bouton dans un bouton), posé par-dessus au coin — la carte
   reste cliquable en entier en dehors de ce coin. */
.etoile-forme { position: absolute; top: 6px; right: 6px; margin: 0; z-index: 1; }
.etoile-bascule {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: none;
  color: var(--texte-secondaire);
  cursor: pointer;
}
.etoile-bascule svg { width: 18px; height: 18px; }
.etoile-bascule:hover { background: var(--surface-haute); }
.etoile-bascule[aria-pressed='true'] { color: var(--accent-marque-encre); }

/* Carte de paiement (admin, mobile) — retour Vincent 13/09. */
.carte-paiement { display: flex; flex-direction: column; gap: 6px; }
.carte-paiement__tete { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.carte-paiement__tete .titre-3 { margin: 0; font-size: var(--taille-titre-3); }
.carte-paiement__parties { margin: 0; }
.carte-paiement__montant { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-top: 4px; }
.carte-paiement__montant b { font-size: var(--taille-affichage-1); font-weight: var(--graisse-bold); letter-spacing: -0.01em; }
.carte-paiement__date { margin: 0; font-size: var(--taille-texte-2); }
.carte-paiement__actions { display: flex; align-items: center; gap: 8px; margin-top: 8px; padding-top: 10px; border-top: var(--epaisseur-trait) solid var(--trait); }
.carte-paiement__actions form { margin: 0; }


/* ---- boutons.css ---- */
/*
 * Boutons, onglets, filtres et tableaux — vocabulaire repris de l'admin
 * LCS Shop (assets/lcs-admin.css : .btn, .btn-ghost, .tab, .cmd-filter,
 * th/td), sur les jetons Stringerz. Chargée après interface.css : ces
 * règles priment. Aucune valeur en dur.
 */

/* ---------- Boutons : petits, propres ---------- */

.bouton {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  min-height: var(--cible-tactile-min);
  padding: 9px 14px;
  border-radius: var(--rayon-bouton);
  font-family: var(--police-texte);
  font-weight: var(--graisse-semibold);
  font-size: var(--taille-texte-2);
  line-height: 1.3;
  transition: background-color 150ms ease, border-color 150ms ease, color 150ms ease;
}
/* Bouton principal = menthe, partout (issue n°249, retour de Vincent du
   24/09 : « au secours cette mise en page, pourquoi des boutons sur deux
   lignes, le bouton noir je ne suis pas chaud » — corrige la règle
   « positive en principal noir » posée en 1.4.50/issue n°244). Un seul
   style de principal dans toute l'app : celui que Ball-E utilisait déjà
   (« Commencer », « Voir les cordages », issue n°140). .bouton--eclat
   devient un alias déprécié — mêmes règles, gardé pour ne pas avoir à
   renommer la classe dans tous les écrans qui s'en servaient déjà (sticky
   « Réserver » de la fiche cordeur mobile, confirmations). Le marine/
   l'encre restent pour les fonds et les pastilles (--surface-haute,
   --action), jamais pour un bouton plein — docs/design-system.md → Actions. */
.bouton--principal,
.bouton--eclat {
  background: var(--accent-marque);
  color: var(--texte);
  border: var(--epaisseur-trait) solid var(--accent-marque);
}
.bouton--principal:hover,
.bouton--principal:focus-visible,
.bouton--eclat:hover,
.bouton--eclat:focus-visible {
  background: var(--accent-marque-encre);
  border-color: var(--accent-marque-encre);
  color: var(--sur-action);
}
.bouton--secondaire {
  background: var(--surface);
  color: var(--texte);
  border: var(--epaisseur-trait) solid var(--trait);
}
.bouton--secondaire:hover,
.bouton--secondaire:focus-visible {
  background: var(--surface);
  border-color: var(--texte);
}
.bouton--marque {
  background: var(--surface-haute);
  color: var(--accent-marque);
  border: var(--epaisseur-trait) solid var(--surface-haute);
}
.bouton--marque:hover,
.bouton--marque:focus-visible {
  background: var(--action-pressee);
  border-color: var(--action-pressee);
}
.bouton--danger {
  background: var(--surface);
  color: var(--erreur);
  border: var(--epaisseur-trait) solid var(--trait);
}
.bouton--danger:hover,
.bouton--danger:focus-visible {
  border-color: var(--erreur);
}
/* --compact réduit le padding et la police, jamais la cible tactile (règle 1,
   issue n°244) : la hauteur minimale reste --cible-tactile-min. */
.bouton--compact {
  min-height: var(--cible-tactile-min);
  padding: 6px 12px;
  font-size: var(--taille-texte-3);
  border-radius: var(--rayon-bouton-compact);
  white-space: nowrap;
}
.bouton--discret {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon-bouton-compact);
  padding: 5px 9px;
  font-size: var(--taille-texte-3);
  color: var(--texte);
  background: var(--surface);
  min-height: var(--cible-tactile-min);
}
.bouton--discret:hover {
  border-color: var(--texte);
}
/* État désactivé (issue n°255, point 5 : « Révoquer l'accès » d'un associé
   sans rien à révoquer, /admin/administrateurs) : même rendu que
   .bouton:disabled (base.css), et pas de contour renforcé au survol — un
   bouton inerte ne réagit pas. */
.bouton--discret:disabled,
.bouton--discret:disabled:hover {
  opacity: 0.6;
  cursor: not-allowed;
  border-color: var(--trait);
}

/* Paire de décision dans une liste (accepter/refuser une demande, prendre en
   charge/refuser une commande, valider/rejeter en admin) : les deux boutons
   restent sur une seule ligne, à droite de la carte — jamais empilés, jamais
   pleine largeur (issue n°249, corrige .actions-commande--decision posée en
   1.4.50/issue n°244, dont le `flex-direction: column` empilait « Accepter »
   pleine largeur puis « Refuser » en dessous). Positif menthe compact
   (.bouton--principal, ou .bouton--secondaire dans une liste où plusieurs
   lignes peuvent réclamer la même décision à la fois — même exception que
   dans docs/design-system.md → Actions), négatif .bouton--discret rendu ici
   sans contour ni couleur (fond transparent, texte secondaire) : un poids
   plus léger que son style par défaut, réservé à cette paire précise —
   .bouton--discret garde son contour partout ailleurs. */
.actions-paire {
  display: flex;
  flex-wrap: nowrap;
  justify-content: flex-end;
  align-items: center;
  gap: var(--espacement-petit);
}
.actions-paire form {
  display: contents;
}
.actions-paire .bouton,
.actions-paire .bouton--discret {
  flex: none;
  width: auto;
}
.actions-paire .bouton--discret {
  border-color: transparent;
  background: transparent;
  color: var(--texte-secondaire);
}
.actions-paire .bouton--discret:hover,
.actions-paire .bouton--discret:focus-visible {
  background: var(--fond);
  border-color: transparent;
}
/* Très petit écran (< 360px) : la paire reste sur une ligne, seul le padding
   se réduit — jamais d'empilement (issue n°249). */
@media (max-width: 359px) {
  .actions-paire .bouton--compact,
  .actions-paire .bouton--discret {
    padding-left: 8px;
    padding-right: 8px;
  }
}

/* Lien-flèche : seul composant tertiaire pour « Voir … »/« Gérer … » (règle 5,
   passe boutons et actions, issue n°244) — texte discret plutôt qu'un bouton
   à bordure, chevron du système de pictos (IconChevron, déjà utilisé par
   .carte--lien), zone cliquable de 44px obtenue par le padding plutôt que par
   la taille du texte (voir docs/design-system.md → Actions). Remplace le
   `→` en dur qu'un `.bouton--discret` gardait à la fin du libellé : la
   variante « corriger seulement la hauteur » d'un premier passage sur ce
   fichier laissait un bouton bordé pour ce qui doit rester un lien texte. */
.lien-fleche {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-height: var(--cible-tactile-min);
  padding: 8px 2px;
  font-family: var(--police-texte);
  font-weight: var(--graisse-semibold);
  font-size: var(--taille-texte-2);
  color: var(--action);
  text-decoration: none;
}
.lien-fleche svg {
  width: 14px;
  height: 14px;
  flex: none;
}
.lien-fleche:hover,
.lien-fleche:focus-visible {
  text-decoration: underline;
}

/* Continuer avec Google (issue n°139) : mise en page seulement, les couleurs
   viennent de .bouton--secondaire (blanc bordé) déjà conforme à la charte
   « bouton blanc bordé » demandée. */
.bouton--google {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--espacement-petit);
  width: 100%;
}
/* Sur mobile, seule l'action principale d'un formulaire reste pleine largeur
   (la cible tactile de 44px est déjà garantie par .bouton, ci-dessus). */
@media (max-width: 899px) {
  form > .bouton--principal:not(.bouton--compact) {
    width: 100%;
  }
}

/* ---------- Onglets soulignés ---------- */

.onglets {
  display: flex;
  gap: 6px;
  border-bottom: var(--epaisseur-trait) solid var(--trait);
}
.onglet {
  padding: 10px 14px;
  font-family: var(--police-texte);
  font-weight: var(--graisse-semibold);
  font-size: var(--taille-texte-2);
  color: var(--texte-secondaire);
  text-decoration: none;
  border: 0;
  background: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  border-radius: 0;
}
.onglet:hover {
  color: var(--texte);
}
.onglet--actif,
.onglet[aria-current='page'] {
  color: var(--action);
  border-bottom-color: var(--action);
  background: none;
}

/* ---------- Filtres en pilules ---------- */

.filtres {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}
.filtre {
  border: var(--epaisseur-trait) solid var(--trait);
  background: var(--surface);
  color: var(--texte-secondaire);
  padding: 6px 12px;
  border-radius: var(--rayon-pastille);
  font-size: var(--taille-texte-3);
  font-weight: var(--graisse-semibold);
  text-decoration: none;
}
.filtre:hover {
  border-color: var(--texte);
}
/* État sélectionné en menthe voilée + encre de marque (11/09, décision B :
   un seul aplat fort par écran — un filtre actif n'en est pas un), cohérent
   avec les pastilles de créneau. */
.filtre--actif {
  background: var(--accent-marque-voile);
  color: var(--accent-marque-encre);
  border-color: var(--accent-marque);
  font-weight: var(--graisse-semibold);
}
/* Compteur d'un filtre (/cordeur/commandes, issue n°202) : un peu plus
   discret que le libellé, jamais un badge à part. */
.filtre small {
  opacity: 0.7;
  margin-left: 4px;
}

/* ---------- Tableaux ---------- */

.tableau {
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
  overflow: hidden;
  background: var(--surface);
}
/* position: relative (issue n°255, point 5) : le libellé caché d'une colonne
   d'actions (<span class="visuellement-cache">, position absolue) se plaçait
   par rapport à un ancêtre hors du tableau — il échappait au défilement
   horizontal de .tableau et élargissait toute la page sur mobile (148px de
   débordement mesurés à 390px sur /admin/administrateurs). Le th devient son
   bloc conteneur, il reste dans le tableau. */
.tableau th {
  position: relative;
  font-size: var(--taille-texte-4);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--texte-secondaire);
  padding: 10px 12px;
  background: var(--fond);
  border-bottom: var(--epaisseur-trait) solid var(--trait);
  font-weight: var(--graisse-semibold);
}
.tableau td {
  padding: 8px 12px;
  font-size: var(--taille-texte-2);
  border-bottom: var(--epaisseur-trait) solid var(--trait);
  vertical-align: middle;
}
.tableau tr:last-child td {
  border-bottom: 0;
}
.tableau tbody tr:hover td {
  background: var(--fond);
}

/* ---------- Étiquettes de statut : géométrie des boutons ---------- */

.etiquette-statut {
  border-radius: var(--rayon-bouton-compact);
  padding: 3px 9px;
  font-size: var(--taille-texte-4);
  font-weight: var(--graisse-semibold);
  letter-spacing: 0.01em;
  text-transform: none;
}

/* ---------- Champs ---------- */

.champ input,
.champ select,
.champ textarea,
.recherche input {
  border-radius: var(--rayon-bouton);
  font-size: var(--taille-texte-2);
}
.champ input:focus,
.champ select:focus,
.champ textarea:focus,
.recherche input:focus {
  outline: var(--epaisseur-focus) solid var(--action);
  outline-offset: 0;
  border-color: var(--action);
}

/* Tableau empilé (variante de .tableau, sous le palier bureau) — retour
   Vincent du 28/09 : sur /admin/administrateurs, le bouton « Révoquer l'accès »
   se trouvait à 561–636 px sur un écran de 390 px, hors de portée sans un
   défilement latéral que rien n'indique. .tableau défile volontairement dans
   lui-même (retour du 17/09) et sert à 48 endroits : on ne change pas ce
   comportement pour tous, on le lève ici, tableau par tableau, avec cette
   classe. Chaque ligne devient une carte ; chaque cellule porte son en-tête
   dans `data-label`. Les rôles ARIA posés sur le tableau et ses lignes
   restituent la sémantique de tableau que `display: block` fait perdre aux
   lecteurs d'écran.
   Chaque sélecteur est doublé avec le préfixe de la coquille admin : lcs.css
   (chargé après ce fichier) redéfinit `.tableau` sous
   `.adm-shell:not(.adm-shell--app)` avec une spécificité de (0,3,0) — sans ce
   doublon, le cadre, le fond et les marges de la cellule reprenaient le dessus. */
@media (max-width: 899px) {
  /* Plus de cadre autour du tableau : c'est chaque ligne qui devient la carte. */
  .tableau.tableau--empile,
  .adm-shell:not(.adm-shell--app) .tableau.tableau--empile {
    overflow: visible;
    border: 0;
    border-radius: 0;
    background: transparent;
  }
  .tableau.tableau--empile thead,
  .adm-shell:not(.adm-shell--app) .tableau.tableau--empile thead {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
  .tableau.tableau--empile tbody,
  .tableau.tableau--empile tr,
  .adm-shell:not(.adm-shell--app) .tableau.tableau--empile tbody,
  .adm-shell:not(.adm-shell--app) .tableau.tableau--empile tr {
    display: block;
  }
  .tableau.tableau--empile tr,
  .adm-shell:not(.adm-shell--app) .tableau.tableau--empile tr {
    margin-bottom: var(--espacement-petit);
    padding: var(--espacement-petit) var(--espacement-champ-horizontal);
    border: var(--epaisseur-trait) solid var(--trait);
    border-radius: var(--rayon);
    background: var(--surface);
  }
  /* Le survol (et son « survol collant » après un tap sur mobile) colore la
     ligne dans base.css et les cellules dans boutons.css : les deux sont
     neutralisés ici, une carte ne change pas de fond sous le doigt. */
  .tableau.tableau--empile tbody tr:hover,
  .adm-shell:not(.adm-shell--app) .tableau.tableau--empile tbody tr:hover {
    background: var(--surface);
  }
  .tableau.tableau--empile tbody tr:hover td,
  .adm-shell:not(.adm-shell--app) .tableau.tableau--empile tbody tr:hover td {
    background: transparent;
  }
  .tableau.tableau--empile td,
  .adm-shell:not(.adm-shell--app) .tableau.tableau--empile td {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--espacement-petit);
    padding: var(--espacement-petit) 0;
    border-bottom: var(--epaisseur-trait) solid var(--trait);
  }
  .tableau.tableau--empile tr:last-child td:not(:last-child),
  .adm-shell:not(.adm-shell--app) .tableau.tableau--empile tr:last-child td:not(:last-child) {
    border-bottom: var(--epaisseur-trait) solid var(--trait);
  }
  .tableau.tableau--empile td:last-child,
  .adm-shell:not(.adm-shell--app) .tableau.tableau--empile td:last-child {
    border-bottom: none;
  }
  .tableau.tableau--empile td[data-label]::before,
  .adm-shell:not(.adm-shell--app) .tableau.tableau--empile td[data-label]::before {
    content: attr(data-label);
    color: var(--texte-secondaire);
    font-size: var(--taille-texte-4);
    font-weight: var(--graisse-semibold);
    text-transform: uppercase;
    letter-spacing: var(--interlettrage-etiquette);
  }
}

/* ---- ecrans.css ---- */
/*
 * Règles par écran — couche d'interface Stringerz (Claude, 02/09/2026).
 * Chargée après interface.css. Une section par écran ; aucune valeur en dur.
 */

/* ---------- /accueil (issue n°140, langage visuel du 09/09) ---------- */

/* Entête « Salut {prénom} » du joueur mobile (issue n°171, maquette
   accueil-joueur-mobile-v3.html) puis du cordeur mobile (issue n°179,
   maquette accueil-cordeur.html) : même mot que leur héros bureau respectif,
   habillage propre à ce h1 — spécificité choisie pour l'emporter sur
   .pagehead h1 (lcs.css, chargé après ecrans.css) sans toucher au pagehead
   générique utilisé par le relais, dont l'accueil bureau n'est pas refait. */
.pagehead h1.pagehead-titre-salut {
  font-size: var(--taille-titre-1);
  font-weight: var(--graisse-noire);
  letter-spacing: -0.02em;
  margin: 0 0 3px;
}

.section-titre {
  font-size: var(--taille-texte-3);
  font-weight: var(--graisse-semibold);
  color: var(--texte-secondaire);
  text-transform: uppercase;
  letter-spacing: var(--interlettrage-etiquette);
  margin: 0 0 var(--espacement-petit);
}

/* Prochaine raquette condensée (accueil-joueur.html) : une carte, une info,
   un chevron — plus de tableau. */
.carte-suivi-condensee {
  display: flex;
  padding: 0;
  overflow: hidden;
}
.carte-suivi-condensee-lien {
  display: flex;
  align-items: center;
  gap: var(--espacement-champ-horizontal);
  width: 100%;
  padding: var(--carte-padding-vertical) var(--carte-padding-horizontal);
  color: inherit;
  text-decoration: none;
}
.carte-suivi-condensee-icone {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: var(--rayon);
  background: var(--accent-marque-voile);
  color: var(--accent-marque-encre);
}
.carte-suivi-condensee-icone svg {
  width: 22px;
  height: 22px;
}
.carte-suivi-condensee-texte {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.carte-suivi-condensee-titre {
  font-weight: var(--graisse-semibold);
  font-size: var(--taille-texte-2);
  color: var(--texte);
}
.carte-suivi-condensee-sous {
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
}
.carte-suivi-condensee-chevron {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  color: var(--texte-secondaire);
}
.carte-suivi-condensee-vide {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--espacement-petit);
  width: 100%;
  padding: var(--carte-padding-vertical) var(--carte-padding-horizontal);
}
.carte-suivi-condensee-vide p {
  margin: 0;
}

/* Invitation aux notifications push (retour Vincent, issue n°211 : « pas très
   bien mis en avant ») : cloche dans le même badge rond que
   .carte-suivi-condensee-icone ci-dessus, plutôt qu'un second style d'icône
   sur cet écran. */
.accueil-push {
  display: flex;
  align-items: flex-start;
  gap: var(--espacement-champ-horizontal);
}
/* .actions-commande empile ses actions pleine largeur par défaut (utile pour
   un bouton principal de tunnel) : sur ce simple encart dismissible, ça
   gonflait la carte et étirait « Activer » sur toute la largeur du texte
   (retour Vincent, issue n°215) — rangée compacte à la place, chaque bouton
   gardant sa propre largeur. */
.accueil-push .actions-commande {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
}
.accueil-push-corps {
  display: flex;
  flex-direction: column;
  gap: var(--espacement-petit);
}

/* Recommandé près de toi / Ton cordeur habituel, carte mobile (issue n°171,
   maquette accueil-joueur-mobile-v3.html) : bandeau photo 120px, badge et
   avatar par-dessus, comme la carte cordeur du bureau (bureau.css,
   .cordeur) — remplace la ligne compacte de commander-fragments.tsx pour
   cette seule carte de l'accueil. */
.carte-cordeur-recommande {
  background: var(--surface);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
  overflow: hidden;
}
.carte-cordeur-recommande-contenu {
  display: block;
  color: inherit;
  text-decoration: none;
}
.carte-cordeur-recommande-media {
  position: relative;
}
.carte-cordeur-recommande-bandeau {
  position: relative;
  height: 120px;
  overflow: hidden;
}
.carte-cordeur-recommande-bandeau--vide {
  background: var(--placeholder-clair-1);
}
/* Motif à cordage, repris tel quel de bureau.css (.cordeur .photo--claire::before) :
   jamais d'image de stock quand le cordeur n'a pas de photo d'atelier
   (issue #52). */
.carte-cordeur-recommande-bandeau--vide::before {
  content: '';
  position: absolute;
  inset: -20%;
  background-image:
    repeating-linear-gradient(-14deg, rgba(2, 17, 34, 0.09) 0 1.5px, transparent 1.5px 16px),
    repeating-linear-gradient(76deg, rgba(2, 17, 34, 0.05) 0 1.5px, transparent 1.5px 16px);
}
.carte-cordeur-recommande-photo {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.carte-cordeur-recommande-badge {
  position: absolute;
  top: 10px;
  left: 10px;
  background: rgba(255, 255, 255, 0.92);
  color: var(--texte);
  font-size: var(--taille-texte-4);
  font-weight: var(--graisse-forte);
  padding: 4px 9px;
  border-radius: var(--rayon-pastille);
}
.carte-cordeur-recommande-avatar {
  position: absolute;
  left: 14px;
  bottom: -18px;
  border: var(--epaisseur-accent-avatar) solid var(--surface);
  box-shadow: var(--ombre-carte);
}
.carte-cordeur-recommande-corps {
  padding: 26px 14px 14px;
}
.carte-cordeur-recommande-ligne1 {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--espacement-petit);
}
.carte-cordeur-recommande-nom {
  font-size: var(--taille-texte-1);
  font-weight: var(--graisse-forte);
  color: var(--texte);
}
.carte-cordeur-recommande-prix {
  font-size: var(--taille-texte-2);
  font-weight: var(--graisse-forte);
  color: var(--texte);
  white-space: nowrap;
}
.carte-cordeur-recommande-note {
  margin: 2px 0 0;
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
}
.carte-cordeur-recommande-creneaux {
  display: flex;
  gap: var(--espacement-petit);
  padding: 0 14px 14px;
  /* Les créneaux dépassent la carte sur mobile (retour Théo via la bulle de
     recette, 13/09 : « je ne peux pas scroller ») : défilement horizontal au
     doigt, barre masquée, dernier créneau visible grâce au padding droit. */
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  scroll-snap-type: x proximity;
  /* Même construction que .atelier-nav (défilement horizontal en flex, non
     positionné) : sans ceci, document.documentElement.scrollWidth déborde
     de la page entière même si le contenu reste visuellement contenu dans
     la bande — voir le commentaire détaillé sur .atelier-nav,
     ecrans-cordeur.css (issue n°251). */
  contain: layout;
}
.carte-cordeur-recommande-creneaux::-webkit-scrollbar {
  display: none;
}
.carte-cordeur-recommande-creneaux .pastille-creneau {
  scroll-snap-align: start;
  flex: 0 0 auto; /* jamais compressé : la rangée défile */
}

/* Ta dernière raquette, carte mobile (issue n°171) : remplace le tableau
   « Dernières poses » — même donnée que la bande bureau (heroRacket). */
.carte-derniere-raquette {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--espacement-petit);
}
.carte-derniere-raquette-nom {
  margin: 0 0 2px;
  font-size: var(--taille-texte-1);
  font-weight: var(--graisse-forte);
  color: var(--texte);
}
.carte-derniere-raquette-detail {
  margin: 0;
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
}

/* Pastilles chiffres du mois (accueil-cordeur.html) : la journée du jour,
   pas un KPI de back-office. */
.pastilles-mois {
  display: flex;
  gap: var(--espacement-petit);
  margin-bottom: var(--espacement-section);
}
.pastille-mois {
  flex: 1;
  background: var(--surface);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
  padding: 10px 8px;
  text-align: center;
}
.pastille-mois b {
  display: block;
  font-size: var(--taille-titre-3);
  font-weight: var(--graisse-titre);
  color: var(--texte);
}
.pastille-mois span {
  font-size: var(--taille-texte-4);
  color: var(--texte-secondaire);
  font-weight: var(--graisse-medium);
}
.pastille-mois--accent {
  background: var(--accent-marque-voile);
  border-color: transparent;
}
.pastille-mois--accent b {
  color: var(--accent-marque-encre);
}

/* Raquettes du jour groupées par statut (accueil-cordeur.html), liseré
   coloré plutôt qu'un tableau. */
.groupe-raquettes {
  display: flex;
  flex-direction: column;
  gap: var(--espacement-petit);
  margin-bottom: var(--espacement-section);
}
/* Réutilisée pour les jours de /cordeur/disponibilites (issue n°145) : même
   liseré à sens, --prete pour un jour avec créneaux, --attente pour un
   congé, neutre (--trait) pour un jour vide. Voir base.css (.semaine). */
.carte-atelier {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--espacement-champ-horizontal);
  background: var(--surface);
  border-radius: var(--rayon);
  border-left: 4px solid var(--trait);
  padding: var(--carte-padding-vertical) var(--carte-padding-horizontal);
  color: inherit;
  text-decoration: none;
}
.carte-atelier--attente {
  border-left-color: var(--attention);
}
.carte-atelier--prete {
  border-left-color: var(--accent-marque);
}
.carte-atelier-texte {
  min-width: 0;
}
.carte-atelier-nom {
  margin: 0 0 2px;
  font-size: var(--taille-texte-2);
  font-weight: var(--graisse-semibold);
  color: var(--texte);
}
.carte-atelier-detail {
  margin: 0;
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
}
.pastille-mini {
  flex: 0 0 auto;
  border: 0;
  font-size: var(--taille-texte-4);
  font-weight: var(--graisse-semibold);
  padding: 4px 10px;
  border-radius: var(--rayon-pastille);
  white-space: nowrap;
}
.pastille-mini--attente {
  background: var(--attention-fond);
  color: var(--lcs-attention-texte);
}
.pastille-mini--prete {
  background: var(--accent-marque-voile);
  color: var(--accent-marque-encre);
}

/* ---------- /commander : carte cordeur façon Doctolib ---------- */

.carte-cordeur {
  grid-template-areas:
    'avatar identite'
    'infos infos'
    'creneaux creneaux';
}
.carte-cordeur .avatar {
  text-decoration: none;
}
.carte-cordeur-nom {
  color: inherit;
  text-decoration: none;
}
/* Liaison liste <-> carte façon Booking (issue #95) : carte.js relie chaque
   carte cordeur à son marqueur — clic (hors lien/bouton) recentre la carte,
   survol met le marqueur en surbrillance. */
.carte-cordeur--liee {
  cursor: pointer;
}
.carte-cordeur--active {
  border-color: var(--texte);
}
.carte-cordeur-creneaux {
  grid-area: creneaux;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--espacement-petit);
}
/* Menthe = disponible/ok (issue n°140, langage visuel du 09/09) : un créneau
   réservable n'est plus un gris neutre, il porte le sens de son état. */
.pastille-creneau {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 34px;
  padding: 0 12px;
  border-radius: var(--rayon-bouton);
  background: var(--accent-marque-voile);
  color: var(--accent-marque-encre);
  border: var(--epaisseur-trait) solid transparent;
  font-family: var(--police-nombre);
  font-weight: var(--graisse-semibold);
  font-size: var(--taille-texte-3);
  text-decoration: none;
  white-space: nowrap;
}
.pastille-creneau:hover,
.pastille-creneau:focus-visible {
  background: var(--surface-haute);
  color: var(--accent-marque);
}
.pastille-creneau--grande {
  min-height: var(--cible-tactile-min);
  font-size: var(--taille-texte-2);
}
.pastille-creneau--complet {
  background: var(--fond);
  color: var(--texte-secondaire);
  text-decoration: line-through;
  pointer-events: none;
}
.carte-cordeur-tous {
  font-size: var(--taille-texte-3);
  margin-left: auto;
}

/* ---------- En-tête de page mobile : titre + action compacte ---------- */

@media (max-width: 899px) {
  .entete-page {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--espacement-petit);
    text-align: left;
  }
  .entete-page > h1,
  .entete-page > .entete-page-titre {
    flex: 1 1 auto;
    min-width: 0;
    text-align: left;
  }
  .entete-page .entete-page-action {
    width: auto;
    flex: 0 0 auto;
  }
}

/* ---------- /poses : lignes de réservation ---------- */

.section-liste {
  display: flex;
  flex-direction: column;
  gap: var(--espacement-petit);
}
.section-repliable > summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  gap: var(--espacement-petit);
}
.section-repliable > summary::-webkit-details-marker {
  display: none;
}
.section-repliable > summary::after {
  content: '';
  width: 8px;
  height: 8px;
  border-right: 2px solid var(--texte-secondaire);
  border-bottom: 2px solid var(--texte-secondaire);
  transform: rotate(-45deg);
  transition: transform 120ms ease;
}
.section-repliable[open] > summary::after {
  transform: rotate(45deg);
}
/* Aide pédagogique sur les tensions (issue n°228) : formulaire raquette et
   étape tensions du tunnel. Même taille que les indications sous les champs,
   avec de l'air avant et après pour ne pas coller au champ suivant. */
.aide-tension {
  margin: 4px 0 16px;
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
}
.aide-tension .aide {
  margin: 0 0 6px;
}
.aide-tension > .section-repliable > summary {
  color: var(--texte);
  font-weight: var(--graisse-semibold);
}
.aide-tension > .section-repliable > p {
  margin: 8px 0 0;
}
.ligne-reservation {
  padding: 0;
  overflow: hidden;
}
.ligne-reservation-corps {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas:
    'statut prix'
    'titre prix'
    'sous prix';
  column-gap: var(--espacement-petit);
  row-gap: 2px;
  padding: var(--carte-padding-vertical) var(--carte-padding-horizontal);
  text-decoration: none;
  color: inherit;
}
.ligne-reservation-corps .etiquette-statut {
  grid-area: statut;
  justify-self: start;
  margin-bottom: 4px;
}
.ligne-reservation-titre {
  grid-area: titre;
  font-family: var(--police-titres);
  font-weight: var(--graisse-titre);
  font-size: var(--taille-titre-3);
  letter-spacing: var(--interlettrage-titre);
}
.ligne-reservation-sous {
  grid-area: sous;
  font-size: var(--taille-texte-2);
  color: var(--texte-secondaire);
}
.ligne-reservation-prix {
  grid-area: prix;
  align-self: center;
  font-weight: var(--graisse-semibold);
  font-size: var(--taille-titre-4);
}
.ligne-reservation-pied {
  display: flex;
  gap: var(--espacement-section);
  padding: var(--espacement-petit) var(--carte-padding-horizontal);
  border-top: var(--epaisseur-trait) solid var(--trait);
  background: var(--fond);
  font-size: var(--taille-texte-3);
}
/* Message non lu (issue n°238) : .etiquette-nouveau habille déjà le texte en
   orange (--attention/--attention-fond) — ce complément n'ajoute que l'icône
   bulle et l'espacement, jamais une deuxième définition de la couleur. */
.ligne-reservation-message {
  gap: 4px;
  text-decoration: none;
}
.ligne-reservation-message-icone {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
}

/* ---------- /messages : liste des conversations ---------- */

.ligne-conversation {
  display: grid;
  grid-template-columns: auto 1fr auto;
  grid-template-areas:
    'avatar titre non-lus'
    'avatar sous date';
  column-gap: var(--espacement-petit);
  row-gap: 2px;
  align-items: center;
  padding: var(--carte-padding-vertical) var(--carte-padding-horizontal);
  text-decoration: none;
  color: inherit;
}
.ligne-conversation .avatar {
  grid-area: avatar;
}
.ligne-conversation-titre {
  grid-area: titre;
  font-family: var(--police-titres);
  font-weight: var(--graisse-medium);
  font-size: var(--taille-titre-4);
  letter-spacing: var(--interlettrage-titre);
}
/* Non-lue : même principe que .notif-titre--non-lue (base.css). */
.ligne-conversation--non-lue .ligne-conversation-titre {
  font-weight: var(--graisse-titre);
}
.ligne-conversation-sous {
  grid-area: sous;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--taille-texte-2);
  color: var(--texte-secondaire);
}
.ligne-conversation-date {
  grid-area: date;
  font-size: var(--taille-texte-3);
}
.ligne-conversation-non-lus {
  grid-area: non-lus;
  align-self: start;
}

/* ---------- /commander/:id : fiche cordeur et créneaux ---------- */

/* Photo pleine largeur + avatar flottant (issue n°163, reprend la maquette
   docs/maquettes/2026-09-09/fiche-cordeur.html) : l'ancien en-tête en carte
   bordée est remplacé par une photo d'atelier en tête de fiche, avatar du
   cordeur à cheval sur son bord bas. Identité, calendrier et bouton
   « Réserver » collant complétés par l'issue n°176. */
.fiche-cordeur {
  display: flex;
  flex-direction: column;
}
.fiche-cordeur-photo {
  position: relative;
  aspect-ratio: 16 / 9;
  background: var(--fond);
}
/* Le découpage (coins arrondis en bas) se fait sur l'image elle-même, pas
   en overflow:hidden sur la boîte : l'avatar doit pouvoir déborder en bas.
   Classe dédiée pour ne pas attraper l'avatar, lui aussi une <img> enfant
   (correctif 11/09 : le sélecteur générique gonflait l'avatar à 100 %). */
.fiche-cordeur-photo-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  border-radius: 0 0 var(--rayon) var(--rayon);
  border: var(--epaisseur-trait) solid var(--trait);
  box-sizing: border-box;
}
/* Cordeur sans photo d'atelier (retour Vincent, 11/09) : pas de grand
   rectangle vide ni d'image générique (règle de l'issue n°52) — la boîte
   perd son ratio 16/9 et devient une simple bande qui porte l'avatar
   (qui déborde en dessous) et le badge. */
.fiche-cordeur-photo--sans {
  aspect-ratio: auto;
  min-height: calc(var(--taille-avatar-fiche-cordeur) - var(--chevauchement-avatar-fiche-cordeur) + var(--espacement-petit));
  background: transparent;
}
.fiche-cordeur-photo .badge-verifie {
  position: absolute;
  top: var(--espacement-petit);
  right: var(--espacement-petit);
  background: var(--surface);
  box-shadow: var(--ombre-carte);
}
.fiche-cordeur-avatar {
  position: absolute;
  bottom: calc(var(--chevauchement-avatar-fiche-cordeur) * -1);
  left: var(--marge-ecran);
  width: var(--taille-avatar-fiche-cordeur);
  height: var(--taille-avatar-fiche-cordeur);
  border: var(--epaisseur-accent-avatar) solid var(--fond);
  font-size: var(--taille-titre-1);
  box-shadow: var(--ombre-carte);
}
.fiche-cordeur-corps {
  padding-top: calc(var(--chevauchement-avatar-fiche-cordeur) + var(--espacement-petit));
  display: flex;
  flex-direction: column;
  gap: var(--espacement-petit);
}
.fiche-cordeur-identite {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--grille-base);
}
.fiche-cordeur-identite .titre-2 {
  margin: 0;
  line-height: 1.2;
}
/* Nom à gauche, prix à droite sur deux lignes (issue n°176, maquette 09/09) :
   remplace l'ancienne pastille « Pose {prix} € » alignée avec la note. */
.fiche-cordeur-entete {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--espacement-petit);
}
.fiche-cordeur-prix {
  flex-shrink: 0;
  text-align: right;
}
.fiche-cordeur-prix b {
  display: block;
  font-family: var(--police-nombre);
  font-weight: var(--graisse-noire);
  font-size: var(--taille-titre-1);
  color: var(--texte);
}
.fiche-cordeur-prix span {
  display: block;
  font-size: var(--taille-texte-4);
  color: var(--texte-secondaire);
}
.fiche-cordeur-sous-ligne {
  margin: 0;
}
.fiche-cordeur-sous-ligne .etoile {
  color: var(--accent-marque-encre);
  font-weight: var(--graisse-semibold);
}
/* Citation courte du cordeur (issue n°176) : première phrase de la bio,
   visible sans dépliage — l'accordéon « À propos et cordages » plus bas
   garde le texte complet. */
.fiche-cordeur-citation {
  margin: 0;
  padding: var(--espacement-petit) var(--carte-padding-horizontal);
  background: var(--accent-marque-voile);
  border-radius: var(--rayon-etiquette) var(--rayon) var(--rayon) var(--rayon);
  font-size: var(--taille-texte-2);
  line-height: var(--interligne);
  color: var(--texte);
}
.fiche-cordeur-infos {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--grille-base) var(--espacement-petit);
  font-size: var(--taille-texte-2);
  color: var(--texte-secondaire);
}
.fiche-cordeur-details {
  display: flex;
  flex-direction: column;
  gap: var(--espacement-petit);
}
.fiche-cordeur-details > summary {
  cursor: pointer;
  font-size: var(--taille-texte-2);
}
.creneaux {
  display: flex;
  flex-direction: column;
  gap: var(--espacement-petit);
}
.creneaux-jour {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.creneaux-jour-titre {
  font-family: var(--police-titres);
  font-weight: var(--graisse-titre);
  font-size: var(--taille-titre-4);
  letter-spacing: var(--interlettrage-titre);
  margin: 0;
}
.creneaux-grille {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: var(--espacement-petit);
}
/* Espace réservé au bouton de réservation collant (issue n°176) : masqué au
   palier bureau avec le reste de .fiche-doctolib (bureau.css), inutile d'y
   conditionner cette marge séparément. */
.fiche-doctolib {
  padding-bottom: var(--hauteur-collant-reserver);
}
.avis {
  gap: 2px;
}

/* ---------- /raquettes : cartes deux par ligne ---------- */

.carte-raquette {
  display: flex;
  flex-direction: column;
  gap: 4px;
  text-decoration: none;
  color: inherit;
  padding: 10px;
}
.carte-raquette-visuel {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 16 / 10;
  border-radius: var(--rayon-etiquette);
  background: var(--fond);
  margin-bottom: 4px;
}
.carte-raquette .etiquette-sport {
  align-self: flex-start;
}
.carte-raquette-titre {
  font-family: var(--police-texte);
  font-weight: var(--graisse-semibold);
  font-size: var(--taille-texte-2);
  line-height: 1.3;
}
.carte-raquette-tensions {
  font-size: var(--taille-texte-2);
  color: var(--texte-secondaire);
}

/* Mes raquettes en cartes sur tous les paliers (issue n°123) : le tableau LCS
   à une ligne laissait la page vide. Trois cartes par ligne en bureau, deux
   sur mobile (réglage admin), une carte « Ajouter » en pointillés qui ferme
   la grille, un encart conseil en dessous. */
.liste-cartes--raquettes {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--espacement-petit);
}
.liste-cartes--raquettes:not(.liste-cartes--deux) {
  grid-template-columns: 1fr;
}
.carte-raquette {
  padding: 0;
  gap: 0;
}
.carte-raquette-lien {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: var(--espacement-petit) var(--espacement-petit) 0;
  color: inherit;
  text-decoration: none;
}
.carte-raquette-lien .carte-raquette-visuel {
  margin-bottom: var(--espacement-petit);
}
.carte-raquette-dessin {
  width: var(--taille-icone-carte-raquette);
  height: var(--taille-icone-carte-raquette);
  color: var(--accent-marque-encre);
}
.carte-raquette-pose {
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
  margin-top: 2px;
}
.carte-raquette-pied {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--espacement-petit);
  padding: var(--espacement-petit);
  border-top: var(--epaisseur-trait) solid var(--trait);
}
.carte-raquette-modifier {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.carte-raquette-modifier-picto {
  width: 14px;
  height: 14px;
  fill: var(--texte-secondaire);
  flex: 0 0 14px;
}
.carte-raquette-ajout {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--espacement-petit);
  border: var(--epaisseur-focus) dashed var(--trait);
  box-shadow: none;
  background: transparent;
  color: var(--texte-secondaire);
  text-decoration: none;
  text-align: center;
  padding: var(--espacement-section);
  min-height: 220px;
}
.carte-raquette-ajout:hover {
  border-color: var(--texte-secondaire);
}
.carte-raquette-ajout-plus {
  width: 52px;
  height: 52px;
  border-radius: var(--rayon-pastille);
  border: var(--epaisseur-focus) dashed var(--trait);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--taille-affichage-1);
  line-height: 1;
}
.carte-raquette-ajout-titre {
  color: var(--texte);
  font-weight: var(--graisse-semibold);
}
.carte-raquette-ajout-texte {
  font-size: var(--taille-texte-3);
  max-width: 32ch;
}
.conseil {
  display: flex;
  align-items: center;
  gap: var(--espacement-champ-horizontal);
  background: var(--fond);
  border-radius: var(--rayon);
  padding: var(--carte-padding-vertical) var(--carte-padding-horizontal);
  font-size: var(--taille-texte-2);
  color: var(--texte-secondaire);
}
.conseil-picto {
  flex: 0 0 40px;
  width: 40px;
  height: 40px;
  border-radius: var(--rayon-pastille);
  background: var(--accent-marque-voile);
  color: var(--accent-marque-encre);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: var(--graisse-semibold);
}
.conseil-titre {
  display: block;
  color: var(--texte);
  font-weight: var(--graisse-semibold);
  margin-bottom: 2px;
}
@media (max-width: 599px) {
  .carte-raquette-pied {
    flex-direction: column;
    align-items: stretch;
    text-align: center;
  }
}
@media (min-width: 900px) {
  .liste-cartes--raquettes,
  .liste-cartes--raquettes:not(.liste-cartes--deux) {
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
    gap: var(--espacement-section);
  }
  .carte-raquette-ajout {
    min-height: 100%;
  }
}

/* ---------- /raquettes/nouvelle : assistant pas à pas (issue #86) -------
   Une question par écran, portée par raquette-assistant.js ; sans lui, les
   .assistant-etape restent toutes visibles (fieldset natif) et le formulaire
   se comporte comme avant #86. */
.assistant-entete {
  display: flex;
  align-items: center;
  gap: var(--espacement-petit);
  margin-bottom: var(--espacement-section);
}
.assistant-retour {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  border: 0;
  background: none;
  padding: 0;
  color: var(--texte-secondaire);
  font-family: var(--police-texte);
  font-size: var(--taille-texte-2);
  cursor: pointer;
}
.assistant-progression {
  display: flex;
  gap: 4px;
  list-style: none;
  margin: 0 0 0 auto;
  padding: 0;
}
.assistant-progression .etape-numero {
  width: calc(var(--grille-base) * 3);
  height: calc(var(--grille-base) * 3);
}
fieldset.assistant-etape {
  border: 0;
  margin: 0 0 var(--espacement-section) 0;
  padding: 0;
  min-width: 0;
}
.assistant-tuiles {
  display: flex;
  flex-direction: column;
  gap: var(--grille-base);
}
.assistant-tuile,
button.assistant-tuile--liste {
  display: flex;
  align-items: center;
  gap: var(--espacement-petit);
  width: 100%;
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
  background: var(--surface);
  padding: var(--espacement-champ-vertical) var(--espacement-champ-horizontal);
  min-height: var(--cible-tactile-min);
  color: var(--texte);
  font-family: var(--police-texte);
  font-size: var(--taille-texte-1);
  text-align: left;
  cursor: pointer;
}
.assistant-tuile:has(input:checked) {
  border-color: var(--action);
}
.assistant-tuile input {
  accent-color: var(--action);
}
/* Étape 1 (sport) et rôle de /inscription : trois grandes tuiles côte à côte
   avec picto, plutôt que la liste empilée par défaut ou de simples pilules —
   même esprit que les cartes de choix de l'étape de réservation (issue
   n°147). Sport : appliqué par raquette-assistant.js (JS.classList.add),
   jamais en dur dans le HTML servi, pour ne rien changer sans script. Rôle :
   classe posée par assistant.js, picto rendu directement par le serveur
   (inscription.tsx, pas de <template> à cloner). */
.choix-groupe.assistant-tuiles-sport,
.choix-groupe.assistant-tuiles-larges {
  flex-direction: row;
  flex-wrap: wrap;
}
.choix-groupe.assistant-tuiles-sport .assistant-tuile,
.choix-groupe.assistant-tuiles-larges .assistant-tuile {
  flex: 1 1 calc(33% - var(--grille-base));
  flex-direction: column;
  text-align: center;
  gap: 4px;
  padding: var(--espacement-section) var(--espacement-petit);
}
.assistant-compteur {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--espacement-petit);
}
.assistant-compteur-bouton {
  flex-shrink: 0;
  width: calc(var(--grille-base) * 10);
  height: calc(var(--grille-base) * 10);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon-pastille);
  background: var(--surface);
  color: var(--texte);
  font-size: var(--taille-titre-2);
  line-height: 1;
  cursor: pointer;
}
.champ input.assistant-compteur-valeur {
  flex: 0 0 calc(var(--grille-base) * 24);
  width: auto;
  text-align: center;
  font-size: calc(var(--taille-titre-1) * 1.6);
}
#assistant-recap .carte-raquette {
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
}

/* ---------- /cordeur/activer et /inscription : assistants génériques
   (issue #87, assets/assistant.js) — mêmes classes .assistant-* que
   raquette-assistant.js (issue #86), plus deux mises en page de tuiles
   propres à ces deux parcours. */
#assistant-recap .carte-cordeur {
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
  padding: var(--espacement-champ-vertical) var(--espacement-champ-horizontal);
}
#assistant-recap .carte-cordeur-details {
  grid-column: 1 / -1;
  margin: var(--espacement-petit) 0 0;
  color: var(--texte-secondaire);
  font-size: var(--taille-texte-2);
}
/* Sept tuiles jour compactes en grille (disponibilités hebdo de
   /cordeur/activer) : rendu directement par le serveur (pas d'amélioration
   JS nécessaire), contrairement aux tuiles rôle ci-dessus ou sport
   (issue #86). */
.choix-groupe.assistant-tuiles-jours {
  flex-direction: row;
  flex-wrap: wrap;
  gap: 4px;
}
.assistant-tuiles-jours .assistant-tuile {
  flex: 1 1 calc(14% - 4px);
  flex-direction: column;
  justify-content: center;
  text-align: center;
  padding: var(--espacement-petit) 4px;
  min-width: 0;
}

/* Page « Stringerz revient bientôt » (/admin/site, mode maintenance, issue
   #89) : vitrine simplifiée, centrée, sans menu (Layout sans prop `nav`). */
.ecran--maintenance {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: var(--espacement-petit);
  padding: var(--marge-ecran);
}
.ecran--maintenance .logo {
  max-width: 220px;
  margin-bottom: var(--espacement-section);
}

/* Bandeau d'annonce (/admin/site, issue #89) : injecté par
   src/web/assets/annonce.js en haut de toutes les pages (voir layout.tsx),
   masqué par défaut tant que le script ne l'a pas rempli. */
.bandeau-annonce {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--espacement-petit);
  padding: var(--espacement-champ-vertical) var(--marge-ecran);
  font-size: var(--taille-texte-2);
  font-weight: var(--graisse-medium);
  text-align: center;
}
.bandeau-annonce--info {
  background: var(--action-douce);
  color: var(--texte);
}
.bandeau-annonce--attention {
  background: var(--lcs-attention-fond);
  color: var(--lcs-attention-texte);
}
.bandeau-annonce a {
  color: inherit;
  text-decoration: underline;
}
.bandeau-annonce-fermer {
  background: none;
  border: none;
  color: inherit;
  font-weight: var(--graisse-semibold);
  cursor: pointer;
  padding: 0 0 0 var(--espacement-petit);
}

/* Graphique de fréquentation (/admin/site, issue #89) : barres SVG dessinées
   côté serveur à partir de domain/site/chart.ts (buildAttendanceChart), pas
   de bibliothèque cliente. */
.graphique-frequentation {
  width: 100%;
  height: 140px;
}
.graphique-frequentation-barre {
  fill: var(--action);
}
.etat-technique {
  display: flex;
  flex-direction: column;
  gap: var(--espacement-petit);
}
.etat-technique-ligne {
  display: flex;
  justify-content: space-between;
  gap: var(--espacement-petit);
  padding: var(--espacement-champ-vertical) 0;
  border-bottom: var(--epaisseur-trait) solid var(--trait);
}
.etat-technique-ligne:last-child {
  border-bottom: none;
}

/* Bloc « État des services » (/admin/site, issue n°138, retour Vincent) :
   nom, pastille et latence groupés pour ne pas se retrouver chacun sur leur
   propre ligne sur mobile ; la date de vérification reste secondaire. */
.etat-service-ligne {
  flex-wrap: wrap;
  align-items: center;
}
.etat-service-nom {
  flex: 1 1 auto;
  min-width: 0;
}
.etat-service-detail {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--espacement-petit);
}
.etat-service-detail > *:not(:first-child)::before {
  content: '· ';
}
.etat-service-detail .etat-service-date {
  font-size: var(--taille-texte-4);
}
@media (max-width: 599px) {
  .etat-service-nom {
    width: 100%;
  }
}

/* Liste des composants d'un incident (/admin/site, issue n°138) : la classe
   `.case` n'était stylée nulle part, cases natives non alignées. */
.champ--cases {
  gap: 4px;
}
.champ--cases .case {
  display: flex;
  align-items: center;
  gap: var(--espacement-petit);
  font-weight: normal;
}
.champ--cases .case input[type='checkbox'] {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  min-height: 0;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  accent-color: var(--texte);
}

/* La base de `.actions-ligne` (base.css, retour recette du 15/09, issue
   n°207) couvre déjà `display: flex; flex-wrap: wrap` — plus besoin de
   correctif propre à Admin → Site ici (ancien issue n°138). */

/* Feuille de route (/admin/feuille-de-route, /feuille-de-route, issue #89) :
   une colonne par statut, cartes triées par ordre d'affichage — même esprit
   que le tableau `.roadmap` de LCS Shop. */
.roadmap {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--espacement-section);
  align-items: start;
}
.roadmap-colonne-titre {
  margin: 0 0 var(--espacement-petit);
}
.roadmap-carte {
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
  padding: var(--carte-padding-vertical) var(--carte-padding-horizontal);
  margin-bottom: var(--espacement-petit);
  background: var(--surface);
}
.roadmap-carte-cible {
  color: var(--texte-secondaire);
  font-size: var(--taille-texte-3);
}

/* Documentation (/admin/documentation, /aide, issue #89) : rendu du Markdown
   léger (src/domain/help/markdown.ts) — titres, listes, liens, tableaux et
   blocs de code pour la documentation technique. */
.contenu-markdown {
  display: flex;
  flex-direction: column;
  gap: var(--espacement-petit);
}
.contenu-markdown ul {
  padding-left: var(--espacement-section);
}
.contenu-markdown table {
  width: 100%;
}
.bloc-code {
  background: var(--surface-douce);
  border-radius: var(--rayon);
  padding: var(--carte-padding-vertical) var(--carte-padding-horizontal);
  overflow-x: auto;
  font-family: var(--police-nombre);
  font-size: var(--taille-texte-2);
}
.sommaire {
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
  padding: var(--carte-padding-vertical) var(--carte-padding-horizontal);
  background: var(--surface);
}
.sommaire ul {
  margin: 0;
  padding-left: var(--espacement-section);
}

/* Fiche cordeur, palier bureau (11/09, retour Vincent) : la colonne
   réservation (dépôt + créneaux) occupe toute la hauteur de la grille façon
   Doctolib de lcs.css, en flux vertical — plus de trou sous le dépôt. Les
   photos supplémentaires restent en colonne gauche, sous les avis. */
@media (min-width: 900px) {
  .fiche-doctolib .fiche-cordeur-reservation {
    grid-column: 2;
    grid-row: 1 / span 3;
    display: flex;
    flex-direction: column;
    gap: var(--espacement-section);
    align-self: start;
  }
  .fiche-doctolib .atelier-bandeau {
    grid-column: 1;
    grid-row: 3;
  }
}

/* Fiche cordeur mobile — « À propos et cordages » (retour Théo via la bulle de
   recette, 13/09 : « pas bien fait quand on clique dessus ») : le contenu
   ouvert n'avait ni cadre ni espacement. */
.fiche-cordeur-details {
  margin: var(--espacement-petit) 0;
}
.fiche-cordeur-details summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: var(--cible-tactile-min);
  padding: 8px 0;
  font-weight: var(--graisse-semibold);
  color: var(--texte);
  cursor: pointer;
  list-style: none;
}
.fiche-cordeur-details summary::-webkit-details-marker { display: none; }
.fiche-cordeur-details summary::after {
  content: '';
  width: 9px; height: 9px;
  border-right: 2px solid var(--texte-secondaire);
  border-bottom: 2px solid var(--texte-secondaire);
  transform: rotate(45deg);
  transition: transform 0.15s;
  margin-right: 4px;
}
.fiche-cordeur-details[open] summary::after { transform: rotate(-135deg); }
.fiche-cordeur-details[open] > :not(summary) {
  animation: fiche-details-ouverture 0.18s ease-out;
}
.fiche-cordeur-details > p,
.fiche-cordeur-details > .chips {
  margin: 0 0 var(--espacement-petit);
}
.fiche-cordeur-details[open] {
  padding: 0 14px 12px;
  background: var(--surface);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
}
.fiche-cordeur-details[open] summary { margin-bottom: 4px; }
@keyframes fiche-details-ouverture { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .fiche-cordeur-details[open] > :not(summary) { animation: none; } }

/* L'ancre #calendrier atterrit sous la barre du haut (56px) + un peu d'air. */
#calendrier { scroll-margin-top: 72px; }

/* ---- ecrans-suivi.css ---- */
/*
 * Suivi de commande (/poses/:id, /cordeur/commandes/:id) — couche d'interface
 * Stringerz (Claude, 02/09/2026). Chargée après ecrans.css. Aucune valeur en dur.
 */

/* Entête de /poses/:id mobile (issue n°178, maquette suivi-commande.html) :
   titre seul, plus de logo ni de nom du cordeur en tête — c'est la carte de
   statut juste en dessous qui porte le « qui ». La flèche retour vivait ici
   avant la barre du haut mobile (barre-haute.tsx, issue n°189), qui la porte
   désormais pour toutes les sous-pages connectées. */
.entete-suivi {
  display: flex;
  align-items: center;
  gap: var(--espacement-petit);
}
.entete-suivi-titre {
  margin: 0;
  font-family: var(--police-titres);
  font-weight: var(--graisse-forte);
  font-size: var(--taille-titre-2);
  color: var(--texte);
}

/* Bandeau de statut (issue n°140, maquette suivi-commande.html) : plus de
   simple filet encre — un fond teinté par le sens du statut (même jeton que
   les cartes de /poses, domain/orders/format.orderStatusClass) et un avatar,
   pour que ce soit d'abord « qui » avant d'être « quoi ». */
.bandeau-suivi {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--espacement-petit);
  background: var(--accent-marque-voile);
  border: 0;
}
.bandeau-suivi.carte--statut-attente {
  background: var(--attention-fond);
}
.bandeau-suivi.carte--statut-ok {
  background: var(--ok-fond);
}
.bandeau-suivi.carte--statut-inactif {
  background: var(--surface-douce);
}
.bandeau-suivi-avatar {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 52px;
  height: 52px;
  border-radius: var(--rayon-pastille);
  background: var(--surface-haute);
  color: var(--accent-marque);
  border: 3px solid var(--surface);
  font-family: var(--police-titres);
  font-weight: var(--graisse-titre);
  font-size: var(--taille-titre-4);
  overflow: hidden;
}
.bandeau-suivi-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.bandeau-suivi-texte {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1;
}
.bandeau-suivi-statut {
  font-family: var(--police-titres);
  font-weight: var(--graisse-titre);
  font-size: var(--taille-titre-3);
  letter-spacing: var(--interlettrage-titre);
}
.bandeau-suivi-aide {
  font-size: var(--taille-texte-2);
  color: var(--texte-secondaire);
}
.bandeau-suivi .bouton {
  flex: 0 0 auto;
}

.recap {
  display: flex;
  flex-direction: column;
  gap: var(--espacement-petit);
}
.recap-lignes {
  margin: 0;
  display: flex;
  flex-direction: column;
}
.recap-ligne {
  display: grid;
  grid-template-columns: 88px 1fr;
  gap: var(--espacement-petit);
  padding: 6px 0;
  border-bottom: var(--epaisseur-trait) solid var(--trait);
  font-size: var(--taille-texte-2);
}
.recap-ligne:last-child {
  border-bottom: 0;
}
.recap-ligne dt {
  color: var(--texte-secondaire);
}
.recap-ligne dd {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  color: var(--texte);
}
.recap-sous {
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
}
.recap-prix {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding-top: var(--espacement-petit);
  border-top: var(--epaisseur-trait) solid var(--trait);
  font-size: var(--taille-texte-2);
}
.recap-prix-ligne {
  display: flex;
  justify-content: space-between;
  color: var(--texte-secondaire);
}
.recap-prix-total {
  color: var(--texte);
  font-family: var(--police-titres);
  font-weight: var(--graisse-titre);
  font-size: var(--taille-titre-3);
  padding-top: 4px;
}

/* Détails compacts (issue n°178, maquette suivi-commande.html) : quatre
   lignes libellé/valeur seulement (Raquette, Cordage, Tension, Prix), la
   valeur en gras à droite — le reste (notes, dépôt, détail Pose/Total) va
   dans l'accordéon « Voir le détail » juste en dessous (.recap-lignes,
   .recap-prix ci-dessus, réutilisés tels quels). */
.recap-compacte-lignes {
  margin: 0;
  display: flex;
  flex-direction: column;
}
.recap-compacte-ligne {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--espacement-petit);
  padding: 7px 0;
  font-size: var(--taille-texte-2);
}
.recap-compacte-ligne:not(:last-child) {
  border-bottom: var(--epaisseur-trait) solid var(--trait);
}
.recap-compacte-ligne dt {
  color: var(--texte-secondaire);
}
.recap-compacte-ligne dd {
  margin: 0;
  font-weight: var(--graisse-semibold);
  color: var(--texte);
  text-align: right;
}

/* Bouton de message (issue n°178, maquette suivi-commande.html) : contour,
   à la taille de son contenu et centré, plutôt qu'un simple lien de section
   repliable — la messagerie existante (details#messages) reste dessous. */
.suivi-message-bouton {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--espacement-petit);
  width: fit-content;
  margin: 0 auto;
}
.section-repliable > summary.suivi-message-bouton::after {
  content: none;
}
.suivi-message-bouton svg {
  width: var(--taille-icone-retour);
  height: var(--taille-icone-retour);
  color: var(--accent-marque-encre);
}

.suivi {
  display: flex;
  flex-direction: column;
  gap: var(--espacement-petit);
}

.actions-secondaires > summary {
  font-size: var(--taille-texte-2);
  color: var(--erreur);
}
.actions-secondaires > summary::after {
  border-color: var(--erreur);
}
.actions-secondaires form {
  margin-top: var(--espacement-petit);
}

/* Moments (issue n°140, règle 7, maquettes confirmation.html et
   celebration-notation.html) : un écran, un visage, une seule action —
   centré, plutôt qu'un simple bandeau/carte parmi d'autres. */
.moment {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--espacement-petit);
  padding: var(--espacement-section) 0;
  text-align: center;
}
.moment-sous-titre {
  max-width: 320px;
  margin: 0 0 var(--grille-base);
}
.moment-motif {
  width: var(--taille-motif-moment);
  height: var(--taille-motif-moment);
  margin-bottom: var(--grille-base);
}
.moment-motif svg {
  width: 100%;
  height: 100%;
}
.moment-motif-montant {
  fill: var(--texte);
}
.moment-motif-cordage {
  stroke: var(--accent-marque);
}
.carte-recap-moment {
  width: 100%;
  text-align: left;
  display: flex;
  flex-direction: column;
  gap: var(--grille-base);
}
.moment-ligne {
  display: flex;
  align-items: center;
  gap: var(--espacement-petit);
  margin: 0;
  padding: var(--grille-base) 0;
  font-size: var(--taille-texte-2);
  color: var(--texte);
}
.moment-ligne-icone {
  width: var(--taille-icone-moment);
  height: var(--taille-icone-moment);
  color: var(--accent-marque-encre);
  flex-shrink: 0;
}
.moment-actions {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--espacement-petit);
  width: 100%;
}

/* Célébration à la récupération (règle 7, fin) : avatar du cordeur avec
   badge coché plutôt qu'une simple icône de statut. */
.avatar-celebration {
  position: relative;
  width: var(--taille-avatar-celebration);
  height: var(--taille-avatar-celebration);
  border-radius: var(--rayon-pastille);
  background: var(--surface-haute);
  color: var(--accent-marque);
  border: var(--epaisseur-accent-avatar) solid var(--accent-marque);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--police-titres);
  font-weight: var(--graisse-titre);
  font-size: var(--taille-titre-2);
  overflow: hidden;
  margin-bottom: var(--grille-base);
}
.avatar-celebration img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.avatar-celebration-badge {
  position: absolute;
  bottom: -2px;
  right: -2px;
  width: var(--taille-badge-celebration);
  height: var(--taille-badge-celebration);
  border-radius: var(--rayon-pastille);
  background: var(--accent-marque);
  border: var(--epaisseur-accent-badge) solid var(--fond);
  color: var(--surface-haute);
  display: flex;
  align-items: center;
  justify-content: center;
}
.avatar-celebration-badge svg {
  width: 60%;
  height: 60%;
}
.carte-notation {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--espacement-petit);
}
.carte-notation-question {
  margin: 0;
  font-weight: var(--graisse-semibold);
  font-size: var(--taille-texte-2);
  color: var(--texte);
}
.note-etoiles-choix--grande .note-etoiles {
  font-size: var(--taille-titre-1);
}

/* ---- ecrans-commande.css ---- */
/*
 * Étape 3 de la commande (/commander/:id/nouvelle) — couche d'interface
 * Stringerz (Claude, 02/09/2026). Aucune valeur en dur.
 */

.bloc-formulaire {
  display: flex;
  flex-direction: column;
  gap: var(--espacement-petit);
}
.bloc-formulaire .titre-3 {
  margin: 0;
}
.bloc-formulaire .champ {
  margin-bottom: 0;
}

.choix-deux {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--espacement-petit);
}
.choix-carte {
  display: flex;
  flex-direction: column;
  /* align-items par défaut de base.css (issue #52) est `center` : sans
     l'écraser ici, le titre et le sous-titre se centrent chacun sur leur
     propre largeur au lieu de rester alignés à gauche (constaté issue n°215,
     texte visuellement collé/décalé selon la longueur du libellé). */
  align-items: flex-start;
  gap: 2px;
  padding: 10px 12px;
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
  background: var(--surface);
  cursor: pointer;
  position: relative;
}
.choix-carte input {
  position: absolute;
  opacity: 0;
  inset: 0;
  cursor: pointer;
}
/* Menthe = choix retenu (issue n°140) : même sens que les créneaux et les
   chronologies, plutôt qu'un gris neutre. */
.choix-carte--actif,
.choix-carte:has(input:checked) {
  border-color: var(--accent-marque);
  background: var(--accent-marque-voile);
}
.choix-carte--inactif {
  opacity: 0.5;
  cursor: not-allowed;
}
.choix-carte-titre {
  font-family: var(--police-titres);
  font-weight: var(--graisse-titre);
  font-size: var(--taille-titre-4);
  letter-spacing: var(--interlettrage-titre);
}
.choix-carte-sous {
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
}

.champs-deux {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--espacement-petit);
}

/* Pose hybride (issue n°229) : bloc travers et mentions liées masqués tant
   que « Pose hybride » n'est pas cochée — pur CSS, comme le reste du tunnel
   (aucun JavaScript de ce formulaire). Portée par conteneur (.tunnel-mobile
   / .tunnel) puisque mobile et bureau partagent les mêmes noms de champs
   dans deux arbres DOM distincts (voir commander-nouvelle.tsx). */
.hybride-mention {
  display: none;
}
.tunnel-mobile:has(input[name='stringingMode'][value='hybrid']:checked) .hybride-mention,
.tunnel:has(input[name='stringingMode'][value='hybrid']:checked) .hybride-mention {
  display: block;
}
.cordage-travers.hybride-mention {
  display: none;
}
.tunnel-mobile:has(input[name='stringingMode'][value='hybrid']:checked) .cordage-travers.hybride-mention,
.tunnel:has(input[name='stringingMode'][value='hybrid']:checked) .cordage-travers.hybride-mention {
  display: flex;
  flex-direction: column;
  gap: var(--espacement-petit);
}

/*
 * Carte plein écran de /commander?vue=carte (issue #84) — filtres en onglets,
 * boutons ronds flottants, chips « Mes lieux », volet du bas façon
 * Mondial Relay. .carte-plein-ecran reste le référentiel de positionnement de
 * tous les éléments flottants, aussi bien en position relative (bureau,
 * carte simplement plus haute) qu'en position fixe (mobile, plein écran sous
 * la barre du haut).
 */
.carte-plein-ecran {
  position: relative;
}
.carte-plein-ecran .cartographie {
  height: var(--hauteur-carte-plein-ecran);
  min-height: var(--hauteur-carte-tablette);
}

.carte-filtres {
  position: absolute;
  top: var(--espacement-petit);
  left: var(--espacement-petit);
  right: var(--espacement-petit);
  z-index: 20;
  display: flex;
  gap: 4px;
  overflow-x: auto;
  padding: 4px;
  background: var(--surface);
  border-radius: var(--rayon-pastille);
  box-shadow: var(--ombre-carte);
}
.filtre-onglet {
  flex: 0 0 auto;
  font-family: var(--police-texte);
  font-size: var(--taille-texte-3);
  font-weight: var(--graisse-medium);
  color: var(--texte-secondaire);
  background: none;
  border: none;
  border-radius: var(--rayon-pastille);
  padding: var(--grille-base) var(--espacement-petit);
  cursor: pointer;
}
.filtre-onglet--actif {
  color: var(--sur-action);
  background: var(--action);
}

.carte-mes-lieux {
  position: absolute;
  top: 52px;
  left: var(--espacement-petit);
  right: var(--espacement-petit);
  z-index: 20;
  display: flex;
  gap: var(--grille-base);
  overflow-x: auto;
}
.chip--lieu {
  flex: 0 0 auto;
  text-decoration: none;
}

.carte-boutons-flottants {
  position: absolute;
  right: var(--espacement-petit);
  bottom: var(--espacement-petit);
  z-index: 20;
  display: flex;
  flex-direction: column;
  gap: var(--espacement-petit);
}
.bouton-rond {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--taille-bouton-rond);
  height: var(--taille-bouton-rond);
  border-radius: var(--rayon-pastille);
  background: var(--surface);
  color: var(--texte);
  border: var(--epaisseur-trait) solid var(--trait);
  box-shadow: var(--ombre-carte);
  cursor: pointer;
}
.bouton-rond:disabled {
  opacity: 0.6;
}

/* Bouton « Rechercher dans cette zone » (issue #95, façon Booking) : apparaît
   après un déplacement de la carte, centré en bas pour ne jamais chevaucher
   les onglets de filtre ni les chips « Mes lieux » posés en haut. */
.bouton-zone-carte {
  position: absolute;
  bottom: var(--espacement-petit);
  left: 50%;
  transform: translateX(-50%);
  z-index: 25;
}

/* Champ de recherche en surimpression (bouton rond « Rechercher ») : le même
   formulaire que la vue Liste, repositionné plutôt que dupliqué. .recherche
   pose `display: flex` sans condition (interface.css) : sans la règle
   [hidden] ci-dessous, l'attribut hidden posé par carte.js resterait sans
   effet (même piège déjà traité par .chat-panneau, voir chat.css). */
#recherche-carte[hidden] {
  display: none;
}
.carte-plein-ecran #recherche-carte {
  position: absolute;
  top: var(--espacement-petit);
  left: var(--espacement-petit);
  right: var(--espacement-petit);
  z-index: 30;
  background: var(--surface);
  padding: var(--espacement-petit);
  border-radius: var(--rayon);
  box-shadow: var(--ombre-carte);
}

.volet {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 40;
  max-height: 70%;
  overflow-y: auto;
  background: var(--surface);
  border-radius: var(--rayon) var(--rayon) 0 0;
  box-shadow: var(--ombre-carte);
  padding: var(--espacement-petit) var(--marge-ecran) var(--marge-ecran);
}
.volet-poignee {
  display: block;
  width: 40px;
  height: 4px;
  margin: 0 auto var(--espacement-petit);
  border-radius: var(--rayon-pastille);
  background: var(--trait);
  border: none;
  cursor: pointer;
}
.volet-cordeur .prix {
  font-family: var(--police-nombre);
  font-weight: var(--graisse-semibold);
}

/* Marqueurs personnalisés (issue #84) : SVG inline dans un L.divIcon
   (src/web/assets/carte.js), couleurs encre/vert/ambre en dur comme le
   marqueur « Toi » déjà présent (JS ne peut pas lire les variables CSS). */
.marqueur-divicon {
  background: none;
  border: none;
}
.marqueur-carte {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--taille-marqueur-carte);
  height: var(--taille-marqueur-carte);
  border-radius: var(--rayon-pastille);
  border: 2px solid var(--surface);
  box-shadow: var(--ombre-carte);
}
.marqueur-groupe {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--taille-marqueur-groupe);
  height: var(--taille-marqueur-groupe);
  border-radius: var(--rayon-pastille);
  background: var(--surface-haute);
  color: var(--accent-marque);
  font-family: var(--police-nombre);
  font-weight: var(--graisse-semibold);
  font-size: var(--taille-texte-3);
  border: 2px solid var(--surface);
  box-shadow: var(--ombre-carte);
}

/* Zone de résultats de /commander (section club + section générale + pagination,
   voir commander.tsx) — empilée verticalement. Issue n°215 : ce bloc portait
   par erreur la classe partagée .recherche-liste (composant de champ de
   recherche admin, lcs.css, issue n°195), qui est en `display: flex;
   align-items: center` et devient sticky sous la barre admin en mobile —
   d'où la pagination affichée sur le côté, au milieu du bloc, au lieu d'être
   en dessous de la liste. Nom dédié pour ne plus hériter de ce composant. */
.resultats-recherche {
  display: flex;
  flex-direction: column;
  gap: var(--espacement-section);
}

/* Mobile : la carte occupe tout l'écran sous la barre du haut (référence
   Mondial Relay, issue #84) — le reste de l'écran (titre, recherche, onglets
   Liste/Carte) est masqué, remplacé par les boutons ronds flottants
   (recherche, localisation, retour en liste). Depuis l'issue #95, carte et
   liste sont toutes les deux toujours rendues (voir commander.tsx,
   .recherche-cordeurs) : sur mobile, une seule des deux zones reste visible
   à la fois, selon `vue` — sur bureau (lcs.css), les deux le sont toujours. */
@media (max-width: 899px) {
  .ecran--carte-plein-ecran > *:not(.recherche-cordeurs) {
    display: none;
  }
  .ecran--carte-plein-ecran .resultats-recherche {
    display: none;
  }
  .ecran:not(.ecran--carte-plein-ecran) .recherche-carte {
    display: none;
  }
  .carte-plein-ecran {
    position: fixed;
    top: var(--hauteur-barre-haute);
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 50;
  }
  .carte-plein-ecran .cartographie {
    position: absolute;
    inset: 0;
    height: auto;
    min-height: 0;
    border-radius: 0;
  }
}

/* Bureau : la carte garde sa place dans la mise en page habituelle (issue
   #84) ; les contrôles flottants restent ancrés sur .carte-plein-ecran
   plutôt que sur le viewport. Depuis l'issue #95, sa hauteur y est fixée à
   --hauteur-carte-recherche (collante au-dessus de la liste, voir lcs.css)
   plutôt que --hauteur-carte-plein-ecran (réservée au mode plein écran mobile
   ci-dessus). */
@media (min-width: 900px) {
  .carte-plein-ecran .cartographie {
    border-radius: var(--rayon);
  }
}

/*
 * Tunnel de réservation mobile : une question par écran (issue n°177,
 * maquette docs/maquettes/2026-09-09/etape-reservation.html) — pendant
 * mobile de .tunnel/.option/.tunnel__* (bureau.css, issue n°174), valeurs
 * reprises telles quelles de la maquette. Classes génériques scopées sous
 * .tunnel-mobile : ce sont des noms déjà pris ailleurs dans l'app
 * (.progression, .question, .pied…), voir docs/design-system.md. Partagée
 * par commander-livraison.tsx et commander-nouvelle.tsx ; toujours dans un
 * bloc .seulement-mobile, donc jamais visible à partir du palier bureau.
 */
.tunnel-mobile {
  padding-bottom: 180px;
}
.tunnel-mobile__etape {
  font-size: var(--taille-texte-3);
  font-weight: var(--graisse-forte);
  text-transform: uppercase;
  letter-spacing: var(--interlettrage-etiquette);
  color: var(--texte-secondaire);
  margin: 0 0 8px;
}
.tunnel-mobile__progression {
  display: flex;
  gap: 5px;
  margin-bottom: 18px;
}
.tunnel-mobile__segment {
  flex: 1;
  height: 4px;
  border-radius: var(--rayon-pastille);
  background: var(--trait);
}
.tunnel-mobile__segment--fait {
  background: var(--accent-marque);
}
.tunnel-mobile__segment--actif {
  background: var(--surface-haute);
}
.tunnel-mobile h1 {
  font-size: var(--taille-titre-1);
  font-weight: var(--graisse-noire);
  letter-spacing: -0.02em;
  text-wrap: balance;
  margin: 0 0 4px;
}
.tunnel-mobile__aide {
  font-size: var(--taille-texte-2);
  color: var(--texte-secondaire);
  margin: 0 0 20px;
}
.tunnel-mobile__sous-titre {
  font-size: var(--taille-titre-3);
  font-weight: var(--graisse-forte);
  margin: 20px 0 10px;
}
.tunnel-mobile__sous-titre:first-of-type {
  margin-top: 0;
}
.tunnel-mobile__options {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.tunnel-mobile__option {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 16px;
  background: var(--surface);
  border: 2px solid var(--trait);
  border-radius: var(--rayon);
  position: relative;
  text-decoration: none;
}
.tunnel-mobile__option input {
  position: absolute;
  opacity: 0;
  inset: 0;
  cursor: pointer;
}
.tunnel-mobile__option:has(input:checked) {
  border-color: var(--accent-marque);
  background: var(--accent-marque-voile);
}
.tunnel-mobile__option-texte {
  flex: 1;
}
.tunnel-mobile__option-titre {
  display: block;
  font-size: var(--taille-texte-1);
  font-weight: var(--graisse-forte);
  color: var(--texte);
}
.tunnel-mobile__option-sous {
  display: block;
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
}
.tunnel-mobile__option-coche {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 2px solid var(--trait);
  flex-shrink: 0;
}
.tunnel-mobile__option:has(input:checked) .tunnel-mobile__option-coche {
  background: var(--accent-marque);
  border-color: var(--accent-marque);
}
.tunnel-mobile__option-chevron {
  color: var(--texte-secondaire);
}
.tunnel-mobile__section {
  margin-top: 20px;
}

/* Bouton collant pleine largeur (issue n°177) : posé au-dessus de la
   navigation basse (.nav-basse, base.css, position fixed elle aussi) plutôt
   que superposé — même calcul que .installer-app (interface.css). */
.tunnel-mobile__pied {
  position: fixed;
  left: 0;
  right: 0;
  bottom: calc(var(--hauteur-nav-basse) + env(safe-area-inset-bottom));
  z-index: 15;
  padding: var(--espacement-petit) var(--marge-ecran);
  background: var(--surface);
  border-top: var(--epaisseur-trait) solid var(--trait);
}
.tunnel-mobile__recap {
  font-size: var(--taille-texte-2);
  color: var(--texte-secondaire);
  text-align: center;
  margin: 0 0 8px;
}
.tunnel-mobile__recap b {
  color: var(--texte);
}
.tunnel-mobile__retour {
  display: block;
  text-align: center;
  font-size: var(--taille-texte-2);
  color: var(--texte-secondaire);
  text-decoration: none;
  margin: 0 0 8px;
}
.tunnel-mobile__reassurance {
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
  text-align: center;
  margin: 8px 0 0;
}

/* ---- ecrans-cordeur.css ---- */
/*
 * Écrans cordeur — couche d'interface Stringerz (Claude, 02/09/2026),
 * vocabulaire de l'admin LCS (capture « Commandes · Approbations »).
 * Aucune valeur en dur.
 */

/* ---------- Compteurs : libellé capitales, chiffre coloré selon le sens ---------- */

.compteurs--deux {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.compteur--info .compteur-valeur {
  color: var(--action);
}

/* ---------- Filtres défilants ---------- */

.filtres--defilants {
  flex-wrap: nowrap;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding-bottom: 2px;
}
.filtres--defilants::-webkit-scrollbar {
  display: none;
}
.filtres--defilants .filtre {
  flex: 0 0 auto;
  white-space: nowrap;
}

/* Indice de défilement (issue n°222) : dégradé de fondu sur le bord droit,
   seul indice qu'il reste des éléments coupés hors écran — .atelier-nav
   (onglets /cordeur) et .filtres--defilants (pastilles /cordeur/commandes,
   /relais/commandes) partagent le même motif de défilement horizontal
   ci-dessus, donc le même correctif plutôt qu'un dégradé par écran. Le canal
   couleur du dégradé n'est jamais affiché (un masque ne retient que
   l'opacité) : var(--texte) évite d'écrire une couleur en dur sans en
   ajouter une nouvelle à tokens.css (règle 6, CLAUDE.md). */
.atelier-nav,
.filtres--defilants {
  mask-image: linear-gradient(to right, var(--texte) calc(100% - 28px), transparent);
  -webkit-mask-image: linear-gradient(to right, var(--texte) calc(100% - 28px), transparent);
}

/* /cordeur débordait de 531 px sur mobile (audit du 24/09, issue n°251) :
   même si .atelier-nav rendait bien son contenu débordant à l'intérieur de
   sa propre bande (overflow-x: auto, largeur figée à celle de l'écran,
   scrollbar masquée), document.documentElement.scrollWidth grandissait quand
   même jusqu'à la largeur des pastilles non compressées — vérifié à la
   souris (Playwright, 375 px) : ni min-width: 0 ni overflow-x: hidden sur
   .atelier-nav ou ses ancêtres n'empêchent cette fuite, seul le fait de
   donner à la bande son propre contexte de mise en page (position: relative
   ou contain: layout) la coupe. contain: layout documente l'intention sans
   toucher au rendu ni au défilement interne (vérifié : toujours une seule
   ligne, toujours défilable). */
.atelier-nav,
.filtres--defilants {
  contain: layout;
}

/* ---------- Commandes cordeur mobile (issue n°202, refonte de la maquette
   du 14/09 : « pas de design sur cette page, l'une des plus importantes »,
   docs/maquettes/2026-09-14/commandes-cordeur.html — bascule mobile de la
   même grammaire que .commandes-cordeur en bureau, voir bureau.css) ---------- */

.commandes-cordeur-recherche {
  margin: 12px 0;
}
.groupe-echeance {
  margin: 22px 0 10px;
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: var(--taille-texte-4);
  font-weight: var(--graisse-semibold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--texte-secondaire);
}
.groupe-echeance:first-of-type {
  margin-top: 0;
}
.groupe-echeance i {
  flex: 1;
  height: var(--epaisseur-trait);
  background: var(--trait);
}
.liste-commandes {
  display: grid;
  gap: var(--espacement-petit);
}
.ligne-commande {
  padding: 12px 14px;
  background: var(--surface);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
  border-left: 4px solid var(--trait);
}
.ligne-commande--a-faire {
  border-left-color: var(--attention);
}
.ligne-commande--en-cours {
  border-left-color: var(--accent-marque);
}
.ligne-commande--prete {
  border-left-color: var(--accent-marque-encre);
}
.ligne-commande__lien {
  display: grid;
  grid-template-columns: 44px 1fr;
  gap: var(--espacement-champ-vertical);
  align-items: start;
  text-decoration: none;
  color: inherit;
}
.ligne-commande__avatar {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--avatar-1-fond);
  color: var(--avatar-1-texte);
  display: grid;
  place-items: center;
  font-weight: var(--graisse-forte);
  font-size: var(--taille-texte-2);
}
.ligne-commande__avatar--2 {
  background: var(--avatar-2-fond);
  color: var(--avatar-2-texte);
}
.ligne-commande__avatar--3 {
  background: var(--avatar-3-fond);
  color: var(--avatar-3-texte);
}
.ligne-commande__avatar--4 {
  background: var(--avatar-4-fond);
  color: var(--avatar-4-texte);
}
.ligne-commande__texte {
  min-width: 0;
}
.ligne-commande__titre {
  display: block;
  font-weight: var(--graisse-semibold);
  font-size: var(--taille-texte-1);
}
.ligne-commande__sous {
  display: block;
  margin: 2px 0 0;
  font-size: var(--taille-texte-2);
  color: var(--texte-secondaire);
  line-height: 1.4;
}
.ligne-commande__quand {
  display: block;
  margin: 2px 0 0;
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
}
/* Même famille de pastille à sens que .commandes-cordeur en bureau (voir
   bureau.css et docs/design-system.md → Pastilles) : encre pour « prête »
   (le joueur est attendu), ambre pour « à faire » (une décision reste au
   cordeur), menthe pour « en cours » (rien à décider). Redéfinie ici,
   scopée à .ligne-commande — même principe que .accueil-cordeur .file
   .pastille, ce n'est pas le doublon que traque design-guard-doublons-css. */
.ligne-commande .pastille {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: var(--rayon-pastille);
  font-size: var(--taille-texte-4);
  font-weight: var(--graisse-semibold);
  white-space: nowrap;
  background: var(--accent-marque-voile);
  color: var(--accent-marque-encre);
}
.ligne-commande .pastille::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent-marque-encre);
}
.ligne-commande .pastille--encre {
  background: var(--action);
  color: var(--sur-action);
}
.ligne-commande .pastille--encre::before {
  background: var(--accent-marque);
}
.ligne-commande .pastille--attente {
  background: var(--attention-fond);
  color: var(--attention-texte);
}
.ligne-commande .pastille--attente::before {
  background: var(--attention);
}
.ligne-commande .pastille--neutre {
  background: rgba(21, 32, 28, 0.06);
  color: var(--texte-secondaire);
}
.ligne-commande .pastille--neutre::before {
  background: var(--texte-secondaire);
}
.ligne-commande__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding-top: 10px;
  margin-top: 10px;
  border-top: var(--epaisseur-trait) solid var(--trait);
}
/* Largeur au contenu, pas étirés à égalité (issue n°249, règle 3) : flex: 1
   forçait un pavé large sur mobile étroit — la pastille peut repasser à la
   ligne (flex-wrap: wrap ci-dessus), mais .actions-paire (boutons.css)
   garde « Prendre en charge »/« Refuser » groupés sur une ligne. */
.ligne-commande__actions .bouton {
  flex: none;
  justify-content: center;
  white-space: nowrap;
}

.entete-page-titre .avatar {
  width: 36px;
  height: 36px;
  font-size: var(--taille-titre-4);
}

/* Mon atelier — repasse design & pages (issue n°127, 07/09) : tarification en
   grille libellé / valeur, tuiles quatre par ligne en bureau, formulaire de
   profil pleine largeur avec champs courts groupés sur une ligne. */
.tarif-grille {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--grille-base) var(--espacement-section);
  margin: 0;
}
.tarif-grille dt {
  color: var(--texte-secondaire);
}
.tarif-grille dd {
  margin: 0;
  text-align: right;
  font-weight: var(--graisse-semibold);
}
.ecran-atelier .carte--formulaire,
.ecran-atelier form,
.ecran-atelier .formulaire {
  max-width: none;
}
.champs-groupe {
  display: grid;
  gap: var(--espacement-petit);
  align-items: end;
}
.champs-groupe .champ {
  margin-bottom: var(--espacement-petit);
}
.ecran.ecran-atelier form > .bouton--principal {
  display: flex;
  width: fit-content;
  margin-left: auto;
}
@media (min-width: 600px) {
  .champs-groupe--2 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .champs-groupe--3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
/* Mon atelier — bande aperçu + nav de rubriques collante (issue n°203,
   maquette validée le 14/09/2026, docs/maquettes/2026-09-14/mon-atelier.html) :
   remplace les tuiles et le formulaire replié par un panneau par rubrique,
   changé sans rechargement par atelier.js (repli complet vers les pages
   /cordeur/... existantes sans JS — voir routes.tsx et docs/design-system.md,
   décision 018). Mobile : pilules défilantes sous l'aperçu, pas de colonne ;
   bureau : voir bureau.css (colonne collante 240px). */
.atelier-apercu {
  display: flex;
  align-items: center;
  gap: var(--espacement-petit);
  padding: var(--carte-padding-vertical) var(--carte-padding-horizontal);
  border-radius: var(--rayon);
  background: var(--surface-haute);
  color: var(--sur-action);
  margin-bottom: var(--espacement-section);
}
.atelier-apercu-nom {
  margin: 0;
  font-weight: var(--graisse-semibold);
  font-size: var(--taille-texte-1);
}
.atelier-apercu-ligne {
  margin: 2px 0 0;
  color: var(--texte-sur-sombre);
  font-size: var(--taille-texte-2);
}
.atelier-apercu-lien {
  /* Lien sur fond marine : pas .bouton--discret (fond blanc sur fond blanc,
     constaté à la fusion de n°203) — un lien clair, souligné, compact. */
  margin-left: auto;
  flex: none;
  padding: 8px 12px;
  border: var(--epaisseur-trait) solid rgba(255, 255, 255, 0.35);
  border-radius: var(--rayon-bouton);
  color: var(--sur-action);
  font-size: var(--taille-texte-2);
  font-weight: var(--graisse-semibold);
  text-decoration: none;
  white-space: nowrap;
}
@media (max-width: 480px) {
  .atelier-apercu { flex-wrap: wrap; }
  .atelier-apercu-lien { margin-left: 0; width: 100%; text-align: center; }
}

.atelier-nav {
  display: flex;
  gap: 6px;
  margin-bottom: var(--espacement-section);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding-bottom: 2px;
}
.atelier-nav::-webkit-scrollbar {
  display: none;
}
.atelier-nav-groupe {
  display: none; /* étiquettes de groupe réservées au bureau, voir bureau.css */
}
.atelier-nav a {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 14px;
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon-pastille);
  background: var(--surface);
  font-size: var(--taille-texte-2);
  font-weight: var(--graisse-semibold);
  color: var(--texte-secondaire);
  white-space: nowrap;
  /* Oublié à la création (issue n°203) : sans ça, chaque onglet garde le
     soulignement natif du lien, jamais voulu sur une pastille bordée (retour
     Vincent, issue n°215 — comparaison avec le menu admin .stab, déjà en
     text-decoration: none). */
  text-decoration: none;
}
.atelier-nav a.on {
  background: var(--accent-marque-voile);
  color: var(--accent-marque-encre);
  border-color: transparent;
}
.atelier-nav-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--attention);
}

/* Panneau de la rubrique active : mêmes espacements verticaux que .ecran
   (base.css) entre ses sections — nécessaire ici, .atelier-panneau n'étant
   plus un enfant direct de .ecran une fois posé dans la grille .atelier. */
.atelier-panneau {
  display: flex;
  flex-direction: column;
  gap: var(--espacement-section);
  min-width: 0;
}

/* ---------- Commande côté cordeur : décision, bon de cordage, gains,
   suivi horizontal, aperçu des messages (issue n°164, maquette
   docs/maquettes/2026-09-11/commande-cordeur.html) ---------- */

.decision {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--espacement-section);
}
.decision-gauche {
  display: flex;
  align-items: center;
  gap: var(--espacement-petit);
  min-width: 0;
}
.avatar--decision {
  width: var(--taille-avatar-decision);
  height: var(--taille-avatar-decision);
  font-size: var(--taille-titre-2);
  flex-shrink: 0;
}
.decision h1 {
  margin: 0 0 3px;
  font-size: var(--taille-titre-2);
  font-weight: var(--graisse-titre);
  line-height: 1.25;
}
.decision-gauche p {
  margin: 0;
  font-size: var(--taille-texte-2);
  color: var(--texte-secondaire);
}
.decision-actions {
  display: flex;
  flex-direction: column;
  gap: var(--espacement-petit);
}
.decision-actions form,
.decision-actions > details {
  margin: 0;
}
.decision-actions .bouton {
  width: 100%;
}
@media (min-width: 760px) {
  .decision {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
  }
  .decision-actions {
    flex-direction: row;
    flex: none;
  }
  .decision-actions .bouton {
    width: auto;
    white-space: nowrap;
  }
}

/* Bon de cordage + gains : colonne latérale à largeur fixe (pas une
   fraction, voir maquette), empilées avant le palier de cet écran. */
.corps-commande-cordeur {
  display: flex;
  flex-direction: column;
  gap: var(--espacement-section);
}
@media (min-width: 760px) {
  .corps-commande-cordeur {
    display: grid;
    grid-template-columns: 1fr var(--largeur-carte-gains);
    align-items: start;
  }
}

.carte-titre {
  margin: 0 0 var(--espacement-petit);
  font-size: var(--taille-texte-4);
  font-weight: var(--graisse-forte);
  letter-spacing: var(--interlettrage-etiquette);
  text-transform: uppercase;
  color: var(--texte-secondaire);
}

.tensions {
  display: flex;
  align-items: baseline;
  gap: var(--grille-base);
  margin-bottom: var(--espacement-section);
}
.tensions-valeur {
  font-size: var(--taille-cordage-tension);
  font-weight: var(--graisse-noire);
  letter-spacing: var(--interlettrage-titre);
  line-height: 1;
}
.tensions-unite {
  font-size: var(--taille-texte-1);
  color: var(--texte-secondaire);
}
.tensions-libelle {
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
  margin-left: var(--grille-base);
}

.grille-infos {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--espacement-section);
}
.grille-infos .info-valeur.nombre {
  white-space: normal; /* « Ven. 11 sept. 09:00–10:00 » doit pouvoir passer à la ligne sur mobile */
}
.grille-infos .info-libelle {
  margin: 0 0 3px;
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
}
.grille-infos .info-valeur {
  margin: 0;
  font-size: var(--taille-texte-1);
  font-weight: var(--graisse-semibold);
}
.grille-infos .info-sous {
  margin: 2px 0 0;
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
}
.carte-cordage .encart {
  margin-top: var(--espacement-section);
}

.pastille--catalogue {
  display: inline-block;
  margin-left: var(--grille-base);
  padding: 3px 9px;
  border-radius: var(--rayon-pastille);
  background: var(--accent-marque-voile);
  color: var(--accent-marque-encre);
  font-size: var(--taille-texte-4);
  font-weight: var(--graisse-forte);
  vertical-align: middle;
}

.gains-net {
  margin: 0 0 4px;
  font-size: var(--taille-gains-net);
  font-weight: var(--graisse-noire);
  letter-spacing: var(--interlettrage-titre);
  color: var(--accent-marque-encre);
}
.gains-sous {
  margin: 0 0 var(--espacement-section);
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
}
.gains-detail {
  display: flex;
  flex-direction: column;
  gap: 7px;
  padding-top: var(--espacement-petit);
  border-top: var(--epaisseur-trait) solid var(--trait);
  font-size: var(--taille-texte-2);
}
.gains-ligne {
  display: flex;
  justify-content: space-between;
  gap: var(--espacement-petit);
  color: var(--texte-secondaire);
}
.gains-ligne--total {
  color: var(--texte);
  font-weight: var(--graisse-semibold);
  padding-top: 7px;
  margin-top: 2px;
  border-top: var(--epaisseur-trait) solid var(--trait);
}

/* Infobulle statique (CSS seule, sans aide.js ni bulle d'aide en base — le
   texte est fixe, pas éditable depuis /admin, à la différence de
   aide-bulle.tsx) : révélée au survol et au focus clavier. */
.infobulle {
  position: relative;
  display: inline-flex;
  vertical-align: middle;
  margin-left: 4px;
}
.infobulle-declencheur {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  padding: 0;
  border: none;
  border-radius: var(--rayon-pastille);
  background: none;
  color: var(--texte-secondaire);
  cursor: pointer;
}
.infobulle-declencheur:hover,
.infobulle-declencheur:focus-visible {
  background: var(--action-douce);
  color: var(--texte);
}
.infobulle-icone {
  width: 14px;
  height: 14px;
}
.infobulle-texte {
  position: absolute;
  z-index: 20;
  bottom: calc(100% + 6px);
  left: 50%;
  transform: translateX(-50%);
  width: max-content;
  max-width: 220px;
  padding: var(--grille-base) var(--espacement-petit);
  border-radius: var(--rayon-etiquette);
  background: var(--surface-haute);
  color: var(--sur-action);
  font-size: var(--taille-texte-3);
  font-weight: var(--graisse-regular);
  text-align: left;
  opacity: 0;
  pointer-events: none;
  transition: opacity 120ms ease;
}
.infobulle-declencheur:hover + .infobulle-texte,
.infobulle-declencheur:focus-visible + .infobulle-texte {
  opacity: 1;
  pointer-events: auto;
}

/* Suivi horizontal (issue n°164) : sorti vers styles/suivi-horizontal.css
   (issue n°169) pour être réutilisé tel quel par la tête bureau de
   /poses/:id, sans en réécrire une troisième version — voir ce fichier. */

/* Aperçu des messages replié (issue n°164) : dernier message en une ligne
   plutôt qu'un simple lien « Messages » — le <details> reste natif (fonctionne
   sans JS), seul son <summary> change d'habillage, sans le chevron générique
   de .section-repliable (le bouton « Répondre » fait déjà office d'affordance). */
.messages-apercu > summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  gap: var(--espacement-petit);
}
.messages-apercu > summary::-webkit-details-marker {
  display: none;
}
.messages-resume-texte {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1;
}
.messages-titre {
  font-size: var(--taille-texte-1);
  font-weight: var(--graisse-semibold);
}
.messages-ligne-apercu {
  font-size: var(--taille-texte-2);
  color: var(--texte-secondaire);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.messages-bouton {
  flex: none;
  width: auto;
}
.messages-apercu[open] > summary {
  margin-bottom: var(--espacement-section);
}

/* Accueil cordeur mobile — en-tête « À faire N · Toutes les commandes »
   (retour Camille via la bulle de recette, 13/09 : « À faire se chevauche
   avec les cards du bas »). Les règles de .dash-box .dt vivent dans lcs.css,
   scopé à l'admin : côté app l'en-tête n'avait aucune mise en forme. */
.adm-shell--app .ecran .dt {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--espacement-petit);
  flex-wrap: wrap;
  margin: var(--espacement-section) 0 var(--espacement-petit);
  font-weight: var(--graisse-bold);
  font-size: var(--taille-titre-3);
}
.adm-shell--app .ecran .dt .muted {
  font-weight: var(--graisse-normal);
  color: var(--texte-secondaire);
}
.adm-shell--app .ecran .dt .icon-btn {
  flex: 0 0 auto;
  white-space: nowrap;
  font-size: var(--taille-texte-2);
  font-weight: var(--graisse-semibold);
  color: var(--accent-marque-encre);
  padding: 6px 0;
  background: none;
  border: 0;
}
.adm-shell--app .ecran .dt + .hint {
  margin: 0 0 var(--espacement-section);
  color: var(--texte-secondaire);
}

/* ---- ecrans-compte.css ---- */
/*
 * Mon compte — couche d'interface Stringerz (Claude, 02/09/2026).
 */

.compte-identite {
  display: flex;
  flex-direction: column;
  gap: var(--espacement-petit);
  /* Retour de Vincent du 24/09 (issue n°250) : cette carte, la plus dense de
     la page (avatar, chips, bouton de déconnexion), touchait presque la
     carte « Photo de profil » suivante malgré le gap standard de .ecran
     (--espacement-section) — marge additionnelle ciblée plutôt qu'un
     espacement plus grand pour toutes les cartes de la page. */
  margin-bottom: var(--espacement-petit);
}
.compte-identite-tete {
  display: flex;
  align-items: center;
  gap: var(--espacement-champ-vertical);
}
.compte-identite-texte {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.compte-identite-nom {
  font-family: var(--police-titres);
  font-weight: var(--graisse-titre);
  font-size: var(--taille-titre-3);
  letter-spacing: var(--interlettrage-titre);
}
.compte-aide {
  font-size: var(--taille-texte-3);
}
.section-repliable.carte > summary {
  margin: 0;
}
.section-repliable.carte[open] > summary {
  margin-bottom: var(--espacement-petit);
}

/* Section « Mes lieux » (issue #84). */
.liste-lieux {
  display: flex;
  flex-direction: column;
  gap: var(--espacement-petit);
  margin: 0;
  padding: 0;
  list-style: none;
}
.ligne-lieu {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--espacement-petit);
  padding: var(--grille-base) 0;
  border-bottom: var(--epaisseur-trait) solid var(--trait);
}
.ligne-lieu:last-child {
  border-bottom: none;
}
.ligne-lieu-label {
  display: block;
  font-family: var(--police-titres);
  font-weight: var(--graisse-medium);
}

/* Repasse Mon compte (issue n°121) : une seule largeur de carte, formulaires
   pleine largeur, cartes-liens unifiées, pastille de langue. */
@media (min-width: 900px) {
  /* Colonne de réglages : une seule largeur pour toutes les cartes. */
  .ecran.ecran-compte {
    max-width: var(--largeur-colonne-reglages);
  }
}
.ecran-compte .carte--formulaire,
.ecran-compte form,
.ecran-compte .formulaire {
  max-width: none;
}
.ecran-compte .bloc-formulaire > .bouton {
  align-self: flex-end;
}
.ecran-compte .carte-lieux .bloc-formulaire .champs-deux {
  grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
}
.carte-lien-groupe {
  padding: 0;
  gap: 0;
}
.carte-lien {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--espacement-petit);
  padding: var(--carte-padding-vertical) var(--carte-padding-horizontal);
  color: inherit;
  text-decoration: none;
}
.carte-lien .titre-3 {
  margin: 0;
}
.carte-lien-chevron {
  width: 18px;
  height: 18px;
  fill: var(--texte-secondaire);
  flex: 0 0 18px;
}
.carte-lien-secondaire {
  border-top: var(--epaisseur-trait) solid var(--trait);
  padding: var(--espacement-petit) var(--carte-padding-horizontal);
}
.carte-ligne {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
}
.carte-ligne .titre-3 {
  margin: 0;
}
@media (max-width: 599px) {
  .ecran-compte .carte-lieux .bloc-formulaire .champs-deux {
    grid-template-columns: 1fr;
  }
  .ecran-compte .bloc-formulaire > .bouton {
    align-self: stretch;
  }
}

/* Notifications push : une ligne état + actions compactes (08/09). */
.push-ligne {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--espacement-petit);
  flex-wrap: wrap;
}
.push-etat {
  margin: 0;
  display: inline-flex;
  align-items: center;
  gap: var(--espacement-petit);
  font-weight: var(--graisse-semibold);
  font-size: var(--taille-texte-2);
}
.push-etat::before {
  content: '';
  width: 8px;
  height: 8px;
  border-radius: var(--rayon-pastille);
  background: var(--texte-secondaire);
  flex: 0 0 8px;
}
.push-etat.push-etat--actif::before {
  background: var(--ok);
}
.push-etat.push-etat--refuse::before {
  background: var(--erreur);
}
.push-actions {
  display: flex;
  align-items: center;
  gap: var(--espacement-petit);
}
.push-actions .bouton {
  width: auto;
}

/* Pied de « Mon compte » : actions uniformes, suppression visible (issue
   n°156 : vivait dans lcs.css avec un fond `--lcs-actif`, couleur d'accent
   admin qui cassait la carte neutre de cet écran joueur). */
.compte-pied {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.compte-pied-liens {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
@media (max-width: 899px) {
  .compte-pied {
    flex-direction: column;
    align-items: stretch;
  }
  .compte-pied .bouton {
    width: 100%;
  }
}

/* ---- ecrans-relais.css ---- */
/*
 * Espace relais — onglets Aujourd'hui / Commandes / Cordeurs (issue n°204,
 * maquettes docs/maquettes/2026-09-14/relais-*.html, valeurs de
 * docs/maquettes/2026-09-11/bureau/bureau.css section « Espace relais »
 * reprises telles quelles). Mon club ajoute une classe depuis l'issue n°224
 * (grille horaires ci-dessous) ; Membres ajoute .champ-lien depuis l'issue
 * n°249 (carte « Inviter en un lien »), .carte/.champ/.tableau/.liste-cartes
 * existants suffisent pour le reste.
 *
 * .pastille n'a pas de définition globale (voir docs/design-system.md,
 * « Pastilles et badges ») : chaque écran la redéfinit, scopée à son
 * conteneur — .relais-ecran ici, comme .accueil-cordeur pour l'accueil
 * cordeur (bureau.css). Même chose pour .avatar, laissé tel quel (composant
 * global, base.css) : seule .pastille a besoin d'être posée ici.
 */

.relais-ecran .rel-grille {
  display: grid;
  grid-template-columns: 3fr 2fr;
  gap: 28px;
  align-items: start;
}

.relais-ecran .file {
  display: grid;
  gap: 10px;
  margin-bottom: 22px;
}

/* 3 colonnes (avatar/texte/pastille) : le scan des étiquettes (module à
   venir, issue n°204 « Aussi ») ajoutera une colonne `auto` de plus pour un
   bouton d'action à droite de la pastille — non rendu pour l'instant, la
   grille actuelle l'accueillera sans réagencement. */
.relais-ecran .raq {
  display: grid;
  grid-template-columns: 44px 1fr auto;
  gap: 14px;
  align-items: center;
  padding: 12px 16px;
  background: var(--surface);
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
}
.relais-ecran .raq b {
  display: block;
  font-size: var(--taille-texte-1);
}
.relais-ecran .raq small {
  display: block;
  font-size: var(--taille-texte-2);
}

.relais-ecran .cmd-groupe {
  font-size: var(--taille-texte-2);
  font-weight: 600;
  color: var(--texte-secondaire);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin: 0 0 10px;
}

.relais-ecran .membre {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--trait);
}
.relais-ecran .membre:last-of-type {
  border-bottom: 0;
}
.relais-ecran .membre b {
  display: block;
  font-size: var(--taille-texte-2);
}
.relais-ecran .membre small {
  display: block;
  font-size: var(--taille-texte-3);
}

.relais-ecran .cordeur-l {
  display: grid;
  grid-template-columns: 44px 1fr auto;
  gap: 14px;
  align-items: center;
  padding: 14px 16px;
  background: var(--surface);
  border: 1px solid var(--trait);
  border-radius: var(--rayon);
}
.relais-ecran .cordeur-l b {
  display: block;
  font-size: var(--taille-texte-1);
}
.relais-ecran .cordeur-l small {
  display: block;
  font-size: var(--taille-texte-2);
}

/* Statut à sens (docs/design-system.md) : menthe par défaut (fait / en cours
   / prêt), ambre en --attente (attend quelqu'un). */
.relais-ecran .pastille {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: var(--rayon-pastille);
  font-size: var(--taille-texte-3);
  font-weight: 600;
  background: var(--accent-marque-voile);
  color: var(--accent-marque-encre);
  white-space: nowrap;
}
.relais-ecran .pastille--attente {
  background: var(--attention-fond);
  color: var(--attention-texte);
}

@media (max-width: 900px) {
  .relais-ecran .rel-grille {
    grid-template-columns: 1fr;
  }
  .relais-ecran .raq,
  .relais-ecran .cordeur-l {
    grid-template-columns: 44px 1fr;
  }
  .relais-ecran .raq .pastille,
  .relais-ecran .cordeur-l .pastille {
    grid-column: 1 / -1;
    justify-self: start;
    margin-top: 4px;
  }
  /* Paire de décision d'une proposition de cordeur (/relais/cordeurs, issue
     n°249) : même repli que la pastille ci-dessus — sans grid-column, la
     grille à 2 colonnes de ce palier ne laisse que 44px à .actions-paire
     (3e enfant de la grille .cordeur-l), qui écrase « Accepter » hors de
     vue (constaté en rendu réel, pas seulement en lecture de code). */
  .relais-ecran .cordeur-l .actions-paire {
    grid-column: 1 / -1;
    justify-content: flex-start;
    margin-top: 4px;
  }
  /* Bouton Scanner + pastille (décision du 14/09/2026) : même repli que la
     pastille seule ci-dessus — .raq-actions n'est pas un enfant direct de la
     grille .raq, grid-column n'a donc d'effet que posé ici, sur le
     conteneur lui-même, pas sur la pastille qu'il enveloppe. Recette
     Vincent du 14/09 : plein largeur, côte à côte plutôt qu'empilés. */
  .relais-ecran .raq-actions {
    grid-column: 1 / -1;
    flex-direction: row;
    align-items: center;
    margin-top: 4px;
  }
}

/* Scanner relais (décisions du 14/09/2026) : zone caméra pleine largeur,
   coins arrondis comme .carte — voir relais-scanner.tsx et
   relais-scanner.js. */
.scanner-camera {
  display: flex;
  flex-direction: column;
  gap: var(--grille-base);
  margin-bottom: calc(var(--grille-base) * 3);
}
.scanner-camera video {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: var(--rayon);
  background: var(--texte);
}
.scanner-repli-forme {
  display: flex;
  flex-direction: column;
  gap: var(--grille-base);
  margin-top: calc(var(--grille-base) * 2);
}
.raq-actions {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--grille-base);
}

/* /relais/commandes, filtre sans résultat (retour recette du 15/09, issue
   n°207) : le gap de .etat-vide (base.css) reste à --espacement-petit pour
   les autres écrans qui le partagent — seul cet état vide-ci ajoute de l'air
   entre l'illustration et le texte, plutôt que de changer le composant
   partagé. Resserré de moitié (retour Vincent, issue n°211 : « remonter le
   logo un petit peu ») — l'ajout du 15/09 était allé trop loin. */
.relais-ecran .etat-vide {
  gap: calc(var(--espacement-section) / 2);
}

/* Sous-titre de rubrique sous le titre : règle générale dans base.css
   (.entete-page-titre:has(> .sous-titre), issues n°211 et n°265). */

/* Grille horaires de /relais/mon-club et /relais/creer (issue n°224) :
   réutilise le langage visuel de .creneau-case/.creneau-case-champ
   (grille de disponibilités du cordeur, bureau.css, scopées à
   .disponibilites-bureau) — même dessin de case, redéfini ici scopé à
   .relais-ecran plutôt que partagé entre fichiers (doublon volontaire,
   comme .pastille ci-dessus ; voir docs/design-system.md, « Grille de
   créneaux à cases cliquables »). Case fermée par défaut ; cocher révèle
   les deux champs heure de début/fin en CSS pur (:has), sans JS. */
.relais-ecran .horaires-grille {
  display: grid;
  gap: var(--espacement-petit);
}
.relais-ecran .horaires-jour {
  display: flex;
  align-items: center;
  gap: calc(var(--grille-base) * 3);
  flex-wrap: wrap;
}
.relais-ecran .horaires-jour-case {
  flex: 0 0 140px;
}
.relais-ecran .creneau-case {
  min-height: var(--cible-tactile-min);
  border-radius: var(--rayon-bouton);
  border: var(--epaisseur-trait) solid var(--trait);
  background: var(--surface);
  display: grid;
  place-items: center;
  font-size: var(--taille-texte-2);
  font-weight: var(--graisse-semibold);
  padding: 0 var(--espacement-petit);
}
.relais-ecran .creneau-case.ferme {
  background: transparent;
  border-style: dashed;
  opacity: 0.65; /* 0.6 : texte à 4,4:1, sous AA pour une case cochable (audit accessibilité du 25/09) */
}
.relais-ecran .creneau-case-champ {
  position: relative;
  display: block;
}
.relais-ecran .creneau-case-champ input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
.relais-ecran .creneau-case-champ:hover .creneau-case {
  border-color: var(--texte);
}
.relais-ecran .creneau-case-champ:has(input:focus-visible) .creneau-case {
  outline: 2px solid var(--accent-marque-encre);
  outline-offset: 2px;
}
.relais-ecran .creneau-case-champ:has(input:checked) .creneau-case {
  background: var(--accent-marque-voile);
  border-color: transparent;
  border-style: solid;
  opacity: 1;
  color: var(--accent-marque-encre);
}
.relais-ecran .horaires-jour-heures {
  display: none;
  align-items: center;
  gap: var(--espacement-petit);
  flex: 1 1 220px;
}
.relais-ecran .horaires-jour:has(.creneau-case-champ input:checked) .horaires-jour-heures {
  display: flex;
}
.relais-ecran .horaires-jour-heures .champ {
  margin: 0;
  flex: 1;
}

/* Champ + bouton « Copier » intégré (carte « Inviter en un lien », issue
   n°249 : « plus de bouton à moitié de largeur ») — le champ URL reste
   pleine largeur, le bouton fait partie du même cadre plutôt que de suivre
   en dessous. Police mono : même raison que .outils-lien-url
   (docs/design-system.md → Typographie), afficher une URL en toutes
   lettres, jamais pour du texte courant. */
.relais-ecran .champ-lien {
  display: flex;
  align-items: stretch;
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon-bouton);
  overflow: hidden;
}
.relais-ecran .champ-lien-champ {
  flex: 1;
  min-width: 0;
  border: 0;
  padding: 0 12px;
  font-family: var(--police-mono);
  font-size: var(--taille-texte-2);
  background: var(--surface);
  color: var(--texte);
}
.relais-ecran .champ-lien-champ:focus {
  outline: none;
}
.relais-ecran .champ-lien-copier {
  flex: none;
  border: 0;
  border-left: var(--epaisseur-trait) solid var(--trait);
  min-height: var(--cible-tactile-min);
  padding: 0 14px;
  font-family: var(--police-texte);
  font-weight: var(--graisse-semibold);
  font-size: var(--taille-texte-2);
  background: var(--accent-marque-voile);
  color: var(--accent-marque-encre);
}
.relais-ecran .champ-lien-copier:hover,
.relais-ecran .champ-lien-copier:focus-visible {
  background: var(--accent-marque);
  color: var(--texte);
}

/* ---- suivi-horizontal.css ---- */
/* Suivi horizontal (issue n°164) : variante de .chronologie (lcs.css, où la
   liste est verticale : étape en grid 24px/1fr, repère étiré en hauteur, trait
   vertical). Chaque étape devient une colonne : pastille centrée, trait
   horizontal qui court du centre de la pastille au centre de la suivante
   (coupé par l'étape, pas par la liste, pour que les libellés ne soient
   jamais rognés), libellé et date centrés dessous. Correctif 0.85.1 : le
   repère héritait de `height: 100%` et repoussait les libellés hors de la
   carte, qui les rognait.

   Partagée par deux écrans (issue n°169) : /cordeur/commandes/:id (dès
   760px, ecrans-cordeur.css d'origine) et la tête bureau de /poses/:id
   (dès 900px, bureau.css) — un seul jeu de règles, sorti dans ce fichier
   propre pour que ni l'un ni l'autre n'en réécrive une troisième version.
   Testé par tests/web/suivi-horizontal.test.ts (pas de doublon). */
@media (min-width: 760px) {
  .suivi-horizontal .chronologie {
    display: flex;
    flex-direction: row;
    align-items: flex-start;
  }
  .suivi-horizontal .chronologie-etape {
    display: flex; /* lcs.css : grid 24px 1fr */
    flex: 1 1 0;
    flex-direction: column;
    align-items: center;
    padding-bottom: 0;
    position: relative;
    overflow: hidden; /* coupe le trait de la dernière étape */
  }
  .suivi-horizontal .chronologie-repere {
    position: relative;
    display: flex;
    justify-content: center;
    width: 100%;
    height: 22px; /* lcs.css : 100 % de la hauteur de l'étape */
  }
  .suivi-horizontal .chronologie-pastille {
    position: relative;
    z-index: 1;
  }
  .suivi-horizontal .chronologie-pastille--courante {
    border-color: var(--accent-marque-encre);
    box-shadow: 0 0 0 5px var(--accent-marque-voile);
    background: var(--surface);
  }
  /* Moitié droite du trait : du centre de la pastille au bord de l'étape
     (base.css lui donne min-height et marges verticales, remises à zéro). */
  .suivi-horizontal .chronologie-trait {
    position: absolute;
    left: 50%;
    right: 0;
    top: 10px;
    bottom: auto;
    width: auto;
    height: 2px;
    min-height: 0;
    margin: 0;
    flex: none;
    background: var(--vert-vif);
    opacity: 0.55;
  }
  .suivi-horizontal .chronologie-etape--future .chronologie-trait,
  .suivi-horizontal .chronologie-pastille--courante + .chronologie-trait {
    background: var(--trait);
    opacity: 1;
  }
  .suivi-horizontal .chronologie-etape:last-child .chronologie-trait {
    display: none;
  }
  /* Moitié gauche : du bord de l'étape au centre de sa pastille. Grise dès
     que l'étape est à venir (le trait qui y mène part d'une étape courante
     ou future), verte sinon — même logique que lcs.css pour la moitié droite. */
  .suivi-horizontal .chronologie-etape:not(:first-child)::before {
    content: '';
    position: absolute;
    left: 0;
    right: 50%;
    top: 10px;
    height: 2px;
    background: var(--vert-vif);
    opacity: 0.55;
  }
  .suivi-horizontal .chronologie-etape--future::before {
    background: var(--trait);
    opacity: 1;
  }
  .suivi-horizontal .chronologie-contenu {
    padding: 10px 8px 0;
    text-align: center;
  }
  .suivi-horizontal .chronologie-libelle,
  .suivi-horizontal .chronologie-date {
    text-align: center;
  }
}

/* ---- bureau.css ---- */
/*
 * Grammaire bureau joueur/cordeur (issue n°166, retour Vincent du 11/09/2026 :
 * sur ordinateur, joueur et cordeur ressemblaient à une interface
 * d'administration — colonne latérale grise, colonne unique au milieu du
 * vide). Maquettes validées « à 200 % » :
 * docs/maquettes/2026-09-11/bureau/ (lire le README en premier) — valeurs
 * reprises telles quelles de bureau.css, source de vérité, sans
 * réinterprétation (leçon de l'issue n°153).
 *
 * Le mobile ne bouge pas : tout ce fichier est sous une seule
 * @media (min-width: 900px), et chaque règle est scopée sous
 * .adm-shell--app (joueur/cordeur uniquement — voir layout.tsx), à
 * l'exception de .entete-site et de ce qu'elle contient : Layout la rend en
 * frère de .adm-shell, avant lui, pour ne pas perturber sa mise en page flex
 * (voir layout.tsx) — elle n'a donc pas de raison d'être présente ailleurs
 * dans le document. L'admin et le rôle relais gardent leur colonne latérale
 * et leur vocabulaire LCS (lcs.css). Testé par tests/web/bureau-socle.test.ts.
 *
 * Les classes génériques déjà utilisées ailleurs dans l'app (.bouton,
 * .avatar, .carte…) ne sont jamais redéfinies ici sans être scopées à un
 * conteneur propre à cette grammaire (.entete-site, .heros__actions,
 * .raquette__cta…) : une règle nue comme .bouton { … } changerait
 * l'apparence de tous les boutons de toutes les pages joueur/cordeur sur
 * bureau, bien au-delà du socle et de l'accueil joueur de cette issue.
 */

@media (min-width: 900px) {
  /* ---------- Conteneur de site (remplace le fond gris de .mainzone) ---------- */
  .adm-shell--app .mainzone {
    padding: 0 0 80px;
    background: var(--fond);
  }
  .adm-shell--app .site {
    width: min(1180px, 100% - 64px);
    margin: 0 auto;
  }

  /* ---------- Colonne latérale, barre du haut et burger : masqués ---------- */
  /* La barre du bas mobile (.nav-basse) n'est de toute façon déjà plus
     rendue au palier bureau (voir navigation-basse.tsx / lcs.css) ; ici on
     retire ce qui restait de la coquille admin. */
  .adm-shell--app .side,
  .adm-shell--app .topbar,
  .adm-shell--app #burger {
    display: none;
  }

  /* ---------- Entête de site ---------- */
  /* Rendue par <EnteteSite> (navigation.tsx), en frère de .adm-shell — voir
     la note en tête de fichier. */
  .entete-site {
    position: sticky;
    top: 0;
    z-index: 10;
    background: var(--fond-translucide);
    backdrop-filter: blur(10px);
    border-bottom: 1px solid var(--trait);
  }
  .entete-site__int {
    display: flex;
    align-items: center;
    gap: 40px;
    height: 68px;
    /* L'entête est rendue hors de .adm-shell--app : la largeur de .site ne
       s'appliquait plus, logo et avatar collaient aux bords (retour Camille
       14/09). Même gabarit que le contenu. */
    width: min(1180px, 100% - 64px);
    margin: 0 auto;
  }
  .entete-site .marque {
    display: flex;
    align-items: center;
    gap: 10px;
    font-family: var(--police-titres-site);
    font-weight: 800;
    font-size: var(--taille-titre-2);
    letter-spacing: -0.02em;
    color: var(--texte);
    text-decoration: none;
  }
  .entete-site .monogramme {
    width: 32px;
    height: 32px;
    border-radius: 9px;
    background: var(--surface-haute);
    display: grid;
    place-items: center;
    flex-shrink: 0;
  }
  .entete-site .monogramme img {
    width: 18px;
    height: 18px;
    display: block;
  }
  .entete-site .nav-site {
    display: flex;
    gap: 4px;
    margin-left: 8px;
  }
  .entete-site .nav-site a {
    padding: 8px 14px;
    border-radius: 999px;
    font-weight: 500;
    color: var(--texte-secondaire);
    text-decoration: none;
  }
  .entete-site .nav-site a:hover {
    background: rgba(21, 32, 28, 0.05);
    color: var(--texte);
  }
  .entete-site .nav-site a.actif {
    color: var(--texte);
    background: var(--accent-marque-voile);
    font-weight: 600;
  }
  .entete-site__droite {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 18px;
  }
  /* Avatar de l'entête : scopé à .entete-site pour ne jamais changer les
     autres avatars (.avatar, .avatar--grand) de l'app — voir la note en
     tête de fichier. */
  .entete-site .avatar {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: var(--avatar-2-fond);
    color: var(--avatar-2-texte);
    font-size: var(--taille-texte-2);
  }
  /* Prénom à côté de l'avatar (issue n°212, pendant bureau de
     .barre-haute-nom ci-dessus dans interface.css). */
  .entete-site__nom {
    font-size: var(--taille-texte-3);
    font-weight: 600;
    color: var(--texte-secondaire);
  }

  /* Menu déroulant sous l'avatar (issue n°206) : popover ancré sous le
     déclencheur, même vocabulaire visuel que .voir-comme-volet (lcs.css,
     coquille admin) — non réutilisé tel quel, .entete-site vit hors de
     .adm-shell--app (voir la note en tête de fichier). */
  .entete-site__menu {
    position: relative;
  }
  .entete-site__menu-volet {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    z-index: 60;
    display: none;
    flex-direction: column;
    gap: 2px;
    width: 260px;
    padding: var(--espacement-petit);
    background: var(--surface);
    border: var(--epaisseur-trait) solid var(--trait);
    border-radius: var(--rayon);
    box-shadow: var(--ombre-carte);
  }
  .entete-site__menu-volet:not([hidden]) {
    display: flex;
  }
  .entete-site__menu-lien {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: var(--espacement-champ-vertical) var(--espacement-champ-horizontal);
    border: none;
    border-radius: var(--rayon-bouton-compact);
    background: none;
    color: var(--texte);
    font-family: inherit;
    font-size: var(--taille-texte-2);
    text-align: left;
    text-decoration: none;
    cursor: pointer;
  }
  .entete-site__menu-lien:hover {
    background: var(--action-douce);
  }
  .entete-site__menu-ic {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
    color: var(--texte-secondaire);
  }
  .entete-site__menu-langue {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--espacement-petit);
    padding: var(--espacement-champ-vertical) var(--espacement-champ-horizontal);
    font-size: var(--taille-texte-2);
  }
  .entete-site__menu-separateur {
    border: none;
    border-top: var(--epaisseur-trait) solid var(--trait);
    margin: var(--espacement-petit) 0;
  }
  .entete-site__menu-deconnexion {
    margin: 0;
  }

  /* ---------- Héros : deux plans ---------- */
  .adm-shell--app .heros {
    display: grid;
    grid-template-columns: 1.05fr 1fr;
    gap: 40px;
    align-items: center;
    padding: 48px 0 40px;
  }
  .adm-shell--app .heros__texte h1 {
    font-family: var(--police-titres-site);
    font-weight: 800;
    font-size: var(--taille-affichage-5);
    line-height: 1;
    letter-spacing: -0.035em;
    margin: 0 0 22px;
    text-wrap: balance;
  }
  .adm-shell--app .heros__texte p {
    font-size: var(--taille-titre-2);
    color: var(--texte-secondaire);
    max-width: 34ch;
    margin: 0 0 30px;
  }
  .adm-shell--app .heros__actions {
    display: flex;
    align-items: center;
    gap: 18px;
  }
  /* Un seul aplat encre par écran (règle n°162) : le bouton principal du
     héros reprend .bouton--principal (couleurs déjà correctes), seule la
     taille change ici. Le lien discret reprend .bouton--discret, mais dans
     un habillage transparent propre au héros plutôt que le contour compact
     utilisé ailleurs dans l'app. */
  .adm-shell--app .heros__actions .bouton--principal {
    padding: 15px 26px;
    font-size: var(--taille-titre-3);
  }
  .adm-shell--app .heros__actions .bouton--discret {
    background: transparent;
    border-color: transparent;
    color: var(--accent-marque-encre);
    padding: 8px 4px;
    font-size: var(--taille-texte-1);
  }
  .adm-shell--app .heros__actions .bouton--discret:hover {
    text-decoration: underline;
  }
  .adm-shell--app .heros__scene {
    position: relative;
  }

  /* ---------- Photo scène : placeholder de marque, jamais de stock (issue n°52) ---------- */
  .adm-shell--app .photo {
    border-radius: 24px;
    background: var(--surface-haute);
    position: relative;
    overflow: hidden;
  }
  .adm-shell--app .photo--heros {
    aspect-ratio: 4 / 3;
  }
  .adm-shell--app .photo::before {
    content: '';
    position: absolute;
    inset: -20%;
    background-image:
      repeating-linear-gradient(-14deg, rgba(61, 245, 176, 0.22) 0 1.5px, transparent 1.5px 18px),
      repeating-linear-gradient(76deg, rgba(61, 245, 176, 0.14) 0 1.5px, transparent 1.5px 18px);
  }
  .adm-shell--app .photo::after {
    content: '';
    position: absolute;
    inset: 0;
    background: radial-gradient(ellipse at 35% 35%, rgba(2, 17, 34, 0) 0%, rgba(2, 17, 34, 0.7) 80%);
  }
  .adm-shell--app .photo__filigrane {
    position: absolute;
    right: -6%;
    top: -10%;
    width: 72%;
    z-index: 1;
    opacity: 0.16;
  }
  .adm-shell--app .photo__etiquette {
    position: absolute;
    left: 18px;
    bottom: 16px;
    z-index: 1;
    color: var(--texte-sur-sombre);
    font-size: var(--taille-texte-3);
    font-weight: 500;
  }

  /* ---------- Carte de suivi « ta raquette » et invitation notifications,
     côte à côte sous le héros (retour Vincent du 17/09 : la carte cachait
     la photo en flottant dessus) ---------- */
  .adm-shell--app .heros__suite {
    display: flex;
    flex-wrap: wrap;
    gap: 20px;
    margin-top: 24px;
  }
  .adm-shell--app .heros__suite > * {
    flex: 1 1 320px;
  }
  .adm-shell--app .carte-suivi {
    display: grid;
    grid-template-columns: 44px 1fr;
    gap: 14px;
    align-items: center;
  }
  .adm-shell--app .carte-suivi__avatar {
    width: 44px;
    height: 44px;
    border-radius: 50%;
    background: var(--avatar-1-fond);
    color: var(--avatar-1-texte);
    display: grid;
    place-items: center;
    font-weight: 700;
    font-size: var(--taille-texte-2);
  }
  .adm-shell--app .carte-suivi__titre {
    font-weight: 700;
    font-size: var(--taille-texte-1);
    margin: 0 0 2px;
  }
  .adm-shell--app .carte-suivi__ligne {
    margin: 0;
    color: var(--texte-secondaire);
    font-size: var(--taille-texte-2);
    display: flex;
    align-items: center;
    gap: 8px;
  }
  .adm-shell--app .carte-suivi__vide {
    grid-column: 1 / -1; /* pas d'avatar : la carte occupe toute sa largeur */
    padding: 4px 0 2px;
  }
  .adm-shell--app .carte-suivi__vide p {
    margin: 0 0 12px;
    font-size: var(--taille-texte-2);
    color: var(--texte-secondaire);
  }
  .adm-shell--app .carte-suivi .pastille {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 9px;
    border-radius: 999px;
    font-size: var(--taille-texte-3);
    font-weight: 600;
    background: var(--accent-marque-voile);
    color: var(--accent-marque-encre);
  }
  .adm-shell--app .carte-suivi .pastille::before {
    content: '';
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--accent-marque-encre);
  }
  .adm-shell--app .carte-suivi .pastille--attente {
    background: var(--attention-fond);
    color: var(--attention-texte);
  }
  .adm-shell--app .carte-suivi .pastille--attente::before {
    background: var(--attention);
  }
  .adm-shell--app .carte-suivi__lien {
    grid-column: 1 / -1;
    margin: 4px 0 0;
    padding-top: 12px;
    border-top: 1px solid var(--trait);
    font-size: var(--taille-texte-2);
    font-weight: 600;
    color: var(--accent-marque-encre);
    text-decoration: none;
  }

  /* ---------- Sections ---------- */
  .adm-shell--app .section {
    padding: 36px 0;
  }
  .adm-shell--app .section__tete {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    margin-bottom: 22px;
  }
  .adm-shell--app .section__tete h2 {
    font-family: var(--police-titres-site);
    font-weight: 800;
    font-size: var(--taille-affichage-2);
    letter-spacing: -0.025em;
    margin: 0;
  }
  .adm-shell--app .section__tete p {
    margin: 0;
    color: var(--texte-secondaire);
  }

  /* ---------- Cordeurs près de toi ---------- */
  .adm-shell--app .cordeurs {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 22px;
  }
  .adm-shell--app .cordeur {
    background: var(--surface);
    border: 1px solid var(--trait);
    border-radius: 16px;
    overflow: hidden;
    display: flex;
    flex-direction: column;
  }
  .adm-shell--app .cordeur:hover {
    border-color: var(--trait-cordeur-survol);
  }
  .adm-shell--app .cordeur__contenu {
    display: flex;
    flex-direction: column;
    color: inherit;
    text-decoration: none;
    flex: 1;
  }
  .adm-shell--app .cordeur .photo {
    border-radius: 0;
    aspect-ratio: 16 / 10;
  }
  .adm-shell--app .cordeur .photo--claire {
    background: var(--placeholder-clair-1);
  }
  .adm-shell--app .cordeur .photo--claire-2 {
    background: var(--placeholder-clair-2);
  }
  .adm-shell--app .cordeur .photo--claire-3 {
    background: var(--placeholder-clair-3);
  }
  .adm-shell--app .cordeur .photo--claire::before {
    background-image:
      repeating-linear-gradient(-14deg, rgba(2, 17, 34, 0.09) 0 1.5px, transparent 1.5px 16px),
      repeating-linear-gradient(76deg, rgba(2, 17, 34, 0.05) 0 1.5px, transparent 1.5px 16px);
  }
  .adm-shell--app .cordeur .photo--claire::after {
    background: linear-gradient(180deg, rgba(2, 17, 34, 0) 55%, rgba(2, 17, 34, 0.35));
  }
  .adm-shell--app .cordeur__badge {
    position: absolute;
    top: 12px;
    left: 12px;
    z-index: 1;
    background: rgba(255, 255, 255, 0.94);
    font-size: var(--taille-texte-3);
    font-weight: 600;
    padding: 5px 10px;
    border-radius: 999px;
    display: flex;
    align-items: center;
    gap: 5px;
  }
  .adm-shell--app .cordeur__avatar {
    position: absolute;
    left: 14px;
    bottom: -22px;
    z-index: 2;
    width: 56px;
    height: 56px;
    border-radius: 50%;
    border: 3px solid var(--surface);
    display: grid;
    place-items: center;
    font-weight: 700;
    font-size: var(--taille-titre-3);
    background: var(--avatar-1-fond);
    color: var(--avatar-1-texte);
  }
  .adm-shell--app .cordeur__avatar--2 {
    background: var(--avatar-2-fond);
    color: var(--avatar-2-texte);
  }
  .adm-shell--app .cordeur__avatar--3 {
    background: var(--avatar-3-fond);
    color: var(--avatar-3-texte);
  }
  .adm-shell--app .cordeur__avatar--4 {
    background: var(--avatar-4-fond);
    color: var(--avatar-4-texte);
  }
  .adm-shell--app .cordeur__corps {
    padding: 30px 16px 16px;
    display: flex;
    flex-direction: column;
    gap: 6px;
    flex: 1;
  }
  .adm-shell--app .cordeur__ligne1 {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
  }
  .adm-shell--app .cordeur__nom {
    font-weight: 700;
    font-size: var(--taille-titre-3);
  }
  .adm-shell--app .cordeur__prix {
    color: var(--texte-secondaire);
    font-size: var(--taille-texte-2);
  }
  .adm-shell--app .cordeur__prix b {
    color: var(--texte);
    font-weight: 700;
  }
  .adm-shell--app .cordeur__meta {
    color: var(--texte-secondaire);
    font-size: var(--taille-texte-2);
    display: flex;
    align-items: center;
    gap: 6px;
  }
  .adm-shell--app .cordeur .creneaux {
    display: flex;
    gap: 8px;
    margin: 0;
    padding: 0 16px 16px;
    flex-wrap: wrap;
  }
  .adm-shell--app .cordeur .creneau {
    padding: 7px 12px;
    border: 1px solid var(--trait);
    border-radius: 999px;
    font-size: var(--taille-texte-2);
    font-weight: 500;
  }
  .adm-shell--app .cordeur .creneau:hover {
    border-color: var(--texte);
  }

  /* ---------- Cordeurs près de toi : tuile de repli (issue n°191) ---------- */
  /* Remplace les colonnes vides de la grille à trois quand la recherche par
     rayon élargi (selectNearbyStringers) n'a pas trouvé assez de cordeurs
     même à 100 km — jamais une colonne blanche. Span 2 quand deux colonnes
     manquent (un seul cordeur trouvé), sinon une seule (deux trouvés). */
  .adm-shell--app .cordeur--vide {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
    background: var(--surface-haute);
    border: 1px dashed var(--trait);
    border-radius: 16px;
    color: var(--texte-sur-sombre);
    font-weight: 600;
    text-decoration: none;
    text-align: center;
    padding: 24px;
  }
  .adm-shell--app .cordeur--vide:hover {
    border-color: var(--trait-cordeur-survol);
    color: var(--sur-action);
  }
  .adm-shell--app .cordeur--vide-2 {
    grid-column: span 2;
  }

  /* ---------- Ta raquette : bande calme ---------- */
  .adm-shell--app .raquette {
    display: grid;
    grid-template-columns: 1.2fr 1fr 1fr 1fr;
    gap: 0;
    background: var(--surface);
    border: 1px solid var(--trait);
    border-radius: 16px;
    overflow: hidden;
  }
  .adm-shell--app .raquette > div {
    padding: 24px 26px;
    border-left: 1px solid var(--trait);
  }
  .adm-shell--app .raquette > div:first-child {
    border-left: 0;
  }
  .adm-shell--app .raquette__nom {
    font-family: var(--police-titres-site);
    font-weight: 800;
    font-size: var(--taille-titre-1);
    letter-spacing: -0.02em;
    margin: 0 0 4px;
  }
  .adm-shell--app .raquette__sous {
    color: var(--texte-secondaire);
    margin: 0;
    font-size: var(--taille-texte-2);
  }
  .adm-shell--app .raquette dt {
    color: var(--texte-secondaire);
    font-size: var(--taille-texte-2);
    margin: 0 0 4px;
  }
  .adm-shell--app .raquette dd {
    margin: 0;
    font-size: var(--taille-titre-2);
    font-weight: 700;
    letter-spacing: -0.01em;
  }
  .adm-shell--app .raquette dd small {
    font-size: var(--taille-texte-2);
    font-weight: 500;
    color: var(--texte-secondaire);
    margin-left: 4px;
  }
  .adm-shell--app .raquette__cta {
    display: flex;
    align-items: center;
    justify-content: flex-end;
  }
  .adm-shell--app .raquette__cta .bouton {
    padding: 13px 22px;
  }

  /* ---------- Pied de site ---------- */
  .adm-shell--app .pied {
    margin-top: 40px;
    padding: 28px 0 40px;
    border-top: 1px solid var(--trait);
    display: flex;
    justify-content: space-between;
    color: var(--texte-secondaire);
    font-size: var(--taille-texte-2);
  }
  .adm-shell--app .pied a {
    margin-left: 18px;
    color: inherit;
    text-decoration: none;
  }
  .adm-shell--app .pied a:hover {
    color: var(--texte);
  }

  /* ---------- Suivi de commande /poses/:id (issue n°169, maquette
     docs/maquettes/2026-09-11/bureau/suivi-commande.html) ---------- */
  .adm-shell--app .tete-page {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 40px;
    align-items: end;
    padding: 52px 0 32px;
  }
  .adm-shell--app .titre-page {
    font-family: var(--police-titres-site);
    font-weight: 800;
    font-size: var(--taille-affichage-5);
    line-height: 1.05;
    letter-spacing: -0.03em;
    margin: 0 0 8px;
    text-wrap: balance;
  }
  .adm-shell--app .sous-titre {
    font-size: var(--taille-titre-3);
    color: var(--texte-secondaire);
    margin: 0;
  }
  /* Pastille de statut de la tête (couleur à sens, issue n°169 : voir
     domain/orders/format.playerHeadlinePastilleClass) — distincte de
     .carte-suivi .pastille (hero de l'accueil), propre à cette tête. */
  .adm-shell--app .tete-page .pastille {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 8px 14px;
    border-radius: 999px;
    font-size: var(--taille-texte-2);
    font-weight: 600;
    background: var(--accent-marque-voile);
    color: var(--accent-marque-encre);
  }
  .adm-shell--app .tete-page .pastille::before {
    content: '';
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--accent-marque-encre);
  }
  .adm-shell--app .tete-page .pastille--attente {
    background: var(--attention-fond);
    color: var(--attention-texte);
  }
  .adm-shell--app .tete-page .pastille--attente::before {
    background: var(--attention);
  }
  .adm-shell--app .tete-page .pastille--neutre {
    background: var(--action-douce);
    color: var(--texte-secondaire);
  }
  .adm-shell--app .tete-page .pastille--neutre::before {
    background: var(--texte-secondaire);
  }

  /* Grille détail 2 colonnes, colonne latérale collante — réutilisée par
     tout écran de détail bureau (pas seulement /poses/:id). */
  .adm-shell--app .grille-2 {
    display: grid;
    grid-template-columns: 1fr 340px;
    gap: 28px;
    align-items: start;
  }
  .adm-shell--app .colonne {
    display: flex;
    flex-direction: column;
    gap: 20px;
  }
  .adm-shell--app .colonne.collant {
    position: sticky;
    top: 92px;
  }
  .adm-shell--app .ligne-cle {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    padding: 10px 0;
    border-top: 1px solid var(--trait);
    font-size: var(--taille-texte-1);
  }
  .adm-shell--app .ligne-cle:first-of-type {
    border-top: 0;
  }
  .adm-shell--app .ligne-cle span:last-child {
    font-weight: 600;
  }
  .adm-shell--app .ligne-cle--total {
    font-weight: 700;
    font-size: var(--taille-titre-3);
  }
  .adm-shell--app .carte-bon-de-cordage h3,
  .adm-shell--app .carte-prix h3,
  .adm-shell--app .carte-messages h3 {
    font-size: var(--taille-titre-3);
    font-weight: 700;
    margin: 0 0 14px;
  }

  /* Bon de cordage : tension en avant, infos clé/valeur à deux colonnes.
     Scopées à .carte-bon-de-cordage (pas de règle nue .tension/.grille-infos
     ici) pour ne jamais changer /cordeur/commandes/:id, qui a ses propres
     .grille-infos et .pastille--catalogue non-bureau (ecrans-cordeur.css) —
     rendu dans le même .adm-shell--app. */
  .adm-shell--app .carte-bon-de-cordage .tension {
    display: flex;
    align-items: baseline;
    gap: 10px;
    margin: 4px 0 18px;
  }
  .adm-shell--app .carte-bon-de-cordage .tension .nombre {
    font-family: var(--police-titres-site);
    font-weight: 800;
    letter-spacing: -0.02em;
    font-size: var(--taille-affichage-5);
    line-height: 1;
  }
  .adm-shell--app .carte-bon-de-cordage .tension small {
    color: var(--texte-secondaire);
    font-size: var(--taille-texte-2);
  }
  .adm-shell--app .carte-bon-de-cordage .grille-infos {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px 24px;
  }
  .adm-shell--app .carte-bon-de-cordage .grille-infos dt {
    color: var(--texte-secondaire);
    font-size: var(--taille-texte-2);
    margin-bottom: 3px;
  }
  .adm-shell--app .carte-bon-de-cordage .grille-infos dd {
    margin: 0;
    font-weight: 600;
    font-size: var(--taille-texte-1);
  }
  .adm-shell--app .carte-bon-de-cordage .grille-infos dd small {
    display: block;
    font-weight: 400;
    color: var(--texte-secondaire);
    font-size: var(--taille-texte-2);
  }
  .adm-shell--app .carte-bon-de-cordage .grille-infos .pastille {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-left: 6px;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: var(--taille-texte-4);
    font-weight: 600;
    background: var(--accent-marque-voile);
    color: var(--accent-marque-encre);
  }

  /* Cordeur, colonne collante : mini-carte centrée, nommage distinct de
     .carte-cordeur (ecrans.css, cordeurs.css, interface.css, lcs.css — une
     tout autre carte, la recherche de cordeurs) pour ne jamais s'y mêler. */
  .adm-shell--app .cordeur-mini {
    text-align: center;
    padding-top: 28px;
  }
  .adm-shell--app .cordeur-mini__avatar {
    width: 84px;
    height: 84px;
    margin: 0 auto;
    border-radius: 50%;
    display: grid;
    place-items: center;
    overflow: hidden;
    font-family: var(--police-titres-site);
    font-weight: 800;
    font-size: var(--taille-affichage-1);
    background: var(--avatar-1-fond);
    color: var(--avatar-1-texte);
  }
  .adm-shell--app .cordeur-mini__avatar img {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
  .adm-shell--app .cordeur-mini__nom {
    margin: 12px 0 2px;
    font-weight: 700;
    font-size: var(--taille-titre-2);
  }
  .adm-shell--app .cordeur-mini__meta {
    margin: 0 0 16px;
    color: var(--texte-secondaire);
    font-size: var(--taille-texte-2);
  }
  .adm-shell--app .cordeur-mini .bouton {
    width: 100%;
    justify-content: center;
  }

  /* Messages : aperçu du dernier message, carte entière repliable (même
     principe que .messages-apercu d'ecrans-cordeur.css, renommé pour ne pas
     réutiliser ses règles 760px sur cette carte plus large). */
  .adm-shell--app .carte-messages {
    cursor: pointer;
  }
  .adm-shell--app .carte-messages__resume {
    list-style: none;
    cursor: pointer;
  }
  .adm-shell--app .carte-messages__resume::-webkit-details-marker {
    display: none;
  }
  .adm-shell--app .message {
    display: grid;
    grid-template-columns: 40px 1fr;
    gap: 14px;
    align-items: center;
  }
  .adm-shell--app .message__auteur {
    margin: 0;
    font-weight: 600;
    font-size: var(--taille-texte-2);
  }
  .adm-shell--app .message__apercu {
    margin: 2px 0 0;
    color: var(--texte-secondaire);
    font-size: var(--taille-texte-2);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .adm-shell--app .carte-messages__bouton {
    display: flex;
    width: 100%;
    justify-content: center;
    margin-top: 16px;
  }
  .adm-shell--app .carte-messages .fil-messages,
  .adm-shell--app .carte-messages form {
    margin-top: 16px;
  }
  .adm-shell--app .aide-contact {
    margin: 0;
    text-align: center;
    font-size: var(--taille-texte-2);
    color: var(--texte-secondaire);
  }
  .adm-shell--app .aide-contact a {
    color: var(--accent-marque-encre);
    font-weight: 600;
  }

  /* Issue n°168 — fiche cordeur bureau. Bloc ajouté en fin de média à la
     fusion (les briques n°168 et n°169 avaient inséré leurs composants au
     même endroit). */

  /* ---------- Fiche cordeur (issue n°168, docs/maquettes/2026-09-11/bureau/
     fiche-cordeur.html) : couverture pleine largeur, grille-2 (à propos /
     cordages / avis à gauche, réservation en trois gestes collante à
     droite), à la place de la grille Doctolib de lcs.css (.fiche-doctolib)
     et de la navigation par étapes du tunnel mobile. Tout est scopé sous
     .fiche-cordeur-bureau (plutôt que laissé nu comme .heros/.cordeurs
     ci-dessus) : .carte, .grille-2, .colonne, .collant, .semaine, .jour,
     .choix… sont la grammaire « compléments partagés » de la maquette,
     appelée à resservir sur d'autres écrans (suivi de commande, compte) —
     tant que ces écrans n'ont pas leur propre passage bureau, mieux vaut ne
     pas changer sous eux l'apparence de classes génériques déjà utilisées
     dans leur rendu mobile actuel. */
  .adm-shell--app .fiche-doctolib,
  .adm-shell--app .ecran > .logo,
  .adm-shell--app .ecran > .etapes {
    display: none;
  }

  .adm-shell--app .couverture {
    position: relative;
    margin-top: 28px;
  }
  .adm-shell--app .couverture .photo {
    aspect-ratio: 21 / 7;
  }
  .adm-shell--app .couverture .photo__etiquette {
    left: auto;
    right: 18px;
  }
  /* Photo de couverture réelle : le motif de cordage et le voile sombre
     décoratifs (.photo::before/::after) sont réservés au placeholder. */
  .adm-shell--app .photo--reelle::before,
  .adm-shell--app .photo--reelle::after {
    content: none;
  }
  .adm-shell--app .couverture-photo-image {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
  }
  .adm-shell--app .couverture__identite {
    display: flex;
    align-items: flex-end;
    gap: 22px;
    margin-top: -40px;
    padding: 0 28px;
    position: relative;
    z-index: 2;
  }
  .adm-shell--app .couverture__avatar {
    flex: none;
    width: 108px;
    height: 108px;
    border-radius: 50%;
    border: 4px solid var(--fond);
    display: grid;
    place-items: center;
    font-family: var(--police-titres-site);
    font-weight: 800;
    font-size: var(--taille-affichage-2);
    background: var(--avatar-1-fond);
    color: var(--avatar-1-texte);
  }
  /* Leçon des avatars ovales (n°166) : flex: none ci-dessus fixe déjà la
     taille ; object-fit vient seulement cadrer l'image dans le rond. */
  .adm-shell--app img.couverture__avatar {
    object-fit: cover;
  }
  .adm-shell--app .couverture__texte {
    padding-top: 50px;
  }
  .adm-shell--app .couverture__texte h1 {
    font-family: var(--police-titres-site);
    font-weight: 800;
    font-size: var(--taille-affichage-4);
    letter-spacing: -0.03em;
    margin: 0 0 4px;
    display: flex;
    align-items: center;
    gap: 12px;
  }
  .adm-shell--app .couverture__texte p {
    margin: 0;
    color: var(--texte-secondaire);
    font-size: var(--taille-texte-1);
  }
  .adm-shell--app .couverture__texte .etoile {
    color: var(--accent-marque-encre);
    font-weight: 700;
  }
  /* .badge-verif est l'alias déprécié de .badge-verifie (cordeurs.css,
     nom canonique — issue n°183) : compte.tsx et commander-cordeur.tsx
     (bureau) utilisent encore l'ancien nom, à faire migrer vers
     .badge-verifie dans une prochaine version pour retirer cet alias. */
  .adm-shell--app .badge-verif,
  .adm-shell--app .badge-verifie {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    background: var(--accent-marque-voile);
    color: var(--accent-marque-encre);
    font-size: var(--taille-texte-3);
    font-weight: 700;
    padding: 5px 10px;
    border-radius: var(--rayon-pastille);
    font-family: var(--police-texte);
    letter-spacing: 0;
  }

  .adm-shell--app .fiche-cordeur-bureau .grille-2 {
    display: grid;
    grid-template-columns: 1fr 340px;
    gap: 28px;
    align-items: start;
    margin-top: 36px;
  }
  .adm-shell--app .fiche-cordeur-bureau .colonne {
    display: flex;
    flex-direction: column;
    gap: 20px;
  }
  .adm-shell--app .fiche-cordeur-bureau .collant {
    position: sticky;
    top: 92px;
  }
  .adm-shell--app .fiche-cordeur-bureau .carte {
    background: var(--surface);
    border: 1px solid var(--trait);
    border-radius: 16px;
    padding: 22px 24px;
  }
  .adm-shell--app .fiche-cordeur-bureau .carte h3 {
    font-size: var(--taille-titre-3);
    font-weight: 700;
    margin: 0 0 14px;
  }
  .adm-shell--app .fiche-cordeur-bureau__texte {
    margin: 0;
    color: var(--texte-secondaire);
    font-size: var(--taille-texte-1);
    line-height: 1.6;
  }

  .adm-shell--app .fiche-cordeur-bureau .cordages {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 12px;
  }
  .adm-shell--app .fiche-cordeur-bureau .cordages div {
    border: 1px solid var(--trait);
    border-radius: 12px;
    padding: 12px 14px;
    font-size: var(--taille-texte-2);
  }
  .adm-shell--app .fiche-cordeur-bureau .cordages b {
    display: block;
    font-weight: 700;
    margin-bottom: 2px;
  }
  .adm-shell--app .fiche-cordeur-bureau .cordages span {
    color: var(--texte-secondaire);
    font-size: var(--taille-texte-2);
  }

  .adm-shell--app .fiche-cordeur-bureau .avis {
    display: flex;
    flex-direction: column;
    gap: 18px;
  }
  .adm-shell--app .fiche-cordeur-bureau .avis__item {
    display: grid;
    grid-template-columns: 36px 1fr;
    gap: 12px;
  }
  .adm-shell--app .fiche-cordeur-bureau .avis__avatar {
    flex: none;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    display: grid;
    place-items: center;
    font-weight: 700;
    font-size: var(--taille-texte-3);
    background: var(--avatar-2-fond);
    color: var(--avatar-2-texte);
  }
  .adm-shell--app .fiche-cordeur-bureau .avis__avatar--3 {
    background: var(--avatar-3-fond);
    color: var(--avatar-3-texte);
  }
  .adm-shell--app .fiche-cordeur-bureau .avis__avatar--4 {
    background: var(--avatar-4-fond);
    color: var(--avatar-4-texte);
  }
  .adm-shell--app .fiche-cordeur-bureau .avis__tete {
    display: flex;
    justify-content: space-between;
    font-size: var(--taille-texte-2);
  }
  .adm-shell--app .fiche-cordeur-bureau .avis__tete .etoile {
    color: var(--accent-marque-encre);
    font-weight: 700;
  }
  .adm-shell--app .fiche-cordeur-bureau .avis__texte {
    margin: 4px 0 0;
    color: var(--texte-secondaire);
    font-size: var(--taille-texte-1);
    line-height: 1.55;
  }
  .adm-shell--app .fiche-cordeur-bureau__lien-avis {
    margin: 16px 0 0;
  }

  /* ---------- Réservation en trois gestes (colonne droite collante) ---------- */
  .adm-shell--app .fiche-cordeur-bureau__prix-ligne {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    margin-bottom: 4px;
  }
  .adm-shell--app .fiche-cordeur-bureau__prix {
    font-family: var(--police-titres-site);
    font-weight: 800;
    letter-spacing: -0.02em;
    font-size: var(--taille-affichage-2);
  }
  .adm-shell--app .fiche-cordeur-bureau__prix-mention {
    color: var(--texte-secondaire);
    font-size: var(--taille-texte-2);
  }
  .adm-shell--app .fiche-cordeur-bureau__prochain {
    margin: 0 0 6px;
    color: var(--texte-secondaire);
    font-size: var(--taille-texte-2);
  }
  .adm-shell--app .fiche-cordeur-bureau__intertitre {
    margin: 18px 0 0;
    font-size: var(--taille-texte-2);
    color: var(--texte-secondaire);
    font-weight: 600;
  }
  .adm-shell--app .fiche-cordeur-bureau .semaine {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 6px;
    margin: 12px 0 16px;
  }
  .adm-shell--app .fiche-cordeur-bureau .jour {
    text-align: center;
    padding: 10px 0 8px;
    border: 1px solid var(--trait);
    border-radius: 10px;
    font-size: var(--taille-texte-3);
    color: var(--texte-secondaire);
    cursor: pointer;
    text-decoration: none;
    display: block;
  }
  .adm-shell--app .fiche-cordeur-bureau .jour b {
    display: block;
    font-size: var(--taille-titre-3);
    color: var(--texte);
    font-weight: 700;
    margin-top: 2px;
  }
  .adm-shell--app .fiche-cordeur-bureau .jour.choisi {
    background: var(--action);
    border-color: var(--action);
    color: rgba(255, 255, 255, 0.7);
  }
  .adm-shell--app .fiche-cordeur-bureau .jour.choisi b {
    color: var(--sur-action);
  }
  .adm-shell--app .fiche-cordeur-bureau .jour.plein {
    opacity: 0.35;
    cursor: default;
  }
  .adm-shell--app .fiche-cordeur-bureau .creneaux {
    display: flex;
    gap: 8px;
    margin: 0 0 6px;
    flex-wrap: wrap;
  }
  .adm-shell--app .fiche-cordeur-bureau .creneau {
    padding: 7px 12px;
    border: 1px solid var(--trait);
    border-radius: 999px;
    font-size: var(--taille-texte-2);
    font-weight: 500;
    text-decoration: none;
    color: var(--texte);
  }
  .adm-shell--app .fiche-cordeur-bureau span.creneau {
    opacity: 0.4;
  }
  .adm-shell--app .fiche-cordeur-bureau a.creneau:hover {
    border-color: var(--texte);
  }
  .adm-shell--app .fiche-cordeur-bureau .creneau.choisi {
    border-color: var(--texte);
    font-weight: 600;
  }
  .adm-shell--app .fiche-cordeur-bureau .choix {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin: 12px 0 18px;
  }
  .adm-shell--app .fiche-cordeur-bureau .choix a {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 14px;
    border: 1px solid var(--trait);
    border-radius: 12px;
    font-size: var(--taille-texte-1);
    cursor: pointer;
    text-decoration: none;
    color: var(--texte);
  }
  .adm-shell--app .fiche-cordeur-bureau .choix a.choisi {
    border-color: var(--texte);
  }
  .adm-shell--app .fiche-cordeur-bureau .choix .rond {
    flex: none;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    border: 2px solid var(--trait);
  }
  .adm-shell--app .fiche-cordeur-bureau .choix a.choisi .rond {
    border: 5px solid var(--texte);
  }
  /* Trois classes : .ecran .bouton--principal (ecrans.css) impose width: auto
     à égalité de spécificité et arrive après dans la cascade. */
  .adm-shell--app .fiche-cordeur-bureau .fiche-cordeur-bureau__reserver {
    display: flex;
    width: 100%;
    box-sizing: border-box;
    align-self: stretch;
    justify-content: center;
    margin-top: 4px;
  }
  .adm-shell--app .fiche-cordeur-bureau__mention {
    margin: 12px 0 0;
    text-align: center;
    color: var(--texte-secondaire);
    font-size: var(--taille-texte-2);
  }

  /* Issue n°167 — accueil cordeur bureau. Bloc ajouté en fin de média à la
     fusion (même point d'insertion que les briques n°168 et n°169). */

  /* ---------- Accueil cordeur (issue n°167, accueil-cordeur.html) ---------- */
  /* Variantes de .heros/.photo déjà scopées .adm-shell--app ci-dessus (socle
     n°166) : partagées entre écrans bureau, comme .section plus haut. */
  .adm-shell--app .heros--cordeur {
    grid-template-columns: 3fr 2fr;
    gap: 28px;
    align-items: start;
    padding-top: 52px;
  }
  .adm-shell--app .grille-2--egale {
    grid-template-columns: 1fr 1fr;
    /* Trouvé le 29/09 (retour Vincent, « Ton atelier vu par les joueurs » :
       « est-ce qu'on peut imaginer qu'elle soit à la même dimension ») :
       cette classe n'égalisait que la largeur des colonnes, jamais la
       hauteur — .grille-2 aligne au contenu naturel (align-items: start,
       voulu pour le détail + colonne collante), sans que --egale ne le
       reprenne. Un seul écran l'utilise (cette grille), sans risque ailleurs. */
    align-items: stretch;
  }
  .adm-shell--app .photo--carree {
    aspect-ratio: 1;
    border-radius: 12px;
  }
  /* « Ton atelier vu par les joueurs » remonte directement sous le héros
     (refus de recette du 13/09, issue n°193) : padding-top standard de
     section plutôt que le plein padding de .section (44px), qui laissait un
     vide disproportionné après le héros. */
  .adm-shell--app .accueil-cordeur .section {
    padding-top: 36px;
  }

  /* .carte, .nombre, .semaine, .jour, .stats, .file, .avatar, .pastille…
     sont déjà des noms pris ailleurs dans l'app (admin/lcs.css, mobile
     base.css, /cordeur/disponibilites) : toute règle ici est scopée sous
     .accueil-cordeur (voir accueil.tsx), jamais nue sous .adm-shell--app
     (note en tête de fichier) — sauf .heros--cordeur/.grille-2--egale/
     .photo--carree ci-dessus, des noms neufs qui ne collisionnent avec rien. */
  .adm-shell--app .accueil-cordeur .carte {
    background: var(--surface);
    border: 1px solid var(--trait);
    border-radius: 16px;
    padding: 22px 24px;
  }
  .adm-shell--app .accueil-cordeur .carte h3 {
    font-size: var(--taille-titre-3);
    font-weight: 700;
    margin: 0 0 14px;
  }
  .adm-shell--app .accueil-cordeur .titre-page {
    font-family: var(--police-titres-site);
    font-weight: 800;
    font-size: var(--taille-affichage-5);
    line-height: 1.05;
    letter-spacing: -0.03em;
    margin: 0 0 8px;
    text-wrap: balance;
  }
  .adm-shell--app .accueil-cordeur .sous-titre {
    font-size: var(--taille-titre-3);
    color: var(--texte-secondaire);
    margin: 6px 0 20px;
  }
  /* Colonne gauche du héros (file du jour) : étirée à la hauteur de la
     colonne droite (refus de recette du 13/09, issue n°193) — la .carte qui
     porte la file (ou son état vide) grandit pour combler l'espace, plutôt
     que de laisser un vide sous une file courte. Une redéfinition de
     .grille-2 vivait ici avant cette issue et écrasait par erreur, via une
     spécificité plus forte, le 1fr 1fr voulu par .grille-2--egale plus haut
     (grille de « Ton atelier vu par les joueurs ») : elle est retirée, ne
     restait plus utile nulle part. */
  .adm-shell--app .accueil-cordeur .colonne-file {
    display: flex;
    flex-direction: column;
    align-self: stretch;
  }
  .adm-shell--app .accueil-cordeur .colonne-file .carte {
    display: flex;
    flex: 1;
    flex-direction: column;
  }
  .adm-shell--app .accueil-cordeur .colonne-file .carte:has(.etat-vide) {
    justify-content: center;
  }
  .adm-shell--app .accueil-cordeur .colonne {
    display: flex;
    flex-direction: column;
    gap: 20px;
  }
  /* Ce mois-ci / Cette semaine côte à côte quand la file est courte (0 à 2
     lignes), pour ne pas laisser la colonne droite bien plus haute que la
     file du jour qu'elle borde. */
  .adm-shell--app .accueil-cordeur .colonne--cote-a-cote {
    flex-direction: row;
  }
  .adm-shell--app .accueil-cordeur .colonne--cote-a-cote > .carte {
    flex: 1;
    min-width: 0;
  }
  .adm-shell--app .accueil-cordeur .collant {
    position: sticky;
    top: 92px;
  }
  .adm-shell--app .accueil-cordeur .nombre {
    font-family: var(--police-titres-site);
    font-weight: 800;
    letter-spacing: -0.02em;
  }
  .adm-shell--app .accueil-cordeur .nombre--grand {
    display: block;
    font-size: var(--taille-affichage-4);
    line-height: 1.05;
    color: var(--accent-marque-encre);
  }

  /* File du jour : une ligne cliquable par commande active. */
  .adm-shell--app .accueil-cordeur .file {
    display: flex;
    flex-direction: column;
  }
  .adm-shell--app .accueil-cordeur .file__item {
    display: grid;
    grid-template-columns: 44px 1fr auto auto;
    gap: 16px;
    align-items: center;
    padding: 16px 0;
    border-top: 1px solid var(--trait);
    color: inherit;
    text-decoration: none;
  }
  .adm-shell--app .accueil-cordeur .file__item:first-child {
    border-top: 0;
    padding-top: 0;
  }
  .adm-shell--app .accueil-cordeur .file__nom {
    font-weight: 700;
    font-size: var(--taille-titre-3);
    margin: 0;
  }
  .adm-shell--app .accueil-cordeur .file__sous {
    margin: 2px 0 0;
    color: var(--texte-secondaire);
    font-size: var(--taille-texte-2);
  }
  .adm-shell--app .accueil-cordeur .file__heure {
    font-weight: 600;
    font-size: var(--taille-texte-2);
    color: var(--texte-secondaire);
    white-space: nowrap;
  }
  .adm-shell--app .accueil-cordeur .file .avatar {
    width: 44px;
    height: 44px;
    border-radius: 50%;
    background: var(--avatar-1-fond);
    color: var(--avatar-1-texte);
    display: grid;
    place-items: center;
    font-weight: 700;
    font-size: var(--taille-texte-2);
  }
  .adm-shell--app .accueil-cordeur .file .avatar--2 {
    background: var(--avatar-2-fond);
    color: var(--avatar-2-texte);
  }
  .adm-shell--app .accueil-cordeur .file .avatar--3 {
    background: var(--avatar-3-fond);
    color: var(--avatar-3-texte);
  }
  .adm-shell--app .accueil-cordeur .file .avatar--4 {
    background: var(--avatar-4-fond);
    color: var(--avatar-4-texte);
  }
  .adm-shell--app .accueil-cordeur .file .pastille {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 9px;
    border-radius: 999px;
    font-size: var(--taille-texte-3);
    font-weight: 600;
    background: var(--accent-marque-voile);
    color: var(--accent-marque-encre);
  }
  .adm-shell--app .accueil-cordeur .file .pastille::before {
    content: '';
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--accent-marque-encre);
  }
  /* Encre : la seule action de l'écran (règle n°162) — une commande qui
     demande une décision, pas juste une information à sens unique. */
  .adm-shell--app .accueil-cordeur .file .pastille--encre {
    background: var(--action);
    color: var(--sur-action);
  }
  .adm-shell--app .accueil-cordeur .file .pastille--encre::before {
    background: var(--accent-marque);
  }
  .adm-shell--app .accueil-cordeur .file .pastille--attente {
    background: var(--attention-fond);
    color: var(--attention-texte);
  }
  .adm-shell--app .accueil-cordeur .file .pastille--attente::before {
    background: var(--attention);
  }
  .adm-shell--app .accueil-cordeur .file .pastille--neutre {
    background: rgba(21, 32, 28, 0.06);
    color: var(--texte-secondaire);
  }
  .adm-shell--app .accueil-cordeur .file .pastille--neutre::before {
    background: var(--texte-secondaire);
  }

  /* Ce mois-ci : 3 statistiques. Colonnes bornées à minmax(0, 1fr) et un
     espace entre elles (issue n°215) : sans ça, une légende sur deux mots
     (« note moyenne », « nouveaux clients ») ne se replie pas dans sa
     propre colonne et vient se coller au chiffre voisin, gap: 0 ne laissant
     aucun espace pour l'absorber. */
  .adm-shell--app .accueil-cordeur .stats {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 0 var(--espacement-petit);
  }
  .adm-shell--app .accueil-cordeur .stats div {
    padding: 4px 0;
  }
  .adm-shell--app .accueil-cordeur .stats .nombre {
    display: block;
    font-size: var(--taille-affichage-2);
    line-height: 1.1;
  }
  .adm-shell--app .accueil-cordeur .stats small {
    display: block;
    color: var(--texte-secondaire);
    font-size: var(--taille-texte-2);
    overflow-wrap: break-word;
  }

  /* Cette semaine : bande de 7 jours. */
  .adm-shell--app .accueil-cordeur .semaine {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 6px;
    margin: 12px 0 16px;
  }
  .adm-shell--app .accueil-cordeur .jour {
    text-align: center;
    padding: 10px 0 8px;
    border: 1px solid var(--trait);
    border-radius: 10px;
    font-size: var(--taille-texte-3);
    color: var(--texte-secondaire);
  }
  .adm-shell--app .accueil-cordeur .jour b {
    display: block;
    margin-top: 2px;
    font-size: var(--taille-titre-3);
    font-weight: 700;
    color: var(--texte);
  }
  .adm-shell--app .accueil-cordeur .jour.choisi {
    background: var(--action);
    border-color: var(--action);
    color: rgba(255, 255, 255, 0.7);
  }
  .adm-shell--app .accueil-cordeur .jour.choisi b {
    color: var(--sur-action);
  }
  .adm-shell--app .accueil-cordeur .jour.plein {
    opacity: 0.35;
  }

  /* Ton atelier vu par les joueurs : photo de couverture + dernier avis. */
  .adm-shell--app .accueil-cordeur .carte--photo-invite {
    display: grid;
    grid-template-columns: 120px 1fr;
    gap: 18px;
    align-items: center;
  }
  .adm-shell--app .accueil-cordeur .carte--photo-invite img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: 12px;
  }
  .adm-shell--app .accueil-cordeur .carte--photo-invite p {
    margin: 0 0 12px;
    color: var(--texte-secondaire);
    font-size: var(--taille-texte-2);
  }
  .adm-shell--app .accueil-cordeur .avis__item {
    display: grid;
    grid-template-columns: 36px 1fr;
    gap: 12px;
  }
  .adm-shell--app .accueil-cordeur .avis__item .avatar {
    width: 36px;
    height: 36px;
    font-size: var(--taille-texte-3);
  }
  .adm-shell--app .accueil-cordeur .avis__tete {
    display: flex;
    justify-content: space-between;
    font-size: var(--taille-texte-2);
  }
  .adm-shell--app .accueil-cordeur .avis__tete b {
    font-weight: 700;
  }
  .adm-shell--app .accueil-cordeur .avis__texte {
    margin: 4px 0 0;
    color: var(--texte-secondaire);
    font-size: var(--taille-texte-1);
    line-height: 1.55;
  }
  .adm-shell--app .accueil-cordeur .etoile {
    color: var(--accent-marque-encre);
    font-weight: 700;
  }
  .adm-shell--app .accueil-cordeur .avis__lien {
    margin: 14px 0 0;
  }

  /* Issue n°170 — Mes raquettes bureau. Bloc ajouté en fin de média à la
     fusion (même point d'insertion que les briques n°167/168/169). */

  /* ---------- Mes raquettes (issue n°170, maquette docs/maquettes/
     2026-09-11/bureau/raquettes.html) : cartes larges, tension en avant.
     `.tete-page`/`.titre-page`/`.sous-titre`/`.pied` sont les compléments
     partagés déjà scopés nus sous .adm-shell--app plus haut (issue n°169) —
     seules .raquettes et .raquette-carte* sont nouvelles ici, aucun nom pris
     ailleurs dans l'app. */
  .adm-shell--app .raquettes {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 22px;
  }
  .adm-shell--app .raquette-carte {
    background: var(--surface);
    border: 1px solid var(--trait);
    border-radius: 16px;
    padding: 22px 22px 18px;
    display: flex;
    flex-direction: column;
    gap: 6px;
  }
  .adm-shell--app .raquette-carte__sport {
    font-size: var(--taille-texte-3);
    font-weight: 600;
    color: var(--texte-secondaire);
  }
  .adm-shell--app .raquette-carte__nom {
    font-family: var(--police-titres-site);
    font-weight: 800;
    font-size: var(--taille-affichage-1);
    letter-spacing: -0.02em;
    margin: 0;
  }
  .adm-shell--app .raquette-carte__tension {
    margin: 10px 0 2px;
  }
  .adm-shell--app .raquette-carte__tension .nombre {
    font-family: var(--police-titres-site);
    font-weight: 800;
    letter-spacing: -0.02em;
    font-size: var(--taille-affichage-3);
    line-height: 1;
  }
  .adm-shell--app .raquette-carte__tension small {
    color: var(--texte-secondaire);
    font-size: var(--taille-texte-2);
    margin-left: 6px;
  }
  .adm-shell--app .raquette-carte__cordage {
    margin: 0;
    color: var(--texte-secondaire);
    font-size: var(--taille-texte-2);
  }
  .adm-shell--app .raquette-carte__pied {
    display: flex;
    gap: 8px;
    margin-top: 16px;
    padding-top: 16px;
    border-top: 1px solid var(--trait);
  }
  .adm-shell--app .raquette-carte__pied .bouton {
    padding: 10px 16px;
    font-size: var(--taille-texte-2);
  }
  /* Carte pointillée « Une autre raquette ? », seulement quand la grille est
     vide (l'aplat de .tete-page suffit sinon, règle n°162 — voir raquettes.tsx). */
  .adm-shell--app .raquette-carte--ajout {
    border-style: dashed;
    align-items: center;
    justify-content: center;
    text-align: center;
    color: var(--texte-secondaire);
    cursor: pointer;
    min-height: 220px;
    text-decoration: none;
  }
  .adm-shell--app .raquette-carte--ajout .plus {
    width: 44px;
    height: 44px;
    border-radius: 50%;
    border: 1px solid var(--trait);
    display: grid;
    place-items: center;
    font-size: var(--taille-affichage-1);
    margin-bottom: 8px;
  }

  /* ---------- Tunnel de réservation : une question par écran (issue
     n°174, maquette reservation-etape.html) — valeurs reprises telles
     quelles de la section « Paquet 2 » de bureau.css (maquette). Classes
     génériques (pas de nom déjà pris ailleurs dans l'app), partagées par
     commander-nouvelle.tsx, commander-livraison.tsx et
     commander-avis.tsx. */
  .adm-shell--app .tunnel {
    width: min(600px, 100%);
    margin: 0 auto;
    padding: 64px 0 80px;
  }
  .adm-shell--app .tunnel__progression {
    display: flex;
    align-items: center;
    gap: 14px;
    font-size: var(--taille-texte-2);
    color: var(--texte-secondaire);
    margin-bottom: 36px;
  }
  .adm-shell--app .tunnel__barre {
    flex: 1;
    height: 4px;
    border-radius: 999px;
    background: var(--trait);
    overflow: hidden;
  }
  .adm-shell--app .tunnel__barre i {
    display: block;
    width: 0;
    height: 100%;
    background: var(--accent-marque-encre);
    border-radius: 999px;
  }
  /* Remplissage en classes plutôt qu'un style="width" inline, bloqué par la
     CSP (style-src 'self', src/api/security-headers.ts) — voir qa.css. */
  .adm-shell--app .tunnel__barre--2-3 i {
    width: 66%;
  }
  .adm-shell--app .tunnel__barre--3-3 i {
    width: 100%;
  }
  .adm-shell--app .tunnel h1 {
    font-family: var(--police-titres-site);
    font-weight: 800;
    font-size: var(--taille-affichage-4);
    line-height: 1.05;
    letter-spacing: -0.03em;
    margin: 0 0 8px;
    text-wrap: balance;
  }
  .adm-shell--app .tunnel__aide {
    color: var(--texte-secondaire);
    font-size: var(--taille-titre-3);
    margin: 0 0 32px;
  }
  .adm-shell--app .tunnel__sous-titre {
    font-size: var(--taille-titre-3);
    font-weight: 700;
    margin: 28px 0 12px;
  }
  .adm-shell--app .tunnel__sous-titre:first-of-type {
    margin-top: 0;
  }
  .adm-shell--app .options {
    display: flex;
    flex-direction: column;
    gap: 12px;
  }
  .adm-shell--app .option {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: center;
    gap: 16px;
    padding: 20px 22px;
    background: var(--surface);
    border: 1px solid var(--trait);
    border-radius: var(--rayon);
    cursor: pointer;
    position: relative;
  }
  .adm-shell--app .option:hover {
    border-color: var(--trait-cordeur-survol);
  }
  .adm-shell--app .option input {
    position: absolute;
    opacity: 0;
    inset: 0;
    cursor: pointer;
  }
  .adm-shell--app .option:has(input:checked),
  .adm-shell--app .option.choisie {
    border-color: var(--texte);
    box-shadow: inset 0 0 0 1px var(--texte);
  }
  .adm-shell--app .option b {
    display: block;
    font-size: var(--taille-titre-3);
    font-weight: 700;
  }
  .adm-shell--app .option small {
    color: var(--texte-secondaire);
    font-size: var(--taille-texte-2);
  }
  .adm-shell--app .option .rond {
    width: 20px;
    height: 20px;
    border-radius: 50%;
    border: 2px solid var(--trait);
  }
  .adm-shell--app .option:has(input:checked) .rond,
  .adm-shell--app .option.choisie .rond {
    border: 6px solid var(--texte);
  }
  .adm-shell--app .option--inactif {
    opacity: 0.5;
    cursor: not-allowed;
  }
  .adm-shell--app .option--inactif input {
    cursor: not-allowed;
  }
  .adm-shell--app .option__chevron {
    color: var(--texte-secondaire);
  }
  .adm-shell--app .tunnel__actions {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 32px;
  }
  /* Mise en page (retour n°199) : question à gauche, récapitulatif collant
     à droite — grille scopée à ce conteneur (pas .grille-2, dont les
     colonnes 1fr/340px servent aux écrans de détail) pour que .tunnel garde
     son gabarit d'origine (question seule, ex. commander-avis.tsx) partout
     ailleurs. */
  .adm-shell--app .tunnel-mise-en-page {
    width: min(940px, 100%);
    margin: 0 auto;
    display: grid;
    /* `fr` n'est pas admis dans min() : la déclaration était ignorée et le
       récapitulatif tombait sous la question (constaté à la fusion de n°199). */
    grid-template-columns: minmax(0, 600px) 300px;
    justify-content: center;
    gap: 28px;
    align-items: start;
  }
  .adm-shell--app .tunnel-mise-en-page .tunnel {
    width: auto;
    margin: 0;
  }
  .adm-shell--app .tunnel-mise-en-page .collant {
    position: sticky;
    top: 92px;
  }
  .adm-shell--app .tunnel__resume {
    font-size: var(--taille-texte-2);
  }
  .adm-shell--app .tunnel__resume-titre {
    margin: 0 0 12px;
    font-size: var(--taille-texte-2);
    font-weight: 600;
    color: var(--texte-secondaire);
  }
  .adm-shell--app .tunnel__resume p {
    margin: 0 0 6px;
    color: var(--texte-secondaire);
  }
  .adm-shell--app .tunnel__resume b {
    color: var(--texte);
  }
  .adm-shell--app .tunnel__resume-total {
    margin: 4px 0 0;
    padding-top: 8px;
    border-top: 1px solid var(--trait);
    font-weight: 700;
    color: var(--texte);
  }
  /* Espacement entre le formulaire d'adresse (commander-livraison.tsx) et
     les propositions de retrait/livraison qui suivent, une fois calculées. */
  .adm-shell--app .tunnel__section {
    margin-top: 28px;
  }

  /* Avis (commander-avis.tsx) : même carte avis que la fiche cordeur bureau
     (issue n°168), scopée à ce conteneur pour ne pas redéfinir .avis* nu. */
  .adm-shell--app .commander-avis-bureau .avis {
    display: flex;
    flex-direction: column;
    gap: 18px;
    margin-top: 24px;
  }
  .adm-shell--app .commander-avis-bureau .avis__item {
    display: grid;
    grid-template-columns: 36px 1fr;
    gap: 12px;
  }
  .adm-shell--app .commander-avis-bureau .avis__avatar {
    flex: none;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    display: grid;
    place-items: center;
    font-weight: 700;
    font-size: var(--taille-texte-3);
    background: var(--avatar-2-fond);
    color: var(--avatar-2-texte);
  }
  .adm-shell--app .commander-avis-bureau .avis__avatar--3 {
    background: var(--avatar-3-fond);
    color: var(--avatar-3-texte);
  }
  .adm-shell--app .commander-avis-bureau .avis__avatar--4 {
    background: var(--avatar-4-fond);
    color: var(--avatar-4-texte);
  }
  .adm-shell--app .commander-avis-bureau .avis__tete {
    display: flex;
    justify-content: space-between;
    font-size: var(--taille-texte-2);
  }
  .adm-shell--app .commander-avis-bureau .avis__tete .etoile {
    color: var(--accent-marque-encre);
    font-weight: 700;
  }
  .adm-shell--app .commander-avis-bureau .avis__texte {
    margin: 4px 0 0;
    color: var(--texte-secondaire);
    font-size: var(--taille-texte-1);
    line-height: 1.55;
  }

  /* ---------- Disponibilités : grille semaine × heures (issue n°172,
     maquette docs/maquettes/2026-09-11/bureau/disponibilites.html, section
     « Paquet 2 » de son CSS embarqué — valeurs reprises telles quelles).
     .tete-page/.titre-page/.sous-titre/.grille-2/.colonne.collant sont les
     classes bureau génériques posées par le socle et /poses/:id, réutilisées
     sans être redéfinies ici (voir plus haut dans ce fichier). Ajoutée en fin
     de fichier pour ne pas rejouer la collision d'insertion notée au
     CHANGELOG 0.87.0. ---------- */
  .adm-shell--app .disponibilites-bureau .carte {
    background: var(--surface);
    border: 1px solid var(--trait);
    border-radius: 16px;
    padding: 22px 24px;
  }
  .adm-shell--app .disponibilites-bureau .carte h3 {
    font-size: var(--taille-titre-3);
    font-weight: 700;
    margin: 0 0 14px;
  }
  .adm-shell--app .disponibilites-bureau .carte--vacances {
    background: var(--accent-marque-voile);
    border-color: transparent;
  }
  .adm-shell--app .disponibilites-bureau .carte--vacances p {
    margin: 0;
    font-size: var(--taille-texte-2);
    line-height: 1.5;
  }
  .adm-shell--app .disponibilites-bureau .carte--vacances .bouton--discret {
    margin-top: 6px;
  }

  .adm-shell--app .disponibilites-bureau .planning-nav {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 16px;
  }
  .adm-shell--app .disponibilites-bureau .planning-nav__label {
    font-size: var(--taille-texte-1);
  }

  /* Semaines empilées de la vue mois (issue n°212) : un intitulé de plage de
     dates au-dessus de chaque grille .planning, un espace avant chacune sauf
     la première. */
  .adm-shell--app .disponibilites-bureau .planning-semaine + .planning-semaine {
    margin-top: 24px;
  }
  .adm-shell--app .disponibilites-bureau .planning-semaine-titre {
    margin: 0 0 8px;
    color: var(--texte-secondaire);
  }

  .adm-shell--app .disponibilites-bureau .planning {
    display: grid;
    grid-template-columns: 64px repeat(7, 1fr);
    gap: 6px;
  }
  .adm-shell--app .disponibilites-bureau .planning__jour {
    text-align: center;
    font-size: var(--taille-texte-2);
    color: var(--texte-secondaire);
    padding-bottom: 6px;
  }
  .adm-shell--app .disponibilites-bureau .planning__jour b {
    display: block;
    font-size: var(--taille-titre-2);
    color: var(--texte);
    font-weight: 700;
  }
  .adm-shell--app .disponibilites-bureau .planning__jour.aujourdhui b {
    color: var(--accent-marque-encre);
  }
  .adm-shell--app .disponibilites-bureau .planning__heure {
    font-size: var(--taille-texte-3);
    color: var(--texte-secondaire);
    display: flex;
    align-items: center;
    justify-content: flex-end;
    padding-right: 10px;
  }

  .adm-shell--app .disponibilites-bureau .creneau-case {
    height: 44px;
    border-radius: 10px;
    border: 1px solid var(--trait);
    background: var(--surface);
    display: grid;
    place-items: center;
    font-size: var(--taille-texte-3);
    font-weight: 600;
  }
  .adm-shell--app .disponibilites-bureau .creneau-case.reserve {
    background: var(--action);
    border-color: var(--action);
    color: var(--sur-action);
    font-weight: 500;
  }
  .adm-shell--app .disponibilites-bureau .creneau-case.ferme {
    background: transparent;
    border-style: dashed;
    opacity: 0.65; /* 0.5 : texte à 3,6:1, sous AA pour une case cochable (audit accessibilité du 25/09) */
  }
  /* Case cliquable : une vraie case à cocher (fonctionne sans JS), invisible
     mais superposée à toute la case, .creneau-case n'étant que son habillage
     visuel — voir disponibilites-grille.js pour le seul rôle laissé au JS
     (bouton Enregistrer). */
  .adm-shell--app .disponibilites-bureau .creneau-case-champ {
    position: relative;
    display: block;
  }
  .adm-shell--app .disponibilites-bureau .creneau-case-champ input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    opacity: 0;
    cursor: pointer;
  }
  .adm-shell--app .disponibilites-bureau .creneau-case-champ:hover .creneau-case {
    border-color: var(--texte);
  }
  .adm-shell--app .disponibilites-bureau .creneau-case-champ:has(input:focus-visible) .creneau-case {
    outline: 2px solid var(--accent-marque-encre);
    outline-offset: 2px;
  }
  .adm-shell--app .disponibilites-bureau .creneau-case-champ:has(input:checked) .creneau-case {
    background: var(--accent-marque-voile);
    border-color: transparent;
    border-style: solid;
    opacity: 1;
    color: var(--accent-marque-encre);
  }

  .adm-shell--app .disponibilites-bureau .legende {
    display: flex;
    gap: 18px;
    margin-top: 14px;
    font-size: var(--taille-texte-2);
    color: var(--texte-secondaire);
  }
  .adm-shell--app .disponibilites-bureau .legende span::before {
    content: '';
    display: inline-block;
    width: 12px;
    height: 12px;
    border-radius: 4px;
    margin-right: 6px;
    vertical-align: -1px;
    border: 1px solid var(--trait);
  }
  .adm-shell--app .disponibilites-bureau .legende .l-ouvert::before {
    background: var(--accent-marque-voile);
    border-color: transparent;
  }
  .adm-shell--app .disponibilites-bureau .legende .l-reserve::before {
    background: var(--action);
    border-color: var(--action);
  }
  .adm-shell--app .disponibilites-bureau .legende .l-ferme::before {
    border-style: dashed;
  }

  .adm-shell--app .disponibilites-bureau .stats {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 0;
  }
  .adm-shell--app .disponibilites-bureau .stats div {
    padding: 4px 0;
  }
  .adm-shell--app .disponibilites-bureau .nombre {
    font-family: var(--police-titres-site);
    font-weight: 800;
    letter-spacing: -0.02em;
  }
  .adm-shell--app .disponibilites-bureau .stats .nombre {
    display: block;
    font-size: var(--taille-affichage-2);
    line-height: 1.1;
  }
  .adm-shell--app .disponibilites-bureau .stats small {
    color: var(--texte-secondaire);
    font-size: var(--taille-texte-2);
  }

  /* Issue n°173 — Mon compte bureau. Bloc ajouté en fin de média à la fusion. */

  /* ---------- Mon compte /compte (issue n°173, paquet 2, maquette
     docs/maquettes/2026-09-11/bureau/compte.html) : formulaire calme,
     connexion Google, notifications, colonne selon le rôle. Réutilise sans
     rien redéfinir les « compléments partagés » déjà nus sous
     .adm-shell--app (.carte, .tete-page/.titre-page/.sous-titre,
     .grille-2/.colonne/.collant, .cordeur-mini*, .couverture__avatar,
     .badge-verif, .carte-lien*). .champs/.champ sont en revanche scopés
     sous .compte-bureau : ces deux noms sont déjà pris par le formulaire
     mobile (ecrans-compte.css) — les redéfinir nus changerait tous les
     champs de l'app sur bureau. .compte-tete/.google/.zone-douce sont neufs
     et nus, comme le couple .interrupteur/.bascule qui n'appartient pas à
     cet écran en particulier : la bascule est reprise telle quelle de la
     maquette pour être partagée avec les disponibilités bureau (issue
     n°172). */
  .adm-shell--app .compte-bureau .carte h3 {
    font-size: var(--taille-titre-3);
    font-weight: 700;
    margin: 0 0 14px;
  }
  .adm-shell--app .compte-bureau .carte form + form {
    margin-top: 20px;
  }
  .adm-shell--app .compte-bureau .champs {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 14px 18px;
    margin-top: 20px;
  }
  .adm-shell--app .compte-bureau .champ {
    display: flex;
    flex-direction: column;
    gap: 6px;
  }
  .adm-shell--app .compte-bureau .champ label {
    font-size: var(--taille-texte-2);
    color: var(--texte-secondaire);
    font-weight: 500;
  }
  .adm-shell--app .compte-bureau .champ input {
    height: 44px;
    border: 1px solid var(--trait);
    border-radius: 10px;
    padding: 0 14px;
    font: inherit;
    font-size: var(--taille-texte-1);
    background: var(--surface);
  }
  .adm-shell--app .compte-bureau .champ--large {
    grid-column: 1 / -1;
  }
  .adm-shell--app .compte-bureau .carte-identite__pied {
    display: flex;
    justify-content: flex-end;
    margin-top: 18px;
  }
  .adm-shell--app .compte-tete {
    display: flex;
    align-items: center;
    gap: 18px;
    margin-bottom: 4px;
  }
  .adm-shell--app .compte-tete .couverture__avatar {
    width: 84px;
    height: 84px;
    font-size: var(--taille-affichage-1);
    border: 0;
  }
  .adm-shell--app .google {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px;
    padding: 12px 14px;
    border: 1px solid var(--trait);
    border-radius: 12px;
    font-size: var(--taille-texte-1);
  }
  /* Texte d'état suivi d'un bouton (connexion Google non reliée, club non
     rattaché) : retour recette du 15/09 (issue n°207), « trop collé au
     bouton juste en dessous » — le margin-bottom par défaut d'un <p> suit
     son propre font-size (13.5px, .texte-secondaire), trop court à côté
     d'un bouton de 40px. */
  .adm-shell--app .compte-bureau .carte > p.texte-secondaire {
    margin: 0 0 14px;
  }
  .adm-shell--app .compte-bureau__google-texte {
    flex: 1;
    min-width: 0;
  }
  .adm-shell--app .zone-douce {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 4px;
    color: var(--texte-secondaire);
    font-size: var(--taille-texte-2);
  }
  .adm-shell--app .zone-douce form {
    display: contents;
  }
  .adm-shell--app .zone-douce button {
    font: inherit;
    color: inherit;
    background: none;
    border: 0;
    padding: 0;
    cursor: pointer;
  }
  .adm-shell--app .zone-douce a {
    color: var(--erreur);
    font-weight: 600;
  }

  /* Confirmation de réservation, bureau (issue n°192, refus de recette du
     13/09 : la version mobile simplement recentrée dans une colonne étroite
     a été jugée « très moche »). Scène de célébration (fond marine, motif
     de cordage, monogramme, illustration raquette) à gauche, récapitulatif
     à droite — .titre-page/.sous-titre/.carte/.grille-infos/.bouton--eclat
     sont repris tels quels du socle (voir plus haut dans ce fichier et
     docs/design-system.md) plutôt que redessinés. Le mobile (.moment,
     ecrans-suivi.css) n'a pas bougé. */
  .adm-shell--app .confirmation-bureau {
    display: grid;
    grid-template-columns: 1fr 1fr;
    align-items: center;
    gap: 64px;
    max-width: 1040px;
    margin: 0 auto;
    padding: 64px 0;
  }
  .adm-shell--app .confirmation-bureau-scene {
    position: relative;
    aspect-ratio: 1 / 1;
    border-radius: 24px;
    background: var(--surface-haute);
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
  }
  .adm-shell--app .confirmation-bureau-motif {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0.5;
  }
  .adm-shell--app .confirmation-bureau-motif-montant {
    fill: var(--filet-sur-sombre);
  }
  .adm-shell--app .confirmation-bureau-motif-cordage {
    stroke: var(--accent-marque);
  }
  .adm-shell--app .confirmation-bureau-monogramme {
    position: absolute;
    top: 28px;
    left: 28px;
  }
  .adm-shell--app .confirmation-bureau-raquette {
    position: relative;
    width: 140px;
    height: 140px;
    color: var(--fond);
  }
  .adm-shell--app .confirmation-bureau-raquette .picto-tete {
    fill: var(--accent-marque);
  }
  .adm-shell--app .confirmation-bureau-recap {
    width: 100%;
    margin: 24px 0 32px;
  }
  .adm-shell--app .confirmation-bureau-recap .grille-infos {
    grid-template-columns: 1fr;
    gap: 16px;
  }
  .adm-shell--app .confirmation-bureau-recap .grille-infos dt {
    color: var(--texte-secondaire);
    font-size: var(--taille-texte-2);
    margin-bottom: 3px;
  }
  .adm-shell--app .confirmation-bureau-recap .grille-infos dd {
    margin: 0;
    font-weight: 600;
    font-size: var(--taille-texte-1);
  }
  .adm-shell--app .confirmation-bureau-actions {
    display: flex;
    align-items: center;
    gap: 24px;
  }

  /* Issue de recette 14/09 — bloc ajouté en fin de média */
  .adm-shell--app .accueil-cordeur .colonne .carte .sous-titre,
  .adm-shell--app .accueil-cordeur .colonne .carte .note,
  .adm-shell--app .accueil-cordeur .colonne .carte p:not(.titre-3) {
    font-size: var(--taille-texte-2);
    line-height: 1.4;
    color: var(--texte-secondaire);
  }
  .adm-shell--app .accueil-cordeur .colonne .carte .nombre--grand {
    font-size: var(--taille-affichage-1);
  }
  /* Déconnexion remontée à côté du titre (retour recette du 15/09, issue
     n°207) : la zone-douce ne garde plus que « Supprimer mon compte »,
     action rare et volontairement mise à distance. */
  .adm-shell--app .compte-bureau .zone-douce { display: flex; align-items: center; margin-top: 16px; }

  /* Disponibilités bureau (retour Camille 14/09 : « la carte avec l'agenda
     doit être plus grande, tout est les uns sur les autres ») : la grille
     héritait d'une largeur de section mobile (520px) et comprimait les sept
     colonnes à 23px. Pleine largeur, cellules jamais sous 40px, les heures
     défilent verticalement dans la carte si la journée est longue. */
  .adm-shell--app .disponibilites-bureau form,
  .adm-shell--app .disponibilites-bureau section,
  .adm-shell--app .disponibilites-bureau .grille-2 {
    width: 100%;
    max-width: none;
  }
  .adm-shell--app .disponibilites-bureau .planning {
    grid-template-columns: 64px repeat(7, minmax(40px, 1fr));
    gap: 6px 5px;
  }
  .adm-shell--app .disponibilites-bureau .creneau-case {
    min-width: 40px;
    font-size: var(--taille-texte-4);
    overflow: hidden;
    white-space: nowrap;
  }

  /* ---------- Commandes cordeur bureau (issue n°202, refus de recette du
     14/09 : « pas de design sur cette page, l'une des plus importantes » —
     maquette docs/maquettes/2026-09-14/commandes-cordeur.html). .titre-page/
     .sous-titre/.pied/.filtres/.filtre déjà globaux (voir plus haut) : rien
     à redéfinir pour eux ici. .commande-cordeur, .pastille, .avatar sont des
     noms pris ailleurs (voir la note en tête du fichier) : toute règle reste
     scopée sous .commandes-cordeur. */
  .adm-shell--app .commandes-cordeur__tete {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 24px;
    margin-bottom: 22px;
  }
  .adm-shell--app .commandes-cordeur__tete .titre-page,
  .adm-shell--app .commandes-cordeur__tete .sous-titre {
    margin-bottom: 0;
  }
  .adm-shell--app .commandes-cordeur__tete .recherche-liste {
    min-width: 280px;
  }
  .adm-shell--app .commandes-cordeur__groupe {
    margin: 26px 0 10px;
    font-size: var(--taille-texte-3);
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--texte-secondaire);
    display: flex;
    gap: 10px;
    align-items: center;
  }
  .adm-shell--app .commandes-cordeur__groupe:first-of-type {
    margin-top: 0;
  }
  .adm-shell--app .commandes-cordeur__groupe i {
    flex: 1;
    height: 1px;
    background: var(--trait);
  }
  .adm-shell--app .commandes-cordeur__liste {
    display: grid;
    gap: 10px;
  }
  .adm-shell--app .commande-cordeur {
    display: grid;
    grid-template-columns: 56px 1fr auto auto;
    gap: 16px;
    align-items: center;
    padding: 14px 18px;
    background: var(--surface);
    border: 1px solid var(--trait);
    border-radius: var(--rayon);
    border-left: 4px solid var(--trait);
    color: inherit;
    text-decoration: none;
  }
  .adm-shell--app .commande-cordeur--a-faire {
    border-left-color: var(--attention);
  }
  .adm-shell--app .commande-cordeur--en-cours {
    border-left-color: var(--accent-marque);
  }
  .adm-shell--app .commande-cordeur--prete {
    border-left-color: var(--accent-marque-encre);
  }
  /* Avatar + texte + quand cliquent vers le détail (issue n°164), les actions
     restent en dehors du lien (un bouton dans un lien n'est pas valide) :
     .commande-cordeur__lien redevient transparente pour la grille avec
     `display: contents`, ses enfants occupent directement les trois
     premières colonnes ci-dessus. */
  .adm-shell--app .commande-cordeur__lien {
    display: contents;
  }
  .adm-shell--app .commande-cordeur__avatar {
    width: 56px;
    height: 56px;
    border-radius: 50%;
    background: var(--avatar-1-fond);
    color: var(--avatar-1-texte);
    display: grid;
    place-items: center;
    font-weight: 700;
    font-size: var(--taille-titre-3);
  }
  .adm-shell--app .commande-cordeur__avatar--2 {
    background: var(--avatar-2-fond);
    color: var(--avatar-2-texte);
  }
  .adm-shell--app .commande-cordeur__avatar--3 {
    background: var(--avatar-3-fond);
    color: var(--avatar-3-texte);
  }
  .adm-shell--app .commande-cordeur__avatar--4 {
    background: var(--avatar-4-fond);
    color: var(--avatar-4-texte);
  }
  .adm-shell--app .commande-cordeur__texte {
    min-width: 0;
  }
  .adm-shell--app .commande-cordeur__titre {
    display: block;
    font-size: var(--taille-titre-3);
    font-weight: 700;
  }
  .adm-shell--app .commande-cordeur__sous {
    display: block;
    margin: 2px 0 0;
    color: var(--texte-secondaire);
    font-size: var(--taille-texte-2);
  }
  .adm-shell--app .commande-cordeur__quand {
    text-align: right;
    font-size: var(--taille-texte-2);
    color: var(--texte-secondaire);
    white-space: nowrap;
  }
  .adm-shell--app .commande-cordeur__quand b {
    display: block;
    font-size: var(--taille-texte-1);
    color: var(--texte);
  }
  .adm-shell--app .commande-cordeur__actions {
    display: flex;
    gap: 8px;
    align-items: center;
  }
  /* Pastille à sens (voir docs/design-system.md → Pastilles) : même famille
     que .accueil-cordeur .file ci-dessus, encre pour « à prendre en charge »
     (la seule décision qui reste au cordeur), ambre pour « prête » (le
     joueur est attendu), menthe pour « en cours » (rien à décider). */
  .adm-shell--app .commandes-cordeur .pastille {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 11px;
    border-radius: 999px;
    font-size: var(--taille-texte-3);
    font-weight: 600;
    white-space: nowrap;
    background: var(--accent-marque-voile);
    color: var(--accent-marque-encre);
  }
  .adm-shell--app .commandes-cordeur .pastille::before {
    content: '';
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--accent-marque-encre);
  }
  .adm-shell--app .commandes-cordeur .pastille--encre {
    background: var(--action);
    color: var(--sur-action);
  }
  .adm-shell--app .commandes-cordeur .pastille--encre::before {
    background: var(--accent-marque);
  }
  .adm-shell--app .commandes-cordeur .pastille--attente {
    background: var(--attention-fond);
    color: var(--attention-texte);
  }
  .adm-shell--app .commandes-cordeur .pastille--attente::before {
    background: var(--attention);
  }
  .adm-shell--app .commandes-cordeur .pastille--neutre {
    background: rgba(21, 32, 28, 0.06);
    color: var(--texte-secondaire);
  }
  .adm-shell--app .commandes-cordeur .pastille--neutre::before {
    background: var(--texte-secondaire);
  }

  /* Issue n°203 — bloc ajouté en fin de média à la fusion. */

  /* Mon atelier — nav de rubriques collante (issue n°203, retour recette du
     14/09 : « un menu sur le côté pour ne pas changer de page à chaque clic,
     sinon on perd la personne »). Colonne 240px collante à gauche (même
     valeur que .colonne.collant ci-dessus), panneau à droite — voir
     ecrans-cordeur.css pour le socle mobile (pilules défilantes). */
  .adm-shell--app .ecran-atelier .atelier {
    display: grid;
    grid-template-columns: var(--largeur-laterale) 1fr;
    gap: 32px;
    align-items: start;
  }
  .adm-shell--app .ecran-atelier .atelier-nav {
    position: sticky;
    top: 92px;
    flex-direction: column;
    align-items: stretch;
    overflow: visible;
    gap: 2px;
  }
  .adm-shell--app .ecran-atelier .atelier-nav-groupe {
    display: block;
    margin: 14px 10px 4px;
    font-size: var(--taille-texte-4);
    font-weight: var(--graisse-forte);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--texte-secondaire);
  }
  .adm-shell--app .ecran-atelier .atelier-nav-groupe:first-child {
    margin-top: 0;
  }
  .adm-shell--app .ecran-atelier .atelier-nav a {
    justify-content: space-between;
    border-radius: var(--rayon-etiquette);
    border-color: transparent;
    padding: 9px 12px;
  }
}

/* ---- lcs.css ---- */
/*
 * Coquille et composants LCS Shop, transposés à Stringerz (Claude, 02/09/2026,
 * à la demande de Vincent : « que ça colle au stock et au portail vending »).
 * Port fidèle de assets/lcs-admin.css et assets/lcs-vending.css du dépôt
 * vincentherman/lcs-shop, sur les jetons de tokens.css. Périmètre : admin et
 * poi uniquement (issue n°146 — cause racine des incohérences visuelles
 * côté joueur/cordeur : ce fichier faisait autorité sur tout le site). Les
 * règles de boutons/cartes qui redéfinissaient .bouton, .carte, etc. sont
 * scopées sous .adm-shell:not(.adm-shell--app), le sélecteur qui isole
 * l'admin/poi des rôles à barre du bas (joueur, cordeur) — voir
 * layout.tsx. .icon-btn et .dash-box restent globales : utilisées aussi par
 * les raccourcis de l'accueil joueur/cordeur. boutons.css fait autorité pour
 * .bouton/.carte côté joueur/cordeur. Aucune valeur en dur hors jetons (les
 * quelques gris LCS sont déclarés en variables locales).
 */

/* Les gris LCS sont des jetons de tokens.css (--lcs-*). */

/* ===== Page ===== */
/* Fond blanc réservé à l'admin/poi (tiroir classique) — :has() déjà utilisé
   plus bas dans ce fichier. Les rôles à barre du bas (joueur, cordeur,
   .adm-shell--app) gardent le fond crème --fond de base.css. Issue n°156 :
   `body:not(:has(.adm-shell--app))` attrapait aussi toute page SANS tiroir
   du tout (vitrine, aide, press-kit, connexion...), qui perdait le fond
   crème pour du blanc ; il faut viser les pages ayant réellement le tiroir
   admin/poi (.adm-shell sans le variant --app). */
body:has(.adm-shell:not(.adm-shell--app)) {
  background: var(--surface);
}
body {
  color: var(--texte);
  font-family: var(--police-texte);
}
.page,
.page--connectee {
  display: block;
  max-width: none;
  padding: 0;
}
.adm-shell {
  display: flex;
  align-items: stretch;
  width: 100%;
  min-height: 100vh;
}

/* ===== Menu latéral ===== */
.side {
  flex: 0 0 var(--lcs-largeur-menu);
  position: sticky;
  top: 0;
  align-self: flex-start;
  height: 100vh;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border-right: var(--epaisseur-trait) solid var(--trait);
  padding: 20px 16px;
  z-index: 120;
}
.sbrand {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2px 8px 16px;
  margin: 0 -4px 6px;
  border-bottom: var(--epaisseur-trait) solid var(--trait);
}
.sbrand img {
  height: 26px;
  width: auto;
}
.grp {
  font-size: var(--taille-texte-4);
  font-weight: var(--graisse-semibold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 19px 12px 7px;
}
.adm-shell:not(.adm-shell--app) .grp {
  color: var(--lcs-gris-groupe);
}
/* Joueur/cordeur (issue n°159) : `.side` reste visible à partir du palier
   bureau pour ces rôles (barre du bas remplacée par le tiroir mobile), mais
   n'avait jamais reçu que le style LCS Shop gris hérité de l'admin — même
   étiquette recolorée sur `--texte-secondaire`, déjà utilisé par
   `.nav-basse-onglet` (barre du bas mobile), pour parler le même langage. */
.adm-shell--app .grp {
  color: var(--texte-secondaire);
}
.stab {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  text-align: left;
  padding: 11px 12px;
  border: none;
  border-radius: 8px;
  font-family: inherit;
  font-size: var(--taille-texte-2);
  font-weight: var(--graisse-medium);
  background: none;
  cursor: pointer;
  margin: 0 0 2px;
  white-space: nowrap;
  text-decoration: none;
  position: relative;
}
.adm-shell:not(.adm-shell--app) .stab {
  color: var(--lcs-gris-menu);
}
.adm-shell--app .stab {
  color: var(--texte-secondaire);
}
.adm-shell:not(.adm-shell--app) .stab:hover {
  background: var(--lcs-survol);
  color: var(--texte);
}
.adm-shell--app .stab:hover {
  background: var(--action-douce);
  color: var(--texte);
}
.adm-shell:not(.adm-shell--app) .stab.active,
.adm-shell:not(.adm-shell--app) .stab[aria-current='page'] {
  background: var(--lcs-actif);
  color: var(--texte);
  font-weight: var(--graisse-semibold);
}
.adm-shell--app .stab.active,
.adm-shell--app .stab[aria-current='page'] {
  background: var(--action-douce);
  color: var(--texte);
  font-weight: var(--graisse-semibold);
}
/* Icône active teintée de l'accent de marque (menthe encre) : le seul
   endroit du menu latéral bureau qui portait déjà cet accent était la barre
   du bas mobile (`.nav-basse-onglet--actif`, color: var(--action)) — ici on
   reprend l'accent plutôt que la simple encre pour rester distinct du
   tiroir admin. */
.adm-shell--app .stab.active .ic,
.adm-shell--app .stab[aria-current='page'] .ic {
  color: var(--accent-marque-encre);
}
.stab .ic {
  flex: 0 0 20px;
  width: 20px;
  height: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.stab .ic svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.stab .tabdot {
  margin-left: auto;
  min-width: 18px;
  height: 18px;
  padding: 0 6px;
  border-radius: var(--rayon-pastille);
  background: var(--action);
  color: var(--sur-action);
  font-size: var(--taille-texte-4);
  font-weight: var(--graisse-semibold);
  line-height: 18px;
  text-align: center;
}
.sfoot {
  margin-top: auto;
  border-top: var(--epaisseur-trait) solid var(--trait);
  padding: 14px 12px 4px;
  font-size: var(--taille-texte-3);
  color: var(--lcs-gris-groupe);
  line-height: 1.6;
}
.sfoot b {
  color: var(--texte);
  font-weight: var(--graisse-semibold);
}
.vbadge {
  display: inline-block;
  margin-top: 8px;
  font-family: var(--police-nombre);
  font-size: var(--taille-texte-4);
  color: var(--texte-secondaire);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: 6px;
  padding: 2px 7px;
  background: var(--lcs-tete-tableau);
}

/* ===== Zone principale et barre du haut ===== */
.adm-shell:not(.adm-shell--app) .mainzone {
  flex: 1;
  min-width: 0;
  padding: 0 32px 80px;
  background: var(--surface);
}
.mainzone {
  flex: 1;
  min-width: 0;
  padding: 0 32px 80px;
}
.topbar {
  display: flex;
  align-items: center;
  gap: 14px;
  position: sticky;
  top: 0;
  z-index: 40;
  background: var(--surface);
  border-bottom: var(--epaisseur-trait) solid var(--trait);
  padding: 11px 32px;
  margin: 0 -32px;
}
.topbar .contexte {
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
}
.topbar .who {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
  white-space: nowrap;
}
.topbar .who a {
  color: inherit;
  text-decoration: none;
}
/* Sélecteur « Voir comme… » (issue #99, admins seulement) : popover ancré
   sous le bouton, même vocabulaire visuel que .chat-panneau (chat.css). */
.voir-comme {
  position: relative;
}
.voir-comme-volet {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 60;
  display: none;
  flex-direction: column;
  gap: var(--espacement-petit);
  width: 280px;
  max-height: 360px;
  padding: var(--espacement-petit);
  background: var(--surface);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
  box-shadow: var(--ombre-carte);
  text-align: left;
  white-space: normal;
}
.voir-comme-volet:not([hidden]) {
  display: flex;
}
.voir-comme-recherche {
  width: 100%;
  padding: var(--espacement-champ-vertical) var(--espacement-champ-horizontal);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon-bouton-compact);
  font-size: var(--taille-texte-2);
  color: var(--texte);
  background: var(--surface);
}
.voir-comme-resultats {
  overflow-y: auto;
  max-height: 220px;
}
.voir-comme-liste {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.voir-comme-forme {
  margin: 0;
}
.voir-comme-resultat {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  padding: var(--espacement-champ-vertical) var(--espacement-champ-horizontal);
  border: none;
  border-radius: var(--rayon-bouton-compact);
  background: none;
  color: var(--texte);
  text-align: left;
  cursor: pointer;
}
.voir-comme-resultat:hover {
  background: var(--action-douce);
}
.voir-comme-nom {
  font-weight: var(--graisse-medium);
  font-size: var(--taille-texte-2);
  display: flex;
  align-items: center;
  gap: 6px;
}
/* Favori (issue n°197) : même ordre que /admin/personnifier, simple repère
   visuel ici — la bascule se fait depuis la liste complète. */
.voir-comme-etoile {
  width: 14px;
  height: 14px;
  flex: 0 0 auto;
  color: var(--accent-marque-encre);
}
.voir-comme-details {
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
}
.voir-comme-vide {
  margin: 0;
  padding: var(--espacement-champ-vertical) var(--espacement-champ-horizontal);
  font-size: var(--taille-texte-2);
  color: var(--texte-secondaire);
}
.topbar .avatar svg {
  width: 16px;
  height: 16px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
}
.topbar .avatar {
  width: 29px;
  height: 29px;
  border-radius: 50%;
  border: var(--epaisseur-trait) solid var(--trait);
  background: var(--surface);
  color: var(--texte);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: var(--graisse-semibold);
  font-size: var(--taille-texte-3);
}
.burger,
.bellbtn {
  position: relative;
  border: var(--epaisseur-trait) solid var(--trait);
  background: var(--surface);
  border-radius: 9px;
  width: 36px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: var(--texte-secondaire);
  padding: 0;
  text-decoration: none;
}
.burger:hover,
.bellbtn:hover {
  color: var(--texte);
  border-color: var(--texte);
}
.burger svg,
.bellbtn svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.burger {
  display: none;
}
.belldot {
  position: absolute;
  top: 6px;
  right: 7px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--erreur);
  border: 1.5px solid var(--surface);
}
.sideveil {
  position: fixed;
  inset: 0;
  background: rgba(21, 32, 28, 0.45);
  z-index: 110;
  display: none;
}
.sideveil.on {
  display: block;
}

/* ===== En-tête de page ===== */
/* .pagehead manquait le scope .adm-shell:not(.adm-shell--app) de ses deux
   voisins (issue n°215) : seul rôle à l'utiliser encore en .adm-shell--app
   (/accueil, « Salut {prénom} »), il gagnait quand même cette marge de
   28px/22px en plus du padding-top de .ecran, d'où un écart bien plus grand
   qu'ailleurs (/commander n'a que le padding de .ecran). Même correctif à
   20px/16px plus bas (mobile). */
.adm-shell:not(.adm-shell--app) .pagehead,
.adm-shell:not(.adm-shell--app) .entete-page,
.adm-shell:not(.adm-shell--app) .entete-page-titre {
  margin: 28px 0 22px;
}
.pagehead h1,
.adm-shell:not(.adm-shell--app) .entete-page h1,
.adm-shell:not(.adm-shell--app) .entete-page-titre h1,
.adm-shell:not(.adm-shell--app) .ecran > h1.titre-1 {
  margin: 0;
  font-family: 'Bricolage Grotesque', var(--police-texte);
  font-weight: 800;
  font-size: var(--taille-affichage-2);
  letter-spacing: -0.025em;
  line-height: 1.15;
  color: var(--texte);
}
.pagehead p {
  margin: 7px 0 0;
  font-size: var(--taille-texte-2);
  color: var(--texte-secondaire);
}
.pagehead p b {
  color: var(--texte);
}
.adm-shell:not(.adm-shell--app) .entete-page {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.adm-shell:not(.adm-shell--app) .entete-page .entete-page-titre {
  margin: 0;
}

/* ===== Écran : plus de colonne mobile, le mainzone porte la mise en page =====
   Scopé à l'admin/poi (issue n°152) : côté joueur/cordeur, .ecran est la
   colonne d'écran de base.css et masquait le logo/la navigation sur
   cordeur-commandes.tsx. */
.adm-shell:not(.adm-shell--app) .ecran {
  max-width: none;
  padding: 0;
  margin: 0;
  gap: 16px;
  min-height: 0;
}
.adm-shell:not(.adm-shell--app) .ecran > .logo,
.adm-shell:not(.adm-shell--app) .ecran > .barre-haute,
.adm-shell:not(.adm-shell--app) .barre-haute,
.adm-shell:not(.adm-shell--app) .navigation {
  display: none !important;
}

/* ===== Barre d'outils, segments ===== */
.barre-outils {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 4px;
}
.recherche input {
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: 9px;
  padding: 9px 10px;
  font-family: inherit;
  font-size: var(--taille-texte-2);
  background: var(--surface);
  color: var(--texte);
}
.recherche input:focus {
  outline: 2px solid var(--action);
  border-color: var(--action);
}
.adm-shell:not(.adm-shell--app) .filtres,
.onglets {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  border: 0;
}
.seg,
.adm-shell:not(.adm-shell--app) .filtre,
.onglet {
  border: var(--epaisseur-trait) solid var(--trait);
  background: var(--surface);
  color: var(--texte-secondaire);
  padding: 6px 12px;
  border-radius: var(--rayon-pastille);
  font-size: var(--taille-texte-3);
  font-weight: var(--graisse-semibold);
  cursor: pointer;
  font-family: inherit;
  transition: 0.15s;
  text-decoration: none;
  margin: 0;
}
.seg:hover,
.adm-shell:not(.adm-shell--app) .filtre:hover,
.onglet:hover {
  border-color: var(--texte);
  color: var(--texte);
}
.seg.active,
.adm-shell:not(.adm-shell--app) .filtre--actif,
.onglet--actif,
.onglet[aria-current='page'] {
  background: var(--texte);
  color: var(--surface);
  border-color: var(--texte);
}

/* ===== Catalogue de raquettes, mode Blocs (/admin/raquettes, issue n°242,
   points 4-5, retour Vincent du 23/09) ===== */

/* Pastille d'état en haut à droite de la carte, verte quand le modèle est
   actif (.etiquette-statut--ok, même composant que /admin/emails) — plus
   empilée avec l'action d'activation, qui garde sa place d'origine
   au-dessus du titre. */
.adm-shell:not(.adm-shell--app) .raquette-carte {
  position: relative;
}
.adm-shell:not(.adm-shell--app) .raquette-carte-statut {
  position: absolute;
  top: var(--espacement-petit);
  right: var(--espacement-petit);
}

/* Jusqu'à six colonnes tant que les cartes n'ont pas de photo (retour
   Vincent : « on peut faire six colonnes »), plus dense que la grille
   générique .liste-cartes (1/2/3 colonnes) — 900px et 600px dupliqués de
   tokens.css (--palier-bureau/--palier-tablette, valeur figée en dur dans
   toute media query, voir base.css) ; 1200px et 1600px sont propres à cette
   grille, aucun jeton --palier-* équivalent n'existe encore. */
.adm-shell:not(.adm-shell--app) .liste-cartes--raquettes-admin {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
}
@media (min-width: 600px) {
  .adm-shell:not(.adm-shell--app) .liste-cartes--raquettes-admin {
    grid-template-columns: repeat(3, 1fr);
  }
}
@media (min-width: 900px) {
  .adm-shell:not(.adm-shell--app) .liste-cartes--raquettes-admin {
    grid-template-columns: repeat(4, 1fr);
  }
}
@media (min-width: 1600px) {
  .adm-shell:not(.adm-shell--app) .liste-cartes--raquettes-admin {
    grid-template-columns: repeat(6, 1fr);
  }
}

/* ===== Cartes KPI =====
   Scopé à l'admin/poi (issue n°152) : .compteurs/.compteur portent la
   grille de compteurs joueur/cordeur (base.css, ecrans-cordeur.css),
   cassée par la grille auto-fit de lcs.css. */
.adm-shell:not(.adm-shell--app) .compteurs,
.adm-shell:not(.adm-shell--app) .compteurs--ligne,
.adm-shell:not(.adm-shell--app) .compteurs--deux {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(178px, 1fr));
  gap: 16px;
}
.dcard,
.adm-shell:not(.adm-shell--app) .compteur {
  background: var(--surface);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: 12px;
  /* Rembourrage de carte unifié (audit mobile du 24/09, issue n°251, point
     2) : ce bloc avait sa propre valeur en dur (18px 20px), différente du
     jeton --carte-padding-vertical/horizontal (tokens.css) déjà utilisé par
     .carte côté app (base.css, interface.css) — l'un des cinq rembourrages
     relevés par l'audit. Même jeton ici plutôt qu'une nouvelle variable :
     .compteur/.dcard sont des cartes comme les autres. */
  padding: var(--carte-padding-vertical) var(--carte-padding-horizontal);
  text-align: left;
  box-shadow: none;
}
.dcard.click {
  cursor: pointer;
}
.dcard.click:hover {
  border-color: var(--texte);
}
.dcard .dn,
.adm-shell:not(.adm-shell--app) .compteur-valeur {
  font-family: var(--police-texte);
  font-weight: var(--graisse-semibold);
  font-size: var(--taille-affichage-1);
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
  color: var(--texte);
  margin: 0;
}
.dcard .dl,
.adm-shell:not(.adm-shell--app) .compteur-libelle {
  font-size: var(--taille-texte-4);
  color: var(--texte-secondaire);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin: 5px 0 0;
  line-height: 1.35;
}
.dcard .dd {
  font-size: var(--taille-texte-4);
  font-weight: var(--graisse-semibold);
  margin-top: 8px;
}
.dcard .dd.up {
  color: var(--ok);
}
.dcard .dd.down {
  color: var(--erreur);
}
.dcard.ok .dn {
  color: var(--ok);
}
.dcard.err .dn {
  color: var(--erreur);
}
.dcard.warn .dn {
  color: var(--attention-texte);
}
.dcard.info .dn {
  color: var(--action);
}

/* ===== Boîtes =====
   .carte est scopée à l'admin/poi (issue n°146) : .dash-box reste globale,
   utilisée aussi par les raccourcis de l'accueil joueur/cordeur (voir
   accueil.tsx, cordeur.tsx). */
.dash-box,
.adm-shell:not(.adm-shell--app) .carte {
  background: var(--surface);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: 12px;
  /* Rembourrage de carte unifié (audit mobile du 24/09, issue n°251, point
     2) : voir le même correctif sur .dcard/.compteur ci-dessus — jeton
     --carte-padding-vertical/horizontal (tokens.css) plutôt qu'une valeur en
     dur dupliquée (18px 20px). */
  padding: var(--carte-padding-vertical) var(--carte-padding-horizontal);
  box-shadow: none;
}
.dash-box .dt,
.adm-shell:not(.adm-shell--app) .carte > h2.titre-3:first-child,
.adm-shell:not(.adm-shell--app) .carte > h2.titre-2:first-child,
.dash-box > h2:first-child {
  font-size: var(--taille-texte-3);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--texte-secondaire);
  margin: 0 0 8px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  font-family: var(--police-texte);
}
/* Chevauchement « À faire 2 » / « Toutes les commandes » sur l'accueil
   cordeur mobile (capture Vincent 12/09, issue n°189) : le bouton (nowrap,
   flex: 0 0 auto — voir .dash-box .dt .icon-btn) ne rétrécit jamais, et sans
   retour à la ligne le libellé de gauche finissait dessous. Scopé à
   .dash-box .dt seul pour ne pas changer les 3 autres sélecteurs partagés
   ci-dessus (titres de carte, jamais suivis d'un bouton). */
.dash-box .dt {
  flex-wrap: wrap;
  row-gap: 6px;
}
.dash-line {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  padding: 7px 0;
  border-bottom: var(--epaisseur-trait) solid var(--fond);
  font-size: var(--taille-texte-2);
  align-items: baseline;
}
.dash-line:last-child {
  border-bottom: none;
}
.adm-shell:not(.adm-shell--app) .etiquette-statut {
  font-size: var(--taille-texte-4);
  font-weight: 700;
  padding: 2px 8px;
  border-radius: var(--rayon-pastille);
  background: var(--fond);
  color: var(--texte-secondaire);
  white-space: nowrap;
  border: 0;
  text-transform: none;
  letter-spacing: 0;
}
.adm-shell:not(.adm-shell--app) .etiquette-statut--erreur {
  background: var(--lcs-erreur-fond);
  color: var(--erreur);
}
.adm-shell:not(.adm-shell--app) .etiquette-statut--attention {
  background: var(--lcs-attention-fond);
  color: var(--lcs-attention-texte);
}
.adm-shell:not(.adm-shell--app) .etiquette-statut--ok {
  background: var(--lcs-ok-fond);
  color: var(--ok);
}
.hint,
.adm-shell:not(.adm-shell--app) .aide,
.adm-shell:not(.adm-shell--app) .texte-secondaire {
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
}
.adm-shell:not(.adm-shell--app) .encart--info {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--lcs-ok-fond);
  border: var(--epaisseur-trait) solid var(--lcs-ok-trait);
  color: var(--ok);
  border-radius: 12px;
  padding: 11px 16px;
  font-size: var(--taille-texte-2);
  font-weight: var(--graisse-semibold);
}
.adm-shell:not(.adm-shell--app) .encart--info .lien {
  color: var(--ok);
}

/* ===== Tableaux =====
   Scopé à l'admin/poi (issue n°152) : le tableau bureau de
   cordeur-commandes.tsx (.tableau, ecrans-cordeur.css) perdait sa mise en
   forme, redéfinie ici sans scope.
   overflow-x plutôt que hidden (retour Vincent du 17/09) : hidden
   coupait purement et simplement les colonnes de droite sur un tableau
   large en mobile — invisibles, impossibles à atteindre, pas seulement
   compressées. Le rayon de la carte reste visible grâce à l'arrondi posé
   sur .tableau lui-même (border-collapse empêche border-radius d'agir sur
   les cellules, mais pas sur le cadre du tableau). */
.adm-shell:not(.adm-shell--app) table,
.adm-shell:not(.adm-shell--app) .tableau {
  width: 100%;
  border-collapse: collapse;
  background: var(--surface);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: 12px;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.adm-shell:not(.adm-shell--app) th,
.adm-shell:not(.adm-shell--app) .tableau th {
  text-align: left;
  font-size: var(--taille-texte-4);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--texte-secondaire);
  padding: 10px 12px;
  border-bottom: var(--epaisseur-trait) solid var(--trait);
  background: var(--lcs-tete-tableau);
  font-weight: var(--graisse-semibold);
}
.adm-shell:not(.adm-shell--app) td,
.adm-shell:not(.adm-shell--app) .tableau td {
  padding: 10px 12px;
  border-bottom: var(--epaisseur-trait) solid var(--trait);
  font-size: var(--taille-texte-2);
  vertical-align: middle;
}
.adm-shell:not(.adm-shell--app) tr:last-child td {
  border-bottom: none;
}
.cell-name {
  font-weight: var(--graisse-semibold);
}
.adm-shell:not(.adm-shell--app) .nombre {
  font-family: var(--police-nombre);
  font-variant-numeric: tabular-nums;
}

/* ===== Boutons =====
   Scopé à l'admin/poi (issue n°146, cause racine des tailles/polices
   incohérentes côté joueur/cordeur) : .icon-btn reste global, utilisé aussi
   par les raccourcis de l'accueil joueur/cordeur (accueil.tsx, cordeur.tsx).
   boutons.css fait autorité pour .bouton côté joueur/cordeur. */
.adm-shell:not(.adm-shell--app) .btn,
.adm-shell:not(.adm-shell--app) .bouton {
  border: none;
  border-radius: 9px;
  padding: 9px 14px;
  font-family: inherit;
  font-size: var(--taille-texte-2);
  font-weight: var(--graisse-semibold);
  cursor: pointer;
  transition: 0.15s;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  width: auto;
  min-height: 0;
  line-height: 1.3;
}
/* Menthe partout, y compris la coquille admin (issue n°249, règle 2) :
   chargée après boutons.css dans la cascade (FEUILLES_APP,
   scripts/build-assets.mjs), cette redéfinition primait encore sur le
   passage en menthe si elle restait en --action ici — « Enregistrer »,
   « Ajouter un modèle » et consorts en admin seraient restés noirs alors
   que le reste de l'app était corrigé. */
.adm-shell:not(.adm-shell--app) .bouton--principal {
  background: var(--accent-marque);
  color: var(--texte);
  border: var(--epaisseur-trait) solid var(--accent-marque);
}
.adm-shell:not(.adm-shell--app) .bouton--principal:hover {
  background: var(--accent-marque-encre);
  border-color: var(--accent-marque-encre);
  color: var(--sur-action);
}
.adm-shell:not(.adm-shell--app) .bouton--secondaire,
.icon-btn,
.adm-shell:not(.adm-shell--app) .bouton--discret {
  background: var(--surface);
  color: var(--texte);
  border: var(--epaisseur-trait) solid var(--trait);
}
.adm-shell:not(.adm-shell--app) .bouton--secondaire:hover,
.icon-btn:hover,
.adm-shell:not(.adm-shell--app) .bouton--discret:hover {
  border-color: var(--texte);
}
/* Bouton discret désactivé (issue n°255, point 5) : pas de contour renforcé
   au survol, même règle que .bouton--discret:disabled (boutons.css), que la
   règle ci-dessus, plus spécifique, écrasait dans la coquille admin. */
.adm-shell:not(.adm-shell--app) .bouton--discret:disabled:hover {
  border-color: var(--trait);
}
.icon-btn,
.adm-shell:not(.adm-shell--app) .bouton--compact,
.adm-shell:not(.adm-shell--app) .bouton--discret {
  border-radius: 7px;
  padding: 5px 9px;
  font-size: var(--taille-texte-3);
}
.adm-shell:not(.adm-shell--app) .bouton--danger {
  background: var(--surface);
  color: var(--erreur);
  border: var(--epaisseur-trait) solid var(--trait);
}
.adm-shell:not(.adm-shell--app) .bouton--danger:hover {
  border-color: var(--erreur);
}
/* Largeur naturelle en admin (pas de bouton pleine largeur comme sur mobile
   joueur/cordeur) — la cible tactile de 44px, elle, reste celle de .bouton
   (retirée par erreur ici avant la passe boutons et actions, issue n°244 :
   « Enregistrer » ressortait systématiquement sous 36px dans l'audit). */
.adm-shell:not(.adm-shell--app) form > .bouton--principal:not(.bouton--compact) {
  width: auto;
}

/* ===== Formulaires =====
   Scopé à l'admin/poi (même cause racine que .bouton ci-dessus, issue n°146
   jamais étendue à ce bloc : .champ/.tuiles en dur écrasait la cible tactile
   minimale et le jeton --largeur-formulaire côté joueur/cordeur — issue n°156). */
.adm-shell:not(.adm-shell--app) .champ label {
  display: block;
  font-size: var(--taille-texte-3);
  font-weight: var(--graisse-semibold);
  margin-bottom: 4px;
}
.adm-shell:not(.adm-shell--app) .champ input,
.adm-shell:not(.adm-shell--app) .champ textarea,
.adm-shell:not(.adm-shell--app) .champ select {
  width: 100%;
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: 8px;
  padding: 9px 10px;
  font-family: inherit;
  font-size: var(--taille-texte-2);
  background: var(--surface);
  color: var(--texte);
  min-height: 0;
}
.adm-shell:not(.adm-shell--app) .champ input:focus,
.adm-shell:not(.adm-shell--app) .champ textarea:focus,
.adm-shell:not(.adm-shell--app) .champ select:focus {
  outline: 2px solid var(--action);
  border-color: var(--action);
}
.adm-shell:not(.adm-shell--app) .carte--formulaire,
.adm-shell:not(.adm-shell--app) .formulaire-reservation {
  max-width: 640px;
}
.adm-shell:not(.adm-shell--app) .tuiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(178px, 1fr));
  gap: 16px;
}
.adm-shell:not(.adm-shell--app) .tuile {
  background: var(--surface);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: 12px;
  padding: 16px 18px;
  box-shadow: none;
  min-height: 0;
}
.adm-shell:not(.adm-shell--app) .tuile:hover {
  border-color: var(--texte);
}
.adm-shell:not(.adm-shell--app) .tuile-valeur {
  font-family: var(--police-texte);
  font-weight: var(--graisse-semibold);
  font-size: var(--taille-affichage-1);
  color: var(--texte);
  font-variant-numeric: tabular-nums;
}
.accueil-geste {
  display: none;
}
.pied-de-page {
  display: none;
}

/* ===== Mobile : menu en tiroir, burger dans la barre du haut ===== */
@media (max-width: 899px) {
  .adm-shell {
    display: block;
  }
  .side {
    position: fixed;
    left: 0;
    top: 0;
    bottom: 0;
    height: auto;
    width: 250px;
    transform: translateX(-102%);
    /* Fermé, le tiroir n'était que décalé hors écran : ses liens restaient
       atteignables à la tabulation et lus par un lecteur d'écran, focus
       invisible hors de l'écran (audit accessibilité du 25/09, WCAG 2.4.3).
       `visibility` bascule après la fin du glissement, sans changer
       l'animation. */
    visibility: hidden;
    transition: transform 0.2s ease, visibility 0s linear 0.2s;
  }
  .side.open {
    transform: none;
    visibility: visible;
    transition: transform 0.2s ease;
  }
  .mainzone {
    padding: 0 16px 60px;
  }
  .topbar {
    padding: 10px 16px;
    margin: 0 -16px;
  }
  .topbar .contexte {
    display: none;
  }
  .topbar .who span.nm {
    display: none;
  }
  /* « Voir comme… » décalé sur mobile (retour Vincent du 23/09, issue
     n°247) : .voir-comme n'est pas le dernier élément de .who (cloche et
     avatar suivent), right: 0 posé sur .voir-comme (lcs.css plus haut)
     ancrait donc le volet sous ces deux icônes plutôt que sous son propre
     bouton — repoussé vers la gauche. Sous 900px, .voir-comme redevient un
     bloc normal et .who (déjà calé sur le bord droit de .topbar par
     margin-left: auto) sert d'ancre à la place : right: 0 tombe alors sur
     le bord droit de l'écran, quel que soit le nombre d'icônes après le
     déclencheur. */
  .topbar .who {
    position: relative;
  }
  .voir-comme {
    position: static;
  }
  .voir-comme-volet {
    max-width: calc(100vw - 32px);
  }
  .burger {
    display: inline-flex;
  }
  /* Joueur et cordeur (issue n°140, règle 1 de Vincent) : plus de tiroir sur
     mobile, la barre du bas (.nav-basse, navigation-basse.tsx) est la seule
     navigation. L'admin (et le rôle relais seul) gardent le tiroir. */
  .adm-shell--app .side,
  .adm-shell--app .sideveil,
  .adm-shell--app .burger {
    display: none;
  }
  /* BarreHaute (issue n°189) remplace .topbar sur mobile pour joueur/cordeur,
     comme EnteteSite le fait déjà sur bureau (bureau.css) — même logo posé en
     tête de .ecran, devenu redondant maintenant que la rubrique et le retour
     vivent dans la barre du haut. */
  .adm-shell--app .topbar,
  .adm-shell--app .ecran > .logo {
    display: none;
  }
  .adm-shell:not(.adm-shell--app) .pagehead,
  .adm-shell:not(.adm-shell--app) .entete-page,
  .adm-shell:not(.adm-shell--app) .entete-page-titre {
    margin: 20px 0 16px;
  }
  .pagehead h1,
  .adm-shell:not(.adm-shell--app) .entete-page h1,
  .adm-shell:not(.adm-shell--app) .entete-page-titre h1,
  .adm-shell:not(.adm-shell--app) .ecran > h1.titre-1 {
    font-size: var(--taille-titre-1);
  }
  .detail-mise-en-page {
    grid-template-columns: 1fr;
    display: grid;
    gap: 14px;
  }
  .adm-shell:not(.adm-shell--app) .compteurs,
  .adm-shell:not(.adm-shell--app) .compteurs--ligne,
  .adm-shell:not(.adm-shell--app) .compteurs--deux,
  .tuiles {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
  }
  .dcard,
  .adm-shell:not(.adm-shell--app) .compteur,
  .tuile {
    padding: 12px 14px;
  }
  .dcard .dn,
  .adm-shell:not(.adm-shell--app) .compteur-valeur,
  .tuile-valeur {
    font-size: var(--taille-titre-1);
  }
  /* Grille 2×2 encore resserrée pour les compteurs du tableau de bord admin
     (retour Vincent du 23/09, issue n°247) : chiffre + libellé, hauteur
     réduite par rapport à la passe du 23/09 (issue n°244) ci-dessus —
     .dcard/.tuile gardent leurs valeurs, propres à d'autres écrans. */
  .adm-shell:not(.adm-shell--app) .compteurs {
    gap: var(--espacement-petit);
  }
  .adm-shell:not(.adm-shell--app) .compteur {
    padding: 10px 12px;
  }
  .adm-shell:not(.adm-shell--app) .compteur-valeur {
    font-size: var(--taille-titre-2);
  }
  .seulement-bureau {
    display: none;
  }
}
@media (min-width: 900px) {
  .seulement-mobile {
    display: none;
  }
  .seulement-bureau {
    display: revert;
  }
}

/* ===== Tableau de bord ===== */
.dash-box .dt .btn,
.dash-box .dt .icon-btn {
  text-transform: none;
  letter-spacing: 0;
}
.dash-line b {
  font-weight: var(--graisse-semibold);
}
.dash-line .muted {
  margin-left: 6px;
  font-size: var(--taille-texte-3);
}
.adm-shell:not(.adm-shell--app) .lien {
  color: var(--texte);
}
@media (max-width: 899px) {
  .dash-box table {
    display: block;
    overflow-x: auto;
  }
}
a.dcard,
a.icon-btn,
a.btn,
.dash-line a.icon-btn {
  text-decoration: none;
  color: inherit;
}
a.dcard:hover {
  border-color: var(--texte);
}
.dash-box .dt .icon-btn {
  color: var(--texte);
  white-space: nowrap;
  flex: 0 0 auto;
}
/* Focus clavier visible (audit qualité, issue #60, WCAG 2.2 §1) : cette
   règle supprimait tout indicateur de focus sans le remplacer — corrigé par
   un contour au jeton --action, même traitement que les liens/onglets
   ci-dessus. */
a.dcard:focus-visible {
  outline: var(--epaisseur-focus) solid var(--action);
  outline-offset: var(--epaisseur-trait);
}

/* ===== Pages publiques (connexion, inscription, vitrine) : colonne centrée ===== */
.page:not(.adm-shell) > .ecran {
  max-width: 440px;
  margin: 48px auto;
  padding: 0 20px;
  gap: 18px;
}
.page:not(.adm-shell) > .ecran > .logo {
  display: block !important;
  height: 28px;
  width: auto;
  margin-bottom: 4px;
}
.page:not(.adm-shell) > .ecran.vitrine {
  max-width: 960px;
}
/* Pages de contenu public (politique de confidentialité, mentions légales…) :
   largeur de lecture confortable, entre la colonne des formulaires et la
   largeur bureau des pages vitrine — ni colonne mobile, ni pleine largeur
   (issue n°161). */
.page:not(.adm-shell) > .ecran.ecran--lecture {
  max-width: var(--largeur-lecture);
}
.page:not(.adm-shell) form > .bouton--principal {
  width: 100%;
  padding: 11px 14px;
}

/* ===== Atelier ===== */
.ecran > h1.titre-1 {
  margin: 28px 0 22px;
}
.liste-ouvertures {
  display: flex;
  flex-direction: column;
}
.page:not(.adm-shell) > .ecran > .logo--pleine {
  height: 72px;
  margin: 0 auto 8px;
}
/* Formulaire de filtres sur une ligne (admin) */
.filtre-ligne {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 10px;
  margin: 0 0 4px;
}
.filtre-ligne .champ {
  margin: 0;
  flex: 1 1 180px;
}
.filtre-ligne .recherche-liste {
  flex: 1 1 260px;
}
@media (min-width: 900px) {
  /* Pas de marge en bureau : les autres .champ de la ligne n'en ont pas non
     plus. En mobile, .recherche-liste garde sa propre marge négative pour
     rester collante bord à bord sous .topbar (voir plus bas). */
  .filtre-ligne .recherche-liste {
    margin: 0;
  }
}
.filtre-ligne .bouton,
.filtre-ligne .btn {
  flex: 0 0 auto;
}

/* Recherche de liste — composant commun (issue n°195, /qa/design) : champ de
   filtrage sur les pages admin qui listent beaucoup (paiements, commandes,
   joueurs, cordeurs, relais, journal, analytique, e-mails, articles,
   catalogue de raquettes). Mode « client » (filtrage immédiat des lignes
   déjà rendues, recherche-liste.js) ou « serveur » (soumission différée de
   300 ms sur les listes paginées) — même apparence dans les deux cas. */
.recherche-liste {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--grille-base) calc(var(--grille-base) * 3);
  position: relative;
  margin: 0 0 16px;
}
.recherche-liste-icone {
  position: absolute;
  left: 14px;
  width: 16px;
  height: 16px;
  color: var(--texte-secondaire);
  pointer-events: none;
}
.recherche-liste-champ {
  flex: 1 1 280px;
  min-height: var(--cible-tactile-min);
  padding: var(--espacement-champ-vertical) 14px var(--espacement-champ-vertical) 38px;
  font-family: var(--police-texte);
  font-size: var(--taille-texte-1);
  color: var(--texte);
  background: var(--surface);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon-bouton);
}
.recherche-liste-champ::placeholder {
  color: var(--texte-secondaire);
}
.recherche-liste-champ:focus {
  outline: var(--epaisseur-focus) solid var(--action);
  outline-offset: var(--epaisseur-trait);
}
.recherche-liste-compteur {
  margin: 0;
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
  white-space: nowrap;
}
/* Vide avant l'exécution de recherche-liste.js (mode client) : pas de
   paragraphe blanc pendant le chargement. */
.recherche-liste-compteur:empty {
  display: none;
}
.recherche-liste-cachee {
  display: none !important;
}
@media (max-width: 899px) {
  .recherche-liste {
    position: sticky;
    top: var(--hauteur-barre-admin);
    z-index: 39;
    background: var(--surface);
    margin: 0 -16px 16px;
    padding: 10px 16px;
  }
}

/* ===== Vitrine ===== */
.page:not(.adm-shell) > .ecran.vitrine {
  max-width: 1040px;
  margin: 24px auto 48px;
  gap: 40px;
}
.vitrine-entete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
.vitrine-entete .logo,
.page:not(.adm-shell) > .ecran.vitrine .logo {
  height: 30px;
  margin: 0;
}
.vitrine-entete-liens {
  display: flex;
  gap: 8px;
}
.vitrine-heros {
  display: grid;
  grid-template-columns: 1.3fr 1fr;
  gap: 32px;
  align-items: center;
}
.vitrine-heros h1 {
  font-family: 'Bricolage Grotesque', var(--police-texte);
  font-weight: 800;
  font-size: var(--taille-affichage-4);
  letter-spacing: -0.025em;
  line-height: 1.1;
  margin: 0 0 12px;
}
.vitrine-heros p {
  font-size: var(--taille-titre-3);
  color: var(--texte-secondaire);
  margin: 0 0 18px;
}
.vitrine-heros form {
  display: flex;
  gap: 10px;
  align-items: flex-end;
  max-width: 460px;
}
.vitrine-heros form .champ {
  flex: 1;
  margin: 0;
}
.vitrine-heros form .bouton--principal {
  width: auto;
  white-space: nowrap;
}
.vitrine-heros-visuel {
  display: flex;
  justify-content: center;
}
.vitrine .liste-cartes {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
}
.vitrine .carte h3,
.vitrine .carte .titre-3 {
  font-size: var(--taille-titre-3);
  margin: 8px 0 6px;
}
.vitrine-pied {
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
  border-top: var(--epaisseur-trait) solid var(--trait);
  padding-top: 16px;
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
}
@media (max-width: 899px) {
  .vitrine-heros {
    grid-template-columns: 1fr;
  }
  .vitrine-heros h1 {
    font-size: var(--taille-affichage-2);
  }
  .vitrine .liste-cartes {
    grid-template-columns: 1fr;
  }
  .vitrine-heros-visuel {
    order: -1;
  }
}
.vitrine-heros form label {
  display: block;
  text-align: left;
}
.vitrine .carte--mise-en-avant,
.vitrine .carte--mise-en-avant p {
  color: var(--texte);
}
.vitrine .carte--mise-en-avant p {
  color: var(--texte-secondaire);
}
.choix-groupe label {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 10px;
  font-size: var(--taille-texte-2);
}
.choix-groupe input[type='radio'],
.choix-groupe input[type='checkbox'] {
  width: 18px;
  height: 18px;
  margin: 0;
  accent-color: var(--texte);
}

/* ===== Créneaux (fiche cordeur) : pastilles qui ne débordent jamais ===== */
.creneaux-grille {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 8px;
}
.pastille-creneau--grande {
  width: 100%;
  min-height: 42px;
  padding: 0 14px;
  font-size: var(--taille-texte-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  justify-content: center;
  border: var(--epaisseur-trait) solid var(--trait);
  background: var(--surface);
  color: var(--texte);
  border-radius: 9px;
}
.pastille-creneau--grande:hover,
.pastille-creneau--grande:focus-visible {
  border-color: var(--texte);
  background: var(--surface);
  color: var(--texte);
}
/* .pastille-creneau--complet : définition canonique dans ecrans.css (doublon
   retiré ici, issue n°183 — mêmes valeurs de fond/texte, plus complète). */
@media (max-width: 599px) {
  .creneaux-grille {
    grid-template-columns: 1fr;
  }
}

/* ===== Créneaux : pastilles qui tiennent leur texte ===== */
.creneaux-grille {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 8px;
}
.pastille-creneau--grande {
  width: 100%;
  min-height: 44px;
  padding: 0 14px;
  justify-content: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: var(--taille-texte-2);
  box-sizing: border-box;
}
@media (max-width: 599px) {
  .creneaux-grille {
    grid-template-columns: 1fr;
  }
}

/* ===== Fiche cordeur (/commander/:id) ===== */
.fiche-cordeur {
  grid-template-columns: 48px 1fr auto;
  grid-template-areas:
    'avatar identite question'
    'infos infos infos'
    'details details details';
  column-gap: 12px;
  row-gap: 10px;
}
.fiche-cordeur .avatar--grand {
  width: 48px;
  height: 48px;
  font-size: var(--taille-titre-2);
  font-weight: var(--graisse-semibold);
}
.fiche-cordeur-identite .titre-2 {
  font-size: var(--taille-titre-2);
  font-weight: var(--graisse-semibold);
}
.fiche-cordeur-question {
  grid-area: question;
  align-self: start;
}
.fiche-cordeur-question form {
  margin: 0;
}
.fiche-cordeur-infos {
  font-size: var(--taille-texte-2);
}
.fiche-cordeur-infos .chip,
.fiche-cordeur-details .chip {
  font-size: var(--taille-texte-3);
  padding: 3px 9px;
  border-radius: 7px;
}
.fiche-cordeur-details > summary {
  font-size: var(--taille-texte-2);
  color: var(--texte-secondaire);
  text-decoration: none;
}
.fiche-cordeur-details .chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.etiquette-nouveau {
  font-size: var(--taille-texte-4);
  padding: 2px 7px;
}

/* ===== Toucher uniforme (demande de Vincent, 02/09) =====
   Pas de flash tactile Android ; le retour au toucher est un léger enfoncement.
   Le contour de focus reste réservé à la navigation clavier (:focus-visible). */
html {
  -webkit-tap-highlight-color: transparent;
}
a,
button,
.stab,
.dcard,
.tuile,
.filtre,
.seg,
.pastille-creneau,
.ligne-reservation-corps,
.carte-cordeur {
  -webkit-tap-highlight-color: transparent;
}
.adm-shell:not(.adm-shell--app) .btn:active,
.adm-shell:not(.adm-shell--app) .bouton:active,
.icon-btn:active,
.stab:active,
a.dcard:active,
.tuile:active,
.filtre:active,
.seg:active,
.pastille-creneau:active,
.burger:active,
.bellbtn:active,
.topbar .avatar:active {
  transform: scale(0.97);
  transition: transform 80ms ease;
}
.ligne-reservation-corps:active,
.carte-cordeur:active,
.dash-line a:active {
  background: var(--lcs-survol);
}
a:focus:not(:focus-visible),
button:focus:not(:focus-visible),
[tabindex]:focus:not(:focus-visible) {
  outline: none;
}
a:focus-visible,
button:focus-visible,
[tabindex]:focus-visible,
.stab:focus-visible,
.dcard:focus-visible {
  outline: 2px solid var(--action);
  outline-offset: 2px;
  border-radius: 9px;
}

/* ===== Chat (messagerie de commande et conversations) =====
   Demande de Vincent (02/09 soir) : plus de bulles noires, un vrai chat
   distinct du reste — fil sur fond papier, bulles claires, saisie en bas. */
.fil-messages {
  list-style: none;
  margin: 0;
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-height: 380px;
  overflow-y: auto;
  background: var(--fond);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: 12px 12px 0 0;
}
.adm-shell:not(.adm-shell--app) .bulle {
  max-width: 82%;
  align-self: flex-start;
  background: var(--surface);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: 14px 14px 14px 4px;
  padding: 8px 12px;
  box-shadow: none;
  color: var(--texte);
}
.adm-shell:not(.adm-shell--app) .bulle--moi {
  align-self: flex-end;
  background: var(--lcs-actif);
  border-color: var(--trait);
  border-radius: 14px 14px 4px 14px;
  color: var(--texte);
}
.adm-shell:not(.adm-shell--app) .bulle-texte {
  margin: 0;
  font-size: var(--taille-texte-2);
  line-height: 1.4;
  white-space: pre-wrap;
  word-break: break-word;
}
.adm-shell:not(.adm-shell--app) .bulle-meta {
  margin: 4px 0 0;
  font-size: var(--taille-texte-4);
  color: var(--texte-secondaire);
}
.adm-shell:not(.adm-shell--app) .bulle--moi .bulle-meta {
  text-align: right;
}
.adm-shell:not(.adm-shell--app) .actions-commande {
  margin: 0;
}
.actions-commande form:has(textarea[name='body']) {
  display: flex;
  align-items: flex-end;
  gap: 8px;
  padding: 10px 12px;
  background: var(--surface);
  border: var(--epaisseur-trait) solid var(--trait);
  border-top: 0;
  border-radius: 0 0 12px 12px;
}
.actions-commande form:has(textarea[name='body']) .champ {
  flex: 1;
  margin: 0;
}
.actions-commande form:has(textarea[name='body']) .champ textarea {
  min-height: 40px;
  max-height: 120px;
  resize: none;
  border-radius: 20px;
  padding: 10px 14px;
  font-size: var(--taille-texte-2);
}
.actions-commande form:has(textarea[name='body']) .bouton {
  flex: 0 0 auto;
  height: var(--cible-tactile-min);
  border-radius: 20px;
  padding: 0 16px;
  background: var(--action);
  color: var(--sur-action);
  border-color: var(--action);
}
.actions-commande .encart--erreur {
  margin: 0 0 8px;
}
/* Fil vide : même cadre, texte discret. */
.section-repliable#messages > .texte-secondaire {
  padding: 16px;
  background: var(--fond);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: 12px;
}
/* Bloc « Messages » replié sur les fiches de commande : titre discret. */
.section-repliable#messages > summary {
  font-size: var(--taille-texte-2);
  font-weight: var(--graisse-semibold);
  margin: 0 0 8px;
}

/* ===== Touche de couleur (Vincent, 02/09 soir) =====
   Le vert du logo là où on agit ou on juge ; l'encre partout ailleurs. */
.pastille-creneau:hover,
.pastille-creneau:focus-visible,
.pastille-creneau:active {
  background: var(--accent-marque);
  color: var(--texte);
  border-color: var(--accent-marque);
}
.stab .tabdot,
.barre-haute-compteur,
.onglet-compteur,
.belldot {
  background: var(--accent-marque);
  color: var(--texte);
  border-color: var(--surface);
}
.belldot {
  width: 9px;
  height: 9px;
}
/* Pression : un ton plus foncé que le défaut menthe ci-dessus (avant l'issue
   n°249, le défaut était en encre et seule la pression passait en menthe —
   devenu incohérent maintenant que le défaut l'est déjà). */
.adm-shell:not(.adm-shell--app) .bouton--principal:active {
  background: var(--accent-marque-encre);
  color: var(--sur-action);
  border-color: var(--accent-marque-encre);
}
.adm-shell:not(.adm-shell--app) .etiquette-statut--ok {
  background: var(--accent-marque);
  color: var(--texte);
}
.dcard.ok .dn {
  color: var(--ok);
}
.adm-shell:not(.adm-shell--app) .avatar {
  background: var(--surface-haute);
  color: var(--accent-marque);
}
.geste-compteur {
  background: var(--accent-marque);
  color: var(--texte);
}

/* ===== Calage du haut de page (étapes + titre) ===== */
.etapes {
  margin: 18px 0 6px;
}
.etapes + h1.titre-1,
.etapes + .pagehead {
  margin-top: 14px;
}
.ecran > h1.titre-1 {
  margin: 20px 0 14px;
}
@media (max-width: 899px) {
  .etapes {
    margin: 14px 0 4px;
  }
  .ecran > h1.titre-1 {
    margin: 12px 0 10px;
  }
  .ecran > h1.titre-1 + .carte,
  .ecran > .pagehead + .carte {
    margin-top: 0;
  }
}

/* ===== Montants et dates insécables ===== */
.nombre,
.prix,
.recap-prix-ligne .nombre,
td .nombre {
  white-space: nowrap;
}
/* Titres de section nus (h2 hors carte) : même registre que les titres de boîte. */
.ecran > h2.titre-2,
.ecran > section > h2.titre-2 {
  font-size: var(--taille-texte-3);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--texte-secondaire);
  margin: 6px 0 8px;
}

/* ===== Certifications (fiche admin cordeur) : une ligne par certification ===== */
.liste-certifications {
  display: flex;
  flex-direction: column;
  gap: 0;
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: 12px;
  overflow: hidden;
  background: var(--surface);
}
.liste-certifications .carte {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas:
    'entete action'
    'detail action';
  column-gap: 12px;
  row-gap: 2px;
  align-items: center;
  border: 0;
  border-bottom: var(--epaisseur-trait) solid var(--trait);
  border-radius: 0;
  padding: 10px 14px;
}
.liste-certifications .carte:last-child {
  border-bottom: 0;
}
.liste-certifications .entete-page {
  grid-area: entete;
  margin: 0;
  justify-content: flex-start;
  gap: 8px;
}
.liste-certifications .chip {
  font-weight: var(--graisse-semibold);
}
.liste-certifications .texte-secondaire {
  grid-area: detail;
  margin: 0;
  font-size: var(--taille-texte-3);
}
.liste-certifications .texte-secondaire + .texte-secondaire {
  grid-area: detail;
  margin-top: 16px;
}
.liste-certifications form {
  grid-area: action;
  margin: 0;
}
.liste-certifications .carte > .texte-secondaire:first-of-type {
  align-self: start;
}

/* ===== Focus : jamais au toucher, épouse la forme au clavier ===== */
a:focus-visible,
button:focus-visible,
[tabindex]:focus-visible,
.stab:focus-visible,
.dcard:focus-visible,
.filtre:focus-visible,
.seg:focus-visible,
.onglet:focus-visible,
.pastille-creneau:focus-visible {
  outline: 2px solid var(--action);
  outline-offset: 2px;
  border-radius: inherit;
}
/* Écran tactile : pas d'anneau au toucher, mais toujours au clavier
   (tablette ou téléphone avec clavier, contacteur) — l'ancienne règle
   visait aussi :focus-visible et retirait tout indicateur de focus sous
   `pointer: coarse`, et même dans un navigateur sans souris détectée
   (WCAG 2.4.7, audit accessibilité du 25/09). Le toucher ne déclenche
   jamais :focus-visible sur un lien ou un bouton : seul le focus « souris »
   est neutralisé ici. */
@media (hover: none), (pointer: coarse) {
  a:focus:not(:focus-visible),
  button:focus:not(:focus-visible),
  [tabindex]:focus:not(:focus-visible),
  summary:focus:not(:focus-visible) {
    outline: none !important;
    box-shadow: none !important;
  }
}

/* ===== Chronologie : étapes faites en vert (coche), courante en anneau vert, à venir grises ===== */
.chronologie {
  list-style: none;
  margin: 0;
  padding: 0;
}
.chronologie-etape {
  display: grid;
  grid-template-columns: 24px 1fr;
  column-gap: 12px;
  align-items: start;
  padding-bottom: 14px;
}
.chronologie-etape:last-child {
  padding-bottom: 0;
}
.chronologie-repere {
  position: relative;
  display: flex;
  justify-content: center;
  height: 100%;
}
.chronologie-pastille {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--vert-vif);
  border: 2px solid var(--vert-vif);
  position: relative;
  z-index: 1;
  flex: 0 0 auto;
}
.chronologie-pastille::after {
  content: '';
  position: absolute;
  left: 6px;
  top: 3px;
  width: 5px;
  height: 9px;
  border: solid var(--surface);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
.chronologie-pastille--courante {
  background: var(--surface);
  border-color: var(--vert-vif);
  box-shadow: 0 0 0 4px var(--lcs-ok-fond);
}
.chronologie-pastille--courante::after {
  border-color: var(--vert-vif);
}
.chronologie-pastille--future {
  background: var(--surface);
  border-color: var(--trait);
}
.chronologie-pastille--future::after {
  display: none;
}
.chronologie-trait {
  position: absolute;
  top: 22px;
  bottom: -14px;
  width: 2px;
  background: var(--vert-vif);
  opacity: 0.55;
}
.chronologie-etape--future .chronologie-trait,
.chronologie-pastille--courante + .chronologie-trait {
  background: var(--trait);
  opacity: 1;
}
.chronologie-contenu {
  padding-top: 1px;
}
.chronologie-libelle {
  margin: 0;
  font-size: var(--taille-texte-2);
  font-weight: var(--graisse-medium);
  color: var(--texte);
}
.chronologie-etape:has(.chronologie-pastille--courante) .chronologie-libelle {
  font-weight: var(--graisse-semibold);
}
.chronologie-etape--future .chronologie-libelle {
  color: var(--texte-secondaire);
  font-weight: var(--graisse-regular);
}
.chronologie-date,
.chronologie-motif {
  margin: 2px 0 0;
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
}
.suivi .titre-2,
.section-repliable#messages > summary {
  font-size: var(--taille-texte-1);
  font-weight: var(--graisse-semibold);
}

/* ===== Chronologie : du vert pour ce qui est fait, l'encre pour l'étape courante ===== */
.chronologie {
  list-style: none;
  margin: 0;
  padding: 0;
}
.chronologie-etape {
  display: grid;
  grid-template-columns: 22px 1fr;
  column-gap: 12px;
}
.chronologie-repere {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.chronologie-pastille {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--accent-marque);
  border: 2px solid var(--accent-marque);
  position: relative;
  flex: 0 0 auto;
}
/* Coche encre sur les étapes faites. */
.chronologie-pastille::after {
  content: '';
  position: absolute;
  left: 5px;
  top: 2px;
  width: 5px;
  height: 9px;
  border: solid var(--texte);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
.chronologie-pastille--courante {
  background: var(--surface);
  border-color: var(--texte);
  box-shadow: 0 0 0 4px var(--lcs-actif);
  animation: chronologie-pulse 1.8s ease-in-out infinite;
}
.chronologie-pastille--courante::after {
  display: none;
}
/* Halo qui pulse (issue n°140, « des moments ») : l'étape en cours est
   vivante, pas juste une couleur figée. */
@keyframes chronologie-pulse {
  0%,
  100% {
    box-shadow: 0 0 0 4px var(--lcs-actif);
  }
  50% {
    box-shadow: 0 0 0 7px var(--lcs-actif);
  }
}
@media (prefers-reduced-motion: reduce) {
  .chronologie-pastille--courante {
    animation: none;
  }
}
.chronologie-pastille--future {
  background: var(--surface);
  border-color: var(--trait);
}
.chronologie-pastille--future::after {
  display: none;
}
.chronologie-trait {
  flex: 1;
  width: 2px;
  min-height: 18px;
  background: var(--accent-marque);
  margin: 2px 0;
}
.chronologie-etape--future .chronologie-trait,
.chronologie-etape:has(.chronologie-pastille--courante) .chronologie-trait {
  background: var(--trait);
}
.chronologie-contenu {
  padding-bottom: 14px;
}
.chronologie-libelle {
  margin: 0;
  font-size: var(--taille-texte-2);
  font-weight: var(--graisse-semibold);
}
.chronologie-etape--future .chronologie-libelle {
  color: var(--texte-secondaire);
  font-weight: var(--graisse-medium);
}
.chronologie-date,
.chronologie-motif {
  margin: 2px 0 0;
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
}
/* Titre des sections repliables (Messages) au registre des boîtes. */
.section-repliable > summary.titre-2 {
  font-size: var(--taille-texte-3);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--texte-secondaire);
}
.chronologie-etape {
  align-items: start;
}
.chronologie-repere {
  padding-top: 0;
  height: 100%;
}
.chronologie-contenu {
  padding-top: 0;
  margin-top: -1px;
}

/* ===== Bouton à picto (texte visible sur bureau, picto seul sur mobile) ===== */
.bouton--icone {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.bouton--icone svg {
  width: 18px;
  height: 18px;
  flex: 0 0 auto;
}
@media (max-width: 899px) {
  .bouton--icone .bouton-icone-libelle {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
  }
  .bouton--icone {
    width: 40px;
    height: 40px;
    padding: 0;
    justify-content: center;
    border-radius: 50%;
  }
  .bouton--icone svg {
    width: 20px;
    height: 20px;
  }
}

/* ===== Dessin de raquette (cartes Mes raquettes) =====
   Vestige de l'ancien dessin à la main, retiré au profit des icônes
   Hugeicons le 07/09 (voir raquette-visuel.tsx) — n'est utilisé par aucune
   page admin. Scopé pour ne plus écraser le visuel joueur (ecrans.css). */
.adm-shell:not(.adm-shell--app) .carte-raquette-visuel {
  aspect-ratio: 1 / 1;
  background: var(--fond);
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 12px;
}
.adm-shell:not(.adm-shell--app) .carte-raquette-dessin {
  height: 100%;
  width: auto;
  max-width: 100%;
}

/* ===== L'attribut hidden l'emporte toujours (les display forcés ci-dessus ne
   doivent jamais réafficher un élément masqué par un script). ===== */
[hidden] {
  display: none !important;
}
/* Tuiles de choix (assistant) : la case radio est invisible, la tuile porte l'état. */
.assistant-tuile input[type='radio'],
.choix-carte input[type='radio'] {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  margin: 0;
  pointer-events: none;
}
.assistant-tuile {
  position: relative;
}
.assistant-progression .etape--fait,
.assistant-progression .etape--actif {
  background: var(--accent-marque);
  border-color: var(--texte);
}

/* ===== Assistant : compteurs de tension ===== */
.assistant-compteur {
  display: flex !important;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin: 6px 0 10px;
}
.assistant-compteur-bouton {
  display: inline-flex !important;
  align-items: center;
  justify-content: center;
  width: 44px !important;
  height: 44px;
  border-radius: 50%;
  border: var(--epaisseur-trait) solid var(--trait);
  background: var(--surface);
  color: var(--texte);
  font-size: var(--taille-titre-1);
  line-height: 1;
  padding: 0;
  cursor: pointer;
}
.assistant-compteur-bouton:hover {
  border-color: var(--texte);
}
.assistant-compteur-bouton:active {
  background: var(--accent-marque);
  border-color: var(--accent-marque);
}
.champ input.assistant-compteur-valeur {
  width: 110px !important;
  flex: 0 0 110px;
  text-align: center;
  font-size: var(--taille-affichage-3);
  font-family: var(--police-nombre);
  border: 0;
  border-bottom: 2px solid var(--trait);
  border-radius: 0;
  padding: 4px 0;
  background: transparent;
}
.champ input.assistant-compteur-valeur:focus {
  outline: none;
  border-bottom-color: var(--texte);
}
.assistant-suivant,
.assistant-etape button.bouton--secondaire {
  margin-top: 8px;
}

/* ===== Carte plein écran ===== */
.marqueur-carte--cordeur {
  background: var(--texte);
}
.marqueur-carte--shop {
  background: var(--ok);
}
.marqueur-carte--club {
  background: var(--attention);
}
/* Club préremplie, pas encore réclamée (issue n°102). */
.marqueur-carte--club-prefilled {
  background: var(--lcs-gris-groupe);
}
/* Marqueur en surbrillance (issue #95, façon Booking) : survol d'une carte
   cordeur de la liste — sélecteur combiné pour l'emporter sur les couleurs
   par nature ci-dessus quel que soit l'ordre des classes posées par carte.js. */
.marqueur-carte.marqueur-carte--actif {
  width: var(--taille-marqueur-carte-actif);
  height: var(--taille-marqueur-carte-actif);
  background: var(--accent-marque);
  z-index: 30;
}
/* Le zoom Leaflet passe en bas à gauche (les onglets de filtre occupent le haut). */
.leaflet-top.leaflet-left {
  top: auto;
  bottom: 24px;
}
.leaflet-bottom.leaflet-right {
  right: 0;
}
/* Sur la carte, la bulle de chat cède la place aux boutons flottants. */
body:has(.cartographie) .chat-bulle {
  right: auto;
  left: 16px;
  bottom: 16px;
}
@media (max-width: 899px) {
  body:has(.cartographie) .leaflet-top.leaflet-left {
    bottom: 80px;
  }
}
/* Sur la carte, pas de chat : la discussion commence une fois le cordeur choisi. */
body:has(.cartographie) .chat-bulle,
body:has(.cartographie) .chat-panneau {
  display: none !important;
}

/* ===== Trouver un cordeur façon Booking (issue #95), bureau uniquement =====
   Plus de bascule Liste/Carte : la carte reste collante au-dessus de la
   liste, sous la barre du haut. Mobile inchangé (voir ecrans-commande.css,
   .recherche-cordeurs reste la même structure, juste réarrangée par le CSS). */
@media (min-width: 900px) {
  .recherche-cordeurs {
    display: flex;
    flex-direction: column;
    gap: var(--espacement-section);
  }
  .recherche-carte {
    position: sticky;
    top: var(--hauteur-barre-haute);
  }
  .recherche-carte .cartographie {
    height: var(--hauteur-carte-recherche);
    min-height: var(--hauteur-carte-recherche);
  }
}

/* ===== Pictos Phosphor (remplis) ===== */
.stab .ic svg,
.bellbtn svg,
.topbar .avatar svg,
.bouton--icone svg,
.assistant-tuile-picto,
.chat-bulle svg {
  fill: currentColor;
  stroke: none;
}
.assistant-tuile-picto {
  width: 36px;
  height: 36px;
  display: block;
  margin: 0 auto 6px;
}

/* ===== Repasse 03/09 : bandeau push, justificatifs, champs fichier ===== */
.adm-shell:not(.adm-shell--app) .encart--info {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.adm-shell:not(.adm-shell--app) .encart--info p {
  margin: 0;
}
.adm-shell:not(.adm-shell--app) .encart--info form {
  margin: 0;
}
button.lien {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--texte-secondaire);
  text-decoration: underline;
  cursor: pointer;
  border-radius: 0;
}
button.lien:hover {
  color: var(--texte);
}
input[type='file'] {
  width: 100%;
  font-size: var(--taille-texte-2);
  padding: 8px 10px;
  border: 1px dashed var(--trait);
  border-radius: 9px;
  background: var(--fond);
  color: var(--texte-secondaire);
}
input[type='file']::file-selector-button {
  font: inherit;
  font-weight: var(--graisse-semibold);
  color: var(--texte);
  background: var(--surface);
  border: 1px solid var(--trait);
  border-radius: 7px;
  padding: 5px 10px;
  margin-right: 10px;
  cursor: pointer;
}
.carte--commande .entete-page {
  margin: 0 0 8px;
  gap: 8px;
}
.carte--commande .champ {
  margin-top: 4px;
}

/* ===== Fiche cordeur façon Doctolib bureau : profil à gauche, agenda à droite ===== */
@media (min-width: 900px) {
  .fiche-doctolib {
    display: grid;
    grid-template-columns: 360px 1fr;
    gap: 20px;
    align-items: start;
  }
  .fiche-doctolib .detail-colonne-principale {
    display: contents;
  }
  .fiche-doctolib .fiche-cordeur {
    grid-column: 1;
    grid-row: 1;
    position: sticky;
    top: 70px;
  }
  .fiche-doctolib .detail-colonne-laterale {
    grid-column: 1;
    grid-row: 2;
  }
  .fiche-doctolib .agenda-depot,
  .fiche-doctolib .agenda,
  .fiche-doctolib .encart--info {
    grid-column: 2;
  }
  .fiche-doctolib .agenda-depot {
    grid-row: 1;
    align-self: start;
  }
  .fiche-doctolib .agenda {
    grid-row: 2 / span 2;
  }
}
.agenda-depot {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  background: var(--surface);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: 12px;
  padding: 12px 16px;
}
.agenda-depot-libelle {
  font-size: var(--taille-texte-3);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--texte-secondaire);
}
.agenda {
  background: var(--surface);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: 12px;
  padding: 16px 18px;
}
.agenda > h2.titre-2 {
  font-size: var(--taille-titre-3);
  font-weight: var(--graisse-semibold);
  text-transform: none;
  letter-spacing: 0;
  color: var(--texte);
  margin: 0 0 12px;
}
.pastille-creneau--agenda {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 40px;
  padding: 0 8px;
  font-size: var(--taille-texte-3);
  white-space: normal;
  text-align: center;
  line-height: 1.25;
}
.pastille-creneau--complet {
  opacity: 0.45;
}
@media (max-width: 899px) {
  .agenda {
    padding: 12px;
    margin: 0 -4px;
  }
}
.mainzone {
  overflow-x: hidden;
}
.fiche-doctolib,
.fiche-doctolib > *,
.agenda,
.agenda-depot {
  min-width: 0;
  max-width: 100%;
}
.agenda-depot .filtres {
  flex-wrap: wrap;
  min-width: 0;
}
.fiche-cordeur-infos {
  flex-wrap: wrap;
}

/* ===== Notation d'une pose : cinq pastilles d'étoiles =====
   Le vert du logo réservé au widget interactif (« on agit ») — issue n°156 :
   .note-etoiles en lecture seule (avis déjà notés) reste marine (base.css),
   la règle globale précédente écrasait aussi les avis affichés partout. */
.note-etoiles-choix {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 8px 0 14px;
}
.note-etoiles-choix .note-etoiles {
  color: var(--vert-vif);
  letter-spacing: 0.04em;
}
.note-etoiles-choix label {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 40px;
  padding: 0 12px;
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon-pastille);
  background: var(--surface);
  cursor: pointer;
  font-size: var(--taille-texte-1);
  letter-spacing: 0.06em;
  color: var(--vert-vif);
}
.note-etoiles-choix label:hover {
  border-color: var(--texte);
}
.note-etoiles-choix label:has(input:checked) {
  background: var(--accent-marque);
  border-color: var(--accent-marque);
  color: var(--texte);
}
.note-etoiles-choix input[type='radio'] {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  margin: 0;
  pointer-events: none;
}

/* ===== Commandes de la carte au-dessus des tuiles Leaflet (calques à 400, contrôles à 1000) ===== */
.carte-filtres,
.carte-boutons-flottants,
.volet {
  z-index: 1001;
}
.carte-filtres {
  position: absolute;
}

/* ===== Bulle de chat : picto Phosphor centré, jamais à la taille du bouton ===== */
.chat-bulle svg {
  width: 26px !important;
  height: 26px !important;
  flex: 0 0 26px;
  fill: var(--accent-marque);
  stroke: none;
}
.chat-bulle {
  overflow: hidden;
  padding: 0;
}

/* ===== Choix du cordage (commande) : un seul choix visible, champs conditionnels ===== */
.choix-carte {
  position: relative;
  cursor: pointer;
}
.choix-carte input[type='radio'] {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  margin: 0;
  pointer-events: none;
}
/* L'état « actif » ne dépend que de la case cochée, jamais de la classe rendue par le serveur. */
.choix-carte--actif:not(:has(input:checked)) {
  border-color: var(--trait);
  background: var(--surface);
  box-shadow: none;
}
.choix-carte:has(input:checked) {
  border-color: var(--texte);
  background: var(--lcs-actif);
  box-shadow: inset 0 0 0 1px var(--texte);
}
/* Champ du cordage apporté : visible seulement avec « Je fournis » ; sélection du catalogue : seulement avec « Du cordeur ». */
.bloc-formulaire:has(input[name='stringSource'][value='stringer']:checked) .champ:has(#stringName) {
  display: none;
}
.bloc-formulaire:has(input[name='stringSource'][value='player']:checked) .champ:has(#stringId) {
  display: none;
}

/* ===== Récap de commande sur mobile : rien ne déborde ===== */
.recap,
.recap-lignes,
.recap-ligne,
.recap-ligne > * {
  min-width: 0;
  max-width: 100%;
}
.recap {
  overflow: hidden;
}
.recap-ligne dd,
.recap-ligne span.nombre,
.recap .nombre {
  white-space: normal;
  overflow-wrap: anywhere;
}
@media (max-width: 899px) {
  .recap-ligne {
    grid-template-columns: 92px minmax(0, 1fr);
    column-gap: 12px;
  }
}

/* ===== Bureau : contenu borné, boutons à leur taille (03→07/09, retour Vincent) ===== */
@media (min-width: 900px) {
  .mainzone > .ecran,
  .mainzone > main.ecran,
  .mainzone .ecran {
    max-width: 1080px;
  }
  .ecran .bouton,
  .ecran button.bouton,
  .ecran .bouton--principal,
  .ecran .bouton--secondaire {
    width: auto;
    display: inline-flex;
  }
  .ecran .carte form .bouton {
    width: auto;
  }
  .ecran .encart {
    max-width: 100%;
  }
  /* Joueur/cordeur (issue n°159) : sur ce palier, `.entete-page` passe en
     ligne (base.css) et le bouton d'action se retrouve seul en haut à
     droite, sans lien visuel avec la grille de cartes en dessous — un trait
     de séparation ancre l'en-tête à la composition, comme le fait déjà
     `.adm-shell:not(.adm-shell--app) .entete-page` côté admin (même jeton
     `--trait`, jamais posé côté app jusqu'ici). Pas de marge en plus : `.ecran`
     (base.css) espace déjà ses enfants avec `gap`. */
  .adm-shell--app .entete-page {
    padding-bottom: 18px;
    border-bottom: var(--epaisseur-trait) solid var(--trait);
  }

  /* Scène de marque des pages point relais (issue n°200, remise-club) : un
     bandeau décoratif au-dessus de l'en-tête, seulement sur bureau. Classe
     neuve non scopée (comme .carte-cordeur-recommande, ecrans.css) : /relais
     et /relais/creer rendent sous .adm-shell:not(.adm-shell--app) (LCS),
     /relais/carte est publique et rend sous .page (pas de nav, voir
     layout.tsx) — pas de .photo/.photo--reelle de bureau.css ici (réservé à
     .adm-shell--app), un cadrage dédié et plus simple suffit dans les deux
     cas. */
  .relais-scene {
    position: relative;
    aspect-ratio: 21 / 6;
    border-radius: var(--rayon);
    overflow: hidden;
    margin-bottom: 20px;
  }
}

/* ===== Sous-menu Administration (barre latérale) ===== */
.ssub {
  display: flex;
  flex-direction: column;
  margin: 2px 0 8px 14px;
  padding-left: 14px;
  border-left: 1px solid var(--trait);
}
.ssub-lien {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  border-radius: 8px;
  font-size: var(--taille-texte-2);
  color: var(--texte-secondaire);
  text-decoration: none;
}
.ssub-lien:hover {
  background: var(--lcs-actif);
  color: var(--texte);
}
.ssub-lien.ssub-lien--actif {
  background: var(--lcs-actif);
  color: var(--texte);
  font-weight: var(--graisse-semibold);
}
.ssub-lien-ic {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
}
/* Deux groupes du sous-menu admin (issue n°115) : « Activité » et « Site et
   réglages », remplacent l'ancienne bordure posée sur le 11ᵉ lien
   (:nth-child, fragile au moindre réordonnancement). */
.ssub-groupe {
  padding: 4px 10px 2px;
  font-size: var(--taille-texte-4);
  font-weight: var(--graisse-semibold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--texte-secondaire);
}
.ssub-groupe--suite {
  margin-top: 6px;
  padding-top: 8px;
  border-top: 1px solid var(--trait);
}

/* ===== Filtres de la carte : pastille compacte avec pictos (retour Vincent, 07/09) ===== */
.carte-filtres {
  left: 12px;
  right: auto;
  top: 12px;
  width: auto;
  max-width: calc(100% - 24px);
  display: inline-flex;
  gap: 2px;
  padding: 4px;
  border-radius: 999px;
  background: var(--surface);
  box-shadow: 0 2px 10px rgba(21, 32, 28, 0.14);
}
.filtre-onglet {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 32px;
  padding: 0 12px;
  border-radius: 999px;
  font-size: var(--taille-texte-2);
}
.filtre-onglet-picto {
  width: 16px;
  height: 16px;
  fill: currentColor;
}
@media (max-width: 899px) {
  .filtre-onglet span {
    display: none;
  }
  .filtre-onglet {
    padding: 0 10px;
  }
  .filtre-onglet[data-filtre='tous'] {
    padding: 0 12px;
  }
}

/* ===== Modifier la raquette : deux colonnes sur bureau ===== */
.raquette-edition {
  display: grid;
  gap: 16px;
}
@media (min-width: 900px) {
  .raquette-edition {
    grid-template-columns: 1fr 1fr;
    align-items: start;
  }
}
.raquette-edition-colonne .titre-3 {
  margin-bottom: 12px;
}
.raquette-edition-apercu {
  display: flex;
  justify-content: center;
  padding: 8px 0 14px;
}
.raquette-edition-apercu .carte-raquette-dessin {
  width: 120px;
  height: 120px;
}
.raquette-edition-actions {
  margin-top: 8px;
  display: flex;
  align-items: center;
  gap: var(--espacement-champ-horizontal);
}
/* Sport en tuiles (mêmes pastilles que l'assistant) dans l'édition */
.raquette-edition #groupe-sport .choix-groupe {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.raquette-edition #groupe-sport .choix-groupe label {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 14px;
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon-pastille);
  cursor: pointer;
}
.raquette-edition #groupe-sport .choix-groupe label:has(input:checked) {
  background: var(--accent-marque);
  border-color: var(--accent-marque);
}
.raquette-edition #groupe-sport .choix-groupe input[type='radio'] {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  margin: 0;
  pointer-events: none;
}
.raquette-edition {
  max-width: 920px;
}
.raquette-edition #groupe-sport .choix-groupe {
  flex-direction: row;
}
.raquette-edition .champs-deux {
  gap: 16px;
}

/* ===== Résultats « Trouver un cordeur » : lignes pleine largeur façon Booking (bureau) ===== */
@media (min-width: 900px) {
  .resultats-recherche .liste-cartes,
  .resultats-recherche .liste-cartes--deux {
    display: grid;
    grid-template-columns: 1fr;
    gap: 12px;
  }
  .carte-cordeur {
    display: grid;
    grid-template-columns: 56px 240px minmax(0, 1fr) auto;
    grid-template-areas:
      'avatar identite creneaux infos'
      'avatar identite creneaux infos';
    column-gap: 18px;
    row-gap: 6px;
    align-items: center;
    padding: 16px 20px;
  }
  .carte-cordeur > .avatar {
    grid-area: avatar;
    width: 56px;
    height: 56px;
    font-size: var(--taille-titre-2);
  }
  .carte-cordeur-identite {
    grid-area: identite;
    min-width: 0;
  }
  .carte-cordeur-identite .titre-3 {
    margin: 0;
    font-size: var(--taille-titre-3);
  }
  .carte-cordeur-infos {
    grid-area: infos;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 6px;
    text-align: right;
  }
  .carte-cordeur-infos .prix {
    font-size: var(--taille-titre-2);
    font-weight: 700;
  }
  .carte-cordeur-creneaux {
    grid-area: creneaux;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    min-width: 0;
  }
  .carte-cordeur-tous {
    margin-left: 4px;
    white-space: nowrap;
  }
}
/* Sous-menu admin : plus d'air (07/09) */
.ssub {
  margin: 4px 0 10px 16px;
  padding-left: 12px;
  gap: 2px;
}
.ssub-lien {
  padding: 7px 10px;
  font-size: var(--taille-texte-2);
  letter-spacing: 0.01em;
}

/* Chiffres tabulaires partout où la police des nombres est utilisée (07/09) */
.nombre,
.pastille-creneau,
[class*='montant'],
[class*='prix'],
[class*='creneau'],
td.nombre,
.compteur-valeur {
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
}
.voir-site {
  height: 34px;
  padding: 0 12px;
  font-size: var(--taille-texte-2);
  margin-right: 8px;
}
@media (max-width: 899px) {
  .voir-site {
    display: none;
  }
}
/* Tiroir mobile : défile jusqu'en bas ; « Voir le site » seulement dans le tiroir */
.stab--mobile-seulement {
  display: none;
}
@media (max-width: 899px) {
  .side {
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding-bottom: 40px;
  }
  .stab--mobile-seulement {
    display: flex;
  }
}
/* Le volet de la carte n'a pas lieu d'être sur bureau (liste visible) */
@media (min-width: 900px) {
  .volet {
    display: none !important;
  }
}

.adm-shell:not(.adm-shell--app) .bouton--danger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: var(--cible-tactile-min);
  padding: 0 16px;
  border-radius: var(--rayon-pastille);
  border: var(--epaisseur-trait) solid var(--erreur);
  background: transparent;
  color: var(--erreur);
  font-weight: var(--graisse-semibold);
  text-decoration: none;
}
.adm-shell:not(.adm-shell--app) .bouton--danger:hover {
  background: var(--erreur);
  color: var(--surface);
}
/* Préférences de notification : une ligne par choix, case à droite */
#carte-push .section-repliable .champ label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--trait);
  font-weight: var(--graisse-medium);
}
#carte-push .section-repliable .champ:last-of-type label {
  border-bottom: 0;
}
#carte-push .section-repliable .champ label input[type='checkbox'] {
  order: 2;
  width: 22px;
  height: 22px;
  margin: 0;
  accent-color: var(--texte);
}
#carte-push .section-repliable summary {
  font-size: var(--taille-texte-1);
  padding: 10px 0 4px;
}
#carte-push .actions-ligne {
  margin-top: 10px;
}

/* ===== Mon compte → Mes lieux : formulaire aligné, libellé perso seulement si « Autre » ===== */
.carte-lieux .lieux-vide {
  margin: 10px 0 14px;
  font-size: var(--taille-texte-2);
}
.carte-lieux form .champs-deux {
  align-items: start;
}
.carte-lieux form .champs-deux label {
  min-height: 20px;
}
.carte-lieux form:has(#lieuLabelChoix option[value='autre']:not(:checked)) .champ:has(#lieuLabelPersonnalise),
.carte-lieux form:not(:has(#lieuLabelChoix option[value='autre']:checked)) .champ:has(#lieuLabelPersonnalise) {
  display: none;
}
.carte-lieux form:has(#lieuLabelChoix option[value='autre']:checked) .champs-deux {
  grid-template-columns: 1fr 1fr;
}
.carte-lieux form:not(:has(#lieuLabelChoix option[value='autre']:checked)) .champs-deux {
  grid-template-columns: 1fr;
}
@media (max-width: 899px) {
  .carte-lieux form .champs-deux {
    grid-template-columns: 1fr !important;
  }
}

/* ===== États vides : plus de large réservée quand c'est le seul contenu =====
   Décision du 07/09 (conversation Design), révisée le 23/09 (issue n°246,
   composant <EtatVide>) : le monogramme qui battait ici a été retiré (règle
   du composant — jamais d'illustration ni de mascotte hors Ball-E), mais un
   état vide qui est tout le contenu d'un écran admin/relais garde une
   colonne pleine largeur plutôt que la colonne de lecture étroite par
   défaut. */
.mainzone .ecran:has(> .etat-vide),
.mainzone .ecran:has(> section > .etat-vide) {
  max-width: none;
}
/* Tiroir mobile : hauteur bornée à l'écran, sinon le contenu s'étire et rien ne défile (07/09) */
@media (max-width: 899px) {
  .side {
    height: 100vh !important;
    height: 100dvh !important;
    max-height: 100dvh !important;
    overflow-y: auto !important;
    overscroll-behavior: contain;
  }
}

/* ---- cordeurs.css ---- */
/* Carte cordeur « place de marché » (issue n°124, 07/09) — /commander et
   partout où CarteCordeur est rendue. Chargée après lcs.css pour remplacer
   la grille Doctolib du 02/09. Même dessin que les fiches du héros de
   l'accueil ; aucune valeur en dur hors tokens.css. */

.carte.carte-cordeur {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0;
  padding: 0;
  overflow: hidden;
  transition: transform 120ms ease, box-shadow 120ms ease;
}
.carte.carte-cordeur:hover {
  transform: translateY(-1px);
  box-shadow: var(--ombre-carte);
}
.carte-cordeur-tete {
  width: 100%;
  display: grid;
  grid-template-columns: var(--taille-avatar-cordeur) minmax(0, 1fr) auto;
  gap: var(--espacement-champ-horizontal);
  align-items: center;
  padding: var(--carte-padding-vertical) var(--carte-padding-horizontal);
  color: inherit;
  text-decoration: none;
}
/* Neutralise les zones de l'ancienne grille (interface.css, lcs.css). */
.carte-cordeur-tete > *,
.carte-cordeur-tete .avatar {
  grid-area: auto;
}
.carte-cordeur-tete .avatar {
  width: var(--taille-avatar-cordeur);
  height: var(--taille-avatar-cordeur);
  font-size: var(--taille-titre-2);
  background: var(--surface-haute);
  color: var(--fond);
}
.carte-cordeur-corps {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}
.carte-cordeur-nom {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--espacement-petit);
}
.carte-cordeur-nom .titre-3 {
  margin: 0;
  line-height: 1.2;
}
.badge-verifie {
  font-size: var(--taille-texte-4);
  font-weight: var(--graisse-semibold);
  letter-spacing: var(--interlettrage-etiquette);
  text-transform: uppercase;
  padding: 3px 8px;
  border-radius: var(--rayon-pastille);
  background: var(--accent-marque-voile);
  color: var(--accent-marque-encre);
}
.carte-cordeur-reperes {
  font-size: var(--taille-texte-2);
  color: var(--texte-secondaire);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.carte-cordeur-prix {
  text-align: right;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
}
.carte-cordeur-prix-n {
  font-family: var(--police-nombre);
  font-weight: var(--graisse-semibold);
  font-size: var(--taille-titre-1);
  letter-spacing: var(--interlettrage-titre);
  line-height: 1;
}
.carte-cordeur-prix small {
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
}
.carte-cordeur-creneaux {
  width: 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--espacement-petit);
  padding: var(--espacement-petit) var(--carte-padding-horizontal) var(--carte-padding-vertical);
  border-top: var(--epaisseur-trait) solid var(--trait);
}
.carte-cordeur-tous {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-height: 34px;
  padding: 0 10px 0 12px;
  border-radius: var(--rayon-etiquette);
  border: var(--epaisseur-trait) solid var(--texte);
  color: var(--texte);
  font-size: var(--taille-texte-2);
  font-weight: var(--graisse-semibold);
  text-decoration: none;
  white-space: nowrap;
}
.carte-cordeur-tous:hover {
  background: var(--action-douce);
}
.carte-cordeur-tous-picto {
  width: 14px;
  height: 14px;
  fill: currentColor;
}
/* Mobile : nom et badges à côté de l'avatar, prix à droite, repères sur une
   ligne pleine largeur en dessous (retour Vincent, capture du 08/09 7 h 17). */
@media (max-width: 599px) {
  .carte-cordeur-tete {
    grid-template-columns: 44px minmax(0, 1fr) auto;
    grid-template-areas:
      'avatar nom prix'
      'reperes reperes reperes';
    row-gap: 6px;
  }
  .carte-cordeur-tete .avatar {
    grid-area: avatar;
    width: 44px;
    height: 44px;
    font-size: var(--taille-titre-3);
  }
  .carte-cordeur-corps {
    display: contents;
  }
  .carte-cordeur-nom {
    grid-area: nom;
  }
  .carte-cordeur-prix {
    grid-area: prix;
  }
  .carte-cordeur-reperes {
    grid-area: reperes;
    white-space: normal;
  }
}

/* Volet de la carte plein écran : même carte, sans bordure ni ombre (le volet
   les porte déjà). */
.volet-cordeur.carte {
  border: 0;
  box-shadow: none;
}
.volet-cordeur .carte-cordeur-tete {
  padding-top: var(--espacement-petit);
}

/* « Voir comme… » (admin) : picto seul sur mobile, picto + libellé en bureau. */
.voir-comme-picto {
  width: 18px;
  height: 18px;
  fill: currentColor;
}
@media (max-width: 899px) {
  .voir-comme-libelle {
    display: none;
  }
  #voir-comme-ouvrir {
    width: var(--cible-tactile-min);
    height: var(--cible-tactile-min);
    padding: 0;
    justify-content: center;
    border-radius: var(--rayon-pastille);
  }
}

/* Fiche cordeur (bureau) : sans ligne « dépôt de la raquette », l'agenda
   remonte au niveau de la carte d'identité au lieu de laisser un trou. */
@media (min-width: 900px) {
  .fiche-doctolib:not(:has(.agenda-depot)) .agenda {
    grid-row: 1 / span 3;
  }
}

/* ---- itineraire.css ---- */
/* Distance et durée en voiture (issue n°122) : ligne « Depuis chez toi : … »
   de la fiche cordeur, et sous-texte des pastilles de choix du dépôt. Aucune
   valeur en dur : uniquement les jetons de tokens.css (CLAUDE.md, règle 6).
   Chargée après cordeurs.css, comme le reste des feuilles d'écran. */

.itineraire-depuis-chez-toi {
  margin-top: 2px;
}

.filtre-itineraire {
  display: block;
  margin-top: 2px;
  font-size: var(--taille-texte-4);
  font-weight: var(--graisse-regular);
  opacity: 0.8;
}

/* ---- connexion.css ---- */
/* Page de connexion (issue n°118, 07/09) : carte centrée dans la fenêtre,
   monogramme dans son rond avec le battement de l'écran de lancement (trois
   fois, puis repos), pastille de langue. Chargée après lcs.css pour primer
   sur la colonne publique ; aucune valeur en dur hors tokens.css. */

.page:not(.adm-shell) > .ecran.ecran--carte {
  max-width: none;
  margin: 0;
  padding: var(--espacement-section) var(--marge-ecran);
  min-height: 100dvh;
  justify-content: center;
  align-items: center;
  gap: 0;
}
.carte-connexion {
  width: 100%;
  max-width: var(--largeur-carte-connexion);
  background: var(--surface);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: 20px;
  padding: 40px 32px 28px;
  box-shadow: var(--ombre-carte);
  display: flex;
  flex-direction: column;
  gap: var(--espacement-section);
  text-align: center;
}
.carte-connexion form {
  text-align: left;
  display: flex;
  flex-direction: column;
  gap: var(--espacement-petit);
}
.carte-connexion-tete {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.carte-connexion .titre-1 {
  margin: 0;
}
.carte-connexion .texte-secondaire {
  margin: 0;
}
.carte-connexion .encart[hidden] {
  display: none;
}

/* Rond du monogramme : même dessin que l'écran de lancement, halo divisé
   par deux pour rester dans la carte. */
.monogramme-rond {
  width: var(--taille-rond-connexion);
  height: var(--taille-rond-connexion);
  margin: 0 auto;
  border-radius: var(--rayon-pastille);
  background: var(--surface-haute);
  border: var(--epaisseur-focus) solid var(--accent-marque);
  display: flex;
  align-items: center;
  justify-content: center;
  animation: monogramme-battement 3s ease-in-out 3;
}
.monogramme-rond svg {
  width: 66%;
  height: 66%;
  overflow: visible;
}
.monogramme-rond-haut {
  fill: var(--sur-action);
}
.monogramme-rond-bas {
  fill: var(--accent-marque);
}
@keyframes monogramme-battement {
  0%,
  100% {
    box-shadow: 0 0 0 0 var(--halo-marque);
    transform: scale(1);
  }
  45% {
    box-shadow: 0 0 0 22px var(--halo-marque-fin);
    transform: scale(1.04);
  }
  60% {
    box-shadow: 0 0 0 0 var(--halo-marque);
    transform: scale(1);
  }
  75% {
    box-shadow: 0 0 0 14px var(--halo-marque-fin);
    transform: scale(1.02);
  }
}
@media (prefers-reduced-motion: reduce) {
  .monogramme-rond {
    animation: none;
  }
}

@media (max-width: 599px) {
  .carte-connexion {
    padding: 32px 20px 22px;
  }
}

/* ===================== Connexion / inscription — maquette Voissa (issue
   n°190, retour Vincent du 12/09/2026, valeurs reprises telles quelles de
   docs/maquettes/2026-09-12/connexion.html et inscription.html — décision
   015 règle 7, jamais de réinterprétation) =====================
   Remplace .carte-connexion ci-dessus sur /connexion et /inscription
   seulement : mot-de-passe-oublie.tsx, mot-de-passe-nouveau.tsx et
   inscription-role.tsx n'ont pas de maquette Voissa et gardent l'ancien
   gabarit. Scène courte puis formulaire sur mobile, deux colonnes à partir
   de 900px (palette-bureau, même seuil que la grammaire bureau joueur/
   cordeur — mais ce fichier n'est pas bureau.css : ces écrans sont publics,
   pas .adm-shell--app, et ont leur propre bascule responsive). La barre du
   haut visiteur (EnteteAcces/BarreHauteAcces) est un frère de .page, rendue
   par Layout via la prop `accesTitre` — voir layout.tsx. */
/* .page (base.css) a un padding-inline et un max-width centré (--marge-ecran,
   --largeur-colonne-tablette, --largeur-contenu-bureau, dès 600px/900px) —
   pensés pour un contenu centré, pas .acces qui doit toucher les bords comme
   dans la maquette. Layout pose cette classe sur .page quand `accesTitre`
   est fourni (layout.tsx) ; connexion.css chargé après base.css dans
   FEUILLES (layout.tsx) l'emporte à spécificité égale, à tous les paliers,
   sans dupliquer les @media de base.css. */
.page--pleine-largeur {
  max-width: none;
  margin-inline: 0;
  padding-inline: 0;
}
.acces {
  min-height: 0;
  display: flex;
  flex-direction: column;
}
.acces__scene {
  position: relative;
  background: var(--surface-haute);
  overflow: hidden;
  display: flex;
  align-items: flex-end;
  padding: 28px 20px;
  min-height: 200px;
  color: var(--sur-action);
}
.acces__scene::before {
  content: '';
  position: absolute;
  inset: -20%;
  background-image:
    repeating-linear-gradient(-14deg, rgba(61, 245, 176, 0.22) 0 1.5px, transparent 1.5px 18px),
    repeating-linear-gradient(76deg, rgba(61, 245, 176, 0.14) 0 1.5px, transparent 1.5px 18px);
}
.acces__scene::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(2, 17, 34, 0) 30%, rgba(2, 17, 34, 0.85));
}
.acces__scene > * {
  position: relative;
  z-index: 1;
}
/* Photo de marque (issue n°200, cordeur-garage) : le motif de cordage
   décoratif est réservé au placeholder ; le voile marine (::after) reste
   au-dessus de la photo pour la lisibilité du titre. .photo-scene
   (photo-scene.css) est display:contents — c'est l'<img> qu'il porte qu'il
   faut positionner, pas le <picture> lui-même, qui ne génère aucune boîte. */
.acces__scene--photo::before {
  content: none;
}
.acces__scene .photo-scene__image {
  position: absolute;
  inset: 0;
  z-index: 0;
}
.acces__scene-filigrane {
  position: absolute;
  right: -10%;
  top: -8%;
  width: 80%;
  z-index: 1;
  opacity: 0.14;
}
.acces__scene h2 {
  font-family: var(--police-titres-site);
  font-weight: 800;
  font-size: var(--taille-affichage-2);
  line-height: 1.05;
  letter-spacing: -0.03em;
  margin: 0 0 12px;
  max-width: 14ch;
}
.acces__scene p {
  margin: 0;
  color: var(--texte-sur-sombre);
  font-size: var(--taille-texte-1);
  max-width: 40ch;
}
.acces__form {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px 18px 40px;
}
.formulaire-acces {
  width: min(440px, 100%);
}
.formulaire-acces h1 {
  font-family: var(--police-titres-site);
  font-weight: 800;
  font-size: var(--taille-affichage-1);
  letter-spacing: -0.03em;
  margin: 0 0 6px;
}
.formulaire-acces > p {
  margin: 0 0 26px;
  color: var(--texte-secondaire);
  font-size: var(--taille-texte-1);
}

/* Champs gris sans bordure (issue n°190). Le mot de passe est encapsulé
   dans .champ-g-groupe (fond gris commun) pour poser le bouton-œil à
   l'intérieur du même bloc plutôt que d'empiler deux fonds. */
.champ-g {
  display: block;
  width: 100%;
  height: 48px;
  padding: 0 16px;
  border: 0;
  border-radius: 14px;
  background: var(--fond-champ-doux);
  font: inherit;
  font-size: var(--taille-titre-3);
  color: var(--texte);
  margin-bottom: 8px;
  box-sizing: border-box;
}
.champ-g::placeholder {
  color: var(--texte-secondaire);
}
.champ-g[readonly] {
  color: var(--texte-secondaire);
}
.champ-g-groupe {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  height: 56px;
  padding: 0 18px;
  border-radius: 14px;
  background: var(--fond-champ-doux);
  margin-bottom: 12px;
  box-sizing: border-box;
}
.champ-g-groupe .champ-g {
  flex: 1;
  min-width: 0;
  height: auto;
  padding: 0;
  margin-bottom: 0;
  background: transparent;
}
.champ-g-oeil {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--texte-secondaire);
  cursor: pointer;
}
.champ-g-oeil svg {
  width: 20px;
  height: 20px;
}

/* Jauge de mot de passe à cinq segments + critères en puces (issue n°190,
   décision 016 : ajoutée à /qa/design avant d'être utilisée ici). Purement
   informative — allumée par acces.js, la validation reste le seul
   MIN_PASSWORD_LENGTH du serveur (domain/auth/validation.ts). */
.jauge {
  display: flex;
  gap: 6px;
  margin: -4px 0 10px;
  padding: 0;
  list-style: none; /* la jauge est une liste : sans ceci, cinq puces « ••••• » */
}
.jauge li,
.jauge i {
  flex: 1;
  height: 4px;
  border-radius: 999px;
  background: var(--trait);
}
.jauge li.ok,
.jauge i.ok {
  background: var(--accent-marque-encre);
}
.criteres {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0 0 18px;
  padding: 0;
  list-style: none;
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
}
.criteres li {
  /* Retour Vincent 14/09 : mêmes gris que les champs, on croyait à des
     champs de saisie — texte nu, séparé par des points. */
  padding: 0;
  background: none;
}
.criteres li + li::before {
  content: '· ';
  color: var(--trait);
}
/* La jauge n'apparaît qu'une fois qu'on tape (acces.js pose .jauge--active) —
   vide, elle ressemblait à des pointillés sans sens (« j'ai … »). */
.jauge:not(.jauge--active) {
  display: none;
}
.criteres li.ok {
  color: var(--accent-marque-encre);
}
.criteres li.ok {
  font-weight: 600;
}
.criteres li.ok::after {
  content: ' ✓';
}

/* Pilule 56px : bouton Google et aplat principal du formulaire, distincts de
   .bouton (boutons.css, garde-fou tests/web/design-guard-bouton.test.ts —
   .pilule n'a pas besoin de .bouton, c'est un composant à part entière,
   documenté dans docs/design-system.md). */
.pilule {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  height: 56px;
  border-radius: 999px;
  font-weight: 700;
  font-size: var(--taille-titre-3);
  border: var(--epaisseur-trait) solid var(--trait);
  background: var(--surface);
  color: var(--texte);
  margin-bottom: 12px;
  text-decoration: none;
  cursor: pointer;
}
.pilule--principal {
  background: var(--action);
  color: var(--sur-action);
  border-color: var(--action);
}
.pilule--principal:hover,
.pilule--principal:focus-visible {
  background: var(--action-pressee);
}

.ou {
  display: flex;
  align-items: center;
  gap: 12px;
  color: var(--texte-secondaire);
  font-size: var(--taille-texte-2);
  margin: 8px 0 18px;
}
.ou::before,
.ou::after {
  content: '';
  flex: 1;
  height: var(--epaisseur-trait);
  background: var(--trait);
}

/* Case CGU + confidentialité (inscription seulement). */
.cgu {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-size: var(--taille-texte-2);
  color: var(--texte-secondaire);
  margin: 4px 0 18px;
  cursor: pointer;
}
.cgu .case {
  flex: none;
  width: 20px;
  height: 20px;
  border-radius: 6px;
  border: 1.5px solid var(--trait);
  margin-top: 1px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.cgu input:checked ~ .case {
  background: var(--action);
  border-color: var(--action);
}
.cgu input:checked ~ .case::after {
  content: '';
  width: 6px;
  height: 10px;
  border: solid var(--sur-action);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg) translate(-1px, -1px);
}
.cgu a {
  color: var(--texte);
  text-decoration: underline;
}

.formulaire-acces .bas {
  text-align: center;
  font-size: var(--taille-texte-2);
  color: var(--texte-secondaire);
  margin-top: 6px;
}
.formulaire-acces .bas a {
  color: var(--accent-marque-encre);
  font-weight: 600;
}
.formulaire-acces .bas--droite {
  text-align: right;
  margin: -2px 0 16px;
}
.formulaire-acces .bas--gauche {
  text-align: left;
  margin: -2px 0 16px;
}

/* Choix de rôle en cartes, avant les champs (inscription seulement) — trois
   rôles réels (joueur/cordeur/relais) contre deux sur la maquette
   (illustrative) : retirer le rôle relais aurait été une régression
   fonctionnelle, non demandée par l'issue n°190. */
.roles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 10px;
  margin-bottom: 16px;
}
.role {
  display: block;
  padding: 14px;
  border-radius: 14px;
  border: var(--epaisseur-trait) solid var(--trait);
  background: var(--surface);
  font-size: var(--taille-texte-2);
  cursor: pointer;
  text-align: left;
}
.role b {
  display: block;
  font-size: var(--taille-texte-1);
  margin-bottom: 2px;
}
.role small {
  color: var(--texte-secondaire);
}
.role:has(input:checked),
.role.choisi {
  border-color: var(--texte);
  box-shadow: inset 0 0 0 1px var(--texte);
}

/* Pastille de langue (interface.css, `align-self: center` suppose un parent
   flex) : .formulaire-acces est un bloc simple, ce conteneur la centre. */
.formulaire-acces-langue {
  display: flex;
  justify-content: center;
  margin: 16px 0;
}

@media (min-width: 900px) {
  .acces {
    min-height: calc(100dvh - 68px);
    flex-direction: row;
  }
  .acces__scene,
  .acces__form {
    flex: 1 1 50%;
    min-width: 0;
  }
  .acces__scene {
    padding: 48px;
    min-height: 0;
  }
  .acces__scene h2 {
    font-size: var(--taille-affichage-5);
  }
  .acces__form {
    padding: 48px 32px;
  }
  .formulaire-acces h1 {
    font-size: var(--taille-affichage-3);
  }
}

/* ---- splash.css ---- */
/*
 * Écran de lancement (issue #81). Sans classe .splash--joue ou .splash--court
 * (posée par le script), le voile est invisible : un navigateur sans JS ne
 * voit jamais l'écran, et une seconde page de la session non plus.
 */

.splash {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: none;
  align-items: center;
  justify-content: center;
  background: var(--surface-haute);
}
.splash--joue,
.splash--court {
  display: flex;
}
.splash-rond {
  width: 148px;
  height: 148px;
  border-radius: var(--rayon-pastille);
  background: var(--surface-haute);
  border: 2px solid var(--accent-marque);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 0 0 0 var(--halo-marque);
}
.splash {
  flex-direction: column;
  gap: 28px;
}
.splash {
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
.splash-rond {
  cursor: pointer;
  outline: none;
}
.splash-aide {
  margin: 0;
  color: var(--texte-sur-sombre);
  font-size: var(--taille-texte-2);
  opacity: 0;
  transition: opacity 0.6s ease;
}
.splash--attente .splash-aide {
  opacity: 1;
}
/* Une fois le tracé et le rebond joués (le script pose .splash--attente à 2 s),
   l'anneau bat à l'infini jusqu'à l'appui. Les deux classes coexistent, donc
   cette règle doit l'emporter sur `.splash--joue .splash-rond` (rebond +
   halo) déclarée plus bas : sélecteur à trois classes. Bug vu le 12/09 sur
   /qa/design (l'anneau restait figé après le rebond). */
.splash--joue.splash--attente .splash-rond,
.splash--court.splash--attente .splash-rond {
  animation: splash-pulsation 3s ease-in-out infinite;
}
/* @keyframes splash-pulsation vit dans base.css (chargée partout) : l'état
   vide l'utilise aussi, sur des pages sans écran de lancement. */
.splash-rond svg {
  width: 62%;
  height: 62%;
  overflow: visible;
}
.splash-trace {
  fill: transparent;
  stroke-width: 60;
  stroke-linejoin: round;
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
}
.splash-trace--blanc {
  stroke: var(--sur-action);
}
.splash-trace--vert {
  stroke: var(--accent-marque);
}

/* Séquence (1,8 s, allongée à la demande de Vincent) : tracé 0 → 0,9 s,
   remplissage 0,8 → 1,2 s, rebond et halo 1,15 → 1,6 s, tenue, fondu 0,4 s. */
.splash--joue .splash-trace {
  animation: splash-tracer 0.6s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
.splash--joue .splash-trace--vert {
  animation-delay: 0.12s;
}
.splash--joue .splash-trace--blanc {
  animation: splash-tracer 1.6s cubic-bezier(0.4, 0, 0.2, 1) forwards, splash-remplir-blanc 0.5s ease 1.4s forwards;
}
.splash--joue .splash-trace--vert {
  animation: splash-tracer 1.6s cubic-bezier(0.4, 0, 0.2, 1) 0.25s forwards, splash-remplir-vert 0.5s ease 1.6s forwards;
}
.splash--joue .splash-rond {
  animation: splash-rebond 0.45s cubic-bezier(0.34, 1.56, 0.64, 1) 1.9s both, splash-halo 0.8s ease-out 2s both;
}
.splash--court .splash-trace--blanc {
  fill: var(--sur-action);
  stroke: none;
}
.splash--court .splash-trace--vert {
  fill: var(--accent-marque);
  stroke: none;
}
.splash--fin {
  animation: splash-fondu 0.45s ease forwards;
  pointer-events: none;
}

@keyframes splash-tracer {
  to {
    stroke-dashoffset: 0;
  }
}
@keyframes splash-remplir-blanc {
  to {
    fill: var(--sur-action);
  }
}
@keyframes splash-remplir-vert {
  to {
    fill: var(--accent-marque);
  }
}
@keyframes splash-rebond {
  0% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.06);
  }
  100% {
    transform: scale(1);
  }
}
@keyframes splash-halo {
  0% {
    box-shadow: 0 0 0 0 var(--halo-marque);
  }
  100% {
    box-shadow: 0 0 0 36px var(--halo-marque-fin);
  }
}
@keyframes splash-fondu {
  to {
    opacity: 0;
  }
}
@media (prefers-reduced-motion: reduce) {
  .splash--joue .splash-trace,
  .splash--joue .splash-rond {
    animation: none;
  }
}

/* Pas de rectangle de focus ni de surlignage tactile sur l'écran de lancement
   (l'appui est un geste, pas un bouton de formulaire). */
.splash,
.splash * {
  -webkit-tap-highlight-color: transparent;
  -webkit-touch-callout: none;
  user-select: none;
}
.splash-rond:focus,
.splash-rond:focus-visible,
.splash:focus,
.splash:focus-visible {
  outline: none !important;
  box-shadow: none;
}
.splash--attente .splash-rond:focus,
.splash--attente .splash-rond:focus-visible {
  outline: none !important;
}

/* ---- chat.css ---- */
/*
 * Chat flottant (issue #83) : bulle en bas à droite et panneau de
 * conversations, superposés à l'écran sur toutes les pages connectées (voir
 * Layout, chat-bulle.tsx). Au-dessus du contenu (.side z-index 120), sous le
 * splash (z-index 300, splash.css) : z-index 200.
 *
 * Sans /chat.js, .chat-bulle est un lien ordinaire vers /messages et
 * .chat-panneau, `hidden`, ne s'affiche jamais (progressive).
 */

.chat-bulle {
  position: fixed;
  right: var(--marge-ecran);
  bottom: calc(var(--marge-ecran) + env(safe-area-inset-bottom));
  z-index: 200;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 52px; /* mobile — 56 px sur bureau, voir @media (min-width: 900px) plus bas */
  height: 52px;
  border-radius: var(--rayon-pastille);
  background: var(--surface-haute);
  color: var(--accent-marque);
  box-shadow: var(--ombre-carte);
  text-decoration: none;
}
.chat-bulle:active {
  transform: scale(0.96);
}
.chat-bulle-badge {
  position: absolute;
  top: -4px;
  right: -4px;
  min-width: calc(var(--grille-base) * 5);
  height: calc(var(--grille-base) * 5);
  padding: 0 var(--grille-base);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--rayon-pastille);
  background: var(--erreur);
  color: var(--blanc);
  font-family: var(--police-nombre);
  font-size: var(--taille-texte-4);
  font-weight: var(--graisse-semibold);
  border: 2px solid var(--surface);
}
.chat-bulle-badge[hidden] {
  display: none;
}

.chat-panneau {
  position: fixed;
  z-index: 200;
  right: var(--marge-ecran);
  bottom: calc(52px + var(--marge-ecran) * 2 + env(safe-area-inset-bottom));
  display: none;
  flex-direction: column;
  width: 380px;
  height: 560px;
  max-height: calc(100vh - 96px);
  background: var(--surface);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
  box-shadow: var(--ombre-carte);
  overflow: hidden;
}
.chat-panneau:not([hidden]) {
  display: flex;
}

.chat-panneau-entete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-shrink: 0;
  padding: var(--espacement-petit) var(--marge-ecran);
  border-bottom: var(--epaisseur-trait) solid var(--trait);
  background: var(--surface);
}
.chat-panneau-titre {
  margin: 0;
  font-size: var(--taille-titre-4);
  font-weight: var(--graisse-titre);
  color: var(--texte);
}
.chat-panneau-fermer {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: calc(var(--grille-base) * 9);
  height: calc(var(--grille-base) * 9);
  border: none;
  border-radius: var(--rayon-pastille);
  background: none;
  color: var(--texte);
  cursor: pointer;
}
.chat-panneau-fermer:hover {
  background: var(--action-douce);
}

.chat-panneau-corps {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: var(--espacement-petit) var(--marge-ecran) var(--marge-ecran);
}

/* Vue « Conversations » : listes et états vides déjà mis en forme par
   ecrans.css/base.css (.liste-cartes, .ligne-conversation, .etat-vide) — le
   fragment n'ajoute que le lien de repli vers la page complète. */
.chat-liste-voir-tout {
  display: block;
  margin-top: var(--espacement-petit);
  text-align: center;
  font-size: var(--taille-texte-2);
  color: var(--texte-secondaire);
}

/* Vue « Fil » : en-tête compacte + fil de bulles (.fil-messages, .bulle,
   .bulle--moi, vocabulaire 0.36.15) + saisie avec bouton rond, plus dense
   que .actions-commande (fil de commande, message-detail.tsx). */
.chat-fil {
  display: flex;
  flex-direction: column;
  gap: var(--espacement-petit);
  height: 100%;
}
.chat-fil-entete {
  display: flex;
  align-items: center;
  gap: var(--espacement-petit);
  flex-shrink: 0;
}
.chat-fil-retour {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: calc(var(--grille-base) * 8);
  height: calc(var(--grille-base) * 8);
  flex-shrink: 0;
  border-radius: var(--rayon-pastille);
  color: var(--texte);
}
.chat-fil-retour:hover {
  background: var(--action-douce);
}
.chat-fil-titre {
  margin: 0;
  font-size: var(--taille-titre-4);
  font-weight: var(--graisse-titre);
  color: var(--texte);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.chat-fil .fil-messages {
  flex: 1;
  min-height: 0;
  max-height: none;
  border-radius: var(--rayon);
}
.chat-fil-form {
  display: flex;
  align-items: flex-end;
  gap: var(--espacement-petit);
  flex-shrink: 0;
}
.chat-fil-form textarea {
  flex: 1;
  resize: none;
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon-bouton);
  padding: var(--espacement-champ-vertical) var(--espacement-champ-horizontal);
  font-family: var(--police-texte);
  font-size: var(--taille-texte-2);
  color: var(--texte);
  background: var(--surface);
}
.chat-fil-form textarea:focus {
  outline: var(--epaisseur-focus) solid var(--action);
  outline-offset: 1px;
  border-color: var(--action);
}
.chat-fil-envoyer {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: calc(var(--grille-base) * 10);
  height: calc(var(--grille-base) * 10);
  flex-shrink: 0;
  border: none;
  border-radius: var(--rayon-pastille);
  background: var(--action);
  color: var(--sur-action);
  cursor: pointer;
}
.chat-fil-envoyer:active {
  background: var(--action-pressee);
}
.chat-fil-fermee {
  margin: 0;
}

/* Mobile (< 900 px) : panneau plein écran avec en-tête et bouton fermer. */
@media (max-width: 899px) {
  .chat-panneau {
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    width: auto;
    height: auto;
    max-height: none;
    border-radius: 0;
    border: none;
  }
  /* Barre du bas visible (joueur, cordeur — issue n°140) : la bulle ne doit
     pas se superposer aux onglets. */
  body:has(.adm-shell--app) .chat-bulle {
    bottom: calc(var(--hauteur-nav-basse) + var(--marge-ecran) + env(safe-area-inset-bottom));
  }
}

/* Bureau (≥ 900 px) : bulle un peu plus grande, panneau ancré en bas à
   droite (380 × 560, voir .chat-panneau ci-dessus). */
@media (min-width: 900px) {
  .chat-bulle {
    width: 56px;
    height: 56px;
  }
}

/* ---- recette.css ---- */
/*
 * Bulle de recette (issue n°157) : bouton flottant et panneau de
 * signalement, même esprit visuel que .chat-bulle/.chat-panneau
 * (chat.css) — empilés juste au-dessus pour ne pas se superposer, même
 * z-index (200, sous le splash à 300).
 *
 * Masqué (.recette-bulle-conteneur[hidden]) tant que recette.js n'a pas
 * confirmé /api/recette-actif : aucune amélioration progressive possible
 * ici (voir recette-bulle.tsx), contrairement à .chat-bulle.
 */

.recette-bulle {
  position: fixed;
  right: var(--marge-ecran);
  bottom: calc(52px + var(--marge-ecran) * 2 + env(safe-area-inset-bottom));
  z-index: 200;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 52px; /* mobile — 56 px sur bureau, voir @media (min-width: 900px) plus bas */
  height: 52px;
  border: none;
  border-radius: var(--rayon-pastille);
  background: var(--surface-haute);
  color: var(--accent-marque);
  box-shadow: var(--ombre-carte);
  cursor: pointer;
}
.recette-bulle:active {
  transform: scale(0.96);
}

.recette-panneau {
  position: fixed;
  z-index: 200;
  right: var(--marge-ecran);
  bottom: calc(52px * 2 + var(--marge-ecran) * 3 + env(safe-area-inset-bottom));
  display: none;
  flex-direction: column;
  width: 400px;
  max-width: calc(100vw - var(--marge-ecran) * 2);
  max-height: calc(100vh - 96px);
  background: var(--surface);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
  box-shadow: var(--ombre-carte);
  overflow: hidden;
}
.recette-panneau:not([hidden]) {
  display: flex;
}

.recette-panneau-entete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-shrink: 0;
  padding: var(--espacement-petit) var(--marge-ecran);
  border-bottom: var(--epaisseur-trait) solid var(--trait);
  background: var(--surface);
}
.recette-panneau-titre {
  margin: 0;
  font-size: var(--taille-titre-4);
  font-weight: var(--graisse-titre);
  color: var(--texte);
}
.recette-panneau-fermer {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: calc(var(--grille-base) * 9);
  height: calc(var(--grille-base) * 9);
  border: none;
  border-radius: var(--rayon-pastille);
  background: none;
  color: var(--texte);
  cursor: pointer;
}
.recette-panneau-fermer:hover {
  background: var(--action-douce);
}

.recette-panneau-corps {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: var(--espacement-petit) var(--marge-ecran) var(--marge-ecran);
}
.recette-panneau-corps .champ {
  margin-bottom: var(--espacement-petit);
}
.recette-panneau-corps select,
.recette-panneau-corps textarea {
  width: 100%;
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon-bouton);
  padding: var(--espacement-champ-vertical) var(--espacement-champ-horizontal);
  font-family: var(--police-texte);
  font-size: var(--taille-texte-2);
  color: var(--texte);
  background: var(--surface);
}
.recette-panneau-corps textarea {
  resize: vertical;
}
.recette-panneau-corps select:focus,
.recette-panneau-corps textarea:focus {
  outline: var(--epaisseur-focus) solid var(--action);
  outline-offset: 1px;
  border-color: var(--action);
}
.recette-panneau-corps .bouton {
  width: 100%;
}

/* Historique des retours pas encore traités pour la page ouverte (issue du
   15/09) : distinct visuellement du formulaire, jamais confondu avec un
   retour déjà envoyé (encart--info). Liste compacte, pas de puce. */
.recette-historique {
  margin-bottom: var(--espacement-petit);
  padding: var(--espacement-petit);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon-bouton);
  background: var(--fond);
}
.recette-historique ul {
  list-style: none;
  margin: var(--espacement-petit) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--espacement-petit);
  max-height: 180px;
  overflow-y: auto;
}
.recette-historique li {
  padding-top: var(--espacement-petit);
  border-top: var(--epaisseur-trait) solid var(--trait);
}
.recette-historique li:first-child {
  padding-top: 0;
  border-top: none;
}

/* Entourer la zone (issue n°264) : calque plein écran posé par recette.js sur
   une capture de la page, où le testeur trace au doigt ou à la souris. Au-dessus
   de la bulle et de son panneau (200) et du splash (300). Le trait prend la
   couleur --erreur, lue par le script via getComputedStyle. */
.recette-dessin {
  position: fixed;
  inset: 0;
  z-index: 400;
  display: none;
  flex-direction: column;
  background: var(--voile-sombre);
}
.recette-dessin:not([hidden]) {
  display: flex;
}
.recette-dessin-barre {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--espacement-petit);
  flex-shrink: 0;
  padding: var(--espacement-petit) var(--marge-ecran);
  background: var(--surface);
}
.recette-dessin-consigne {
  flex: 1 1 100%;
  margin: 0;
}
.recette-dessin-zone {
  flex: 1;
  min-height: 0;
  overflow: auto;
  padding: var(--espacement-petit);
}
.recette-dessin-toile {
  display: block;
  width: 100%;
  height: auto;
  background: var(--surface);
  cursor: crosshair;
  touch-action: none; /* le doigt trace, il ne fait pas défiler la page */
}
.recette-dessin-apercu {
  margin-bottom: var(--espacement-petit);
}
.recette-dessin-apercu img {
  display: block;
  width: 100%;
  height: auto;
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon-etiquette);
}

/* /admin/recette : vignette de la capture à côté du texte du retour,
   cliquable pour l'agrandir (nouvel onglet, route admin de service). */
.recette-retour-vignette-lien {
  display: block;
  margin-top: var(--espacement-petit);
}
.recette-retour-vignette {
  display: block;
  width: 100%;
  max-width: 160px;
  height: auto;
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon-etiquette);
}

/* Barre du bas visible (joueur, cordeur — issue n°140) : la bulle ne doit
   pas se superposer aux onglets, comme .chat-bulle (chat.css). */
@media (max-width: 899px) {
  body:has(.adm-shell--app) .recette-bulle {
    bottom: calc(var(--hauteur-nav-basse) + 52px + var(--marge-ecran) * 2 + env(safe-area-inset-bottom));
  }
}

/* Bureau (≥ 900 px) : bulle un peu plus grande, comme .chat-bulle ;
   panneau un peu plus large aussi (retour Jonathan/Vincent du 15/09,
   historique + formulaire à l'étroit dans les 400px de base). */
@media (min-width: 900px) {
  .recette-bulle {
    width: 56px;
    height: 56px;
    bottom: calc(56px + var(--marge-ecran) * 2 + env(safe-area-inset-bottom));
  }
  .recette-panneau {
    width: 460px;
    bottom: calc(56px * 2 + var(--marge-ecran) * 3 + env(safe-area-inset-bottom));
  }
}

/* ---- ball-e.css ---- */
/*
 * Bulle Ball-E (issue n°214, remplace le panneau à formulaire unique de
 * l'issue n°210) : bouton flottant et tunnel à écrans de recommandation de
 * cordages, même esprit visuel que .recette-bulle/.recette-panneau
 * (recette.css) — classes préfixées ball-e- pour ne jamais les confondre.
 * Empilée juste au-dessus de la bulle de recette pour ne pas se superposer
 * (même z-index 200, sous le splash à 300 — voir le commentaire de
 * recette.css sur l'empilement avec .chat-bulle) : recette occupe le
 * niveau 1 (bulle) et 2 (panneau), Ball-E prend le niveau 2 (bulle) et 3
 * (panneau).
 *
 * Masquée (.ball-e-bulle-conteneur[hidden]) tant que ball-e.js n'a pas
 * confirmé /api/ball-e-actif : ni le réglage plateforme ni le rôle joueur
 * ne sont connus sans JavaScript, donc pas d'amélioration progressive
 * possible ici — même dégradation que .recette-bulle-conteneur.
 *
 * Mascotte (src/web/assets/ball-e/) : rendu 3D avec dégradés/ombres, exception
 * assumée au design system plat du reste de l'app (décision de Vincent, voir
 * docs/decisions/020-ball-e-tunnel-mascotte.md) — jamais aplatie, mais
 * toujours dimensionnée par jeton (--taille-mascotte-ball-e, tokens.css),
 * jamais au-delà de 180px (CHANGELOG 1.3.46).
 *
 * Plein écran mobile (retour Vincent, 17/09) : même principe que
 * .partage-viral-panneau (partage-viral.css, issue n°226) — le panneau
 * bascule en plein écran sous 900px, bureau gardant le panneau compact.
 *
 * Passe de fidélité visuelle du 18/09 (retour Vincent, capture des 4 écrans
 * de Stringerz_Mascotte_Integration à l'appui) : tout ce qui suit — bulle de
 * dialogue à pointe, barre de progression en pilules, cartes de choix 2×2 à
 * icône, bouton menthe — est propre à ce tunnel, construit à partir des
 * jetons existants (--accent-marque, --accent-marque-voile,
 * --accent-marque-encre, --rayon, --espacement-*), jamais une couleur
 * inventée. Le bouton menthe (.ball-e-bouton) n'étend pas .bouton--principal
 * — c'est un aplat scopé à ce fichier, qui ne change rien ailleurs dans
 * l'app — mais partage désormais ses couleurs : depuis l'issue n°249,
 * .bouton--principal est en menthe partout, Ball-E étant la référence dont
 * ce changement s'est inspiré (design-system.md → Actions).
 */

.ball-e-bulle {
  position: fixed;
  right: var(--marge-ecran);
  bottom: calc(52px * 2 + var(--marge-ecran) * 3 + env(safe-area-inset-bottom));
  z-index: 200;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 52px;
  height: 52px;
  border: none;
  border-radius: var(--rayon-pastille);
  background: var(--surface-haute);
  color: var(--accent-marque);
  box-shadow: var(--ombre-carte);
  cursor: pointer;
}
.ball-e-bulle:active {
  transform: scale(0.96);
}
.ball-e-bulle svg {
  width: 24px;
  height: 24px;
}

.ball-e-panneau {
  position: fixed;
  z-index: 200;
  right: var(--marge-ecran);
  bottom: calc(52px * 3 + var(--marge-ecran) * 4 + env(safe-area-inset-bottom));
  display: none;
  flex-direction: column;
  width: 400px;
  max-width: calc(100vw - var(--marge-ecran) * 2);
  max-height: calc(100vh - 96px);
  background: var(--surface);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
  box-shadow: var(--ombre-carte);
  overflow: hidden;
}
.ball-e-panneau:not([hidden]) {
  display: flex;
}

/* En-tête : logo Stringerz à gauche (comme sur les 4 écrans de la maquette),
   progression à droite, fermer tout à droite — jamais le logo texte de la
   maquette (erreur du designer confirmée par Vincent), le vrai <Logo />. */
.ball-e-panneau-entete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--espacement-petit);
  flex-shrink: 0;
  padding: var(--espacement-petit) var(--marge-ecran);
  border-bottom: var(--epaisseur-trait) solid var(--trait);
  background: var(--surface);
}
.ball-e-panneau-entete .logo {
  flex-shrink: 0;
}
.ball-e-panneau-fermer {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: calc(var(--grille-base) * 9);
  height: calc(var(--grille-base) * 9);
  border: none;
  border-radius: var(--rayon-pastille);
  background: none;
  color: var(--texte);
  cursor: pointer;
}
.ball-e-panneau-fermer:hover {
  background: var(--action-douce);
}

/* Barre de progression « X / N » + pilules (façon maquette : texte au-dessus
   de 4 barres arrondies, remplies en menthe jusqu'à l'étape courante) — même
   mécanique JS que l'ancienne version à points (.etape--fait/.etape--actif
   togglés par ball-e.js sur les enfants de #ball-e-progression-points),
   seul l'habillage change ici. */
.ball-e-progression {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 4px;
  min-width: 0;
}
.ball-e-progression-compteur {
  color: var(--texte-secondaire);
  font-family: var(--police-nombre);
  font-size: var(--taille-texte-4);
  white-space: nowrap;
}
.ball-e-progression-pilules {
  display: flex;
  gap: 4px;
  list-style: none;
  margin: 0;
  padding: 0;
}
.ball-e-progression-pilule {
  width: 22px;
  height: 4px;
  border-radius: var(--rayon-pastille);
  background: var(--trait);
}
.ball-e-progression-pilule.etape--fait,
.ball-e-progression-pilule.etape--actif {
  background: var(--accent-marque);
}

.ball-e-panneau-corps {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: var(--espacement-section) var(--marge-ecran) var(--marge-ecran);
}

/* Un écran à la fois (togglé par ball-e.js) : mascotte, titre, contenu de
   la question, bouton — toujours centrés. */
.ball-e-ecran {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--espacement-petit);
  text-align: center;
}
.ball-e-ecran[hidden] {
  display: none;
}
.ball-e-titre {
  margin: 0;
  /* Bricolage Grotesque est déjà la police de personnalité de la maison
     (vitrine, titres bureau joueur/cordeur, --police-titres-site dans
     tokens.css), jamais une police en dur ajoutée pour l'occasion
     (CLAUDE.md, règle 6). */
  font-family: var(--police-titres-site);
  font-size: var(--taille-titre-3);
  font-weight: var(--graisse-titre);
  color: var(--texte);
}

.ball-e-mascotte {
  width: var(--taille-mascotte-ball-e);
  height: var(--taille-mascotte-ball-e);
  object-fit: contain;
  flex-shrink: 0;
}

/* Mascotte + bulle de dialogue : reprend la disposition de la maquette
   (mascotte à gauche, bulle à droite, pointe vers la mascotte) — mais un
   composant propre à Ball-E plutôt qu'une extension de .bulle/.bulle-texte
   (base.css, pensé pour un fil de messages, pas pour une bulle « au repos »
   avec pointe) : fond --accent-marque-voile (le même jeton que
   .bulle--moi utilise pour son fond sombre côté opposé, et que
   .carte-suivi-condensee-icone utilise déjà pour ce même vert pâle — pas
   une couleur inventée), texte en --accent-marque-encre pour un contraste
   correct sur ce fond clair. */
.ball-e-mascotte-groupe {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  align-self: stretch;
}
.ball-e-bulle-dialogue {
  position: relative;
  max-width: 200px;
  background: var(--accent-marque-voile);
  border-radius: var(--rayon);
  padding: var(--espacement-champ-vertical) var(--espacement-champ-horizontal);
  text-align: left;
}
/* Pointe : triangle CSS pur, jamais une image — pointe vers la mascotte
   (à gauche de la bulle, la mascotte étant toujours à gauche du groupe). */
.ball-e-bulle-dialogue::before {
  content: '';
  position: absolute;
  left: -7px;
  top: 18px;
  width: 0;
  height: 0;
  border-top: 7px solid transparent;
  border-bottom: 7px solid transparent;
  border-right: 8px solid var(--accent-marque-voile);
}
.ball-e-bulle-dialogue-texte {
  margin: 0;
  color: var(--accent-marque-encre);
  font-weight: var(--graisse-semibold);
  font-size: var(--taille-texte-2);
  line-height: 1.35;
}

/* Bouton principal du tunnel : aplat menthe + flèche, façon maquette — voir
   le commentaire en tête de fichier sur pourquoi ce n'est pas
   .bouton--eclat. */
.ball-e-bouton {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--espacement-petit);
  width: 100%;
  min-height: var(--cible-tactile-min);
  padding: var(--bouton-padding-vertical) var(--marge-ecran);
  border-radius: var(--rayon-pastille);
  border: none;
  background: var(--accent-marque);
  color: var(--texte);
  font-family: var(--police-titres-site);
  font-weight: var(--graisse-titre);
  font-size: var(--taille-titre-4);
  cursor: pointer;
}
.ball-e-bouton:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.ball-e-bouton-fleche {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
}
/* « Nouvelle recherche » (écran résultat) : action secondaire, jamais un
   second aplat menthe à côté du bouton principal du panneau. */
.ball-e-bouton--discret {
  background: none;
  border: var(--epaisseur-trait) solid var(--trait);
  color: var(--texte);
}

/* Cartes de choix — 2×2 pour le niveau (pas d'icône : aucun picto pertinent
   pour « Débutant »/« Expert ») ; --trois pour les trois écrans à curseur
   franc de la v2 (issue n°233 : puissance/contrôle, effet,
   confort/durabilité), icône en médaillon menthe pâle pour chaque pôle
   (même motif que .carte-suivi-condensee-icone, ecrans.css : 40px, fond
   --accent-marque-voile, glyphe --accent-marque-encre). */
.ball-e-grille-choix {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--espacement-petit);
  align-self: stretch;
  margin: 4px 0;
}
.ball-e-grille-choix--trois {
  grid-template-columns: repeat(3, 1fr);
}
.ball-e-tuile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 72px;
  padding: var(--espacement-petit);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
  background: var(--surface);
  text-align: center;
  cursor: pointer;
}
.ball-e-tuile:has(input:checked) {
  border-color: var(--accent-marque-encre);
  background: var(--accent-marque-voile);
}
.ball-e-tuile input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}
.ball-e-tuile-icone {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: var(--rayon-pastille);
  background: var(--accent-marque-voile);
  color: var(--accent-marque-encre);
}
.ball-e-tuile-icone svg {
  width: 20px;
  height: 20px;
}
.ball-e-tuile-libelle {
  font-family: var(--police-texte);
  font-weight: var(--graisse-semibold);
  font-size: var(--taille-texte-2);
  color: var(--texte);
}
.ball-e-aide {
  margin: 0;
  color: var(--texte-secondaire);
  font-size: var(--taille-texte-3);
}

/* Trois points de chargement (écran « analyse »), façon maquette — CSS pur,
   jamais une image ni une dépendance d'animation externe. */
.ball-e-points-chargement {
  display: flex;
  gap: 6px;
}
.ball-e-points-chargement span {
  width: 8px;
  height: 8px;
  border-radius: var(--rayon-pastille);
  background: var(--accent-marque);
  animation: ball-e-point-pulsation 1.2s ease-in-out infinite;
}
.ball-e-points-chargement span:nth-child(2) {
  animation-delay: 0.15s;
}
.ball-e-points-chargement span:nth-child(3) {
  animation-delay: 0.3s;
}
@keyframes ball-e-point-pulsation {
  0%, 80%, 100% { opacity: 0.25; }
  40% { opacity: 1; }
}

.ball-e-resultats-liste {
  align-self: stretch;
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--espacement-petit);
  text-align: left;
}
.ball-e-resultats-liste li {
  padding: var(--espacement-champ-vertical) var(--espacement-champ-horizontal);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
  display: flex;
  flex-direction: column;
  gap: var(--espacement-petit);
}
.ball-e-resultats-liste li .ball-e-bouton {
  align-self: flex-start;
}

/* Bouton « C'est celui-là » (issue n°233) : quand plusieurs raquettes
   existent, un petit sélecteur inline apparaît le temps de choisir, avant
   attache — jamais de présélection automatique. */
.ball-e-attache-choix {
  display: flex;
  flex-wrap: wrap;
  gap: var(--espacement-petit);
  align-items: center;
}
.ball-e-attache-statut {
  margin: 0;
}

/* Animation (timings repris de animation_states.json, fourni par Vincent) :
   transform/opacity uniquement, aucune dépendance externe. Respiration en
   boucle sur l'écran de repos, léger mouvement d'avancée à chaque écran
   affiché, petit rebond à l'apparition du résultat. */
@keyframes ball-e-avancer {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: translateY(0); }
}
.ball-e-ecran:not([hidden]) {
  animation: ball-e-avancer 220ms ease-out;
}
@media (prefers-reduced-motion: reduce) {
  .ball-e-ecran:not([hidden]),
  .ball-e-points-chargement span {
    animation: none;
  }
}

/* Mobile (< 900 px) : bulle décalée au-dessus de la barre du bas (issue
 * n°140), et panneau en plein écran plutôt qu'en panneau flottant compact
 * (retour Vincent du 17/09) — même bascule que .partage-viral-panneau. */
@media (max-width: 899px) {
  body:has(.adm-shell--app) .ball-e-bulle {
    bottom: calc(var(--hauteur-nav-basse) + 52px * 2 + var(--marge-ecran) * 3 + env(safe-area-inset-bottom));
  }

  .ball-e-panneau {
    inset: 0;
    width: 100%;
    max-width: 100%;
    height: 100%;
    max-height: 100%;
    border: none;
    border-radius: 0;
    box-shadow: none;
  }
  .ball-e-panneau-entete {
    padding-top: calc(var(--espacement-petit) + env(safe-area-inset-top));
  }
  .ball-e-panneau-corps {
    padding-top: calc(var(--espacement-section) * 2);
    padding-bottom: calc(var(--marge-ecran) + env(safe-area-inset-bottom));
  }
  .ball-e-titre {
    font-size: var(--taille-titre-1);
  }
  .ball-e-mascotte {
    width: calc(var(--taille-mascotte-ball-e) * 1.15);
    height: calc(var(--taille-mascotte-ball-e) * 1.15);
  }
}

/* Bureau (≥ 900 px) : mêmes ajustements que .recette-bulle/.recette-panneau,
   panneau flottant compact conservé (assez de place pour lui). */
@media (min-width: 900px) {
  .ball-e-bulle {
    width: 56px;
    height: 56px;
    bottom: calc(56px * 2 + var(--marge-ecran) * 3 + env(safe-area-inset-bottom));
  }
  .ball-e-panneau {
    width: 460px;
    bottom: calc(56px * 3 + var(--marge-ecran) * 4 + env(safe-area-inset-bottom));
  }
}

/* ---------------------------------------------------------------------------
   Rapprochement de la maquette (créas de Vincent, 19/09) : fond de scène
   derrière la mascotte, mascotte plus grande sur mobile, bandeau d'atouts
   de l'intro, fiche « setup » de l'écran résultat, lien discret « Modifier
   mes réponses ». Aucune couleur hors tokens.css.
   --------------------------------------------------------------------------- */
.ball-e-mascotte-groupe {
  position: relative;
  padding: 32px 0 10px;
  isolation: isolate;
}
.ball-e-decor {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  overflow: hidden;
}
/* arc de court : un anneau menthe voile, très large, dont on ne voit qu'une
   courbe qui traverse la scène derrière le personnage */
.ball-e-decor-arc {
  position: absolute;
  left: -40%;
  right: -40%;
  top: 30%;
  height: 160%;
  border-radius: 50%;
  border: 3px solid var(--accent-marque-voile);
  opacity: 0.9;
}
/* halo au sol, comme l'ombre douce sous la mascotte de la maquette */
.ball-e-decor-sol {
  position: absolute;
  left: 14%;
  width: 44%;
  bottom: 6px;
  height: 22px;
  border-radius: 50%;
  background: var(--accent-marque-voile);
  filter: none;
  opacity: 0.8;
}
.ball-e-panneau-corps .ball-e-mascotte {
  width: 150px;
  height: 150px;
}
@media (min-width: 900px) {
  .ball-e-panneau-corps .ball-e-mascotte {
    width: var(--taille-mascotte-ball-e);
    height: var(--taille-mascotte-ball-e);
  }
}

/* Intro — trois arguments dans une carte, séparés par un filet */
.ball-e-atouts {
  list-style: none;
  margin: 0;
  padding: var(--espacement-champ-vertical) 0;
  align-self: stretch;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  background: var(--surface);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
}
.ball-e-atouts li {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 0 6px;
  text-align: center;
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
}
.ball-e-atouts li + li {
  border-left: var(--epaisseur-trait) solid var(--trait);
}
.ball-e-atouts strong {
  color: var(--texte);
  font-size: var(--taille-texte-2);
}
.ball-e-atout-icone,
.ball-e-setup-icone {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--accent-marque-voile);
  color: var(--accent-marque-encre);
  flex-shrink: 0;
}
.ball-e-atout-icone svg,
.ball-e-setup-icone svg {
  width: 20px;
  height: 20px;
}

/* Résultat — fiche setup : une ligne par donnée, libellé gris au-dessus de
   la valeur en gras, filets entre les lignes */
.ball-e-setup {
  align-self: stretch;
  margin: 0;
  padding: 4px var(--espacement-champ-horizontal);
  background: var(--surface);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
  text-align: left;
}
.ball-e-setup-ligne {
  display: flex;
  align-items: center;
  gap: var(--espacement-champ-horizontal);
  padding: 10px 0;
}
.ball-e-setup-ligne + .ball-e-setup-ligne {
  border-top: var(--epaisseur-trait) solid var(--trait);
}
.ball-e-setup dt {
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
}
.ball-e-setup dd {
  margin: 2px 0 0;
  font-weight: var(--graisse-semibold);
  color: var(--texte);
}
/* liste des trois cordages, dépliée par « Voir les cordages » */
.ball-e-resultats-liste li {
  background: var(--surface);
}
/* « Modifier mes réponses » : un lien, pas un bouton */
.ball-e-lien {
  background: none;
  border: none;
  padding: 6px 0;
  color: var(--accent-marque-encre);
  font: inherit;
  font-weight: var(--graisse-semibold);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

/* ---- vitrine.css ---- */
/* Vitrine publique — refonte du 07/09/2026 (démo), héros « place de marché »
   le soir même. Une seule feuille, aucune couleur ni police en dur hors
   jetons : marine, accent, halo et Bricolage viennent de tokens.css. Le signe
   de la page est le tamis (SVG) tendu sur le héros ; une seule corde à
   l'accent, animée une fois au chargement. Feuille consolidée : les
   surcharges empilées de la journée ont été fusionnées règle par règle. */

.vitrine2 {
  --v-marine: var(--surface-haute);
  --v-vert: var(--accent-marque);
  --v-vert-encre: var(--accent-marque-encre);
  --v-vert-voile: var(--accent-marque-voile);
  --v-halo: var(--halo-marque);
  --v-papier: var(--fond);
  --v-blanc: var(--surface);
  --v-encre: var(--texte);
  --v-doux: var(--texte-secondaire);
  --v-trait: var(--trait);
  --v-ombre: var(--ombre-vitrine);
  --v-ombre-forte: var(--ombre-vitrine-forte);
  --v-filet-clair: var(--filet-sur-sombre);
  --v-titres: var(--police-vitrine-titres);
  --v-large: 1180px;
  font-family: var(--police-texte);
  color: var(--v-encre);
  background: var(--v-papier);
  overflow-x: hidden;
}
.vitrine2 * {
  box-sizing: border-box;
}
.vitrine2-h2 {
  font-family: var(--v-titres);
  font-weight: 800;
  font-size: clamp(28px, 3.4vw, 42px);
  line-height: 1.08;
  letter-spacing: -0.02em;
  margin: 0 0 14px;
}
.vitrine2-paragraphe {
  font-size: var(--taille-titre-3);
  line-height: 1.55;
  color: var(--v-doux);
  max-width: 52ch;
  margin: 0 0 22px;
}
.vitrine2-paragraphe--marche {
  color: var(--v-encre);
  font-weight: 600;
  border-left: 3px solid var(--v-vert);
  padding-left: 14px;
}
.vitrine2-bouton {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 48px;
  padding: 0 22px;
  border-radius: 999px;
  font-weight: 700;
  font-size: var(--taille-texte-1);
  text-decoration: none;
  border: 0;
  cursor: pointer;
  white-space: nowrap;
  transition: transform 120ms ease;
}
.vitrine2-bouton:active {
  transform: scale(0.98);
}
.vitrine2-bouton--vert {
  background: var(--v-vert);
  color: var(--v-marine);
}
.vitrine2-bouton--encre {
  background: var(--v-encre);
  color: var(--v-papier);
}
.vitrine2-bouton--filet {
  background: transparent;
  color: var(--v-papier);
  border: 1px solid var(--v-filet-clair);
}
.vitrine2-lien {
  color: inherit;
  text-decoration: none;
  font-weight: 600;
  opacity: 0.85;
}
.vitrine2-lien:hover {
  opacity: 1;
}

/* ----- En-tête (marine, aligné à droite : le logo est dans le héros) ----- */
.vitrine2-entete {
  background: var(--v-marine);
  color: var(--v-papier);
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 16px;
  padding: 14px max(20px, calc((100% - var(--v-large)) / 2)) 6px;
}
.vitrine2-entete-liens {
  display: flex;
  align-items: center;
  gap: 18px;
}
.vitrine2-entete-marque {
  display: none;
  margin-right: auto;
}
.vitrine2-entete-marque img {
  display: block;
  height: 24px;
  width: auto;
}
/* Pages intérieures sans héros (blog) : la marque reste dans la barre sur
   bureau. Sur l'accueil elle est dans le héros, d'où le masquage par défaut. */
.vitrine2-entete--interieur .vitrine2-entete-marque {
  display: block;
}
.vitrine2-entete-liens a {
  white-space: nowrap;
}
.vitrine2-entete-liens .vitrine2-bouton {
  height: 40px;
  padding: 0 16px;
}
.vitrine2-langue {
  display: inline-flex;
  padding: 3px;
  border-radius: 999px;
  border: 1px solid var(--v-filet-clair);
  gap: 2px;
}
.vitrine2-langue-choix {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 34px;
  height: 28px;
  border-radius: 999px;
  font-size: var(--taille-texte-3);
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--v-papier);
  opacity: 0.75;
  text-decoration: none;
}
.vitrine2-langue-choix--actif {
  background: var(--v-papier);
  color: var(--v-marine);
  opacity: 1;
}
/* La pastille du pied n'apparaît que sur mobile, celle de l'en-tête que sur bureau. */
.vitrine2-langue--pied {
  display: none;
}

/* ----- Héros ----- */
.vitrine2-heros {
  position: relative;
  background: var(--v-marine);
  color: var(--v-papier);
  padding: 24px max(20px, calc((100% - var(--v-large)) / 2)) 72px;
  overflow: hidden;
}
.vitrine2-tamis {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0.26;
}
.vitrine2-tamis-corde {
  stroke: var(--v-papier);
  stroke-width: 1;
  stroke-opacity: 0.4;
}
.vitrine2-tamis-corde--vive {
  stroke: var(--v-vert);
  stroke-width: 4;
  stroke-opacity: 1;
  stroke-dasharray: 600;
  stroke-dashoffset: 600;
  animation: vitrine2-tension 1.4s cubic-bezier(0.2, 0.8, 0.2, 1) 0.2s forwards;
  filter: drop-shadow(0 0 10px var(--v-vert));
}
@keyframes vitrine2-tension {
  to {
    stroke-dashoffset: 0;
  }
}
.vitrine2-heros-contenu {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(360px, 0.95fr);
  grid-template-areas:
    'texte visuel'
    'action visuel';
  align-items: center;
  column-gap: 48px;
  row-gap: 0;
}
.vitrine2-heros-texte {
  grid-area: texte;
  align-self: end;
}
.vitrine2-heros-action {
  grid-area: action;
  align-self: start;
}
.vitrine2-heros-visuel {
  grid-area: visuel;
  flex-direction: column;
  align-items: stretch;
  justify-content: flex-start;
}
.vitrine2-photo {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: 24px;
  box-shadow: var(--v-ombre-forte);
}
.vitrine2-heros-visuel .vitrine2-fiches {
  width: auto;
  margin: -72px 24px 0;
  padding: 0;
}
.vitrine2-heros-visuel .vitrine2-monogramme {
  display: none;
}
.vitrine2-logo--heros {
  display: block;
  margin: 0 0 34px;
}
.vitrine2-logo--heros img {
  height: 64px;
  display: block;
}
.vitrine2-surtitre {
  margin: 0 0 16px;
  font-size: var(--taille-texte-2);
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--v-vert);
}
.vitrine2-titre {
  font-family: var(--v-titres);
  font-weight: 800;
  font-size: clamp(38px, 5.4vw, 68px);
  line-height: 0.98;
  letter-spacing: -0.03em;
  margin: 0 0 22px;
  max-width: 13ch;
}
.vitrine2-titre-verbes {
  display: block;
  color: var(--v-vert);
  font-size: 0.72em;
  margin-top: 0.18em;
  letter-spacing: -0.02em;
}
.vitrine2-soustitre {
  font-size: var(--taille-titre-2);
  line-height: 1.5;
  max-width: 44ch;
  margin: 0 0 28px;
  opacity: 0.86;
}
.vitrine2-recherche {
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--v-papier);
  border-radius: 999px;
  padding: 6px 6px 6px 18px;
  max-width: 540px;
  box-shadow: var(--v-ombre-forte);
}
.vitrine2-recherche-picto {
  width: 20px;
  height: 20px;
  fill: var(--v-doux);
  flex: 0 0 20px;
}
.vitrine2-recherche input {
  flex: 1 1 0;
  width: 0;
  min-width: 0;
  border: 0;
  background: transparent;
  font: inherit;
  font-size: var(--taille-titre-3);
  color: var(--v-encre);
  padding: 10px 6px;
  outline: none;
}
.vitrine2-recherche input::placeholder {
  color: var(--v-doux);
}
.vitrine2-confiance {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 22px;
  margin: 22px 0 0;
  font-size: var(--taille-texte-2);
  opacity: 0.85;
}
.vitrine2-confiance span {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.vitrine2-confiance-picto {
  width: 14px;
  height: 14px;
  fill: var(--v-vert);
}
.vitrine2-heros-visuel {
  position: relative;
  display: flex;
  justify-content: center;
}

/* ----- Fiches cordeurs (la preuve de la place de marché) ----- */
.vitrine2-fiches {
  position: relative;
  width: 440px;
  max-width: 100%;
  padding: 16px 0;
}
.vitrine2-monogramme {
  position: absolute;
  right: -140px;
  top: -60px;
  width: 440px;
  opacity: 0.07;
  pointer-events: none;
}
.vitrine2-fiche {
  position: relative;
  background: var(--v-blanc);
  color: var(--v-encre);
  border-radius: 18px;
  padding: 16px 18px;
  box-shadow: var(--v-ombre);
  display: grid;
  grid-template-columns: 52px 1fr auto;
  gap: 14px;
  align-items: center;
  margin: 0 0 -8px;
}
.vitrine2-fiche:nth-child(2) {
  transform: translateX(-22px) rotate(-1.2deg);
  opacity: 0.92;
}
.vitrine2-fiche--avant {
  transform: translateX(10px);
  z-index: 2;
  box-shadow: 0 0 0 2px var(--v-vert), var(--v-ombre-forte);
}
.vitrine2-fiche:nth-child(4) {
  transform: translateX(-14px) rotate(1.4deg);
  opacity: 0.92;
}
.vitrine2-fiche-avatar {
  width: 52px;
  height: 52px;
  border-radius: 50%;
  background: var(--v-marine);
  color: var(--v-papier);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--v-titres);
  font-weight: 800;
  font-size: var(--taille-titre-2);
}
.vitrine2-fiche-corps {
  min-width: 0;
}
.vitrine2-fiche-nom {
  font-weight: 700;
  font-size: var(--taille-titre-3);
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 3px;
}
.vitrine2-fiche-badge {
  font-size: var(--taille-texte-4);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 3px 8px;
  border-radius: 999px;
  background: var(--v-vert-voile);
  color: var(--v-vert-encre);
}
.vitrine2-fiche-meta {
  display: block;
  font-size: var(--taille-texte-2);
  color: var(--v-doux);
  white-space: nowrap;
}
.vitrine2-fiche-prix {
  text-align: right;
}
.vitrine2-fiche-prix-n {
  font-family: var(--v-titres);
  font-weight: 800;
  font-size: var(--taille-titre-1);
  letter-spacing: -0.02em;
  display: block;
  line-height: 1;
}
.vitrine2-fiche-prix small {
  font-size: var(--taille-texte-3);
  color: var(--v-doux);
}
.vitrine2-fiche-dispo {
  position: absolute;
  top: -12px;
  right: 16px;
  background: var(--v-vert);
  color: var(--v-marine);
  font-size: var(--taille-texte-3);
  font-weight: 700;
  padding: 5px 10px;
  border-radius: 999px;
  box-shadow: var(--v-ombre);
}
.vitrine2-fiches-legende {
  position: relative;
  margin: 26px 0 0;
  text-align: center;
  font-size: var(--taille-texte-2);
  opacity: 0.7;
}

/* ----- Le fil : la corde descend à travers « Faire recorder, autrement » ----- */
.vitrine2-fil {
  position: relative;
  padding: 72px max(20px, calc((100% - var(--v-large)) / 2)) 48px;
}
.vitrine2-fil-corde {
  position: absolute;
  left: 50%;
  top: 0;
  bottom: -40px;
  width: 4px;
  margin-left: -2px;
  background: var(--v-vert);
  box-shadow: 0 0 14px var(--v-halo);
  transform-origin: top;
  transform: scaleY(0);
  transition: transform 1.6s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.vitrine2-fil.est-la .vitrine2-fil-corde {
  transform: scaleY(1);
}
.vitrine2-fil-entete {
  position: relative;
  text-align: center;
  max-width: 640px;
  margin: 0 auto 44px;
  background: var(--v-papier);
  padding: 8px 24px;
}
.vitrine2-fil-titre {
  margin-bottom: 12px;
}
.vitrine2-fil-intro {
  font-size: var(--taille-titre-2);
  line-height: 1.55;
  color: var(--v-doux);
  margin: 0;
}
/* Deux lignes, même gris que les autres sections (retour Vincent). */
.vitrine2-fil-intro--maintenant {
  margin-top: 8px;
}
.vitrine2-fil-temps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 40px;
}
.vitrine2-temps {
  position: relative;
  width: calc(50% - 56px);
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.7s ease, transform 0.7s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.vitrine2-temps.est-la {
  opacity: 1;
  transform: none;
}
.vitrine2-temps--gauche {
  margin-right: auto;
  text-align: right;
}
.vitrine2-temps--droite {
  margin-left: auto;
  text-align: left;
}
.vitrine2-temps-noeud {
  position: absolute;
  top: 18px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--v-papier);
  border: 4px solid var(--v-vert);
  box-shadow: 0 0 0 4px var(--v-papier);
  transition: background 0.4s ease 0.3s, box-shadow 0.4s ease 0.3s;
}
.vitrine2-temps.est-la .vitrine2-temps-noeud {
  background: var(--v-vert);
  box-shadow: 0 0 0 4px var(--v-papier), 0 0 18px var(--v-halo);
}
.vitrine2-temps--gauche .vitrine2-temps-noeud {
  right: -67px;
}
.vitrine2-temps--droite .vitrine2-temps-noeud {
  left: -67px;
}
.vitrine2-temps-verbe {
  font-family: var(--v-titres);
  font-weight: 800;
  font-size: clamp(34px, 4.2vw, 56px);
  line-height: 1;
  letter-spacing: -0.03em;
  margin: 0 0 10px;
}
.vitrine2-temps p {
  margin: 0;
  color: var(--v-doux);
  font-size: var(--taille-titre-3);
  line-height: 1.5;
  max-width: 40ch;
}
.vitrine2-temps--gauche p {
  margin-left: auto;
}

/* ----- Cordeurs ----- */
.vitrine2-cordeurs {
  margin: 24px max(20px, calc((100% - var(--v-large)) / 2));
  padding: 56px;
  border-radius: 28px;
  background: var(--v-blanc);
  border: 1px solid var(--v-trait);
  display: grid;
  grid-template-columns: minmax(0, 1.2fr) minmax(260px, 0.8fr);
  gap: 48px;
  align-items: center;
}
.vitrine2-points {
  list-style: none;
  padding: 0;
  margin: 0 0 26px;
  display: grid;
  gap: 10px;
}
.vitrine2-points li {
  padding-left: 26px;
  position: relative;
  line-height: 1.45;
}
.vitrine2-points li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 7px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--v-vert);
  box-shadow: inset 0 0 0 3px var(--v-blanc), 0 0 0 1px var(--v-encre);
}
.vitrine2-cordeurs-visuel {
  display: flex;
  justify-content: center;
}

/* ----- Appareil (vraie capture de l'app) ----- */
.vitrine2-appareil {
  position: relative;
  width: 260px;
  border-radius: 44px;
  padding: 10px;
  background: var(--v-encre);
  box-shadow: var(--v-ombre-forte);
  transform: rotate(2deg);
}
.vitrine2-appareil img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 34px;
  aspect-ratio: 390 / 780;
  object-fit: cover;
  object-position: top;
}
.vitrine2-appareil-encoche {
  position: absolute;
  top: 18px;
  left: 50%;
  transform: translateX(-50%);
  width: 96px;
  height: 26px;
  border-radius: 999px;
  background: var(--v-encre);
  z-index: 1;
}

/* ----- Clubs ----- */
.vitrine2-clubs {
  position: relative;
  margin: 64px 0 0;
  padding: 80px max(20px, calc((100% - var(--v-large)) / 2));
  background: var(--v-encre);
  color: var(--v-papier);
  overflow: hidden;
}
.vitrine2-clubs-points {
  position: absolute;
  inset: 0;
  background-image: radial-gradient(var(--v-vert) 1.2px, transparent 1.3px);
  background-size: 26px 26px;
  opacity: 0.22;
  mask-image: radial-gradient(ellipse at 70% 50%, var(--texte) 20%, transparent 70%);
}
.vitrine2-clubs-contenu {
  position: relative;
  max-width: 640px;
}
.vitrine2-clubs-picto {
  width: 36px;
  height: 36px;
  fill: var(--v-vert);
  margin-bottom: 18px;
}
.vitrine2-chiffre {
  color: var(--v-vert);
  font-size: clamp(44px, 6vw, 84px);
  display: block;
  letter-spacing: -0.04em;
  line-height: 1;
  margin-bottom: 6px;
}
.vitrine2-clubs .vitrine2-paragraphe {
  color: var(--v-papier);
  opacity: 0.85;
}
.vitrine2-clubs .vitrine2-bouton--encre {
  background: var(--v-papier);
  color: var(--v-encre);
}

/* ----- Conseils (articles récents, issue n°225) ----- */
.vitrine2-conseils {
  padding: 64px max(20px, calc((100% - var(--v-large)) / 2)) 8px;
}
.vitrine2-conseils-entete {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 32px;
}
.vitrine2-conseils-entete .vitrine2-h2 {
  margin: 0;
}
/* .blog-grille (blog.css) centre sa propre largeur de lecture (68ch/960px) :
   ici la centrale est déjà faite par le padding de .vitrine2-conseils, donc
   on l'étire jusqu'à --v-large plutôt que de cumuler deux centrages. */
.vitrine2-conseils .blog-grille {
  max-width: none;
  margin: 0;
}

/* ----- Fin ----- */
.vitrine2-fin {
  text-align: center;
  padding: 88px 20px 64px;
}
.vitrine2-fin .vitrine2-paragraphe {
  margin-left: auto;
  margin-right: auto;
}

/* ----- Pied de page ----- */
.vitrine2-pied {
  background: var(--v-marine);
  color: var(--v-papier);
  padding: 64px max(20px, calc((100% - var(--v-large)) / 2)) 28px;
}
.vitrine2-pied-haut {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 40px;
  padding-bottom: 40px;
  border-bottom: 1px solid var(--v-filet-clair);
}
/* Signature : monogramme et phrase sur une ligne, centrés, puis les trois
   cordes sur toute la largeur (retour Vincent, 22 h). */
.vitrine2-pied-marque {
  grid-column: 1 / -1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 22px;
  margin-bottom: 8px;
}
.vitrine2-pied-signature-ligne {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
}
.vitrine2-pied-marque img {
  height: 44px;
  display: block;
}
.vitrine2-pied-signature {
  font-family: var(--v-titres);
  font-weight: 800;
  font-size: var(--taille-affichage-1);
  letter-spacing: -0.02em;
  line-height: 1.15;
  margin: 0;
}
.vitrine2-pied-cordes {
  width: 100%;
  height: 24px;
  display: block;
}
.vitrine2-pied-cordes line {
  stroke: var(--v-filet-clair);
  stroke-width: 1;
}
.vitrine2-pied-cordes .vitrine2-pied-cordes-vive {
  stroke: var(--v-vert);
  stroke-width: 3;
}
.vitrine2-pied-colonne h3 {
  font-size: var(--taille-texte-2);
  font-weight: 700;
  margin: 0 0 14px;
  opacity: 0.6;
}
.vitrine2-pied-colonne a {
  display: block;
  color: var(--v-papier);
  text-decoration: none;
  font-size: var(--taille-texte-1);
  margin-bottom: 10px;
  opacity: 0.9;
}
.vitrine2-pied-colonne a:hover {
  color: var(--v-vert);
  opacity: 1;
}
.vitrine2-pied-bas {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  padding-top: 22px;
  font-size: var(--taille-texte-2);
  opacity: 0.7;
}
.vitrine2-pied-legal {
  display: flex;
  gap: 18px;
}
.vitrine2-pied-legal a {
  color: inherit;
  text-decoration: none;
}

/* ----- Mouvement réduit ----- */
@media (prefers-reduced-motion: reduce) {
  .vitrine2-tamis-corde--vive {
    animation: none;
    stroke-dashoffset: 0;
  }
  .vitrine2-fil-corde,
  .vitrine2-temps {
    transition: none;
    transform: none;
    opacity: 1;
  }
}

/* ----- Mobile ----- */
@media (max-width: 899px) {
  .vitrine2-entete {
    padding: 14px 16px 6px;
    justify-content: space-between;
  }
  .vitrine2-entete-marque {
    display: block;
  }
  .vitrine2-entete-liens {
    gap: 12px;
  }
  .vitrine2-bouton--inscription {
    display: none;
  }
  .vitrine2-lien--connexion {
    display: inline-flex;
    align-items: center;
    height: 36px;
    padding: 0 14px;
    border: 1px solid var(--v-filet-clair);
    border-radius: 999px;
    font-size: var(--taille-texte-2);
    font-weight: 700;
    opacity: 1;
  }
  .vitrine2-logo--heros {
    display: none;
  }
  .vitrine2-langue--entete {
    display: none;
  }
  .vitrine2-langue--pied {
    display: inline-flex;
  }
  .vitrine2-pied-bas {
    justify-content: center;
    text-align: center;
  }
  .vitrine2-entete-liens .vitrine2-bouton {
    height: 36px;
    padding: 0 14px;
    font-size: var(--taille-texte-2);
  }
  .vitrine2-heros {
    padding-top: 10px;
    padding-bottom: 44px;
  }
  .vitrine2-tamis {
    opacity: 0.2;
  }
  /* Mobile : le titre, puis le visuel (les fiches), puis l'action — on
     n'a pas peur de faire défiler, on a peur d'entasser (retour Vincent). */
  .vitrine2-heros-contenu {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      'texte'
      'visuel'
      'action';
    row-gap: 32px;
  }
  .vitrine2-heros-texte {
    min-width: 0;
  }
  /* Mobile allégé (retour Vincent, 21 h 12) : logo plus petit, titre sur
     trois lignes max, verbes sur une ligne, sous-titre plus court à l'écran,
     repères de confiance sur une ligne. */
  .vitrine2-logo--heros {
    margin-bottom: 26px;
  }
  .vitrine2-logo--heros img {
    height: 36px;
  }
  .vitrine2-surtitre {
    font-size: var(--taille-texte-4);
    margin: 18px 0 12px;
  }
  .vitrine2-titre {
    max-width: none;
    font-size: var(--taille-affichage-3);
    margin-bottom: 0;
  }
  .vitrine2-titre-verbes {
    font-size: 0.66em;
    margin-top: 0.25em;
  }
  .vitrine2-soustitre {
    font-size: var(--taille-texte-1);
    line-height: 1.5;
    margin-bottom: 20px;
    opacity: 0.8;
  }
  .vitrine2-fiches-legende {
    margin-top: 18px;
  }
  .vitrine2-recherche {
    padding: 6px 6px 6px 14px;
  }
  .vitrine2-recherche input {
    font-size: var(--taille-titre-3);
    padding: 10px 4px;
  }
  .vitrine2-recherche .vitrine2-bouton {
    height: 44px;
    padding: 0 16px;
    font-size: var(--taille-texte-2);
  }
  .vitrine2-recherche-picto {
    display: none;
  }
  .vitrine2-confiance {
    gap: 6px 14px;
    margin-top: 14px;
    font-size: var(--taille-texte-2);
    opacity: 0.75;
  }
  .vitrine2-fiches {
    width: 100%;
    padding: 0;
  }
  .vitrine2-photo {
    border-radius: 18px;
  }
  .vitrine2-heros-visuel .vitrine2-fiches {
    margin: -44px 12px 0;
  }
  .vitrine2-monogramme {
    right: -120px;
    top: -40px;
    width: 320px;
  }
  .vitrine2-fiche {
    grid-template-columns: 44px 1fr auto;
    padding: 14px;
  }
  .vitrine2-fiche:nth-child(2),
  .vitrine2-fiche--avant {
    transform: none;
  }
  .vitrine2-fiche:nth-child(2) {
    opacity: 0.9;
  }
  .vitrine2-fiche:nth-child(4) {
    display: none;
  }
  .vitrine2-fiche-avatar {
    width: 44px;
    height: 44px;
    font-size: var(--taille-texte-1);
  }
  .vitrine2-fiche-meta {
    white-space: normal;
  }
  .vitrine2-h2 {
    font-size: var(--taille-affichage-1);
  }
  .vitrine2-fil {
    padding-top: 48px;
  }
  .vitrine2-fil-corde {
    left: 18px;
    margin-left: 0;
  }
  .vitrine2-fil-entete {
    text-align: left;
    margin-left: 44px;
    padding: 0;
  }
  .vitrine2-fil-intro {
    font-size: var(--taille-titre-3);
  }
  .vitrine2-temps,
  .vitrine2-temps--gauche,
  .vitrine2-temps--droite {
    width: auto;
    margin: 0 0 0 44px;
    text-align: left;
  }
  .vitrine2-temps--gauche .vitrine2-temps-noeud,
  .vitrine2-temps--droite .vitrine2-temps-noeud {
    left: -37px;
    right: auto;
    top: 14px;
  }
  .vitrine2-temps--gauche p {
    margin-left: 0;
  }
  .vitrine2-temps-verbe {
    font-size: var(--taille-affichage-3);
  }
  .vitrine2-cordeurs {
    grid-template-columns: 1fr;
    padding: 28px 22px;
    border-radius: 20px;
  }
  .vitrine2-appareil {
    width: 240px;
    transform: none;
  }
  .vitrine2-clubs {
    padding-top: 56px;
    padding-bottom: 56px;
  }
  .vitrine2-pied {
    padding-top: 44px;
  }
  .vitrine2-pied-haut {
    grid-template-columns: 1fr 1fr;
    gap: 28px 20px;
  }
  .vitrine2-pied-signature {
    font-size: var(--taille-titre-2);
  }
  .vitrine2-pied-marque img {
    height: 36px;
  }
}

/* Placée en fin de fichier pour ne pas décaler les exceptions repérées par
   numéro de ligne dans tests/web/design-guard-font-size.test.ts.
   L'input de recherche n'a pas de contour (`outline: none`, .vitrine2-recherche input) :
   l'anneau de focus se pose sur la pilule, en menthe pour rester visible
   sur le héros marine (audit accessibilité du 25/09, WCAG 2.4.7). */
.vitrine2-recherche:has(input:focus-visible) {
  outline: var(--epaisseur-focus) solid var(--accent-marque);
  outline-offset: var(--epaisseur-trait);
}

/* ---- aide-bulle.css ---- */
/*
 * Bulles d'aide contextuelles (issue n°115) : picto « i » à côté d'un titre
 * de page ou de bloc admin (aide-bulle.tsx), panneau positionné juste
 * en dessous, chargé à la demande par aide.js. Un titre de page peut être
 * suivi directement du picto : garder un espacement cohérent avec le texte.
 *
 * Positionnement stable sur toutes les pages (issue n°198, retour Vincent du
 * 13/09 : « elles flottent un peu partout » — picto aligné à droite de
 * l'écran, loin du titre). Deux cas rencontrés dans les pages existantes :
 *  - le titre et le picto sont des enfants directs de .entete-page, qui
 *    devient une ligne flex à partir de 900px (base.css) ou en dessous
 *    (ecrans.css, lcs.css) : sans garde-fou, le titre s'étire (flex: 1) ou
 *    justify-content: space-between écarte les deux seuls enfants — le picto
 *    finit collé au bord droit du conteneur plutôt qu'au dernier mot du
 *    titre. Corrigé en empêchant le titre de grandir et en laissant le picto
 *    absorber l'espace libre restant (une action réelle qui suivrait, ex.
 *    bouton d'export, garde son alignement à droite).
 *  - le titre est seul dans une <section>/.carte (pas de .entete-page) : un
 *    h1/h2/h3, élément de bloc, envoie le picto à la ligne suivante. Corrigé
 *    en le passant en inline-block dans ce seul cas, pour qu'il ne réserve
 *    plus toute la largeur du conteneur.
 */

.aide-bulle {
  position: relative;
  display: inline-flex;
  vertical-align: middle;
  margin-left: var(--espacement-petit);
}

:is(h1, h2, h3):has(+ .aide-bulle) {
  display: inline-block;
}
.entete-page :is(h1, h2, h3):has(+ .aide-bulle) {
  flex: 0 1 auto;
}
:is(h1, h2, h3) + .aide-bulle {
  margin-right: auto;
}

.aide-bulle-declencheur {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--taille-cible-aide-bulle);
  height: var(--taille-cible-aide-bulle);
  padding: 0;
  border: none;
  border-radius: var(--rayon-pastille);
  background: none;
  color: var(--texte-secondaire);
  cursor: pointer;
}
.aide-bulle-declencheur:hover,
.aide-bulle-declencheur[aria-expanded='true'] {
  background: var(--action-douce);
  color: var(--texte);
}
.aide-bulle-icone {
  width: var(--taille-icone-aide-bulle);
  height: var(--taille-icone-aide-bulle);
}

/* left recalculé en pixels par aide.js (positionner()) à chaque ouverture,
   pour que le panneau reste dans l'écran (flip gauche/droite) : la valeur
   ci-dessous n'est que le repli sans JS (aligné à gauche du picto). */
.aide-bulle-panneau {
  position: absolute;
  /* Au-dessus de .chat-bulle et .recette-bulle (z-index 200, coin bas-droit
     fixe) : même correctif que .installer-app (14/09/2026, interface.css)
     — sans ça, un panneau assez long pour atteindre le bas de l'écran se
     fait passer devant par ces deux bulles, texte caché derrière (retour
     Vincent : « le texte touche les autres icônes »). */
  z-index: 210;
  top: calc(100% + 6px);
  left: 0;
  width: min(360px, 92vw);
  padding: var(--espacement-petit) 14px;
  background: var(--surface-haute);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
  box-shadow: var(--ombre-carte);
  text-align: left;
}
.aide-bulle-panneau[hidden] {
  display: none;
}

.aide-bulle-titre {
  margin: 0 0 4px;
  font-size: var(--taille-texte-2);
  font-weight: var(--graisse-semibold);
  /* --surface-haute est un fond sombre (jeton foncé, tokens.css) : --texte
     (quasi noir) y était presque invisible (retour Vincent, 14/09) — mêmes
     jetons que .bulle--moi (base.css), le seul autre endroit du site qui
     pose du texte sur ce même fond. */
  color: var(--sur-action);
}
.aide-bulle-texte {
  font-size: var(--taille-texte-3);
  color: var(--texte-sur-sombre);
}
.aide-bulle-texte p {
  margin: 0 0 6px;
}
.aide-bulle-texte p:last-child {
  margin-bottom: 0;
}
.aide-bulle-texte ul {
  margin: 0 0 6px;
  padding-left: 18px;
}
.aide-bulle-texte strong {
  color: var(--sur-action);
}

/* Aperçu statique dans le formulaire d'édition (admin-aide-bulle-formulaire.tsx,
   issue n°115) : même habillage que le panneau flottant, mais posé dans le
   flux normal du document plutôt qu'en position absolue. */
.aide-bulle-panneau--apercu {
  position: static;
  width: auto;
  margin-bottom: var(--espacement-petit);
}
.aide-bulle-panneau--apercu:last-child {
  margin-bottom: 0;
}

/* ---- partage-viral.css ---- */
/*
 * Partage viral (issue n°226) : icône dans les en-têtes joueur/cordeur/POI
 * (partage-viral.tsx), panneau flottant contenant le QR de la racine du
 * site. Déclencheur en <summary> réutilisant .bellbtn (lcs.css) — jamais
 * .bellbtn nu redéfini ici, seul le marqueur natif de <details> est retiré.
 * Panneau positionné comme .aide-bulle-panneau (aide-bulle.css) : mêmes
 * jetons de surface flottante, alignement à droite plutôt qu'à gauche pour
 * rester sous l'icône qui est toujours la plus proche du bord de l'écran.
 */

.partage-viral {
  position: relative;
}

.partage-viral > summary {
  list-style: none;
}
.partage-viral > summary::-webkit-details-marker {
  display: none;
}

.partage-viral-panneau {
  position: absolute;
  z-index: 210;
  top: calc(100% + 8px);
  right: 0;
  width: min(280px, 92vw);
  padding: var(--espacement-petit) 14px;
  background: var(--surface-haute);
  border: var(--epaisseur-trait) solid var(--trait);
  border-radius: var(--rayon);
  box-shadow: var(--ombre-carte);
  text-align: center;
}

/* Plein écran, centré, fond translucide (retour Vincent du 17/09 : le QR
   doit être grand et lisible au milieu de l'écran sur mobile, pas dans un
   petit popover accroché à l'icône — bureau inchangé, assez de place pour
   le popover compact ci-dessus). */
@media (max-width: 899px) {
  .partage-viral-panneau {
    position: fixed;
    inset: 0;
    z-index: 300;
    width: auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--espacement-champ-vertical);
    padding: 32px;
    background: rgba(255, 255, 255, 0.94);
    border: none;
    border-radius: 0;
    box-shadow: none;
  }
  .partage-viral-texte {
    color: var(--texte-principal);
    font-size: var(--taille-texte-1);
    font-weight: var(--graisse-semibold);
  }
  .partage-viral-fermer {
    top: 20px;
    right: 20px;
    width: 40px;
    height: 40px;
    font-size: var(--taille-affichage-1);
    color: var(--texte-principal);
  }
  .partage-viral-qr svg {
    max-width: min(280px, 80vw);
  }
}

.partage-viral-texte {
  margin: 0 0 var(--espacement-petit);
  font-size: var(--taille-texte-3);
  color: var(--texte-sur-sombre);
}

.partage-viral-fermer {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 28px;
  height: 28px;
  border: none;
  background: transparent;
  font-size: var(--taille-titre-2);
  line-height: 1;
  color: var(--texte-secondaire);
  cursor: pointer;
}

.partage-viral-qr {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 160px;
}
.partage-viral-qr svg {
  width: 100%;
  height: auto;
  max-width: 220px;
  background: var(--blanc);
  border-radius: var(--rayon-etiquette);
}

/* ---- accordeon.css ---- */
/* Accordéon (issue n°196) : coquille visuelle au-dessus du <details> natif —
   en-tête 56px avec titre, infobulle et résumé d'état, chevron à droite ;
   comportement (mémoire par rubrique, bascule tout ouvrir/replier, sommaire)
   dans src/web/assets/accordeon.js. Distinct de .section-repliable (lcs.css),
   une disclosure simple sans résumé ni mémoire — voir docs/design-system.md.
   Jetons uniquement (CLAUDE.md, règle 6). */

.accordeon {
  display: flex;
  flex-direction: column;
  gap: var(--espacement-petit);
}
.accordeon-barre {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--espacement-petit);
}
.accordeon-sommaire {
  flex: 1 1 auto;
  justify-content: flex-end;
}
.accordeon-section {
  padding: 0;
}
.accordeon-entete {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--espacement-petit);
  min-height: 56px;
  padding: var(--grille-base) var(--carte-padding-horizontal);
  cursor: pointer;
  list-style: none;
}
.accordeon-entete::-webkit-details-marker {
  display: none;
}
.accordeon-entete-titre {
  display: flex;
  align-items: center;
  gap: var(--espacement-petit);
}
.accordeon-entete-titre .titre-2 {
  margin: 0;
}
.accordeon-resume {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-align: right;
}
.accordeon-chevron {
  width: 16px;
  height: 16px;
  flex: 0 0 16px;
  fill: var(--texte-secondaire);
  transition: transform 0.15s ease;
}
.accordeon-section[open] > .accordeon-entete .accordeon-chevron {
  transform: rotate(90deg);
}
.accordeon-corps {
  padding: var(--carte-padding-vertical) var(--carte-padding-horizontal);
  border-top: var(--epaisseur-trait) solid var(--trait);
}

@media (max-width: 559px) {
  .accordeon-resume {
    order: 3;
    flex-basis: 100%;
    text-align: left;
  }
}

/* ---- photo-scene.css ---- */
/* Photos de marque (issue n°200) : remplissage générique du composant
   <PhotoScene> (src/web/photo-scene.tsx). Le cadrage (arrondi, ratio,
   superposition) reste au conteneur de chaque écran (.photo de bureau.css,
   .acces__scene de connexion.css, .carte-cordeur-recommande-bandeau de
   ecrans.css…) ; ce fichier ne pose que le <picture> et son remplissage.
   .photo-scene est display:contents : il n'existe pas dans la boîte, l'<img>
   qu'il porte se comporte comme si elle était l'enfant direct du conteneur,
   sans toucher à l'empilement (z-index) déjà en place. */
.photo-scene {
  display: contents;
}

/* Classe de remplissage par défaut, pour les scènes qui n'avaient encore
   aucune <img> à cet endroit (héros, atelier vu par les joueurs, scène de
   connexion/inscription, en-tête des pages point relais). Les scènes qui
   remplaçaient déjà une vraie photo (couverture cordeur, carte cordeur
   recommandée…) réutilisent leur propre classe existante. */
.photo-scene__image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* ---- photos.css ---- */
/* Photo de profil (07/09) — Mon compte et avatars photo partout. */
.avatar--photo {
  object-fit: cover;
  border-radius: 50%;
  display: inline-block;
}
.avatar--grand {
  width: 96px;
  height: 96px;
  font-size: var(--taille-affichage-3);
}
.compte-photo-ligne {
  display: flex;
  align-items: center;
  gap: 20px;
  flex-wrap: wrap;
}
.compte-photo-apercu img,
.compte-photo-apercu .avatar {
  width: 96px;
  height: 96px;
  border-radius: 50%;
  object-fit: cover;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
/* Zone de chevauchement pour le badge « Retirer ma photo » (issue n°250) :
   remplace le bouton pleine largeur par une croix posée sur le coin de la
   photo — voir .compte-photo-retirer-badge plus bas. */
.compte-photo-apercu-zone {
  position: relative;
  display: inline-flex;
}
.compte-photo-retirer {
  position: absolute;
  top: -6px;
  right: -6px;
  margin: 0;
}
.compte-photo-retirer-badge {
  width: var(--cible-tactile-min);
  height: var(--cible-tactile-min);
  padding: 0;
  border-radius: 50%;
  border: var(--epaisseur-trait) solid var(--trait);
  background: var(--surface);
  color: var(--texte-secondaire);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: var(--ombre-carte);
}
/* Gris neutre au repos, teinté vers l'alerte seulement au survol/pression
   (design system → Pastilles et badges) : le rouge reste réservé à un état
   d'alerte confirmé, jamais la couleur par défaut d'une action destructrice. */
.compte-photo-retirer-badge:hover,
.compte-photo-retirer-badge:active {
  color: var(--erreur);
  border-color: var(--erreur);
}
.compte-photo-retirer-icone {
  width: 16px;
  height: 16px;
}
.compte-photo-form {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  flex: 1;
  min-width: 200px;
}
.compte-photo-bouton {
  cursor: pointer;
}
.compte-photo-aide {
  margin: 4px 0 0;
  font-size: var(--taille-texte-2);
  max-width: 44ch;
}
@media (max-width: 899px) {
  .compte-photo-form .bouton {
    width: 100%;
  }
  .compte-photo-form {
    width: 100%;
  }
}
.topbar .avatar--photo-lien img,
.avatar--photo-lien img {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: cover;
}
.avatar--photo-lien {
  padding: 0;
  overflow: hidden;
}

/* Cadrage dans un rond (photo.js v2) */
.compte-photo-cadrage {
  margin-top: 12px;
  display: grid;
  gap: 10px;
  justify-items: start;
}
.compte-photo-rond {
  width: min(280px, 80vw);
  aspect-ratio: 1;
  border-radius: 50%;
  overflow: hidden;
  border: 3px solid var(--accent-marque);
  box-shadow: 0 0 0 4px var(--surface), 0 8px 24px rgba(0, 0, 0, 0.18);
  touch-action: none;
  cursor: grab;
  background: var(--surface-haute);
}
.compte-photo-rond canvas {
  display: block;
  width: 100%;
  height: 100%;
}
.compte-photo-zoom {
  display: flex;
  align-items: center;
  gap: 10px;
  width: min(280px, 80vw);
  font-size: var(--taille-texte-2);
  color: var(--texte-secondaire);
}
.compte-photo-zoom input[type='range'] {
  flex: 1;
  accent-color: var(--texte);
}
.compte-photo-cadrage[hidden] {
  display: none;
}
/* ----- Photos d'atelier (issue n°120, 2/3) ----- */
.atelier-photos-grille {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 12px;
}
.atelier-photo {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.atelier-photo img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: 12px;
  border: 1px solid var(--trait);
  background: var(--lcs-actif);
}
.atelier-photo--ajout {
  justify-content: center;
  align-items: stretch;
  border: 1px dashed var(--trait);
  border-radius: 12px;
  padding: 16px;
  gap: 10px;
}
.atelier-photo--ajout .bouton {
  width: 100%;
  cursor: pointer;
}
.atelier-bandeau {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(180px, 1fr);
  gap: 8px;
  margin-bottom: 12px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  border-radius: 14px;
}
.atelier-bandeau-photo {
  scroll-snap-align: start;
  display: block;
}
.atelier-bandeau-photo img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: 12px;
  display: block;
}

/* ---- blog.css ---- */
/*
 * Blog public (/conseils, /conseils/:slug — issue n°130). Typographie de lecture :
 * titres en Bricolage Grotesque (--police-vitrine-titres, comme la vitrine),
 * texte courant en Inter (--police-texte), mesure resserrée pour le corps
 * d'article (~68 caractères). Mobile d'abord, images pleine largeur de la
 * colonne. Aucune valeur en dur : tout passe par tokens.css.
 */

/* Écrans publics (issue n°225) : `.page:not(.adm-shell) > .ecran` (lcs.css)
   plafonne à 440px — le gabarit des formulaires de connexion, hérité par
   défaut. Sans cette classe, la colonne d'article (68ch) et la grille de
   cartes ne l'atteignaient jamais : sur bureau, tout le contenu du blog
   flottait dans une colonne mobile au milieu d'un espace vide. Même motif
   que `.ecran.statut-page` (statut.css) et `.ecran.associes-page`
   (associes.css), déjà en place pour sortir un écran public de ce gabarit
   resserré. */
.page:not(.adm-shell) > .ecran.blog-page {
  max-width: var(--largeur-contenu-bureau);
  margin: var(--espacement-section) auto;
  gap: var(--espacement-section);
}

.blog-entete {
  max-width: 68ch;
  margin: 0 auto var(--espacement-section);
  text-align: center;
}

.blog-entete-titre {
  font-family: var(--police-vitrine-titres);
}

.blog-filtre-tag {
  display: flex;
  align-items: center;
  gap: var(--espacement-petit);
  justify-content: center;
  margin-bottom: var(--espacement-section);
}

.blog-grille {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--espacement-section);
  max-width: var(--largeur-contenu-bureau);
  margin: 0 auto;
}

.blog-carte {
  display: flex;
  flex-direction: column;
  border-radius: var(--rayon);
  overflow: hidden;
  background: var(--surface);
  box-shadow: var(--ombre-carte);
  text-decoration: none;
  color: inherit;
}

.blog-carte-couverture {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  background: var(--surface-douce);
}

.blog-carte-corps {
  padding: var(--carte-padding-vertical) var(--carte-padding-horizontal);
  display: flex;
  flex-direction: column;
  gap: var(--espacement-petit);
}

.blog-carte-titre {
  font-family: var(--police-vitrine-titres);
  margin: 0;
}

.blog-carte-extrait {
  color: var(--texte-secondaire);
  margin: 0;
}

.blog-carte-meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--espacement-petit);
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
}

.blog-tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--espacement-petit);
  list-style: none;
  margin: 0;
  padding: 0;
}

.blog-tag {
  border-radius: var(--rayon-etiquette);
  background: var(--action-douce);
  color: var(--texte);
  padding: 2px var(--espacement-petit);
  font-size: var(--taille-texte-4);
  text-decoration: none;
}

.blog-vide {
  text-align: center;
  color: var(--texte-secondaire);
  padding: var(--espacement-section) 0;
}

/* Admin --------------------------------------------------------------- */

.admin-blog-couverture-apercu {
  max-width: 240px;
  border-radius: var(--rayon);
}

/* Article ------------------------------------------------------------ */

.blog-article {
  max-width: 68ch;
  margin: 0 auto;
}

.blog-article-retour {
  display: inline-block;
  margin-bottom: var(--espacement-champ-vertical);
}

.blog-article-entete {
  margin-bottom: var(--espacement-section);
}

.blog-article-titre {
  font-family: var(--police-vitrine-titres);
}

.blog-article-meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--espacement-petit);
  color: var(--texte-secondaire);
  font-size: var(--taille-texte-2);
}

.blog-article-couverture {
  width: 100%;
  border-radius: var(--rayon);
  margin: var(--espacement-section) 0;
}

.blog-corps {
  font-family: var(--police-texte);
  line-height: var(--interligne);
}

.blog-corps img.image {
  width: 100%;
  border-radius: var(--rayon);
  margin: var(--espacement-section) 0;
}

.blog-corps blockquote {
  margin: var(--espacement-section) 0;
  padding-left: var(--espacement-section);
  border-left: var(--epaisseur-focus) solid var(--accent-marque);
  color: var(--texte-secondaire);
}

.blog-cta {
  margin-top: var(--espacement-section);
  padding: var(--espacement-section);
  border-radius: var(--rayon);
  background: var(--surface-haute);
  color: var(--texte-sur-sombre);
  text-align: center;
}

.blog-cta-bouton {
  margin-top: var(--espacement-petit);
}

.blog-lies {
  max-width: var(--largeur-contenu-bureau);
  margin: var(--espacement-section) auto 0;
}

@media (min-width: 600px) {
  .blog-grille {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 900px) {
  .blog-grille {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* Titre : jamais de mot seul sur la dernière ligne (le « ? » orphelin d'un
   titre en question), à toutes les largeurs. */
.blog-article-titre,
.blog-carte-titre {
  text-wrap: balance;
}

/* Photo de couverture : recadrée en bandeau dès la tablette. Sans cela une
   photo portrait (1145 × 1374 pour l'article sur le recordage) occupait à elle
   seule tout l'écran — le premier paragraphe démarrait à 1 084 px du haut d'un
   écran de 900 px. `object-position` haut-centre : dans une photo portrait, le
   sujet est rarement dans le tiers bas. Mobile inchangé (image entière). */
@media (min-width: 600px) {
  .blog-article-couverture {
    aspect-ratio: 16 / 9;
    object-fit: cover;
    object-position: center 35%;
  }
}

/* Bureau (retour Vincent du 28/09 : « pas du tout adapté pour le PC »).
   Avant, la colonne de lecture restait au gabarit mobile — 15 px dans 612 px,
   titre à 26 px — au milieu d'un écran de 1440 px, et le bloc « À lire aussi »
   (960 px) ne s'alignait pas sur elle. Ici : lecture à 17 px sur 70
   caractères, grand titre, respiration entre paragraphes et une seule
   colonne de contenu, sur laquelle l'appel à l'action et les articles liés
   s'alignent. */
@media (min-width: 900px) {
  /* Grille « pleine largeur » (retour Vincent du 28/09 : « plus large, le
     texte et peut-être la photo ») : le conteneur prend toute la largeur du
     contenu (960px), le texte reste dans la colonne de lecture centrée déjà
     utilisée par les pages de contenu public (--largeur-lecture, 720px, issue
     n°161), et seule la photo déborde sur les trois colonnes. Une photo
     portrait n'est pas rendue plus lisible en l'élargissant (elle en devient
     plus haute) : elle est recadrée en bandeau. */
  .blog-article {
    max-width: var(--largeur-contenu-bureau);
    display: grid;
    grid-template-columns: 1fr min(var(--largeur-lecture), 100%) 1fr;
  }

  .blog-article > * {
    grid-column: 2;
  }

  .blog-article > .blog-article-retour {
    justify-self: start;
  }

  .blog-article > .blog-article-couverture {
    grid-column: 1 / -1;
    aspect-ratio: 21 / 9;
  }

  .blog-article-titre {
    font-size: var(--taille-affichage-4);
    line-height: 1.15;
  }

  .blog-article-meta {
    font-size: var(--taille-texte-1);
  }

  .blog-corps {
    font-size: var(--taille-lecture);
    line-height: 1.7;
  }

  /* .contenu-markdown est une colonne flexible avec un `gap` : les marges n'y
     fusionnent pas, elles s'additionneraient au gap (paragraphe + gap +
     titre = 56 px). Ici le rythme ne tient qu'aux marges. */
  .blog-corps {
    gap: 0;
  }

  .blog-corps p {
    margin: 0 0 var(--espacement-section);
  }

  /* Le convertisseur (domain/help/markdown.ts) décale d'un niveau : « ## »
     donne un <h3>, « ### » un <h4> (le « # » est réservé au titre de page). */
  .blog-corps h3 {
    font-size: var(--taille-affichage-1);
    line-height: 1.25;
    margin: var(--espacement-section) 0 var(--espacement-champ-vertical);
  }

  .blog-corps h4 {
    font-size: var(--taille-titre-2);
    margin: var(--espacement-section) 0 var(--espacement-petit);
  }

  .blog-corps ul,
  .blog-corps ol {
    margin: 0 0 var(--espacement-section);
    padding-left: calc(var(--espacement-section) * 1.5);
  }

  .blog-corps li + li {
    margin-top: var(--espacement-petit);
  }

  .blog-corps blockquote {
    margin: var(--espacement-petit) 0 var(--espacement-section);
  }

  .blog-lies {
    max-width: var(--largeur-contenu-bureau);
  }
}

/* ---- calendrier.css ---- */
/* Vue calendrier de la prise de créneau (issue n°141) — langage visuel de
   l'issue n°140 (maquette fiche-cordeur.html, 09/09, validée par Vincent) :
   points menthe sur les jours disponibles plutôt qu'un nombre de places, jour
   choisi en aplat marine, créneaux du jour en pastilles menthe. Jetons
   uniquement (tokens.css), voir CLAUDE.md. */

.calendrier-disponibilites {
  display: flex;
  flex-direction: column;
  gap: var(--espacement-section);
}

.calendrier-entete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: calc(var(--grille-base) * 2);
}

.calendrier-mois {
  font-weight: var(--graisse-semibold);
}

.calendrier-jours-semaine {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: calc(var(--grille-base) * 1);
  margin-bottom: calc(var(--grille-base) * 1);
  text-align: center;
  font-size: var(--taille-texte-4);
  font-weight: var(--graisse-semibold);
  color: var(--texte-secondaire);
}

.calendrier-grille {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: calc(var(--grille-base) * 1);
}

.calendrier-jour--passe {
  opacity: 0.4;
  pointer-events: none;
}

.calendrier-jour {
  aspect-ratio: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: calc(var(--grille-base) * 1);
  min-height: var(--cible-tactile-min);
  border-radius: var(--rayon-bouton);
  border: 1px solid var(--trait);
  background: var(--surface);
  text-decoration: none;
  color: var(--texte);
  font-weight: var(--graisse-semibold);
}

.calendrier-jour--disponible {
  border-color: var(--accent-marque);
}

.calendrier-jour--selectionne {
  background: var(--surface-haute);
  border-color: var(--surface-haute);
  color: var(--texte-sur-sombre);
}
.calendrier-jour--selectionne.calendrier-jour--disponible {
  color: var(--blanc);
}

.calendrier-jour-numero {
  font-size: var(--taille-texte-2);
}

.calendrier-jour-point {
  width: 4px;
  height: 4px;
  border-radius: var(--rayon-pastille);
  background: var(--accent-marque-encre);
}
.calendrier-jour--selectionne .calendrier-jour-point {
  background: var(--accent-marque);
}

.calendrier-jour-creneaux {
  display: flex;
  flex-direction: column;
  gap: calc(var(--grille-base) * 2);
}

/* Intertitre « MARDI 9 SEPTEMBRE » (issue n°176) : capitales espacées,
   secondaire — même traitement que les étiquettes de section (voir
   --interlettrage-etiquette). */
.calendrier-jour-creneaux-titre {
  font-size: var(--taille-texte-3);
  font-weight: var(--graisse-forte);
  color: var(--texte-secondaire);
  text-transform: uppercase;
  letter-spacing: var(--interlettrage-etiquette);
  margin: 0;
}

.creneaux-grille {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--espacement-petit);
}

.carte-creneau {
  display: block;
  text-align: center;
  text-decoration: none;
  padding: 10px 4px;
  border-radius: var(--rayon-bouton);
  border: var(--epaisseur-trait) solid transparent;
  background: var(--accent-marque-voile);
  color: var(--accent-marque-encre);
  font-weight: var(--graisse-semibold);
  font-size: var(--taille-texte-2);
}

.carte-creneau--complet {
  background: var(--surface-douce);
  color: var(--texte-secondaire);
  text-decoration: line-through;
}

/* Créneau choisi par défaut (issue n°176) : reprend l'aplat encre du jour
   sélectionné plutôt que la voile menthe, pour rester lisible comme choix
   actif du bouton « Réserver » collant sous le calendrier. */
.carte-creneau--actif {
  background: var(--surface-haute);
  color: var(--accent-marque);
  border-color: var(--surface-haute);
}

/* Bouton de réservation collant (issue n°176, maquette 09/09) : au-dessus de
   la barre de navigation basse (.nav-basse, base.css) — masqué au palier
   bureau par le même mécanisme que .fiche-doctolib (bureau.css), puisqu'il
   vit à l'intérieur de ce conteneur. */
.fiche-cordeur-collant {
  position: fixed;
  left: 0;
  right: 0;
  bottom: calc(var(--hauteur-nav-basse) + env(safe-area-inset-bottom));
  z-index: 9;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--grille-base);
  padding: var(--espacement-petit) var(--marge-ecran) var(--espacement-section);
  background: var(--fond);
  border-top: var(--epaisseur-trait) solid var(--trait);
}
.fiche-cordeur-collant-reassurance {
  display: flex;
  align-items: center;
  gap: var(--grille-base);
  font-size: var(--taille-texte-3);
  color: var(--texte-secondaire);
}
.fiche-cordeur-collant__bouton {
  width: 100%;
  text-align: center;
}
.fiche-cordeur-collant__bouton[aria-disabled='true'] {
  opacity: 0.35;
  pointer-events: none;
}

/* ---- annonce.css ---- */
/* Page d'annonce (issue n°266, src/web/pages/annonce.tsx) : en-tête, texte,
   trois cartes, pied. Aucune valeur en dur : tout vient de tokens.css. Les
   cartes réutilisent `.carte` ; seule la mise en page est propre à la page.
   `.pastille` n'a pas de définition globale (docs/design-system.md,
   Pastilles) : elle est donc redéfinie ici, scopée à `.annonce`, avec les
   couleurs de la pastille « fait / prêt » (menthe voilée). */

.annonce {
  display: flex;
  flex-direction: column;
  flex: 1;
  width: 100%;
  max-width: var(--largeur-contenu-bureau);
  margin: 0 auto;
  gap: var(--espacement-section);
}

.annonce-entete {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--espacement-section);
  padding-block: var(--marge-ecran);
}

.annonce-connexion {
  display: inline-flex;
  align-items: center;
  min-height: var(--cible-tactile-min);
  padding-inline: var(--espacement-petit);
}

.annonce-corps {
  display: flex;
  flex: 1;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--espacement-section);
  padding-block: var(--contenu-padding-bureau);
}

.annonce .pastille {
  display: inline-block;
  padding: var(--grille-base) var(--espacement-petit);
  border-radius: var(--rayon-pastille);
  background: var(--accent-marque-voile);
  color: var(--accent-marque-encre);
  font-size: var(--taille-texte-3);
  font-weight: var(--graisse-semibold);
}

.annonce-titre {
  margin: 0;
  max-width: var(--largeur-lecture);
  font-size: var(--taille-affichage-1);
  line-height: 1.15;
}

.annonce-intro {
  margin: 0;
  max-width: var(--largeur-lecture);
}

.annonce-cartes {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--espacement-petit);
  width: 100%;
  margin: 0;
  padding: 0;
  list-style: none;
}

.annonce-carte {
  display: flex;
  flex-direction: column;
  gap: var(--grille-base);
}

.annonce-carte p {
  margin: 0;
}

.annonce-carte-titre {
  font-size: var(--taille-titre-3);
  font-weight: var(--graisse-semibold);
}

.annonce-ouverture {
  margin: 0;
  font-weight: var(--graisse-semibold);
}

.annonce-pied {
  padding-block: var(--marge-ecran);
  text-align: center;
  color: var(--texte-secondaire);
  font-size: var(--taille-texte-3);
}

@media (min-width: 900px) {
  .annonce-cartes {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
  .annonce-titre {
    font-size: var(--taille-affichage-3);
  }
}
