/*
 * BARBERX, L'OUTIL — la charte de BarberX, « ÉCRIN, OR ET CHAMPAGNE » (D-BX-2, 5 novembre 2026).
 *
 * Source : `claude-master/transverse/charte-barber-x.md` (bloc DA-APP) ; les VALEURS viennent de `ineed-app`
 * `packages/charte/src/couleurs.json`, les noirs, les voiles et les polices du site (`ineed`, `assets/styles/app.css`). Elles sont
 * COPIÉES, jamais approchées : si la charte change, c'est ici qu'on recopie, et chaque token qui a un pendant dans l'app le nomme en
 * fin de commentaire (« — `nomDansLApp` »). Garde : `tests/Ecran/CharteTest`.
 *
 * ⚠️ LA RÈGLE DES TROIS COULEURS (décision du 9 octobre) :
 *   - l'OR, et son dégradé = L'ACTION : le bouton principal. Le libellé posé dessus est `--sur-or`, jamais un texte doré ;
 *   - le CHAMPAGNE = CE QUI ACCOMPAGNE : liens, contours, onglet actif, filets, chiffres, ce qui est choisi ;
 *   - le ROUGE (`--rouge-fond`) = les gestes DESTRUCTIFS, eux seuls. `--erreur` dit une erreur, il ne porte pas un geste.
 *   Les textes secondaires restent neutres (`--doux`).
 *
 * ⚠️ AUCUNE COULEUR HORS DU BLOC `:root` : les voiles sont des tokens composés ici (garde `CharteTest`). Recopies assumées, et chacune
 * le dit : `theme-color`, le manifeste, les icônes rastérisées, les e-mails (un client de messagerie ne lit pas une variable CSS).
 * ⚠️ `--trait` est un DÉCOR (1,5:1) : le bord d'un champ est `--trait-fort`.
 * ⚠️ Cormorant est FINE : sous 18 px environ, un texte se lit mieux en Manrope. Un petit titre prend `--corps` 600, jamais `--titres`.
 */

/* ═══ Les polices, auto-hébergées (SIL OFL 1.1 : `assets/fonts/OFL-*.txt`), servies par AssetMapper. Aucune requête vers un tiers :
   l'écran d'un outil connecté n'a pas à prévenir Google. Replis ajustés repris du site, mesurés par lui. ⚠️ Une graisse demandée et
   absente d'ici serait FABRIQUÉE par le navigateur (garde `CharteTest`). */
@font-face { font-family: "Cormorant Garamond"; font-weight: 600; font-style: normal; font-display: swap; src: url("../fonts/cormorant-garamond-latin-600-normal-nIkH7HP.woff2") format("woff2"); }
@font-face { font-family: "Cormorant Garamond"; font-weight: 700; font-style: normal; font-display: swap; src: url("../fonts/cormorant-garamond-latin-700-normal-F4mFegS.woff2") format("woff2"); }
@font-face { font-family: "Manrope"; font-weight: 400; font-style: normal; font-display: swap; src: url("../fonts/manrope-latin-400-normal-scmC9DJ.woff2") format("woff2"); }
@font-face { font-family: "Manrope"; font-weight: 600; font-style: normal; font-display: swap; src: url("../fonts/manrope-latin-600-normal-s3nkSBB.woff2") format("woff2"); }
@font-face { font-family: "Manrope"; font-weight: 700; font-style: normal; font-display: swap; src: url("../fonts/manrope-latin-700-normal-f4J6bba.woff2") format("woff2"); }
@font-face { font-family: "Cormorant repli"; font-weight: 600 700; src: local("Georgia Bold"), local("Georgia-Bold"), local("Times New Roman Bold"); size-adjust: 76%; }
@font-face { font-family: "Manrope repli"; font-weight: 400 700; src: local("Arial"), local("ArialMT"), local("Helvetica"); size-adjust: 101%; }

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

:root {
    color-scheme: dark;          /* les contrôles natifs (listes, barres de défilement) suivent le fond */

    /* Les noirs d'Écrin. */
    --fond: #0A0907;             /* le noir chaud d'Écrin — `fond` */
    --surface: #17140E;          /* cartes, champs — `surface` */
    --surface-haute: #231E15;    /* une piste, une carte sur une carte — `surfaceHaute` */
    --trait: #3D3424;            /* filets de DÉCOR, jamais le bord d'un champ — `bordure` */
    --trait-fort: #9C8B63;       /* le bord d'un champ ou d'une pastille — `bordureChamp` */
    --filet-carte: #6C5B43;      /* le filet d'une carte : `filetCarte` de l'app, calculé par `meler` */

    /* L'or : l'action. */
    --or: #E4B43C;               /* — `accent` */
    --or-haut: #F8DE7E;          /* — `accentHaut` */
    --or-bas: #C6992A;           /* — `accentBas` */
    --sur-or: #0A0907;           /* — `surAccent` */
    --degrade-or: linear-gradient(165deg, var(--or-haut) 0%, var(--or) 48%, var(--or-bas) 100%);

    /* Le champagne : ce qui accompagne. */
    --champagne: #E6C192;        /* — `champagne` */
    --sur-champagne: #0A0907;    /* — `surChampagne` */
    --voile-champagne: rgba(230, 193, 146, .12);

    /* Le texte. */
    --texte: #F7F3EA;            /* — `texte` */
    --doux: #CFC7B4;             /* — `texteDoux` */
    --eteint: #A69D88;           /* — `texteEteint` */
    --argent: #CFC7B4;           /* le second métal, dans les jauges — `argent` */

    /* Les états. */
    --succes: #7DD3A0;           /* — `succes` */
    --erreur: #FF8A7A;           /* un texte ou un bord d'erreur — `danger` */
    --rouge-fond: #A51D2D;       /* le fond d'un geste destructif — `dangerFond` */
    --sur-rouge: #FFFFFF;        /* — `surDanger` */

    /* Les voiles, composés ici parce qu'une règle ne porte aucune couleur. */
    --voile-clair: rgba(255, 255, 255, .03);
    --voile-erreur: rgba(255, 138, 122, .08);
    --voile-succes: rgba(125, 211, 160, .08);
    --voile-or: rgba(228, 180, 60, .14);         /* l'aire sous une courbe */
    --filet-erreur: rgba(255, 138, 122, .35);
    --filet-succes: rgba(125, 211, 160, .35);
    /* La lueur d'Écrin : l'or à 11 % qui éclaire le haut d'un écran de l'app (`ui.tsx`). Elle remplace le décor animé d'avant : rien ne bouge. */
    --lueur: radial-gradient(110% 55% at 50% -12%, rgba(228, 180, 60, .11), rgba(228, 180, 60, 0) 70%);
    /* Le fond d'un QR code : il doit rester lisible d'un téléphone, quel que soit le thème. */
    --papier: #FFFFFF;

    /* Les polices de l'app (`polices` de `theme.ts`). */
    --titres: "Cormorant Garamond", "Cormorant repli", Georgia, serif;
    --corps: "Manrope", "Manrope repli", "Segoe UI", system-ui, sans-serif;
    --chasse-fixe: var(--chasse-fixe);
    --titres-graisse: 700;       /* `polices.titre` */
    --titres-graisse-demi: 600;  /* `polices.titreDemi` */

    /* Les coins de l'app : `rayons` de `theme.ts`, un token par rôle. */
    --rayon-bouton: 999px;
    --rayon-pastille: 999px;
    --rayon-carte: 16px;
    --rayon-champ: 12px;
    --rayon-puce: 10px;
    --cible: 48px;               /* la cible minimale d'un toucher (`CIBLE_MIN` de l'app) */

    /* D-PAGE-LARGE (14 octobre 2026) : la marge latérale de la barre ET du contenu, un seul point — le logo et le titre
       de chaque page commencent sur la même colonne, à toutes les largeurs. */
    --gouttiere: clamp(20px, 3vw, 40px);
    /* La borne de LECTURE : elle s'applique au texte courant, pas à la page (un paragraphe sur 2 480 px ne se lit plus). */
    --lecture: 80ch;
}

html { background: var(--fond); -webkit-text-size-adjust: 100%; }

/* Le fond : le noir d'Écrin, éclairé par le haut comme un écran de l'app. La lueur a sa propre hauteur, en pixels : posée sur toute la
   page, elle s'étirerait avec un long tableau et ne se verrait plus. */
body {
    background: var(--lueur) no-repeat top center / 100% 560px, var(--fond);
    color: var(--texte);
    font-family: var(--corps);
    font-size: 16px;
    line-height: 1.65;
    min-height: 100vh;
    overflow-x: hidden;
    -webkit-font-smoothing: antialiased;
}

/* Les titres en Cormorant : son petit œil demande des tailles plus grandes que celles d'une sans-serif, sinon un titre se lit plus
   petit que le texte qu'il annonce. h3 reste en Manrope : à sa taille, Cormorant serait trop fine. */
h1, h2 { font-family: var(--titres); font-weight: var(--titres-graisse); letter-spacing: -.01em; line-height: 1.1; font-variant-numeric: lining-nums; }
h3 { font-family: var(--corps); font-weight: 600; line-height: 1.3; }
h1 { font-size: clamp(2.2rem, 5vw, 2.75rem); margin-bottom: 24px; }
h2 { font-size: 1.75rem; }
a { color: var(--champagne); text-decoration: none; transition: color .2s ease; }
a:hover { color: var(--texte); }
ul { list-style: none; }
a:focus-visible, button:focus-visible, summary:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible { outline: 2px solid var(--champagne); outline-offset: 3px; }

/* La barre. Plate, un filet dessous : le logo porte seul. */
.barre {
    position: relative; z-index: 1;
    border-bottom: 1px solid var(--trait);
}
/* D-BARRE-LARGE : écart 16 → 10 px entre les éléments — sans lui, un nom de 19 caractères repasse la barre sur deux lignes à 1280 (mesuré). */
.barre__nav { margin: 0; padding: 14px var(--gouttiere); display: flex; align-items: center; gap: 12px 10px; flex-wrap: wrap; }
/* Le logo est le dessin d'Écrin, jamais un nom écrit dans une police. Le nom accessible est `Plateforme::NOM`, dans l'`alt`. */
.barre__marque { display: flex; align-items: center; margin-right: 2px; }
.barre__marque img { display: block; height: 30px; width: auto; }
/* Sur un grand écran, la boîte des liens n'existe pas : les liens restent des éléments de la barre (D-BARRE-LARGE). */
.barre__liens { display: contents; }
.barre__lien { font-weight: 600; font-size: .85rem; letter-spacing: -.005em; color: var(--doux); padding: 4px 0; border-bottom: 2px solid transparent; }
.barre__lien:hover { color: var(--texte); }
.barre__lien.is-active { color: var(--texte); border-bottom-color: var(--champagne); }
/* Sans compte, et dans le sas du code : aucun lien. La barre s'efface, le logo se pose au-dessus de la carte, comme sur l'écran de
   démarrage de l'app. `:has` plutôt qu'une condition de gabarit : la règle suit ce que la barre contient vraiment. */
.barre:not(:has(.barre__lien)) { border-bottom: 0; }
.barre:not(:has(.barre__lien)) .barre__nav { flex-direction: column; justify-content: center; padding-top: clamp(40px, 9vh, 88px); padding-bottom: 0; }
.barre:not(:has(.barre__lien)) .barre__marque { margin: 0; }
.barre:not(:has(.barre__lien)) .barre__marque img { height: clamp(44px, 12vw, 56px); }
.barre:not(:has(.barre__lien)) .barre__deconnexion { order: 2; margin-top: 8px; }
.barre:not(:has(.barre__lien)) ~ .page { padding-top: 28px; }
/* D-MENU-SURVEILLANCE : la surveillance de la production est en alerte. Le mot reste lisible ; l'alerte se dit aussi au lecteur d'écran (aria-label). */
.barre__alerte { display: inline-block; width: 8px; height: 8px; margin-left: 6px; vertical-align: 2px; border-radius: 50%; background: var(--erreur); }
/* L'onglet Production / Recette (13 septembre 2026) : la recette se voit à chaque écran. */
.onglets-env { display: inline-flex; border: 1px solid var(--trait-fort); border-radius: var(--rayon-pastille); overflow: hidden; font-size: .76rem; }
.onglets-env__onglet { padding: 2px 8px; color: var(--doux); }
.onglets-env__onglet:hover { color: var(--texte); }
.onglets-env__onglet.is-active { background: var(--champagne); color: var(--sur-champagne); font-weight: 600; }
.is-recette .onglets-env__onglet.is-active { background: var(--erreur); color: var(--sur-or); }
.bandeau-recette { border: 1px dashed var(--erreur); color: var(--erreur); border-radius: var(--rayon-champ); padding: 10px 16px; margin: 0 0 24px; font-weight: 600; }
.pastille--recette { border-color: var(--erreur); color: var(--erreur); }
.barre__compte { margin-left: auto; color: var(--doux); display: inline-flex; align-items: center; gap: 10px; font-size: .88rem; }
.barre__deconnexion { margin: 0; }
.barre__nom { display: block; max-width: 8ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--doux); border-bottom: 1px dotted transparent; }
/* D-BARRE-LARGE tient en Manrope, plus large qu'Inter : à 1280, un nom long se coupe plutôt que de repasser la barre sur deux lignes (mesuré,
   lots/2026-11/D-BX-2.md). Il reste entier dans son infobulle, et l'adresse de la page mène au compte. La borne tombe où la place existe. */
@media (min-width: 1500px) { .barre__nom { max-width: none; } }
/* Sur un téléphone : le logo et la déconnexion sur la première ligne, puis les liens, tous visibles, jamais cachés derrière un menu :
   l'outil a peu d'écrans, et chaque lien reste une cible de toucher. */
@media (max-width: 759px) {
    .barre:has(.barre__lien) .barre__nav { padding-bottom: 10px; }
    .barre__liens { display: flex; flex-wrap: wrap; gap: 0 18px; order: 3; width: 100%; }
    .barre__lien { display: inline-flex; align-items: center; min-height: 40px; padding: 0; }
    .barre:has(.barre__lien) .barre__deconnexion { order: 1; margin-left: auto; }
    .onglets-env { order: 4; }
    .barre__compte { order: 5; }
    .barre__nom { max-width: 22ch; }
}
.barre__nom:hover { color: var(--texte); border-bottom-color: currentColor; }

.pastille { font-size: .78rem; border: 1px solid var(--trait-fort); border-radius: var(--rayon-pastille); padding: 2px 10px; color: var(--doux); font-family: var(--corps); font-weight: 600; }
.pastille--actif { border-color: var(--succes); color: var(--succes); }
.pastille--suspendu, .pastille--parti { border-color: var(--erreur); color: var(--erreur); }
/* D-BX-3 : un salon en test est un contrat à venir ; « sans contrat » garde la pastille neutre. */
.pastille--en_test { border-color: var(--champagne); color: var(--champagne); }
.salons__tuiles .tuile { flex: 1 1 220px; }
.salon__precision { display: block; margin-top: 2px; font-size: .82rem; white-space: normal; }

/* Les clients par offre (D-CLIENTS-BARBERX, 12 octobre 2026). L'onglet actif est un contour champagne sur son voile, le texte reste clair. */
.onglets-offre { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 18px; }
.onglets-offre__onglet { display: inline-flex; align-items: center; gap: 8px; min-height: 40px; padding: 6px 16px; border: 1px solid var(--trait); border-radius: var(--rayon-pastille); color: var(--doux); font-weight: 600; font-size: .92rem; }
.onglets-offre__onglet:hover { color: var(--texte); border-color: var(--trait-fort); }
.onglets-offre__onglet.is-active { color: var(--texte); border-color: var(--champagne); background: var(--voile-champagne); }
.onglets-offre__compte { font-variant-numeric: tabular-nums; font-size: .78rem; font-weight: 600; min-width: 22px; padding: 1px 7px; border-radius: var(--rayon-pastille); text-align: center; background: var(--surface); color: var(--doux); }
.onglets-offre__onglet.is-active .onglets-offre__compte { color: var(--champagne); }
.client-nom { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; }
/* D-PASTILLE-X (12 octobre 2026) : l'offre BarberX se dit par le X de sa marque (une image, ses couleurs de marque),
   plus par une pastille. Aligné sur la ligne du texte, rien n'est dessiné autour : le logo porte seul. */
.marque-offre { display: inline-block; vertical-align: -3px; line-height: 0; margin-left: 8px; }
.marque-offre img { display: block; width: 18px; height: 18px; }
/* Le X suit le DERNIER MOT du nom (texte courant) au lieu de tomber seul sous un nom qui passe à la ligne (390 px) :
   le nom et ses marques ne sont plus des boîtes flex, ils coulent comme une phrase. */
.client-nom:has(.marque-offre) { display: inline; }
.client-nom:has(.marque-offre) .pastille { display: inline-block; margin-left: 8px; line-height: 1.4; }
.onglets-offre__marque { display: block; width: 16px; height: 16px; flex: none; }

/* D-PAGE-LARGE (Alexandre, 14 octobre 2026 : « on peut éventuellement élargir le contenu de toutes les pages, ça nous laisse
   + de place pour faire un dashboard complet sur l'accueil ») : plus de borne de 1 200 px ; le contenu prend la largeur de la
   barre, même gouttière. Les tableaux, cartes larges et grilles s'étendent ; le TEXTE courant garde sa borne de lecture, et les
   formulaires gardent la leur (.carte : 68ch). ⚠️ Aucun test ne garde la largeur : la garde est la mesure, lots/2026-10/D-PAGE-LARGE.md. */
.page { position: relative; z-index: 1; padding: 40px var(--gouttiere) 80px; }
.page :is(p:not([class]), p.note, .help-text):not(td *, th *) { max-width: var(--lecture); }
/* Le cadre Turbo et l'élément du contrôleur `rafraichir` ne sont pas des boîtes : la page se met en page comme sans eux. */
.rafraichi { display: contents; }

/* Cartes — celles de l'app : la surface, un filet, les coins de 16. Aucune ombre : l'app n'en a pas. */
.carte {
    background: var(--surface);
    border: 1px solid var(--filet-carte);
    border-radius: var(--rayon-carte);
    padding: 30px;
    max-width: 68ch;
}
.carte--etroite { max-width: 440px; margin: 0 auto 60px; }
.carte--large { max-width: none; margin-bottom: 24px; }
.carte__titre { font-family: var(--corps); font-weight: 600; margin-bottom: 8px; color: var(--texte); }
.carte p { color: var(--doux); }
.carte > p:not(:last-child) { margin-bottom: 12px; }
.carte .carte__titre { color: var(--texte); }
.carte .alerte { color: var(--erreur); }
.carte .flash { color: var(--succes); }
.carte h1 { margin-bottom: 20px; }

/* Formulaires */
.formulaire { display: flex; flex-direction: column; gap: 6px; }
.formulaire > div { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }
.formulaire label { font-size: .9rem; font-weight: 600; color: var(--texte); margin-top: 10px; }
.formulaire input, .formulaire select, .formulaire textarea {
    font: inherit; padding: 12px 14px; border-radius: var(--rayon-champ);
    border: 1.5px solid var(--trait-fort); background: var(--fond); color: var(--texte);
    min-height: var(--cible);
    transition: border-color .2s ease;
}
.formulaire input:focus, .formulaire select:focus, .formulaire textarea:focus { outline: none; border-color: var(--champagne); }
.formulaire input[type="radio"], .formulaire input[type="checkbox"] { width: auto; min-height: 0; accent-color: var(--champagne); }
/* Une case à cocher et sa phrase sur une ligne (D-PARC-CONFIG : la confirmation d'un changement de base). */
.formulaire .case { display: flex; align-items: center; gap: 8px; color: var(--texte); }
.formulaire .help-text { font-size: .85rem; color: var(--doux); }
/* D-ECRAN — la fiche : les dates d'un écran tiennent sur une ligne ; la confirmation « Révoquer », ouverte dans la cellule
   des actions, garde une largeur de lecture (mesuré au banc : sans borne, elle écrasait les autres colonnes et débordait). */
td[data-ecran-appareil], td[data-ecran-vu] { white-space: nowrap; }
[data-revoquer-ecran] .note { white-space: normal; max-width: 34ch; }
/* Le titre d'un groupe de boutons radio (D-ECRAN : l'orientation) — l'allure d'un `label`, sans en être un. */
.formulaire .formulaire__legende { font-size: .9rem; font-weight: 600; color: var(--texte); margin-top: 10px; }
.formulaire ul { color: var(--erreur); font-size: .9rem; }
.formulaire .bouton { margin-top: 18px; align-self: flex-start; }

/* Boutons — ceux de l'app (`ui.tsx`). Le PRINCIPAL : une pilule pleine dans le dégradé or, libellé noir en Manrope 700. Le SECONDAIRE :
   un contour champagne. Aucune ombre. Cible de 48 px. */
.bouton {
    display: inline-flex; align-items: center; justify-content: center; min-height: var(--cible); padding: 11px 26px; border-radius: var(--rayon-bouton);
    font-family: var(--corps); font-size: .98rem; font-weight: 700; line-height: 1.2; cursor: pointer; border: 0;
    background: var(--degrade-or); color: var(--sur-or);
    transition: filter .2s ease, background-color .2s ease, color .2s ease;
}
.bouton:hover { filter: brightness(1.07); color: var(--sur-or); }
.bouton:active { transform: translateY(1px); }
.bouton--discret { background: transparent; border: 1.5px solid var(--champagne); color: var(--champagne); min-height: 40px; padding: 7px 18px; font-weight: 600; font-size: .92rem; }
.bouton--discret:hover { filter: none; background: var(--voile-champagne); color: var(--texte); }
/* Un bouton désactivé doit en AVOIR l'air : « Envoyer ce récap » sans destinataire paraissait actif et ne faisait rien (14 septembre 2026). */
.bouton:disabled, .bouton:disabled:hover { opacity: .45; cursor: not-allowed; filter: none; }

.alerte { background: var(--voile-erreur); color: var(--erreur); border: 1px solid var(--filet-erreur); border-radius: var(--rayon-puce); padding: 10px 14px; margin: 0 0 14px; }
.alerte-inline { color: var(--erreur); }
.flash { background: var(--voile-succes); color: var(--succes); border: 1px solid var(--filet-succes); border-radius: var(--rayon-puce); padding: 10px 14px; margin: 0 0 20px; max-width: 68ch; }

/* En-têtes de section, fil d'Ariane */
.entete { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 20px; }
.entete h1, .entete h2 { margin: 0; }
.entete--section { margin-top: 40px; margin-bottom: 14px; }
.fil { margin: 0 0 8px; font-size: .9rem; color: var(--doux); }
.m { font-family: var(--chasse-fixe); font-size: .85em; color: var(--doux); font-weight: 400; }
.note { font-size: .88rem; color: var(--doux); margin-top: 12px; }

.fiche { display: grid; grid-template-columns: max-content 1fr; gap: 6px 24px; max-width: 68ch; }
.fiche dt { color: var(--doux); }
.fiche dd { white-space: pre-line; }

/* Tableaux — sur une carte.
   D-TABLEAUX (Alexandre, 14 octobre 2026 : « effectivement la colonne est trop large pour le nom des clients ») : la carte prend
   la largeur de son CONTENU, jamais plus que la page. Depuis D-PAGE-LARGE, un tableau à 100 % donnait l'excès de largeur à sa
   colonne la plus longue (« Client » : 1 645 px à 2560 pour des noms de 622 px, le statut 1 000 px plus loin) ; répartir cet
   excès entre les colonnes ne l'aurait que déplacé (des trous entre chaque donnée, ou une carte à moitié vide). Le bord GAUCHE
   reste sur la gouttière, comme le titre ; un tableau au contenu long (un journal, un message) prend toujours toute la largeur,
   et un tableau plus large que la page défile dans sa carte, comme avant. ⚠️ Aucun test ne garde la largeur : la garde est la mesure,
   lots/2026-10/D-TABLEAUX.md. */
.tableau-cadre { width: fit-content; max-width: 100%; overflow-x: auto; background: var(--surface); border: 1px solid var(--filet-carte); border-radius: var(--rayon-carte); }
.tableau { border-collapse: collapse; width: 100%; font-variant-numeric: tabular-nums; }
.tableau th, .tableau td { text-align: left; padding: 14px 18px; border-bottom: 1px solid var(--trait); vertical-align: top; }
.tableau th { font-size: .84rem; color: var(--doux); font-weight: 600; }
.tableau tr:last-child td { border-bottom: 0; }
.tableau tr:hover td { background: var(--voile-clair); }
.actions { white-space: nowrap; display: flex; gap: 14px; align-items: center; }
.en-ligne { display: inline; }
tr.is-epuise td:first-child { box-shadow: inset 3px 0 0 var(--erreur); }
tr.is-courant td { background: var(--voile-champagne); }

/* Consommation */
.mois { display: flex; gap: 20px; font-size: .95rem; }
.conso { display: inline-flex; flex-direction: column; line-height: 1.3; }
.conso__etat { font-size: .78rem; color: var(--doux); }
.conso--ok strong { color: var(--succes); }
.conso--epuise strong, .conso--epuise .conso__etat { color: var(--erreur); }
.conso--sans_forfait .conso__etat { font-style: italic; }

/* Tableau de bord */
.tuiles { display: flex; gap: 16px; flex-wrap: wrap; margin-bottom: 28px; }
.tuile {
    background: var(--surface);
    border: 1px solid var(--filet-carte); border-radius: var(--rayon-carte); padding: 20px 26px; min-width: 200px;
    color: var(--texte); display: flex; flex-direction: column; transition: border-color .2s ease;
}
.tuile:hover { border-color: var(--champagne); color: var(--texte); }
.tuile__nombre { font-family: var(--titres); font-size: 3rem; font-weight: var(--titres-graisse); line-height: 1.05; font-variant-numeric: lining-nums tabular-nums; color: var(--champagne); }
.tuile__libelle { color: var(--doux); font-size: .9rem; }
.tuile--alerte { border-color: var(--filet-erreur); }
.tuile--alerte .tuile__nombre { color: var(--erreur); }
.liste { padding-left: 1.2em; list-style: disc; }
.liste li { margin: 6px 0; }

/* Restrictions (W3) — un libellé et son motif par ligne */
.restrictions-grille { display: grid; grid-template-columns: max-content minmax(180px, 280px); gap: 10px 20px; align-items: center; margin: 16px 0 6px; }
.formulaire .restrictions-grille label { margin-top: 0; }
@media (max-width: 560px) { .restrictions-grille { grid-template-columns: 1fr; } }

/* Échéancier et paiements (W4) — le statut est calculé au jour, la pastille le dit */
.pastille--payee { border-color: var(--succes); color: var(--succes); }
.pastille--due { border-color: var(--champagne); color: var(--champagne); }
.pastille--supplement { border-color: var(--champagne); color: var(--champagne); }
.supplement { margin-top: 16px; }
.supplement summary { cursor: pointer; font-weight: 600; }
.pastille--en_retard { border-color: var(--erreur); color: var(--erreur); }
tr.echeance--en_retard td:first-child { box-shadow: inset 3px 0 0 var(--erreur); }
.reglement { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.reglement input {
    font: inherit; padding: 6px 10px; border-radius: var(--rayon-puce);
    border: 1.5px solid var(--trait-fort); background: var(--fond); color: var(--texte);
}
.reglement input[type="text"] { width: 14em; }
.reglement .bouton { padding: 6px 16px; min-height: 40px; }
.lien-bouton { background: none; border: 0; padding: 0; font: inherit; font-size: .85rem; color: var(--doux); text-decoration: underline; cursor: pointer; }
.lien-bouton:hover { color: var(--texte); }

/* Restrictions d'une enseigne : le geste commun, puis chaque établissement */
.restrictions-enseigne { border: 1px solid var(--trait); border-radius: var(--rayon-carte); padding: 14px 16px; background: var(--voile-champagne); margin-bottom: 18px; }
.restrictions-salon + .restrictions-salon { border-top: 1px solid var(--trait); margin-top: 16px; padding-top: 16px; }

/* Installations et déploiements (W5) */
.installations { display: grid; gap: 14px; }
.installation { border: 1px solid var(--trait); border-radius: var(--rayon-carte); padding: 16px 18px; display: grid; gap: 12px; }
.installation__tete { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.installation__gestes { display: flex; flex-wrap: wrap; gap: 10px 16px; align-items: center; }
.fiche--serree { gap: 4px 20px; font-size: .92rem; }
.deployer { display: flex; flex-wrap: wrap; gap: 8px 14px; align-items: center; padding: 8px 12px; border-radius: var(--rayon-carte); background: var(--voile-champagne); }
.deployer label { font-size: .85rem; color: var(--doux); display: inline-flex; gap: 6px; align-items: center; }
.deployer .bouton { padding: 8px 18px; }
.vitrines-lancees { margin: 6px 0 0; padding-left: 18px; display: grid; gap: 4px; }
.etat-ok { color: var(--succes); }
.note-inline { font-size: .8rem; color: var(--doux); margin-left: 6px; }
.pastille--reussi { border-color: var(--succes); color: var(--succes); }
.pastille--echoue { border-color: var(--erreur); color: var(--erreur); }
.pastille--en_cours { border-color: var(--champagne); color: var(--champagne); }
/* D-SONDES-JETON : la pastille « Sondes » d’une installation BarberX (`EtatDesSondes::ton()`). */
.pastille--sondes-ok { border-color: var(--succes); color: var(--succes); }
.pastille--sondes-alerte { border-color: var(--erreur); color: var(--erreur); }
.journal { font-family: var(--chasse-fixe); font-size: .82rem; line-height: 1.5; white-space: pre-wrap; overflow-wrap: anywhere; background: var(--fond); border: 1px solid var(--trait); border-radius: var(--rayon-puce); padding: 14px 16px; max-height: 70vh; overflow: auto; margin: 12px 0; }

/* Sur un téléphone, rien ne sort de la page : les gestes d'un en-tête passent à la ligne, une liste déroulante garde la largeur de sa carte
   (une option longue l'étirait de 359 px). Les `.actions` d'une cellule restent sur une ligne : c'est le tableau qui défile. */
.pastille { display: inline-block; white-space: nowrap; }
.tuile .pastille { align-self: flex-start; }
.installations { grid-template-columns: minmax(0, 1fr); }
.fiche dd { min-width: 0; overflow-wrap: anywhere; }
.formulaire input, .formulaire select, .formulaire textarea { max-width: 100%; }
.formulaire select { min-width: 0; }
@media (max-width: 759px) {
    .actions:not(td .actions) { flex-wrap: wrap; white-space: normal; }
    .installation__tete > * { min-width: 0; overflow-wrap: anywhere; }
    .bouton { white-space: normal; }
    .carte { overflow-wrap: anywhere; }
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { transition: none !important; }
}
.conso__depassement { display: block; font-size: .78rem; color: var(--erreur); font-weight: 600; }
/* La barre porte les onglets depuis le 13 septembre 2026 : le bouton de déconnexion s y fait discret pour tenir sur une ligne. */
.barre__deconnexion .bouton { min-height: 36px; padding: 5px 13px; font-size: .84rem; }

/* ═══ Le récap d'un client (W6b) — graphiques dessinés par le serveur, aucun JavaScript ═══ */
.recap-tuiles .tuile { flex: 1 1 170px; }
.recap-portees { display: flex; flex-wrap: wrap; gap: 8px; margin: -8px 0 24px; }
.recap-portee { border: 1px solid var(--trait); border-radius: var(--rayon-pastille); padding: 5px 14px; font-size: .88rem; color: var(--doux); }
.recap-portee:hover { color: var(--texte); }
.recap-portee.is-active { background: var(--champagne); border-color: var(--champagne); color: var(--sur-champagne); font-weight: 600; }
.recap-variation { display: inline-block; margin-left: 4px; font-size: .75rem; font-weight: 600; color: var(--doux); white-space: nowrap; }
.recap-variation--hausse { color: var(--succes); }
.recap-variation--baisse { color: var(--erreur); }
.recap-courbe { margin: 8px 0 0; }
.recap-courbe svg { display: block; width: 100%; height: 200px; overflow: visible; }
.recap-courbe__grille { stroke: var(--trait); stroke-dasharray: 4 6; vector-effect: non-scaling-stroke; }
.recap-courbe__axe { stroke: var(--trait); vector-effect: non-scaling-stroke; }
.recap-courbe__aire { fill: var(--voile-or); }
.recap-courbe__ligne { fill: none; stroke: var(--or); stroke-width: 2.5; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
.recap-courbe__ligne--en-ligne { stroke: var(--champagne); stroke-dasharray: 6 4; }
.recap-courbe__legende { display: flex; flex-wrap: wrap; gap: 8px 20px; margin-top: 10px; font-size: .85rem; color: var(--doux); }
.recap-pastille { display: inline-block; width: 18px; height: 3px; vertical-align: middle; background: var(--or); border-radius: 2px; }
.recap-pastille--en-ligne { background: var(--champagne); }
.recap-colonnes { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 24px; margin-bottom: 24px; }
.recap-colonnes .carte { max-width: none; margin: 0; }
.recap-barre { display: grid; grid-template-columns: 9.5rem 1fr 6.5rem; align-items: center; gap: 12px; margin: 10px 0; font-size: .92rem; }
.recap-barre__piste { height: 10px; background: var(--surface-haute); border-radius: 6px; overflow: hidden; }
.recap-barre__jauge { display: block; height: 100%; border-radius: 6px; background: var(--or); }
.recap-barre__jauge--en_ligne { background: var(--champagne); }
.recap-barre__jauge--recurrence { background: var(--argent); }
.recap-barre__valeur { text-align: right; font-variant-numeric: tabular-nums; }
/* Les réglages des récaps : une carte par portée, les cases à cocher alignées sur leur texte. */
.reglages-recap .carte { display: flex; flex-direction: column; gap: 6px; }
/* D-PAGE-LARGE : sur la page large, une carte par portée s'étirait sur 2 480 px (un select de 1 900 px) — elles se rangent en grille. */
.reglages-recap { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 440px), 1fr)); gap: 24px; align-items: start; }
.reglages-recap .carte--large { margin-bottom: 0; }
.reglages-recap > .bouton { grid-column: 1 / -1; justify-self: start; margin-top: 0; }
.reglages-recap .case { display: flex; align-items: center; gap: 8px; color: var(--texte); }
.reglages-recap .case input { width: auto; }

.carte h3 { margin: 18px 0 4px; font-size: 1rem; color: var(--doux); }

/* ═══ Clients des salons (D-CLIENTS, 27 octobre 2026) ═══
   Les fichiers des salons, la recherche, et deux gestes irréversibles. Les couleurs sont les tokens de la charte ; le geste
   destructif est sur `--rouge-fond`, le rouge de la charte. Sous 760 px, chaque ligne devient une carte : neuf
   colonnes ne tiennent pas sur un téléphone, et un tableau qui défile cache le bouton du geste hors de l'écran. */
.visuellement-cache { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.fichiers-salons { display: flex; flex-wrap: wrap; gap: 6px 14px; margin: 0 0 18px; font-size: .92rem; }
.fichiers-salons__lien { color: var(--doux); padding: 2px 0; border-bottom: 2px solid transparent; }
.fichiers-salons__lien:hover { color: var(--texte); }
.fichiers-salons__lien.is-active { color: var(--texte); border-bottom-color: var(--champagne); }
.recherche-clients { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin: 0 0 14px; max-width: 68ch; }
.recherche-clients input[type="search"] { flex: 1 1 260px; min-width: 0; min-height: var(--cible); font: inherit; padding: 11px 16px; border-radius: var(--rayon-pastille); border: 1.5px solid var(--trait-fort); background: var(--fond); color: var(--texte); }
.recherche-clients input[type="search"]:focus { outline: none; border-color: var(--champagne); }
.recherche-clients .bouton { padding: 10px 22px; }
.compte-clients { color: var(--doux); font-size: .92rem; margin: 0 0 12px; }
.compte-clients strong { color: var(--texte); font-variant-numeric: tabular-nums; }
.client-parc__nom { display: block; font-weight: 600; }
.client-parc__etiquettes { display: inline-flex; flex-wrap: wrap; gap: 6px; margin: 4px 8px 0 0; }
.client-parc__contact { display: block; white-space: nowrap; }
.client-parc__salon { display: block; white-space: nowrap; }
.client-parc__gestes { min-width: 230px; }
.geste { margin: 0 0 8px; }
.geste > summary { list-style: none; cursor: pointer; padding: 6px 14px; font-size: .88rem; }
.geste > summary::-webkit-details-marker { display: none; }
.geste[open] > summary { border-color: var(--texte); }
.geste__formulaire { margin-top: 10px; padding: 14px 16px; border: 1px solid var(--filet-erreur); border-radius: var(--rayon-carte); background: var(--voile-erreur); max-width: 44ch; white-space: normal; }
.geste__formulaire label { margin-top: 4px; }
.geste__formulaire input[type="email"] { padding: 10px 12px; }
.geste__effet { color: var(--doux); font-size: .88rem; line-height: 1.55; }
.geste__effet strong { color: var(--texte); }
.geste__formulaire .alerte { margin: 4px 0; font-size: .88rem; }
.geste__formulaire .bouton { margin-top: 10px; white-space: normal; text-align: left; }
.bouton--danger { border-color: var(--erreur); color: var(--erreur); }
.bouton--danger:hover { background: var(--voile-erreur); color: var(--erreur); }
.bouton--danger-plein { background: var(--rouge-fond); color: var(--sur-rouge); padding: 10px 20px; }
.bouton--danger-plein:hover { color: var(--sur-rouge); }
.pagination { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-top: 18px; }
.pagination__formulaire { display: contents; }
.pagination__ou { color: var(--doux); font-size: .9rem; font-variant-numeric: tabular-nums; }
@media (max-width: 760px) {
    .tableau-cadre--cartes { background: none; border: 0; backdrop-filter: none; -webkit-backdrop-filter: none; width: 100%; overflow: visible; }
    .tableau--cartes thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
    .tableau--cartes, .tableau--cartes tbody, .tableau--cartes tr, .tableau--cartes td { display: block; width: 100%; }
    .tableau--cartes tr { background: var(--surface); border: 1px solid var(--trait); border-radius: var(--rayon-carte); padding: 14px 16px; margin-bottom: 12px; }
    .tableau--cartes td { border: 0; padding: 6px 0; display: grid; grid-template-columns: 7.5em 1fr; gap: 10px; }
    /* Chaque élément d'une cellule (l'adresse PUIS le téléphone, chaque salon) dans la colonne des valeurs, empilés. */
    .tableau--cartes td > * { grid-column: 2; }
    .client-parc__contact, .client-parc__salon { white-space: normal; overflow-wrap: anywhere; }
    .tableau--cartes td::before { content: attr(data-libelle); color: var(--doux); font-size: .8rem; padding-top: 3px; }
    .tableau--cartes td:first-child { display: block; padding-bottom: 10px; }
    .tableau--cartes td:first-child::before, .tableau--cartes td.client-parc__gestes::before { content: none; }
    .tableau--cartes td.client-parc__gestes { display: block; min-width: 0; padding-top: 10px; border-top: 1px solid var(--trait); margin-top: 6px; }
    .tableau--cartes tr:hover td { background: none; }
    .geste__formulaire { max-width: none; }
}

/* ═══ Expéditeurs SMS à déclarer (D-EXPEDITEURS, 29 octobre 2026) ═══
   Activer est le geste ATTENDU (l'or de la charte : `.bouton`) ; refuser reste le geste à part (le rouge de la charte,
   derrière un « Refuser… »). Sous 760 px, la ligne devient une carte comme les clients des salons. */
.expediteurs__nom { font-family: var(--chasse-fixe); font-size: 1.05rem; color: var(--texte); letter-spacing: .02em; }
.expediteurs__ailleurs { display: block; margin-top: 6px; font-size: .85rem; color: var(--erreur); max-width: 36ch; }
.expediteurs__gestes { min-width: 260px; }
.expediteurs__activer { margin: 0 0 10px; padding: 14px 16px; border: 1px solid var(--trait); border-radius: var(--rayon-carte); background: var(--voile-champagne); max-width: 44ch; white-space: normal; }
.expediteurs__activer .case { align-items: flex-start; margin-top: 6px; }
.expediteurs__activer .case input { width: auto; margin-top: 4px; }
.expediteurs__activer .bouton { margin-top: 10px; white-space: normal; text-align: left; }
.expediteurs__rappel { margin: 0 0 6px; color: var(--texte); }
@media (max-width: 760px) {
    .tableau--cartes td.expediteurs__gestes::before { content: none; }
    .tableau--cartes td.expediteurs__gestes { display: block; min-width: 0; padding-top: 10px; border-top: 1px solid var(--trait); margin-top: 6px; }
    .expediteurs__activer, .expediteurs__ailleurs { max-width: none; }
}

/* D-BX-1 — la double authentification. Le champ du code se lit et se touche de loin : on le tape aussi sur un écran de vitrine. */
.code-saisie { font-size: 1.6rem; letter-spacing: .2em; text-align: center; }
.double-auth__qr { display: flex; justify-content: center; padding: 12px; background: var(--papier); border-radius: var(--rayon-champ); width: fit-content; margin: 12px auto; }
.double-auth__secret, .double-auth__codes { font-family: var(--chasse-fixe); overflow-wrap: anywhere; }
.double-auth__codes { display: grid; grid-template-columns: repeat(auto-fit, minmax(22ch, 1fr)); gap: 6px 16px; list-style: none; padding: 0; margin: 12px 0; white-space: nowrap; }
