/* =========================================================================
   By Zigzag — Styles des sections de la page vitrine
   ========================================================================= */

/* ---- Hero ---- */
.hero {
    position: relative;
    min-height: var(--hauteur-plein-ecran);
    display: flex;
    align-items: center;
    /* Fond placeholder : dégradé teinté (aucune image hotlinkée).
       TODO (client) : remplacer par une vraie photo du salon.

       ⚠️⚠️ LA TRAME DÉRIVE DEPUIS LE 10 SEPTEMBRE 2026, ET ELLE FRAPPAIT LE SALON
       NEUF. Elle valait `#181818 / #151515` en dur — deux crans du thème SOMBRE.
       Le voile au-dessus (`--hero-tint`, `--hero-overlay`) dérive, lui, depuis
       l'étape 1 : sur un thème clair on voyait donc du NOIR à travers un rideau
       blanc, c'est-à-dire un grand lavis gris sale en haut de la page.
       ⚠️ ZigZag ne le voit pas — il a posé sa photo. Le cas atteint est celui du
       salon qui vient d'ouvrir, et *un gérant ne doit RIEN avoir à remplir pour
       que son site soit correct.*

       ⚠️ LE SENS DE L'ÉCART EST CONSERVÉ, PAS SEULEMENT SA VALEUR : la trame était
       plus CLAIRE que la page (0x18 et 0x15 contre 0x12), et elle le reste ici en
       dérivant vers `--c-bg-elev`. Sur le thème sombre elle rend `#181818` et
       `#151515` à l'octet près — le site d'aujourd'hui ne bouge pas. */
    background:
        var(--hero-tint),
        repeating-linear-gradient(
            45deg,
            color-mix(in srgb, var(--c-bg) 33%, var(--c-bg-elev)) 0 22px,
            color-mix(in srgb, var(--c-bg) 67%, var(--c-bg-elev)) 22px 44px
        );
    overflow: hidden;
}

/* ═══ ⚠️⚠️ LE FOND VIDÉO DU HERO *(lot CMS étape 4, 10 septembre 2026)* ═══
   Il RECOUVRE la photo, il ne la remplace pas : `.hero--photo::before` reste
   dessous et rend la bannière d'avant ce lot dès que la vidéo ne se charge
   pas — réseau lent, vidéo retirée de YouTube, lecture refusée. *La seule
   valeur qui dégrade vers du connu.*

   ⚠️ **`pointer-events: none` N'EST PAS UNE FINITION** : sans lui, cette
   couche mange les clics sur « Réserver maintenant » et sur le numéro de
   téléphone — deux boutons plein écran devenus inertes, **sans la moindre
   erreur**, et le premier geste de la page avec eux. */
.hero__video {
    position: absolute;
    inset: 0;
    overflow: hidden;
    pointer-events: none;
}

/* ═══ ⚠️⚠️ LE FOND EST UNE `<video>` NATIVE DEPUIS LE 13 SEPTEMBRE 2026 AU SOIR ═══
   Il a été une iframe YouTube pendant une journée, rognée ×1,4 pour pousser
   le titre et le logo hors champ, et révélée 4 s après la lecture pour laisser
   YouTube ranger ses commandes centrales. Mesuré à l'écran, refusé par
   Alexandre (« c'est trop long ») : le fond est désormais un FICHIER dans
   `<video>`, comme sur le-glacier.com. Ce qui est parti avec l'iframe : le
   zoom (`object-fit: cover` remplit le cadre sans rien perdre d'autre que ce
   que le cadre coupe), le calcul 16/9 en `vh`, le délai de révélation.

   ⚠️ LE LECTEUR NAÎT INVISIBLE ET `video_accueil.js` POSE `.hero__video--lue`
   SUR L'ÉVÉNEMENT NATIF `playing` : la photo reste le socle, et un autoplay
   REFUSÉ (économie d'énergie, données réduites) laisse la photo — sans aucun
   bouton au milieu, puisqu'il n'y a pas de lecteur. */
.hero__video video {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    opacity: 0;
    transition: opacity 0.6s ease;
}

.hero__video--lue video {
    opacity: 1;
}

/* ⚠️⚠️ UN MOUVEMENT DE FOND EST EXACTEMENT CE QUE `prefers-reduced-motion`
   DÉSIGNE — une animation permanente que personne n'a demandée, sur toute la
   hauteur du premier écran. On la retire, la photo reste.

   ⚠️ DEPUIS LE 12 SEPTEMBRE 2026, CETTE RÈGLE EST LA CEINTURE, PAS LE
   MÉCANISME : c'est `video_accueil.js` qui, sous `prefers-reduced-motion`, ne
   crée PAS la vidéo — la requête ne part pas. Cette règle reste
   pour le cas où le réglage change après le chargement de la page. *Une garde
   doit énoncer sa portée, sinon on lui prête celle de son nom.* */
@media (prefers-reduced-motion: reduce) {
    .hero__video {
        display: none;
    }
}

.hero__overlay {
    position: absolute;
    inset: 0;
    background: var(--hero-overlay);
}

/* Fondu du bas du hero vers le fond noir de la page : la photo se dissout
   dans la section suivante au lieu d'une coupe nette. Au-dessus de la photo
   et de l'overlay, mais SOUS le contenu (hero__inner, z-index 2). */
.hero::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: clamp(90px, 18vh, 180px);
    background: linear-gradient(to bottom, transparent, var(--c-bg));
    pointer-events: none;
    z-index: 1;
}

/* Hero avec photo du salon (Media catégorie hero, changeable en admin).
   Le flou vit sur ::before : le contenu au-dessus reste net ; le scale
   masque le halo clair que blur() crée sur les bords. L'overlay sombre
   (élément suivant) garantit le contraste du texte. */
.hero--photo::before {
    content: '';
    position: absolute;
    inset: 0;
    background: var(--hero-image) center / cover no-repeat;
    filter: blur(8px);
    transform: scale(1.06);
}

/* Logo du salon centré au-dessus du titre (Media catégorie logo). */
.hero__logo {
    display: block;
    margin: 0 auto 1.25rem;
    width: clamp(110px, 24vw, 200px);
    height: auto;
    filter: drop-shadow(0 4px 18px rgba(0, 0, 0, 0.6));
}

.hero__inner {
    position: relative;
    z-index: 2; /* au-dessus du fondu de bas de hero (.hero::after) */
    text-align: center;
    padding: 4rem 1.25rem;
}

.hero__eyebrow {
    text-transform: uppercase;
    letter-spacing: 4px;
    font-size: 0.85rem;
    color: var(--c-accent-texte);
    font-weight: 600;
}

/* Logo en grand au centre quand il remplace le titre texte. */
.hero__logo--titre {
    width: clamp(160px, 32vw, 300px);
}

/* La ligne dorée passe SOUS le logo : même style que l'eyebrow,
   seuls les espacements changent (le baseline gris a disparu). */
.hero__eyebrow--sous-logo {
    margin: 0.75rem 0 2.25rem;
}

.hero__title {
    font-size: clamp(3rem, 12vw, 7rem);
    text-transform: uppercase;
    margin: 0.25rem 0;
}

.hero__baseline {
    font-size: clamp(1.1rem, 3vw, 1.5rem);
    color: var(--c-text-muted);
    margin-bottom: 2rem;
}

.hero__actions {
    display: flex;
    gap: 1rem;
    justify-content: center;
    flex-wrap: wrap;
}

/* ---- Prestations ---- */
.presta-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 1rem;
}

.presta-card {
    display: grid;
    grid-template-columns: 1fr auto auto;
    align-items: center;
    gap: 1.5rem;
    background-color: var(--c-bg-elev);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    padding: 1.25rem 1.5rem;
    transition: border-color 0.18s ease, transform 0.12s ease;
}

/* ═══ « VOTRE DERNIER RENDEZ-VOUS » — LA CARTE INVERSÉE (item 40, 31 août 2026) ═══
   *« Au même format que les autres prestations juste d'une couleur inversée pour
   que le client voie en premier le rdv qu'il a pris la dernière fois »*
   (Alexandre).

   ⚠️ ELLE HÉRITE DE `.presta-card` ET N'EN REDÉFINIT QUE LES COULEURS. La
   grille, les espacements et le comportement au survol restent CEUX DE LA
   LISTE : c'est ce qui la fait s'aligner sur ses voisines, y compris le jour où
   la liste changera de mise en page. Une carte bâtie à part aurait dérivé au
   premier ajustement — et personne ne l'aurait vu venir, les deux blocs étant
   justes séparément.

   ⚠️ LES SOUS-ÉLÉMENTS SONT REPEINTS NOMMÉMENT, un par un. `.presta-card__desc`
   et `__duration` sont en `--c-text-muted`, `__price` en `--c-accent` : sur un
   fond accent, les trois deviendraient illisibles ou invisibles. Poser la
   couleur de fond sans les reprendre aurait donné une carte à moitié effacée,
   parfaitement valide dans le HTML — le genre de défaut qu'aucun test de ce
   dépôt ne peut voir. */
.presta-card--reprise {
    background-color: var(--c-accent-texte);
    border-color: var(--c-accent-texte);
    color: var(--c-accent-contrast);
}

.presta-card--reprise .presta-card__desc,
.presta-card--reprise .presta-card__duration,
.presta-card--reprise .presta-card__price {
    color: var(--c-accent-contrast);
}

/* ═══ ⚠️ LA CARTE DE REPRISE CHANGE DE PLACE AU TÉLÉPHONE (6 septembre 2026) ═══
   *(Demande d'Alexandre, capture à l'appui : « il déménage dans le hero
   UNIQUEMENT sur téléphone ».)*

   Sur grand écran elle reste en tête de la liste des prestations ; sous 560 px
   elle vit dans le hero, dans l'espace laissé libre sous le numéro. **Les DEUX
   sont dans le HTML** — `_reprise.html.twig` est leur point unique — et c'est
   cette paire de règles, ELLE SEULE, qui décide laquelle s'affiche.

   ⚠️ **LE SEUIL N'EST ÉCRIT QUE LÀ.** Aucun gabarit ne sait sur quel écran il
   est rendu ; le recopier dans du PHP ou du JS donnerait deux sources pour un
   seul point de rupture, qui divergeraient au premier changement. C'est le
   motif déjà écrit dans `_barre.html.twig` du board pour ses boutons de vue.

   ⚠️ **560 px, PAS 760** : c'est le seuil où `.presta-card` se réorganise déjà
   pour le téléphone (plus bas dans cette feuille). En prendre un autre ferait
   exister une bande de largeurs où la carte a la forme du téléphone tout en
   étant encore dans la liste — un état que personne n'a décidé.

   ⚠️ **`display: grid` ET PAS `block` DANS LA REPRISE** : `.presta-card` est une
   grille (trois colonnes, réorganisées en deux sous 560 px). La rétablir en
   `block` empilerait ses trois zones sans gouttière et sans alignement — le
   genre de défaut qu'aucun test de ce dépôt ne peut voir, puisqu'il ne mesure
   aucun pixel. */
.presta-card--reprise-etroit {
    display: none;
}

@media (max-width: 560px) {
    .presta-card--reprise-large {
        display: none;
    }

    .presta-card--reprise-etroit {
        display: grid;
        /* Le hero centre son contenu ; la carte, elle, aligne son texte à
           gauche comme dans la liste des prestations. Sans ça, les trois zones
           héritent du `text-align: center` de `.hero__inner`. */
        text-align: left;
        margin-top: 1.25rem;
    }
}

/* Le surtitre : c'est lui qui dit POURQUOI cette carte est en tête. */
.presta-card__reprise-surtitre {
    margin: 0 0 0.25rem;
    font-family: var(--police-titre);
    text-transform: uppercase;
    letter-spacing: 2px;
    font-size: 0.72rem;
    opacity: 0.75;
}

/* ⚠️ LE BOUTON S'INVERSE AUSSI : `btn--primary` est un fond accent sur texte
   sombre — posé sur une carte accent, il DISPARAÎT. Ici, le contraste est
   repris à l'envers. */
.presta-card--reprise .btn--primary {
    background: var(--c-accent-contrast);
    color: var(--c-accent-texte);
    border-color: var(--c-accent-contrast);
}

/* ═══ « VOTRE PROCHAIN RENDEZ-VOUS » — LA MÊME CARTE, AVEC TROIS GESTES (lot Z-1c) ═══
   Elle hérite de `.presta-card--reprise` (couleurs inversées, deux
   emplacements) et n'en change que ce que ses gestes exigent.

   ⚠️ UNE SEULE COLONNE : trois boutons et un message ne tiennent pas dans la
   colonne `auto` de droite où la carte « la dernière fois » pose son unique
   bouton — la rangée passe SOUS le texte.

   ⚠️ LES GESTES SONT REPEINTS NOMMÉMENT, comme les sous-éléments de la carte
   inversée : `btn--ghost` a un contour `--c-border` presque invisible sur l'or,
   `btn--danger` un texte rouge illisible sur l'or, et `.rdv-card__aide` est en
   `--c-text-muted`. Rien de tout ça ne se voit dans le HTML. */
.presta-card--prochain {
    grid-template-columns: 1fr;
}

.presta-card--prochain .rdv-card__actions {
    margin-top: 0;
}

/* « Réserver la même chose une autre fois » passe sur deux lignes à 390 px :
   sans ça, son texte s'aligne à gauche sous deux voisins centrés (vu au rendu). */
.presta-card--prochain .btn {
    text-align: center;
}

.presta-card--prochain .btn--ghost,
.presta-card--prochain .btn--ghost:hover,
.presta-card--prochain .btn--ghost:disabled:hover {
    color: var(--c-accent-contrast);
    border-color: var(--c-accent-contrast);
}

.presta-card--prochain .rdv-card__aide,
.presta-card--prochain .rdv-card__tel a,
.presta-card--prochain .presta-card__tous a {
    color: var(--c-accent-contrast);
}

.presta-card__tous {
    margin: 0.8rem 0 0;
    font-size: 0.9rem;
}

.presta-card__tous a {
    text-decoration: underline;
}

.presta-card:hover {
    border-color: var(--c-accent-texte);
    transform: translateY(-2px);
}

.presta-card--placeholder {
    border-style: dashed;
    opacity: 0.7;
}

.presta-card__name {
    font-size: 1.2rem;
    margin: 0 0 0.2rem;
    text-transform: none;
    letter-spacing: 0;
}

.presta-card__desc {
    margin: 0;
    color: var(--c-text-muted);
    font-size: 0.92rem;
}

.presta-card__meta {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    line-height: 1.3;
}

.presta-card__duration {
    color: var(--c-text-muted);
    font-size: 0.85rem;
}

.presta-card__price {
    font-family: var(--police-titre);
    font-size: 1.5rem;
    font-weight: 700;
    color: var(--c-accent-texte);
}

/* ---- Concept ---- */
/* DEUX ÉTATS, et un seul modificateur pour passer de l'un à l'autre.

   Sans photo (`salon` vide au board) : UNE colonne, le texte borné pour rester
   lisible plutôt que de s'étirer sur toute la largeur. Les quatre cases
   « Photo à venir » de la maquette d'origine ne reviennent pas — un gabarit
   d'attente barré donne à un site l'air d'un chantier.

   Avec photos : DEUX colonnes, texte à gauche, photos du LIEU à droite, comme
   sur byzigzag.webergeron.com. C'est .concept__grid--illustre, posé par le
   gabarit quand — et seulement quand — il a des images à mettre. Le CSS ne
   décide donc jamais seul d'ouvrir une colonne qui resterait vide. */
.concept__grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 3rem;
}

.concept__grid--illustre {
    grid-template-columns: 1fr 1fr;
    align-items: center;
}

.concept__text {
    max-width: 68ch;
}

.concept__text p {
    color: var(--c-text-muted);
}

.gallery {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.75rem;
    /* Portée par un <ul> dans la galerie publique : pas de puces, pas de
       retrait — la grille reste une grille. */
    list-style: none;
    margin: 0;
    padding: 0;
}

/* ⚠️ LA SECONDE TRAME DU DÉPÔT, ET ELLE N'AVAIT PAS ÉTÉ SIGNALÉE — 10 septembre
   2026. Même défaut que celle du hero, à quatorze pixels près : `#1d1d1d` et
   `#191919` en dur, avec une légende qui suit le thème par-dessus
   (`.gallery__placeholder` lit `--c-text-muted`). Sur `clair` : **3,00:1**.
   *On corrige l'instance qu'on regarde, pas le défaut — sauf à chercher la
   VALEUR dans tout le dépôt*, ce qu'un balayage par CLARTÉ CALCULÉE a fait ici,
   là où ma première liste de teintes écrite de mémoire avait manqué celle-ci.
   ⚠️ Elle encadre `--c-bg-elev` (#1b1b1b) au lieu de `--c-bg` : c'est pour ça
   qu'elle dérive de lui, et non du fond de page comme le hero. Rendu identique
   sur le thème sombre. */
.gallery__item {
    aspect-ratio: 1 / 1;
    display: flex;
    align-items: center;
    justify-content: center;
    background:
        repeating-linear-gradient(
            45deg,
            color-mix(in srgb, var(--c-bg-elev) 78%, var(--c-bg-elev-2)) 0 14px,
            color-mix(in srgb, var(--c-bg-elev) 78%, var(--c-bg)) 14px 28px
        );
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
}

.gallery__placeholder {
    color: var(--c-text-muted);
    font-size: 0.85rem;
    text-transform: uppercase;
    letter-spacing: 1px;
}

/* Item photo : l'image remplit la case, légende superposée en bas.
   Fond UNI, qui remplace les rayures de .gallery__item : celles-ci annoncent
   « emplacement à remplir », ce qui est juste pour un gabarit et faux ici. On
   les verrait par transparence sous un PNG à fond transparent, et surtout à la
   place d'une photo qui ne charge pas — donnant à une vraie galerie l'air d'un
   chantier. La case garde sa forme dans les deux cas et affiche le texte
   alternatif : la grille reste une grille. */
.gallery__item--photo {
    position: relative;
    margin: 0;
    overflow: hidden;
    background: var(--c-bg-elev);
}

.gallery__item--photo img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* ⚠️⚠️ LA LÉGENDE NE SUIT PAS LE THÈME, ET C'EST L'INVERSE DES QUATRE AUTRES
   CORRECTIFS DU 10 SEPTEMBRE 2026 — à ne pas « harmoniser ».

   Elle se pose sur une PHOTO, pas sur une surface du thème. Le voile sombre
   n'est donc pas une couleur de thème restée en dur : c'est ce qui rend un texte
   lisible par-dessus une image **dont personne ne connaît les couleurs**. Il
   reste noir, sur les deux thèmes, et il le doit.

   ⚠️ **CE QUI ÉTAIT FAUX, C'ÉTAIT LE TEXTE** : `var(--c-text)` le faisait suivre
   le thème, donc `#241f17` — presque noir — posé sur ce voile noir dès qu'un
   salon passait en clair. **3,00:1 pour la trame voisine, pire encore ici.**
   Un texte sur image se peint clair, toujours, et ce blanc n'est pas une couleur
   de thème recopiée : c'est la seule valeur juste des deux côtés.

   ⚠️ **TROUVÉE EN ÉLARGISSANT LA GARDE, PAS EN REGARDANT L'ÉCRAN** — la galerie
   du décor de dev n'a pas de photo, donc cette règle ne se rendait nulle part.
   *La borne d'une garde se trouve en la posant trop large, puis en regardant ce
   qu'elle casse.* */
.gallery__caption {
    position: absolute;
    inset: auto 0 0 0;
    padding: 0.4rem 0.7rem;
    font-size: 0.8rem;
    color: #ffffff;
    background: linear-gradient(transparent, rgba(0, 0, 0, 0.75));
}

/* Galerie en section autonome (photos du salon et réalisations) : grille plus
   large, qui se remplit d'elle-même — trois photos ou douze, la mise en page
   tient sans qu'on compte. */
.gallery--large {
    grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
    margin-top: 2rem;
}

.gallery--large .gallery__item {
    aspect-ratio: 4 / 5;
}

/* ---- Horaires ---- */
/* Fond « élevé » qui se fond dans la section précédente (fond de base) sur les
   premiers pixels : couture galerie → horaires adoucie (voir aussi .section--avis
   pour le bas du bloc élevé). */
.section--horaires {
    background-color: var(--c-bg-elev);
    background-image: linear-gradient(to bottom, var(--c-bg), transparent 120px);
    background-repeat: no-repeat;
}

.horaires-list {
    list-style: none;
    margin: 0 auto;
    padding: 0;
    max-width: 620px;
}

.horaires-row {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.85rem 0.25rem;
    border-bottom: 1px solid var(--c-border);
}

.horaires-row__day {
    font-weight: 600;
}

.horaires-row__slots {
    color: var(--c-text-muted);
    text-align: right;
}

.horaires-row.is-closed .horaires-row__slots {
    font-style: italic;
}

/* « une semaine sur N », ou la reprise annoncée (6 septembre 2026).
   Posée SOUS les horaires, plus petite : c'est une précision, pas l'horaire.
   `display: block` la met à la ligne — la ligne du jour est un flex, une
   deuxième phrase à côté écraserait les heures sur les écrans étroits.

   ⚠️ CETTE RÈGLE DOIT EXISTER POUR QUE LA CLASSE VEUILLE DIRE QUELQUE CHOSE.
   Ce dépôt a déjà porté trois classes de gabarit auxquelles AUCUNE règle ne
   correspondait — invisibles à tout ce qui lit le HTML servi. */
.horaires-row__reprise {
    display: block;
    font-size: 0.85em;
    font-style: italic;
    opacity: 0.75;
}

.horaires-row.is-today {
    color: var(--c-accent-texte);
}

.horaires-row.is-today .horaires-row__slots {
    color: var(--c-accent-texte);
}

/* ---- Adresse ---- */
.adresse__grid {
    display: grid;
    grid-template-columns: 1fr 1.2fr;
    gap: 2.5rem;
    align-items: center;
}

.adresse__line {
    font-size: 1.1rem;
    margin-bottom: 0.5rem;
}

.adresse__map {
    aspect-ratio: 16 / 10;
    border-radius: var(--radius);
    overflow: hidden;
    border: 1px solid var(--c-border);
}

/* ---- Page "bientôt disponible" ---- */
.section--soon {
    min-height: var(--hauteur-plein-ecran);
    display: flex;
    align-items: center;
}

.soon {
    text-align: center;
    max-width: 620px;
    margin: 0 auto;
}

.soon__lead {
    color: var(--c-text-muted);
    font-size: 1.1rem;
    margin-bottom: 2rem;
}

.soon__actions {
    display: flex;
    gap: 1rem;
    justify-content: center;
    flex-wrap: wrap;
}

/* ---- Responsive sections ---- */
@media (max-width: 800px) {
    /* Le concept repasse en UNE colonne : le texte d'abord, les photos du lieu
       dessous — deux colonnes de 190 px rendraient l'un illisible et les autres
       minuscules. */
    .concept__grid--illustre,
    .adresse__grid {
        grid-template-columns: 1fr;
    }
}

@media (max-width: 560px) {
    .presta-card {
        grid-template-columns: 1fr auto;
        grid-template-areas:
            "main meta"
            "action action";
        gap: 0.75rem 1rem;
    }
    .presta-card__main { grid-area: main; }
    .presta-card__meta { grid-area: meta; }
    .presta-card__action { grid-area: action; }
    .presta-card__action .btn { width: 100%; }

    /* Galerie « Nos coupes » : sur mobile, 2 photos carrées par ligne
       (au lieu d'une seule colonne 4/5 issue de l'auto-fill desktop). */
    .gallery--large {
        grid-template-columns: 1fr 1fr;
        gap: 0.5rem;
    }
    .gallery--large .gallery__item {
        aspect-ratio: 1 / 1;
    }
}

/* ---- Tunnel de réservation ---- */
.section--tunnel {
    min-height: var(--hauteur-plein-ecran);
}

.tunnel-steps {
    display: flex;
    justify-content: center;
    gap: 0.5rem;
    list-style: none;
    margin: 0 0 2.5rem;
    padding: 0;
    flex-wrap: wrap;
}

.tunnel-steps__step {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4rem 0.9rem;
    border: 1px solid var(--c-border);
    border-radius: 999px;
    color: var(--c-text-muted);
    font-size: 0.85rem;
}

.tunnel-steps__num {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.5rem;
    height: 1.5rem;
    border-radius: 50%;
    background-color: var(--c-bg-elev-2);
    font-size: 0.78rem;
    font-weight: 600;
}

.tunnel-steps__step.is-current {
    border-color: var(--c-accent-texte);
    color: var(--c-text);
}

.tunnel-steps__step.is-current .tunnel-steps__num {
    background-color: var(--c-accent-texte);
    color: var(--c-accent-contrast);
}

.tunnel-steps__step.is-done {
    color: var(--c-accent-texte);
}

/* ═══ « La dernière fois, vous aviez pris… » — item 40 ═══
   Le raccourci d'une cliente qui revient, à l'entrée du tunnel.

   ⚠️ ELLE EMPRUNTE LA LARGEUR ET LE CADRE DE `.tunnel-recap`, jamais ses
   classes : les deux blocs se suivent à l'écran et doivent s'aligner, mais ils
   ne disent pas la même chose — l'un RÉCAPITULE ce qu'on vient de choisir,
   l'autre PROPOSE quelque chose. Réutiliser la classe aurait fait hériter d'un
   style pensé pour l'autre contexte, et le lot du 24 août a mesuré ce que ça
   coûte : une pastille de 12 px conçue pour une ligne de tableau, reprise dans
   un cadre de 9 rem, perdue au milieu du vide — avec la bonne classe dans le
   HTML et 1887 tests verts.

   ⚠️ LA BORDURE EST PLEINE, PAS SEULEMENT À GAUCHE : c'est ce qui la distingue
   du récapitulatif juste en dessous, sur un écran où les deux peuvent
   apparaître à la suite. */
.tunnel-reprise {
    max-width: 640px;
    margin: 0 auto 1.6rem;
    padding: 1.1rem 1.4rem;
    background-color: var(--c-bg-elev);
    border: 1px solid var(--c-accent-interface);
    border-radius: var(--radius);
}

.tunnel-reprise__titre {
    font-family: var(--police-titre);
    text-transform: uppercase;
    letter-spacing: 2px;
    font-size: 0.8rem;
    color: var(--c-accent-texte);
    margin: 0 0 0.5rem;
}

.tunnel-reprise__detail {
    margin: 0 0 0.9rem;
    line-height: 1.5;
}

.tunnel-reprise__meta {
    display: block;
    margin-top: 0.15rem;
    font-size: 0.85rem;
    color: var(--c-text-muted);
}

/* Récapitulatif de la sélection en cours */
.tunnel-recap {
    max-width: 640px;
    margin: 0 auto 2.5rem;
    padding: 1.1rem 1.4rem;
    background-color: var(--c-bg-elev);
    border: 1px solid var(--c-border);
    border-left: 3px solid var(--c-accent-interface);
    border-radius: var(--radius);
}

.tunnel-recap__title {
    font-family: var(--police-titre);
    text-transform: uppercase;
    letter-spacing: 2px;
    font-size: 0.8rem;
    color: var(--c-accent-texte);
    margin: 0 0 0.6rem;
}

.tunnel-recap__list {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    margin: 0;
}

.tunnel-recap__list div {
    display: flex;
    gap: 0.75rem;
    align-items: baseline;
}

.tunnel-recap__list dt {
    min-width: 110px;
    color: var(--c-text-muted);
    font-size: 0.85rem;
}

.tunnel-recap__list dd {
    margin: 0;
    font-weight: 500;
}

/* Photo du coiffeur choisi, alignée avec son nom dans le récap. */
.tunnel-recap__coiffeur {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.tunnel-recap__photo {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    object-fit: cover;
    border: 1px solid var(--c-border);
}

.tunnel-recap__ajout {
    margin: 0.75rem 0 0;
}

/* Sous la liste des coiffeurs (étape 2), le même bouton, détaché de la liste. */
.tunnel-recap__ajout--bas {
    margin-top: 2rem;
    text-align: center;
}

/* ═══ LE BOUTON D'AJOUT DU TUNNEL (Z-1a, 24 septembre 2026) ═══
   « Ajouter une prestation à la suite » / « Modifier ma sélection » (le partiel
   `_lien_selection`, étapes 2 à 4) et « Ajouter une autre prestation » (étape 1).
   ZigZag : un lien texte, des clients de 40 ans et plus qui ne savent pas où
   cliquer. Un vrai bouton : contour d'accent, texte en casse normale (les
   capitales de `.btn` se lisent mal sur une phrase), 48 px de haut au moins —
   la cible tactile recommandée —, et pleine largeur sur téléphone. */
/* Z-1d (recette d'Alexandre, 25 septembre 2026) : « Changer de coiffeur » et
   « Choisir un autre créneau » étaient des liens gris dans `.tunnel-back` — « un
   bouton un peu plus voyant ». Ils prennent CE MÊME habit (`tunnel-retour`) : même
   geste de navigation dans le tunnel, même aspect, aucune valeur nouvelle. */
.btn.tunnel-lien-selection,
.btn.tunnel-ajout-autre,
.btn.tunnel-retour,
.btn.presta-card__ajouter {
    border-color: var(--c-accent-texte);
    color: var(--c-accent-texte);
    text-transform: none;
    letter-spacing: 0.3px;
}

.btn.tunnel-lien-selection,
.btn.tunnel-ajout-autre,
.btn.tunnel-retour {
    min-height: 3rem;
    font-size: 1rem;
}

.btn.tunnel-ajout-autre {
    display: flex;
    width: 100%;
    margin: 0 0 0.75rem;
}

.tunnel-lien-selection__signe,
.tunnel-ajout-autre__signe {
    font-size: 1.25rem;
    line-height: 1;
}

/* L'ancre de « Ajouter une autre prestation » : l'intertitre arrive sous
   l'en-tête collant, pas dessous. */
.tunnel-catalogue {
    scroll-margin-top: 5rem;
}

@media (max-width: 560px) {
    .btn.tunnel-lien-selection,
    .btn.tunnel-retour {
        display: flex;
        width: 100%;
    }
}

/* Le rendez-vous RÉGULIER, proposé à l'étape de confirmation (lot 13). Encart
   à part dans le formulaire de confirmation : cocher change la route appelée
   (POST /api/client/series au lieu de POST /api/reservations), donc l'engagement
   pris. Ça ne doit pas ressembler à une option de mise en forme. */
.tunnel-recurrence {
    margin: 0 0 1.1rem;
    padding: 1rem 1.2rem;
    background-color: var(--c-bg-elev);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
}

/* ⚠️ `center`, PAS `baseline`. Signalé par Alexandre le 30 août 2026 : la case
   paraissait décalée vers le bas. `align-items: baseline` pose le BAS de la case
   sur la ligne de base du texte — ce qui est correct pour deux textes, et faux
   pour une case et un texte. Le libellé tient sur une ligne : le centrage est
   donc exact, sans compensation. */
.tunnel-recurrence__case {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    color: var(--c-text);
    font-weight: 500;
    cursor: pointer;
}

/* La case ne rétrécit pas quand le libellé se replie sur deux lignes — c'est
   la cause de débordement relevée au lot H, et elle vaut ici aussi. */
/* ═══ ITEM 17 (2ᵉ passe) — LES CASES DU TUNNEL SONT DESSINÉES, PLUS SUBIES ═══
   Signalé par Alexandre le 30 août 2026, capture à l'appui : les deux cases
   n'avaient pas la même allure. Et pour une raison qui ne se voit qu'à
   l'écran : `accent-color` ne teinte QUE l'état COCHÉ. Décochée, une case
   restait le carré blanc du navigateur, au milieu d'une carte sombre.

   ⚠️ C'EST EXACTEMENT LE DÉFAUT QUE CE PROJET NOMME : *vérifier le HTML servi
   n'est pas vérifier un écran*. La règle `accent-color` était là, un test
   l'aurait trouvée, et la case était quand même laide — dans un seul de ses
   deux états.

   `appearance: none` retire le dessin du navigateur ; tout ce qui suit le
   remplace, DANS LES DEUX ÉTATS. La coche est une image de fond plutôt qu'un
   `::after` : sur un élément remplacé, les pseudo-éléments ne sont pas rendus
   de façon fiable partout. */
.tunnel-case {
    flex: none;
    appearance: none;
    -webkit-appearance: none;
    width: 1.15rem;
    height: 1.15rem;
    margin: 0;
    border: 1px solid var(--c-border);
    border-radius: 5px;
    background-color: var(--c-bg);
    cursor: pointer;
    transition: background-color 0.12s ease, border-color 0.12s ease;
}

.tunnel-case:hover {
    border-color: var(--c-accent-texte);
}

.tunnel-case:checked {
    border-color: var(--c-accent-texte);
    background-color: var(--c-accent-texte);
    /* La coche, dessinée en SVG et teintée en sombre pour rester lisible sur
       le doré. `%23` est un « # » échappé : une URL de données n'admet pas le
       caractère brut. */
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%231a1a1a' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round' d='M3.5 8.5l3 3 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: center;
    background-size: 0.9rem;
}

/* ⚠️ LE FOCUS RESTE VISIBLE. Retirer le dessin du navigateur retire AUSSI son
   anneau de focus : sans cette règle, la case devient invisible au clavier. */
.tunnel-case:focus-visible {
    outline: 2px solid var(--c-accent);
    outline-offset: 2px;
}

/* ITEM 17 — « PRÉVENEZ-MOI SI UNE PLACE SE LIBÈRE PLUS TÔT ».

   ⚠️ ELLE EMPRUNTE LA MISE EN FORME DE SA VOISINE, PAS SA CLASSE. Reprendre
   `.tunnel-recurrence__case` aurait rangé une case de consentement dans le bloc
   des réglages de récurrence : le jour où ce bloc bouge, celle-ci suivrait sans
   raison. *Tout emprunt d'une classe conçue pour un autre contexte se regarde à
   l'écran* — ce dépôt l'a payé avec la pastille d'équipe reprise à 9 rem.

   ⚠️ ET LE `small` EST EN BLOC : c'est lui qui porte « la place revient à la
   première personne qui la prend ». Sur la même ligne, il se ferait tronquer au
   téléphone — et c'est précisément la phrase qui évite quatre déçus sur cinq. */
/* ⚠️ ENCADRÉE COMME SA VOISINE, ET DÉCOLLÉE DU BOUTON — demande d'Alexandre du
   30 août 2026 : « encadre le prévenez-moi comme la section du dessus, avec un
   peu de margin en dessous pour pas coller le bouton de confirmation ».

   Les valeurs sont celles de `.tunnel-recurrence`, à l'identique : deux cartes
   de la même page qui se ressembleraient « à peu près » se verraient. Le
   `margin-bottom` est le seul écart, et c'est celui qu'il a demandé. */
/* ⚠️ UNE GRILLE, PARCE QUE LE LIBELLÉ TIENT SUR DEUX LIGNES. En flex, la case ne
   pouvait s'aligner que sur le BLOC entier (donc trop bas) ou sur la ligne de
   base (donc décalée) — c'est ce qu'Alexandre voyait. En grille, la case et le
   TITRE partagent une rangée qu'on centre vraiment, et l'explication se range
   sous le titre.

   ⚠️ ET IL N'Y A AUCUNE VALEUR MAGIQUE À AJUSTER : pas de `margin-top` calculé
   sur une hauteur de ligne, qui se déréglerait au premier changement de taille
   de police. C'est la structure qui aligne, pas un nombre. */
.tunnel-alerte {
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: center;
    column-gap: 0.6rem;
    margin: 1.1rem 0 1.4rem;
    padding: 1rem 1.2rem;
    background-color: var(--c-bg-elev);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    color: var(--c-text);
    font-weight: 500;
    cursor: pointer;
}

.tunnel-alerte small {
    grid-column: 2;   /* sous le TITRE, jamais sous la case */
    display: block;
    margin-top: 0.15rem;
    color: var(--c-text-muted);
    font-weight: 400;
    font-size: 0.82rem;
    line-height: 1.35;
}

/* `minmax(0, 1fr)` et non `1fr` : un `<select>` a une largeur minimale
   intrinsèque (celle de sa plus longue option), et `1fr` la laisserait
   déborder la grille au lieu de la comprimer. Mesuré au lot H. */
.tunnel-recurrence__reglages {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(11rem, 100%), 1fr));
    gap: 0.9rem;
    margin-top: 0.9rem;
}

.tunnel-recurrence__reglages label {
    display: block;
    font-size: 0.9rem;
    color: var(--c-text-muted);
    margin-bottom: 0.35rem;
}

.tunnel-recurrence__reglages select {
    width: 100%;
    padding: 0.65rem 0.85rem;
    background-color: var(--c-bg);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    color: var(--c-text);
    font: inherit;
}

.tunnel-recurrence__reglages select:focus {
    outline: 2px solid var(--c-accent);
    outline-offset: -1px;
}

/* Le refus du moteur, sous SON champ. Le texte porte l'information (il nomme
   la borne) ; la couleur ne fait que la souligner. */
.tunnel-recurrence__error {
    margin: 0.4rem 0 0;
    font-size: 0.88rem;
    color: #e88;
}

/* ═══ LE BLOC RÉGULIER EST REPLIÉ TANT QUE LA CASE N'EST PAS COCHÉE ═══
   Demande de ZigZag, 5 septembre 2026.

   ⚠️ PAS D'ATTRIBUT `hidden`, ET C'EST MESURÉ : `.tunnel-recurrence__reglages`
   déclare `display: grid` juste au-dessus, et TOUTE règle d'auteur qui
   redéclare `display` bat le `hidden` du navigateur — sans que rien ne le
   signale. Le projet a déjà payé ce défaut sur un bouton indéniçable
   (25 août 2026) : l'attribut valait `true` pendant que l'élément
   s'affichait.

   ⚠️ PAS DE JAVASCRIPT NON PLUS, et ce n'est pas de l'élégance : la case
   revient COCHÉE quand le moteur refuse la série et qu'on réaffiche l'écran
   (`recurrence is not null ? 'checked'`). Une bascule en JS devrait rejouer
   cet état au chargement ; `:checked` le fait par construction, au premier
   rendu, sans script à recompiler.

   ⚠️ LE `@supports` EST LA GARDE, PAS UNE PRÉCAUTION DE STYLE. Sans lui, un
   navigateur sans `:has()` appliquerait le `display: none` et NE POURRAIT
   JAMAIS le lever : les champs deviendraient inatteignables. Enveloppé, son
   absence rend l'écran d'AVANT ce lot — tout est visible, rien n'est perdu.
   Une dégradation vers du connu. */
@supports selector(:has(*)) {
    .tunnel-recurrence__reglages,
    .tunnel-recurrence .field__help {
        display: none;
    }

    .tunnel-recurrence:has(.tunnel-case:checked) .tunnel-recurrence__reglages {
        display: grid;
    }

    .tunnel-recurrence:has(.tunnel-case:checked) .field__help {
        display: block;
    }
}

.tunnel-recurrence .field__help {
    margin: 0.9rem 0 0;
    font-size: 0.85rem;
    color: var(--c-text-muted);
}

/* Étape 1 : prestations sélectionnées (enchaînement) */
.tunnel-selection {
    list-style: none;
    margin: 0 0 0.75rem;
    padding: 0;
    display: flex;
    flex-direction: column;
}

.tunnel-selection__item {
    display: flex;
    align-items: baseline;
    gap: 0.75rem;
    padding: 0.45rem 0;
    border-bottom: 1px solid var(--c-border);
}

.tunnel-selection__name {
    font-weight: 500;
}

.tunnel-selection__meta {
    color: var(--c-text-muted);
    font-size: 0.9rem;
    margin-left: auto;
}

/* ═══ « RETIRER », ÉCRIT ET CERNÉ (Z-1d, recette d'Alexandre, 25 septembre 2026) ═══
   Une croix grise seule « on dirait pas que c'est un bouton cliquable ». Le geste
   devient un vrai bouton : le mot, un contour, 44 px de haut (la cible tactile).
   Sur la ligne, la durée et le prix passent SOUS le nom (grille) : à 390 px, nom,
   prix et bouton ne tiennent pas sur une ligne sans rogner le nom.
   `--retirable` borne ce rangement à l'étape 1 — le récapitulatif des étapes
   suivantes réutilise `tunnel-selection__item` sans bouton, et ne bouge pas. */
.tunnel-selection__item--retirable {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    column-gap: 0.75rem;
    row-gap: 0.1rem;
    align-items: center;
}

.tunnel-selection__item--retirable .tunnel-selection__meta {
    grid-column: 2;
    grid-row: 2;
    margin-left: 0;
}

.tunnel-selection__item--retirable .tunnel-selection__rang,
.tunnel-selection__item--retirable .tunnel-selection__remove {
    grid-row: 1 / span 2;
}

.tunnel-selection__remove {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    min-height: 2.75rem;
    padding: 0 0.8rem;
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    color: var(--c-text);
    font-size: 0.95rem;
    line-height: 1;
    text-decoration: none;
    white-space: nowrap;
}

.tunnel-selection__remove-signe {
    font-size: 1.15rem;
}

.tunnel-selection__remove:hover {
    border-color: #d66; /* même teinte destructrice que .btn--danger */
    color: #d66;
}

.tunnel-selection__total {
    margin: 0.75rem 0 1rem;
    font-weight: 600;
}

/* Titre « Ajouter une prestation » / message de plafond atteint */
.tunnel-selection__ajouter {
    text-align: center;
    font-family: var(--police-titre);
    text-transform: uppercase;
    letter-spacing: 2px;
    font-size: 1rem;
    color: var(--c-text-muted);
    margin: 0 0 1.25rem;
}

.tunnel-selection__limite {
    text-align: center;
    color: var(--c-text-muted);
    margin: 0 0 1.25rem;
}

/* Rang dans l'enchaînement (O4). L'ORDRE EST UNE INFORMATION — la coupe passe
   avant la couleur — et se lit ici d'un coup d'œil, au lieu de se déduire de
   l'ordre des lignes. Décoratif pour les lecteurs d'écran (aria-hidden) : la
   liste est un <ol>, qui porte déjà la numérotation sémantiquement. */
.tunnel-selection__rang {
    flex-shrink: 0;
    width: 1.4rem;
    height: 1.4rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: var(--c-accent);
    color: var(--c-accent-contrast);
    font-family: var(--police-titre);
    font-weight: 700;
    font-size: 0.75rem;
    line-height: 1;
}

/* Déroulé heure par heure de l'enchaînement (récapitulatif avant confirmation,
   puis page « rendez-vous confirmé »). Chaque bloc porte SON horaire et SON
   prix : c'est ce qui rend un enchaînement lisible d'un coup d'œil. */
.tunnel-deroule {
    list-style: none;
    margin: 0 0 1rem;
    padding: 0;
    display: flex;
    flex-direction: column;
}

.tunnel-deroule__bloc {
    display: flex;
    align-items: baseline;
    gap: 0.75rem;
    padding: 0.5rem 0;
    border-bottom: 1px solid var(--c-border);
}

.tunnel-deroule__bloc:last-child {
    border-bottom: none;
}

.tunnel-deroule__heure {
    flex-shrink: 0;
    font-family: var(--police-titre);
    font-variant-numeric: tabular-nums;
    color: var(--c-accent-texte);
    font-size: 0.9rem;
}

.tunnel-deroule__nom {
    font-weight: 500;
}

.tunnel-deroule__prix {
    margin-left: auto;
    color: var(--c-text-muted);
    font-size: 0.9rem;
    white-space: nowrap;
}

/* Entrée vers l'étape 1 depuis l'accueil, sous la liste des prestations : les
   cartes, elles, gardent leur raccourci direct vers le choix du coiffeur. */
.presta-list__enchainer {
    text-align: center;
    margin: 1.75rem 0 0;
    color: var(--c-text-muted);
}

.presta-list__enchainer a {
    color: var(--c-accent-texte);
    font-weight: 500;
}

@media (max-width: 560px) {
    /* Le prix passe sous le nom plutôt que de comprimer les deux. */
    .tunnel-deroule__bloc {
        flex-wrap: wrap;
        gap: 0.25rem 0.75rem;
    }
    .tunnel-deroule__heure {
        flex: 1 1 100%;
    }
}

/* Étape 2 : cartes coiffeurs */
.coiffeur-list {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 280px));
    justify-content: center;
    gap: 1.25rem;
    list-style: none;
    margin: 0;
    padding: 0;
}

.coiffeur-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.9rem;
    padding: 1.75rem 1.25rem;
    background-color: var(--c-bg-elev);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    text-align: center;
}

.coiffeur-card:hover {
    border-color: var(--c-accent-texte);
}

.coiffeur-card__avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 64px;
    height: 64px;
    border-radius: 50%;
    border: 2px solid var(--avatar-color);
    color: var(--avatar-color);
    font-family: var(--police-titre);
    font-size: 1.3rem;
    font-weight: 600;
}

/* Photo du coiffeur (repli sur .coiffeur-card__avatar si absente). */
.coiffeur-card__photo {
    width: 64px;
    height: 64px;
    border-radius: 50%;
    object-fit: cover;
    border: 2px solid var(--avatar-color);
}

.coiffeur-card__name {
    font-family: var(--police-titre);
    font-size: 1.1rem;
    letter-spacing: 0.5px;
}

/* ITEM 15 — « prochain horaire dispo », sous le nom.
   Volontairement DISCRET : c'est une aide au choix, pas l'information
   principale de la carte — celle-ci reste le nom, et l'action reste « Choisir ».
   La phrase est composée par le contrôleur (règle de calendrier, donc de
   fuseau) ; ce style ne fait que la ranger. */
.coiffeur-card__prochain {
    color: var(--c-text-muted);
    font-size: 0.85rem;
}

/* Étape 3 : les semaines EMPILÉES (Z-1a, 24 septembre 2026). Plus de navigation
   « semaine précédente / suivante » : « Afficher plus » ajoute la suivante en
   dessous. Chaque semaine porte un titre, sinon rien ne dit où l'une s'arrête. */
.creneau-semaines {
    display: grid;
    gap: 1.75rem;
}

.creneau-semaine__titre {
    font-family: var(--police-titre);
    font-size: 1rem;
    font-weight: 600;
    letter-spacing: 0.5px;
    margin: 0 0 0.75rem;
    /* Défile sous l'en-tête collant, pas dessous, quand l'ancre y mène. */
    scroll-margin-top: 5rem;
}

.creneau-semaine {
    scroll-margin-top: 5rem;
}

/* « Afficher plus » : pleine largeur et haute sur téléphone — le pouce la trouve
   sans viser, et le client de 40 ans et plus la lit comme un bouton. */
.creneau-plus {
    margin-top: 1.5rem;
    text-align: center;
}

.creneau-plus__cta {
    width: 100%;
    max-width: 26rem;
    min-height: 3rem;
    text-transform: none;
    font-size: 1rem;
}

.creneau-plus__cta[aria-busy="true"] {
    opacity: 0.6;
    cursor: progress;
}

.creneau-plus__aide,
.creneau-plus__fin {
    margin: 0.6rem 0 0;
    color: var(--c-text-muted);
    font-size: 0.9rem;
}

/* ═══ « REMONTER EN HAUT » (Z-1d, recette d'Alexandre, 25 septembre 2026) ═══
   Une flèche ronde, « flottante sur téléphone » : `sticky` dans `.creneau-liste`, elle
   suit le bas de l'écran pendant qu'on fait défiler les semaines, puis se POSE à sa
   place juste au-dessus d'« Afficher plus » — au repos, elle ne recouvre rien. Collée à droite,
   48 px (la cible tactile), SOUS la bannière de consentement (z-index 80) : elle ne
   la masque jamais. Le JavaScript la cache près du haut, où elle n'a rien à faire. */
.creneau-remonter {
    position: sticky;
    bottom: 1rem;
    z-index: 20;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 3rem;
    height: 3rem;
    margin: 0.75rem 0 0 auto;
    border: 1px solid var(--c-accent-texte);
    border-radius: 50%;
    background-color: var(--c-bg-elev);
    color: var(--c-accent-texte);
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.35);
    text-decoration: none;
    transition: opacity 0.18s ease;
}

.creneau-remonter__fleche {
    font-size: 1.4rem;
    line-height: 1;
}

.creneau-remonter.is-cache {
    opacity: 0;
    pointer-events: none;
}

/* Le chemin vers le début de l'horizon, quand la page commence plus loin. */
.creneau-precedentes {
    margin: 0 0 1.25rem;
    text-align: center;
}

.creneau-precedentes__cta {
    text-transform: none;
}

/* Accordéon des horaires par jour (étape 3 du tunnel). */
.creneau-accordion {
    display: grid;
    gap: 0.6rem;
}

.creneau-day {
    background-color: var(--c-bg-elev);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    overflow: hidden;
}

/* Ligne d'un jour sans disponibilité (non dépliable). */
.creneau-day.is-empty {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.9rem 1.25rem;
    opacity: 0.55;
}

/* Jour RÉVOLU (6 septembre 2026) : « les jours passés sont grisés ».
   Il hérite de `.is-empty` — même boîte, même absence de dépliage — et pousse
   l'effacement d'un cran, pour qu'il se distingue d'un jour travaillé et
   complet, qui reste une information utile.

   ⚠️ CETTE RÈGLE DOIT EXISTER POUR QUE LA CLASSE VEUILLE DIRE QUELQUE CHOSE.
   Ce dépôt a déjà porté trois classes écrites dans un gabarit auxquelles AUCUNE
   règle ne correspondait — invisible à tout ce qui lit le HTML servi. */
.creneau-day.is-passe {
    opacity: 0.35;
    color: var(--c-text-muted, inherit);
}

/* En-tête cliquable d'un jour (résumé du <details>). */
.creneau-day__summary {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.9rem 1.25rem;
    cursor: pointer;
    list-style: none; /* masque le triangle natif (WebKit ci-dessous) */
    user-select: none;
}

.creneau-day__summary::-webkit-details-marker {
    display: none;
}

/* Chevron doré, pivote à l'ouverture. */
.creneau-day__summary::after {
    content: "";
    flex-shrink: 0;
    width: 0.55rem;
    height: 0.55rem;
    border-right: 2px solid var(--c-accent-interface);
    border-bottom: 2px solid var(--c-accent-interface);
    transform: rotate(45deg);
    transition: transform 0.2s ease;
}

.creneau-day[open] .creneau-day__summary::after {
    transform: rotate(-135deg);
}

.creneau-day__summary:hover .creneau-day__title {
    color: var(--c-accent-texte);
}

.creneau-day__title {
    font-family: var(--police-titre);
    font-size: 1rem;
    letter-spacing: 0.5px;
    margin: 0;
}

.creneau-day__count {
    margin-left: auto;
    color: var(--c-text-muted);
    font-size: 0.85rem;
    white-space: nowrap;
}

/* Ligne « jour sans dispo » : garder le compteur à droite. */
.creneau-day.is-empty .creneau-day__count {
    margin-left: 0;
}

/* ⚠️ UNE GRILLE À PISTES ÉGALES, ET PLUS UN FLEX (Z-4, 27 août 2026).
   Demande d'Alexandre : « les cases horaires VIP doivent faire la même taille
   que les autres histoire que ce soit plus propre visuellement ».

   ═══ LA CAUSE, MESURÉE ═══
   Un créneau majoré porte du TEXTE EN PLUS dans le bouton (« 18:00 +7 € »).
   En `flex`, chaque case prend la largeur de son contenu : les majorées étaient
   donc plus larges, et la rangée partait de travers.

   ⚠️ CE QU'IL NE FALLAIT SURTOUT PAS FAIRE POUR ÉGALISER : retirer le montant.
   Le commentaire du lot 29 juste dessous porte sa raison — « la couleur n'est
   pas le seul signal », et c'est une information de PRIX. Égaliser est un geste
   de MISE EN PAGE, jamais de contenu : défaire au nom du visuel une décision
   d'accessibilité la perdrait sans que rien ne le signale.

   `auto-fill` + `minmax` : les cases s'alignent sur une largeur commune, et le
   nombre par rangée suit la place disponible. La largeur minimale tient
   « 20:30 +15 € » — le pire cas réel, deux chiffres de supplément.

   ⚠️ ET LA LARGEUR NE SUFFISAIT PAS : mesurée à 7,5 rem, elle tenait
   « 19:30 +5 € » mais PAS « 20:30 +15 € », qui passait à la ligne et rendait sa
   case DEUX FOIS PLUS HAUTE que ses voisines. La grille égalise les LARGEURS,
   pas les hauteurs.

   ⚠️ MA VÉRIFICATION À L'ÉCRAN N'AVAIT PAS PU LE VOIR : le décor que j'avais
   posé portait un supplément à UN chiffre. Le navigateur ne montre que la
   branche qu'on lui donne — c'est l'espace des états, et il a fallu la capture
   d'Alexandre pour l'atteindre.

   D'où les deux gardes, ensemble : `nowrap` empêche le retour à la ligne quoi
   qu'il arrive, et la largeur minimale est calée sur le pire cas. L'une sans
   l'autre laisse un défaut — `nowrap` seul déborderait, la largeur seule
   retomberait au premier montant plus long. */
/* ═══ ⚠️ « AVEC [X] » S'ALIGNE SUR LES CRÉNEAUX QU'IL ANNONCE ═══
   *« il faut que tu alignes le Avec [employé] comme tu le vois sur le screen, il
   est trop collé à gauche »* (Alexandre, 28 août 2026, capture à l'appui).

   Ce titre héritait de `rdv-creneaux__titre`, une classe pensée pour un bloc
   pleine largeur SANS retrait — alors qu'il coiffe désormais une grille qui,
   elle, porte `padding: 0 1.25rem`. Le titre commençait donc 20 px à gauche de
   ce qu'il titre. C'est l'emprunt d'une classe conçue pour un autre contexte,
   le défaut que ce dépôt a déjà payé avec la pastille d'équipe posée dans la
   médiathèque : le HTML est juste, l'écran ne l'est pas.

   ⚠️ LE RETRAIT N'EST PAS RECOPIÉ EN DUR : les deux règles se suivent, et la
   valeur est la même parce qu'elle est écrite une fois. Un jour où la grille
   change de padding, celui-ci doit suivre — c'est écrit ici pour qu'on le voie. */
.creneau-day__avec {
    margin: 0.2rem 0 0.5rem;
    padding: 0 1.25rem;
    color: var(--c-text-muted);
    font-size: 0.9rem;
}

.creneau-day__slots {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(8.75rem, 1fr));
    gap: 0.5rem;
    list-style: none;
    margin: 0;
    padding: 0 1.25rem 1.1rem;
}

.creneau-slot {
    display: block;
    /* Voir la grille ci-dessus : sans `nowrap`, « 20:30 +15 € » passe à la
       ligne et la case grandit en hauteur. */
    white-space: nowrap;
    padding: 0.45rem 0.9rem;
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    background-color: var(--c-bg);
    color: var(--c-text);
    font-variant-numeric: tabular-nums;
    text-align: center;
    text-decoration: none;
}

.creneau-slot:hover {
    border-color: var(--c-accent-texte);
    background-color: var(--c-accent-texte);
    color: var(--c-accent-contrast);
}

/* ═══ HEURES DE POINTE (lot 29) ═══
   Un créneau majoré porte son supplément DANS le bouton : le client lit le
   surcoût sans quitter la liste, et sans avoir à comparer deux écrans.

   ⚠️ LA COULEUR N'EST PAS LE SEUL SIGNAL — le montant est écrit en toutes
   lettres. Un liseré seul serait invisible pour qui ne distingue pas les
   teintes, et c'est une information de PRIX. */
.creneau-slot--vip {
    border-color: var(--c-accent-texte);
}

.creneau-slot__vip {
    display: inline-block;
    margin-left: 0.35rem;
    font-size: 0.78em;
    font-variant-numeric: tabular-nums;
    opacity: 0.85;
}

/* Le bandeau qui explique la majoration, au-dessus de la liste des jours. */
.creneau-vip-note,
.creneau-reouverture {
    margin: 0 0 1rem;
    padding: 0.7rem 0.9rem;
    border-left: 3px solid var(--c-accent-interface);
    background-color: var(--c-bg-alt, var(--c-bg));
    color: var(--c-text-muted);
    font-size: 0.92rem;
}

.creneau-vip-note strong,
.creneau-reouverture strong {
    color: var(--c-text);
    white-space: nowrap;
}

/* .creneau-slot sert aussi de bouton (modification de RDV côté client). */
button.creneau-slot {
    cursor: pointer;
    font: inherit;
    line-height: 1.2;
}

.creneau-day__slots form {
    margin: 0;
}

/* Semaine sans dispo : bloc d'aide au rebond (prochaine dispo + autres coiffeurs). */
/* Écran de modification d'un RDV (espace client). */
.modif-actuel {
    padding: 0.9rem 1.1rem;
    background-color: var(--c-bg-elev);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    line-height: 1.6;
}

.modif-presta {
    display: flex;
    flex-wrap: wrap;
    align-items: end;
    gap: 0.75rem;
    margin: 1.25rem 0;
}

.modif-presta label {
    width: 100%;
    color: var(--c-text-muted);
    font-size: 0.88rem;
}

.modif-presta select {
    flex: 1 1 260px;
    padding: 0.55rem 0.75rem;
    background-color: var(--c-bg);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    color: var(--c-text);
    font: inherit;
    color-scheme: dark;
}

.modif-presta select:focus {
    outline: 2px solid var(--c-accent);
    outline-offset: -1px;
}

/* Étape 4 : formulaire coordonnées */
.tunnel-form {
    display: flex;
    flex-direction: column;
    gap: 1.1rem;
}

.tunnel-form__row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1.1rem;
}

.tunnel-form label {
    display: block;
    font-size: 0.9rem;
    color: var(--c-text-muted);
    margin-bottom: 0.35rem;
}

.tunnel-form input[type="text"],
.tunnel-form input[type="email"],
.tunnel-form input[type="tel"] {
    width: 100%;
    padding: 0.65rem 0.85rem;
    background-color: var(--c-bg-elev);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    color: var(--c-text);
    font: inherit;
}

.tunnel-form input:focus {
    outline: 2px solid var(--c-accent);
    outline-offset: -1px;
}

/* Listbox (select) : même rendu sombre que les inputs. */
.tunnel-form select {
    width: 100%;
    padding: 0.65rem 0.85rem;
    background-color: var(--c-bg-elev);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    color: var(--c-text);
    font: inherit;
    color-scheme: dark; /* flèche native lisible sur fond sombre */
    cursor: pointer;
}

.tunnel-form select:focus {
    outline: 2px solid var(--c-accent);
    outline-offset: -1px;
}

/* Cases à cocher personnalisées : carré doré coché (même langage que l'admin). */
.tunnel-form input[type="checkbox"] {
    appearance: none;
    -webkit-appearance: none;
    flex-shrink: 0;
    width: 20px;
    height: 20px;
    margin: 0;
    border: 1px solid var(--c-border);
    border-radius: 6px;
    background: var(--c-bg);
    cursor: pointer;
    display: inline-grid;
    place-content: center;
    vertical-align: middle;
    transition: background-color 0.15s ease, border-color 0.15s ease;
}

.tunnel-form input[type="checkbox"]::before {
    content: "✓";
    font-size: 0.8rem;
    font-weight: 700;
    line-height: 1;
    color: var(--c-accent-contrast);
    transform: scale(0);
    transition: transform 0.12s ease;
}

.tunnel-form input[type="checkbox"]:checked {
    background: var(--c-accent);
    border-color: var(--c-accent-texte);
}

.tunnel-form input[type="checkbox"]:checked::before {
    transform: scale(1);
}

.tunnel-form input[type="checkbox"]:focus-visible,
.tunnel-form input[type="radio"]:focus-visible {
    outline: 2px solid var(--c-accent);
    outline-offset: 2px;
}

/* Boutons radio : même pastille dorée, mais ronde (choix unique). */
.tunnel-form input[type="radio"] {
    appearance: none;
    -webkit-appearance: none;
    flex-shrink: 0;
    width: 20px;
    height: 20px;
    margin: 0;
    border: 1px solid var(--c-border);
    border-radius: 50%;
    background: var(--c-bg);
    cursor: pointer;
    display: inline-grid;
    place-content: center;
    vertical-align: middle;
    transition: background-color 0.15s ease, border-color 0.15s ease;
}

.tunnel-form input[type="radio"]::before {
    content: "";
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--c-accent-contrast);
    transform: scale(0);
    transition: transform 0.12s ease;
}

.tunnel-form input[type="radio"]:checked {
    background: var(--c-accent);
    border-color: var(--c-accent-texte);
}

.tunnel-form input[type="radio"]:checked::before {
    transform: scale(1);
}

/* Case + libellé sur une ligne (option « rendre récurrent »). */
.tunnel-form .tunnel-check {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin: 0;
    color: var(--c-text);
    font-size: 0.95rem;
    cursor: pointer;
}

.tunnel-form__consent {
    padding: 1rem 1.2rem;
    background-color: var(--c-bg-elev);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    font-size: 0.88rem;
}

.tunnel-form__consent label {
    display: inline;
    color: var(--c-text);
    font-size: inherit;
    margin: 0 0 0 0.4rem;
}

/* Option « rendre récurrent » (client connecté). */
.tunnel-form__recurrence {
    padding: 1rem 1.2rem;
    background-color: var(--c-bg-elev);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
}

.tunnel-form__recurrence-options {
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px dashed var(--c-border);
}

.tunnel-form__recurrence-options[hidden] {
    display: none;
}

.tunnel-note {
    margin: 0.75rem 0 0;
    font-size: 0.85rem;
    color: var(--c-text-muted);
    line-height: 1.6;
}

.tunnel-merci__recurrence {
    margin: 0 auto 1rem;
    max-width: 46ch;
    padding: 0.85rem 1.1rem;
    background-color: var(--c-bg-elev);
    border: 1px solid var(--c-accent-interface);
    border-radius: var(--radius);
    color: var(--c-text);
    font-size: 0.92rem;
    line-height: 1.6;
}

.tunnel-form .help-text,
.tunnel-form .form-help {
    font-size: 0.8rem;
    color: var(--c-text-muted);
    margin-top: 0.25rem;
}

.tunnel-form ul {
    list-style: none;
    margin: 0.3rem 0 0;
    padding: 0;
    color: #e88;
    font-size: 0.85rem;
}

.tunnel-form .btn {
    align-self: center;
    margin-top: 0.5rem;
}

/* --- Espace client (connexion, inscription, mon compte) ------------------ */
/* Mêmes règles visuelles que le formulaire du tunnel. */
.client-form {
    display: flex;
    flex-direction: column;
    gap: 1.1rem;
    max-width: 480px;
    margin: 0 auto;
}

.client-form label {
    display: block;
    font-size: 0.9rem;
    color: var(--c-text-muted);
    margin-bottom: 0.35rem;
}

/* ⚠️ UNE EXCLUSION, PAS UNE ÉNUMÉRATION (item 1, 23 août 2026).
   Cette règle listait `text`, `email`, `tel`, `password` — et OUBLIAIT `date`.
   Le champ « Date de naissance » de l'espace client retombait donc au style
   NATIF du navigateur : un rectangle blanc minuscule au milieu de champs
   sombres. Alexandre l'a vu sur sa prod ; aucun test ne pouvait le voir, une
   suite ne regarde pas une mise en page.

   ⚠️ ON CORRIGE LA CAUSE, PAS L'INSTANCE : ajouter `date` à la liste aurait
   marché aujourd'hui et raté `number`, `search`, `url` demain. Une énumération
   de types finit toujours par en manquer un — et le manque est SILENCIEUX.

   C'est l'idiome que `admin.css` emploie DÉJÀ pour `.admin-form` : on habille
   tout, SAUF les trois types qui ne sont pas des boîtes de saisie (une case à
   cocher ou un sélecteur de couleur habillé ainsi devient illisible). On ne
   réinvente pas — ce dépôt a déjà tranché ce problème un étage plus haut. */
.client-form input:not([type="checkbox"]):not([type="radio"]):not([type="color"]),
.client-form select,
.client-form textarea {
    width: 100%;
    padding: 0.65rem 0.85rem;
    background-color: var(--c-bg-elev);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    color: var(--c-text);
    font: inherit;
}

.client-form input:focus,
.client-form select:focus,
.client-form textarea:focus {
    outline: 2px solid var(--c-accent);
    outline-offset: -1px;
}

.client-form__row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1.1rem;
}

/* Valeur AFFICHÉE et non saisissable (O7 : l'adresse e-mail sur « ma fiche »).
   Volontairement PAS un `input` désactivé : un champ grisé ressemble à une
   saisie qu'on refuse, alors qu'il n'y a simplement rien à saisir ici. */
.client-form__lecture {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.2rem 0.6rem;
    padding: 0.65rem 0.85rem;
    background-color: var(--c-bg-elev);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
}

.client-form__lecture-label {
    font-size: 0.9rem;
    color: var(--c-text-muted);
}

/* `min-width: 0` + coupure forcée : une adresse longue doit se replier dans la
   boîte, jamais l'élargir — c'est la cause de débordement du lot H. */
.client-form__lecture-valeur {
    min-width: 0;
    overflow-wrap: anywhere;
    color: var(--c-text);
}

.client-form__consent {
    padding: 1rem 1.2rem;
    background-color: var(--c-bg-elev);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    font-size: 0.88rem;
}

.client-form__consent label {
    display: inline;
    color: var(--c-text);
    font-size: inherit;
    margin: 0 0 0 0.4rem;
}

.client-form ul {
    list-style: none;
    margin: 0.3rem 0 0;
    padding: 0;
    color: #e88;
    font-size: 0.85rem;
}

.client-form .btn {
    align-self: center;
    margin-top: 0.5rem;
}

.client-form__alt {
    text-align: center;
    margin-top: 1.5rem;
    color: var(--c-text-muted);
}

.client-form__alt a {
    color: var(--c-accent-texte);
}

.section__subtitle {
    font-family: var(--police-titre);
    font-size: 1.15rem;
    letter-spacing: 1.5px;
    text-transform: uppercase;
    color: var(--c-accent-texte);
    margin: 2rem 0 0.8rem;
}

.client-table-vide {
    color: var(--c-text-muted);
    margin-bottom: 2rem;
}

.client-table-vide a {
    color: var(--c-accent-texte);
}

/* Tableaux de l'espace client (RDV à venir / passés). */
.client-table {
    width: 100%;
    border-collapse: collapse;
    background: var(--c-bg-elev);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    overflow: hidden;
    margin-bottom: 2rem;
}

.client-table th,
.client-table td {
    padding: 0.8rem 1rem;
    text-align: left;
    border-bottom: 1px solid var(--c-border);
    font-size: 0.92rem;
}

.client-table th {
    font-family: var(--police-titre);
    font-weight: 500;
    letter-spacing: 1px;
    text-transform: uppercase;
    font-size: 0.75rem;
    color: var(--c-text-muted);
    background: var(--c-bg-elev-2);
}

.client-table tr:last-child td {
    border-bottom: none;
}

/* Colonne d'action (Annuler / avis) : vraie cellule de tableau alignée à
   droite. On NE met PAS display:flex sur le <td> — cela le sort de la grille
   de colonnes et désaligne l'action des lignes. Les formulaires passent en
   inline-flex pour être poussés à droite par text-align. */
.client-table__actions {
    text-align: right;
    vertical-align: middle;
}

.client-table__actions form {
    display: inline-flex;
    gap: 0.4rem;
    align-items: center;
}

.client-table .aide {
    font-size: 0.8rem;
    color: var(--c-text-muted);
}

/* Badges de statut (mêmes codes couleur que l'admin). */
.badge {
    display: inline-block;
    padding: 0.2rem 0.6rem;
    border: 1px solid var(--c-border);
    border-radius: 99px;
    font-size: 0.75rem;
    letter-spacing: 0.5px;
    color: var(--c-text-muted);
    white-space: nowrap;
}

.badge--confirmee { border-color: rgba(90, 160, 90, 0.7); color: #8fce8f; }
.badge--en_attente { border-color: rgba(201, 161, 74, 0.7); color: var(--c-accent-texte); }
.badge--annulee { border-color: rgba(176, 34, 38, 0.7); color: #e88; }
.badge--honoree { border-color: var(--c-border); color: var(--c-text-muted); }

/* =========================================================================
   Rendez-vous du client : cartes, récapitulatifs, créneaux (T4).

   Des CARTES et non un tableau pour les rendez-vous À VENIR : ce bloc porte
   désormais deux boutons ET, quand une action est fermée, la phrase qui
   l'explique. Empilé dans une cellule de tableau, tout cela finit dans un
   défilement horizontal — or c'est l'écran le plus consulté au téléphone.
   Les rendez-vous PASSÉS, eux, restent un tableau : rien à y faire.
   ========================================================================= */
.rdv-liste {
    list-style: none;
    margin: 0 0 2rem;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

/* Ancre invisible d'un bloc secondaire d'un passage (item 41).

   ⚠️ ELLE EXISTE POUR ÊTRE VISÉE, PAS POUR ÊTRE VUE. Les e-mails du moteur
   pointent `#rdv-{id}` sur N'IMPORTE LEQUEL des blocs d'un passage ; la carte
   porte l'id du premier, ces repères portent les autres. Sans règle, le
   `<span>` vide n'occuperait rien — ce qui marcherait — mais la classe serait
   écrite dans le gabarit sans exister dans la feuille : exactement le défaut
   mesuré le 25 août (trois classes de la vue Semaine, zéro règle, invisibles à
   tout ce qui lit le HTML). On l'écrit donc, et on dit ce qu'elle fait.

   ⚠️ PAS DE `display: none` : un élément non rendu n'est pas une cible d'ancre
   fiable — plusieurs navigateurs refusent de faire défiler jusqu'à lui. Une
   boîte de taille nulle, elle, reste visée. */
.rdv-card__ancre {
    display: inline-block;
    width: 0;
    height: 0;
    overflow: hidden;
}

.rdv-card {
    padding: 1.1rem 1.25rem;
    background: var(--c-bg-elev);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
}

.rdv-card__head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.6rem;
}

.rdv-card__quand {
    margin: 0;
    font-family: var(--police-titre);
    font-size: 1.05rem;
    letter-spacing: 0.5px;
    color: var(--c-text);
}

/* Paires libellé/valeur : deux colonnes au large, empilées en étroit. */
.rdv-card__meta,
.rdv-recap__meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem 2rem;
    margin: 0.75rem 0 0;
}

.rdv-card__meta dt,
.rdv-recap__meta dt {
    font-size: 0.72rem;
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--c-text-muted);
    margin-bottom: 0.15rem;
}

.rdv-card__meta dd,
.rdv-recap__meta dd {
    margin: 0;
    font-size: 0.95rem;
}

.rdv-card__actions,
.rdv-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
    margin-top: 1rem;
}

.rdv-actions form {
    margin: 0;
}

/* Explication d'une action fermée : LISIBLE SANS SURVOL, sous les boutons
   qu'elle décrit (aria-describedby). Jamais un title — un client sur
   téléphone ne survole rien. */
.rdv-card__aide {
    margin: 0.8rem 0 0;
    font-size: 0.88rem;
    line-height: 1.5;
    color: var(--c-text-muted);
}

.rdv-card__aide p {
    margin: 0 0 0.3rem;
}

.rdv-card__aide p:last-child {
    margin-bottom: 0;
}

.rdv-card__tel a {
    color: var(--c-accent-texte);
}

/* =========================================================================
   Rendez-vous RÉGULIERS du client (séries) : la liste des occurrences, et la
   distinction MAINTENU / ANNULÉ qui décide si le client se déplace.

   Réemploie .rdv-liste / .rdv-card / .policy-note : une série est un
   rendez-vous qui se répète, elle n'a pas besoin d'un autre langage visuel.
   ========================================================================= */
.serie-card__sous-titre {
    margin: 1rem 0 0.4rem;
    font-family: var(--police-titre);
    font-size: 0.8rem;
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--c-text-muted);
}

.serie-occurrences {
    list-style: none;
    margin: 0;
    padding: 0;
    font-size: 0.92rem;
    line-height: 1.7;
    color: var(--c-text-muted);
}

/* La pastille de gauche porte l'information, PAS la couleur seule : le texte
   voisin dit « maintenu » ou « annulé » en toutes lettres. Un client
   daltonienne ne doit pas avoir à distinguer un vert d'un rouge pour savoir
   si elle doit se déplacer. */
.serie-occurrences li {
    padding-left: 1rem;
    position: relative;
}

.serie-occurrences li::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0.7em;
    width: 0.4rem;
    height: 0.4rem;
    border-radius: 50%;
    background: var(--c-border);
}

.serie-occurrences--maintenues li {
    color: var(--c-text);
}

.serie-occurrences--maintenues li::before {
    background: var(--c-accent);
}

.serie-occurrences--annulees li {
    text-decoration: line-through;
}

/* Variante APPUYÉE de .policy-note, réservée à ce qui engage un déplacement :
   « ce rendez-vous est maintenu, venez-y ». Le reste des avertissements garde
   le ton normal — tout mettre en avant revient à ne rien mettre en avant. */
.policy-note--fort {
    background: rgba(201, 161, 74, 0.16);
    border-color: var(--c-accent-texte);
    font-size: 1rem;
}

/* Variante OFFRE de .policy-note — l'offre d'anniversaire du salon, au-dessus
   du champ de date de naissance (5 septembre 2026).

   ⚠️ ELLE EXISTE PARCE QUE LE MODIFICATEUR ÉTAIT ÉCRIT SANS RÈGLE. Une classe
   posée dans un gabarit et absente de la feuille est INVISIBLE à tout ce qui
   lit le HTML : le test voit la classe, l'écran ne voit rien. Le projet a payé
   ce défaut le 25 août sur trois classes de la vue Semaine.

   ⚠️ ET L'ÉCART RESTE FAIBLE, DÉLIBÉRÉMENT. Ce n'est pas une réclame : c'est
   la contrepartie d'une donnée qu'on demande. Une bordure gauche verte suffit
   à la distinguer d'un avertissement — tout crier reviendrait à ne rien dire,
   comme le note la variante --fort juste au-dessus. */
/* Un encart posé SOUS un champ de formulaire — il commente la saisie qu'on
   vient de faire, il ne l'annonce pas.

   ⚠️ L'ESPACE AU-DESSUS EST LA RAISON D'ÊTRE DE CETTE CLASSE. En
   `.field__help` le texte vivait à 0,3 rem de l'input : collé, il se lisait
   comme une légende du champ. Signalé à l'écran par Alexandre le 6 septembre
   2026 — aucun test de ce dépôt ne peut mesurer une distance rendue.

   ⚠️ ET IL EST PLUS PETIT QUE L'ENCART ORDINAIRE : celui-ci n'engage rien, il
   explique. Le mettre à la même taille que la politique d'annulation, qui est
   une règle opposable, donnerait le même poids à deux choses inégales. */
/* ⚠️ LA POSITION, PAS LE POIDS — et c'est pour ça que c'est un modificateur
   à part de `--apres-champ`. Le partiel `_offre_anniversaire` a TROIS branches
   exclusives (dépliant d'enseigne, offre du salon, note jaune) et elles sont
   désormais TOUTES sous le champ de date. Poser les marges sur `--apres-champ`,
   qui n'habille que la note jaune, n'en aurait servi qu'UNE sur trois — et le
   défaut ne se serait vu qu'à l'écran, chez un salon qui a une offre.
   `--apres-champ` garde ce qui lui est propre : sa taille de texte réduite. */
.policy-note--sous-champ,
.offres-salons.policy-note--sous-champ {
    /* ⚠️ RESSERRÉ AU-DESSUS, NORMAL EN DESSOUS — demande d'Alexandre du
       6 septembre 2026, après l'avoir vu à l'écran : *« un peu moins d'espace
       entre l'input de la date et cette notif, et on garde l'espace normal
       après entre la notif et la case à cocher »*.

       L'encart COMMENTE le champ qui le précède et n'a rien à voir avec la case
       qui le suit : l'écart doit dire cette appartenance.

       ⚠️ LES DEUX MARGES SONT ÉCRITES, ET LE ZÉRO EST LE POINT. `.policy-note`
       pose `margin: 1rem 0` ; ne déclarer que le haut laisserait 1 rem en bas,
       qui S'AJOUTE au `gap: 1.1rem` de `.client-form` — l'écart d'en dessous
       vaudrait alors presque le double d'un écart ordinaire, soit le contraire
       de « normal ». À zéro, c'est le `gap` du formulaire qui décide seul, donc
       exactement l'écart des autres blocs.

       ⚠️ AUCUN TEST DE CE DÉPÔT NE MESURE UNE DISTANCE RENDUE : ces deux
       valeurs se jugent à l'œil, et elles l'ont été. */
    /* ⚠️ MARGE NÉGATIVE, ET C'EST LA SEULE FAÇON D'ÊTRE PLUS SERRÉ QUE
       L'ORDINAIRE. `.client-form` est une pile flex à `gap: 1.1rem` (17,6 px) :
       ce `gap` s'applique AUSSI au-dessus de l'encart, et une marge positive s'y
       AJOUTE. Un `margin-top: 0.35rem` donnait 23 px — mesuré —, soit PLUS que
       l'écart courant entre deux blocs, l'inverse de ce qui est demandé. Il faut
       donc retrancher au `gap`, pas s'ajouter à lui.
       ⚠️ NE PAS « NETTOYER » CE MOINS en le croyant fautif : le repasser en
       positif rééloignerait l'encart du champ qu'il commente. */
    margin-top: -0.55rem;
    margin-bottom: 0;
}

.policy-note--apres-champ {
    /* Plus petit que l'encart ordinaire : celui-ci n'engage rien, il explique.
       Le mettre à la taille de la politique d'annulation, qui est une règle
       opposable, donnerait le même poids à deux choses inégales.
       ⚠️ L'ESPACEMENT N'EST PLUS ICI : il vit sur `--sous-champ`, que les TROIS
       branches du partiel portent. Le remettre ici le reperdrait pour l'offre. */
    font-size: 0.78rem;
}

.policy-note--offre {
    background: rgba(127, 176, 105, 0.10);
    border-color: rgba(127, 176, 105, 0.35);
    /* ⚠️ APRÈS `border-color`, ET C EST TOUT LE SUJET. La propriété raccourcie
       réinitialise les QUATRE côtés : écrite au-dessus, cette ligne était
       silencieusement annulée et la bordure d accent disparaissait. Mesuré au
       navigateur — `getComputedStyle` rendait la couleur du raccourci, quand
       la feuille, elle, se lisait comme si les deux coexistaient. Aucun test
       de ce dépôt ne peut voir ça. */
    border-left-color: #7fb069;
}

.policy-note--offre .policy-note__icon {
    background: rgba(127, 176, 105, 0.20);
    color: #7fb069;
}

/* LE DÉPLIANT DES OFFRES D'ANNIVERSAIRE, un bloc par établissement — écrans
   de RACINE d'un site d'enseigne (5 septembre 2026).

   ⚠️ CETTE RÈGLE EXISTE PARCE QUE LA CLASSE ÉTAIT ÉCRITE SANS ELLE il y a une
   heure, sur `policy-note--offre` : une classe posée dans un gabarit et absente
   de la feuille est invisible à tout ce qui lit le HTML. On ne recommence pas.

   ⚠️ `<details>` NATIF : le style ne fait que l'habiller. Rien ici ne masque le
   contenu — c'est le navigateur qui replie, et le HTML servi le porte quand
   même. */
.offres-salons {
    margin: 1rem 0;
}

.offres-salons > summary {
    cursor: pointer;
    padding: 0.55rem 0;
    color: var(--c-accent-texte);
    font-size: 0.85rem;
}

/* Les encarts internes se resserrent : dans un dépliant ils sont plusieurs, et
   l'espacement d'un encart isolé y creuserait la page. */
.offres-salons .policy-note {
    margin: 0.5rem 0 0;
}

/* Récapitulatif d'un rendez-vous (pages « annuler » et « déplacer »). */
.rdv-recap {
    padding: 1.1rem 1.25rem;
    background: var(--c-bg-elev);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    margin-bottom: 1.25rem;
}

.rdv-recap__label {
    margin: 0 0 0.2rem;
    font-size: 0.72rem;
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--c-text-muted);
}

.rdv-recap__quand {
    margin: 0;
    font-family: var(--police-titre);
    font-size: 1.15rem;
    letter-spacing: 0.5px;
}

.rdv-recap__meta--large {
    margin: 1.25rem 0;
}

/* Choix de la nouvelle date : champ date natif (calendrier du téléphone). */
.rdv-jour {
    margin: 1.5rem 0 0.5rem;
}

/* Z-1d : les semaines empilées sous le champ date — le titre de la première semaine ne
   se colle pas au bouton « Voir les créneaux » (vu à 390 px). */
.rdv-jour + .creneau-liste,
.rdv-jour + .creneau-precedentes {
    margin-top: 1.5rem;
}

.rdv-jour label {
    display: block;
    font-size: 0.9rem;
    color: var(--c-text-muted);
    margin-bottom: 0.35rem;
}

.rdv-jour__champ {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
}

.rdv-jour input[type="date"] {
    flex: 1 1 12rem;
    padding: 0.65rem 0.85rem;
    background-color: var(--c-bg-elev);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    color: var(--c-text);
    font: inherit;
    /* Sans cela, iOS étire le champ hors de son conteneur. */
    min-width: 0;
    -webkit-appearance: none;
    appearance: none;
}

.rdv-jour input[type="date"]:focus {
    outline: 2px solid var(--c-accent);
    outline-offset: -1px;
}

/* Créneaux proposés — un groupe par coiffeur. */
.rdv-creneaux {
    padding: 1rem 1.1rem;
    background: var(--c-bg-elev);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    margin-bottom: 0.9rem;
}

.rdv-creneaux__titre {
    margin: 0 0 0.7rem;
    font-family: var(--police-titre);
    letter-spacing: 0.5px;
    font-size: 0.95rem;
}

/* ⚠️ LA MÊME GRILLE QUE LE TUNNEL (Z-4) — la demande visait « le tunnel de
   réservation ET de modification client », et ces deux écrans partagent la
   classe `.creneau-slot` mais PAS celle de leur liste. Corriger la seule que
   l'on regarde aurait laissé l'autre de travers, avec le même défaut et sans
   que rien ne le dise : c'est le §5.4 du projet, « on corrige l'instance qu'on
   regarde, pas le défaut ».
   Le pas est le même que là-bas, délibérément : deux valeurs différentes
   feraient deux rendus différents pour un seul geste. */
.rdv-creneaux__liste {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(8.75rem, 1fr));
    gap: 0.5rem;
}

.rdv-creneaux__vide {
    margin: 0;
    color: var(--c-text-muted);
    font-size: 0.9rem;
}

.rdv-creneaux__intro {
    margin: 1.5rem 0 0.7rem;
    color: var(--c-text-muted);
}

.rdv-creneaux__note {
    margin: 0.5rem 0 0;
    color: var(--c-text-muted);
    font-size: 0.9rem;
    line-height: 1.5;
}

.rdv-creneaux__note a {
    color: var(--c-accent-texte);
}

/* L'heure ACTUELLE du rendez-vous, reconnaissable parmi les propositions :
   le moteur l'y laisse (il exclut la réservation d'elle-même du calcul), et
   la voir barrée d'un repère évite de « déplacer » vers le même créneau. */
.creneau-slot.is-actuel {
    border-color: var(--c-accent-texte);
    color: var(--c-accent-texte);
}

.creneau-slot.is-actuel::after {
    content: " (actuel)";
    font-size: 0.75em;
    letter-spacing: 0.5px;
}

/* Bascule « avant → après » du récapitulatif de déplacement. */
.rdv-bascule {
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
    gap: 0.75rem;
    margin-bottom: 1.25rem;
}

.rdv-bascule__cote {
    flex: 1 1 12rem;
    padding: 1rem 1.1rem;
    background: var(--c-bg-elev);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
}

.rdv-bascule__cote--avant {
    opacity: 0.7;
}

.rdv-bascule__cote--apres {
    border-color: var(--c-accent-texte);
}

.rdv-bascule__fleche {
    align-self: center;
    margin: 0;
    font-size: 1.5rem;
    color: var(--c-accent-texte);
}

.rdv-bascule__quand {
    margin: 0.2rem 0 0;
    font-size: 0.9rem;
    color: var(--c-text-muted);
}

.rdv-bascule__heure {
    margin: 0.1rem 0;
    font-family: var(--police-titre);
    font-size: 1.6rem;
    font-variant-numeric: tabular-nums;
    color: var(--c-text);
}

.rdv-bascule__qui {
    margin: 0;
    font-size: 0.9rem;
    color: var(--c-text-muted);
}

@media (max-width: 560px) {
    /* Boutons pleine largeur : cibles tactiles confortables, et un
       « Annuler » qui ne se clique pas par erreur à côté de « Déplacer ». */
    .rdv-card__actions .btn,
    .rdv-actions .btn,
    .rdv-actions form {
        flex: 1 1 100%;
    }
    .rdv-actions form .btn {
        width: 100%;
    }
    /* Les deux cartes s'empilent : la flèche prend sa propre ligne pour
       rester ENTRE elles (sinon elle se colle à droite de « Avant », où elle
       ne relie plus rien) et pointe vers le bas. */
    .rdv-bascule__fleche {
        flex: 1 1 100%;
        align-self: center;
        text-align: center;
        margin: 0;
    }

    .rdv-bascule__fleche span {
        display: inline-block;
        transform: rotate(90deg);
    }
}

.tunnel-back {
    text-align: center;
    margin-top: 2rem;
}

.tunnel-back a {
    color: var(--c-text-muted);
}

/* Étape 5 : confirmation */
.tunnel-merci {
    text-align: center;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1.25rem;
}

.tunnel-merci__check {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 72px;
    height: 72px;
    border-radius: 50%;
    border: 2px solid var(--c-accent-interface);
    color: var(--c-accent-texte);
    font-size: 2rem;
}

.tunnel-merci .tunnel-recap {
    width: 100%;
    text-align: left;
    margin-bottom: 0;
}

.tunnel-merci__adresse {
    color: var(--c-text-muted);
    line-height: 1.7;
}

/* Les deux sorties de la page de confirmation, même style et alignées (Z-1d) :
   côte à côte et de même largeur quand elles tiennent, l'une sous l'autre en
   pleine largeur au téléphone — jamais un bouton à gauche et un lien centré. */
.tunnel-merci__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-top: 1rem;
}

.tunnel-merci__actions .btn {
    flex: 1 1 14rem;
}

@media (max-width: 560px) {
    .tunnel-form__row {
        grid-template-columns: 1fr;
    }
}

/* ---- Avis clients ---- */
/* Bas du bloc élevé (horaires + avis) : se fond dans la section suivante
   (adresse, fond de base). La couture horaires ↔ avis reste plate (élevé/élevé). */
.section--avis {
    background-color: var(--c-bg-elev);
    background-image: linear-gradient(to top, var(--c-bg), transparent 120px);
    background-repeat: no-repeat;
}

.avis-list {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 1.25rem;
    list-style: none;
    margin: 0;
    padding: 0;
}

/* ---- Carrousel d'avis : 3 visibles, pagination par flèches ---- */
.avis-carousel {
    position: relative;
}

/* La liste d'avis devient une bande horizontale à défilement (scroll-snap).
   Surcharge le display:grid de .avis-list (règle placée après). */
.avis-carousel__track {
    display: flex;
    gap: 1.25rem;
    overflow-x: auto;
    /* Snap « proximity » : agréable au doigt sans forcer la position. Les
       flèches ne font que régler scrollLeft ; le défilement fluide est porté
       par scroll-behavior (désactivé si l'utilisateur réduit les animations). */
    scroll-snap-type: x proximity;
    scroll-behavior: smooth;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none; /* Firefox : on navigue aux flèches */
}
.avis-carousel__track::-webkit-scrollbar {
    display: none; /* Chrome/Safari */
}
@media (prefers-reduced-motion: reduce) {
    .avis-carousel__track {
        scroll-behavior: auto;
    }
}

/* 3 cartes visibles (2 gouttières entre elles) ; flex-grow pour remplir
   proprement quand il y a moins de 3 avis (pas de carte étriquée). */
.avis-carousel__track > .avis-card {
    flex: 1 0 calc((100% - 2 * 1.25rem) / 3);
    scroll-snap-align: start;
}

/* Flèches : masquées par défaut, révélées seulement si le JS les active
   ET qu'il y a débordement (sans JS, la bande reste défilable au doigt). */
.avis-carousel__arrow {
    display: none;
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 3;
    align-items: center;
    justify-content: center;
    width: 2.75rem;
    height: 2.75rem;
    padding: 0;
    font-size: 1.6rem;
    line-height: 1;
    color: var(--c-accent-texte);
    background: var(--c-bg);
    border: 1px solid var(--c-border);
    border-radius: 50%;
    cursor: pointer;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.45);
    transition: background 0.2s ease, color 0.2s ease, opacity 0.2s ease;
}
.avis-carousel.is-enhanced .avis-carousel__arrow {
    display: flex;
}
.avis-carousel__arrow:hover {
    background: var(--c-accent);
    color: var(--c-bg);
}
.avis-carousel__arrow:disabled {
    opacity: 0.3;
    cursor: default;
    background: var(--c-bg);
    color: var(--c-accent-texte);
}
.avis-carousel__arrow--prev { left: -1.1rem; }
.avis-carousel__arrow--next { right: -1.1rem; }

/* Tablette : 2 avis visibles. */
@media (max-width: 900px) {
    .avis-carousel__track > .avis-card {
        flex-basis: calc((100% - 1.25rem) / 2);
    }
}

/* Mobile : 1 avis visible, flèches ramenées sur les bords. */
@media (max-width: 560px) {
    .avis-carousel__track > .avis-card {
        flex-basis: 100%;
    }
    .avis-carousel__arrow--prev { left: 0; }
    .avis-carousel__arrow--next { right: 0; }
}

.avis-card {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    padding: 1.5rem;
    background-color: var(--c-bg);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
}

.avis-card__stars {
    color: var(--c-accent-texte);
    letter-spacing: 3px;
    margin: 0;
}

.avis-card__text {
    margin: 0;
    font-size: 0.95rem;
    line-height: 1.6;
    color: var(--c-text);
    flex: 1;
}

.avis-card__footer {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 0.5rem;
}

.avis-card__author {
    font-weight: 600;
    font-size: 0.9rem;
}

.avis-card__source {
    font-size: 0.78rem;
    color: var(--c-text-muted);
}

/* Encart d'appel à l'action, sous les listes d'avis (SEO). Nommé `avis-cta`
   et non `avis-google` (son nom chez By Zigzag) : il porte toujours notre
   propre porte « Noter mon rendez-vous », et n'accueille les boutons Google
   que lorsque la source Google répond. */
.avis-cta {
    margin-top: 2rem;
    text-align: center;
}

.avis-cta p {
    color: var(--c-text-muted);
    margin: 0 0 0.75rem;
}

/* Boutons « noter mon rendez-vous » / « laisser un avis sur Google » / « voir
   la fiche » côte à côte. */
.avis-cta__actions {
    display: flex;
    justify-content: center;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-bottom: 1rem;
}

/* Barre d'actions de l'espace client (boutons différenciés). */
.compte-actions {
    display: flex;
    justify-content: center;
    gap: 0.75rem;
    flex-wrap: wrap;
    margin-top: 1rem;
}

/* =========================================================================
   Pages légales (mentions légales, politique de confidentialité)
   Contenu texte long : lisibilité et rythme vertical.
   ========================================================================= */
.legal h2 {
    font-family: var(--police-titre);
    font-size: 1.25rem;
    color: var(--c-accent-texte);
    margin: 2rem 0 0.6rem;
}

.legal p,
.legal li {
    color: var(--c-text-muted);
    line-height: 1.7;
}

.legal ul {
    padding-left: 1.2rem;
    margin: 0.5rem 0 1rem;
}

.legal li {
    margin-bottom: 0.4rem;
}

.legal strong {
    color: var(--c-text);
}

.legal a {
    color: var(--c-accent-texte);
}

.legal__back {
    margin-top: 2.5rem;
}

/* Avis Google : note agrégée, avatar auteur, attribution. */
.avis-google-note strong {
    color: var(--c-accent-texte);
    font-size: 1.15rem;
}

.avis-google-note span[aria-hidden] {
    color: var(--c-accent-texte);
    letter-spacing: 1px;
}

.avis-card__avatar {
    display: inline-block;
    width: 1.6rem;
    height: 1.6rem;
    border-radius: 50%;
    object-fit: cover;
    vertical-align: middle;
    margin-right: 0.4rem;
}

.avis-google__attribution {
    font-size: 0.8rem;
    color: var(--c-text-muted);
    letter-spacing: 0.5px;
}

/* =========================================================================
   Avis — compléments du lot O1-vitrine.

   Tout ce qui précède (carrousel, cartes, encart Google) vient du portage du
   design By Zigzag et est repris tel quel. Ne s'ajoutent ici que les éléments
   que By Zigzag n'a pas : l'accroche de la note FUSIONNÉE (lui n'affiche que
   la note Google), le badge « avis vérifié », les sous-titres des deux flux
   séparés, et l'encart du jour du lancement.
   ========================================================================= */

/* L'accroche : la note fusionnée, en gros, juste sous le titre de section. */
.avis-resume {
    display: flex;
    align-items: baseline;
    justify-content: center;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin: 0.6rem 0 0.3rem;
}

.avis-resume__note {
    font-family: var(--police-titre);
    font-size: 2rem;
    line-height: 1;
    color: var(--c-accent-texte);
}

.avis-resume__etoiles {
    color: var(--c-accent-texte);
    font-size: 1.15rem;
    letter-spacing: 2px;
}

.avis-resume__total {
    color: var(--c-text-muted);
    font-size: 0.95rem;
}

/* D'où vient la note : discret, mais jamais absent (la note change quand une
   source disparaît — il faut pouvoir l'expliquer). */
.avis-resume__sources {
    font-size: 0.88rem;
    margin-bottom: 0;
}

/* Titre de chacun des deux flux (Google / ce site), avec sa note propre : la
   note est fusionnée pour l'accroche, mais la distinction des sources doit
   rester lisible. */
.avis-bloc__titre {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.6rem;
    font-family: var(--police-titre);
    font-size: 1rem;
    letter-spacing: 1.5px;
    text-transform: uppercase;
    color: var(--c-accent-texte);
    margin: 2.2rem 0 1rem;
}

.avis-bloc__note {
    font-family: var(--police-texte);
    font-size: 0.82rem;
    letter-spacing: 0;
    text-transform: none;
    color: var(--c-text-muted);
}

.avis-bloc__troncature {
    margin: -0.4rem 0 0.9rem;
    font-size: 0.85rem;
    color: var(--c-text-muted);
}

/* Le badge « avis vérifié » — l'argument commercial du produit. Assez présent
   pour se voir sur une carte, assez sobre pour ne pas manger la citation. */
.avis-badge {
    display: inline-block;
    padding: 0.15rem 0.55rem;
    border: 1px solid var(--c-accent-interface);
    border-radius: 99px;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.5px;
    color: var(--c-accent-texte);
    white-space: nowrap;
}

.avis-card__verifie {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin: 0;
    padding-top: 0.7rem;
    border-top: 1px solid var(--c-border);
}

.avis-card__verifie-aide {
    font-size: 0.75rem;
    color: var(--c-text-muted);
}

/* ═══ L'ORIGINE D'UNE CARTE, DANS UNE BANDE MÊLÉE (item 9, 25 août 2026) ═══
   Depuis que Google et le site partagent un seul carrousel, l'origine n'est
   plus portée par un en-tête de bloc : chaque carte doit la dire. Sans elle, un
   visiteur ne sait plus ce qu'il lit — et l'attribution exigée par Google, en
   bas de section, perdrait son objet.

   ⚠️ Reprend la géométrie de `.avis-card__verifie`, qu'elle REMPLACE comme
   dernière ligne de la carte : les deux ne coexistent jamais dans un même
   bloc — le badge « vérifié » vit désormais À L'INTÉRIEUR de cette ligne. */
.avis-card__origine {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin: 0;
    padding-top: 0.7rem;
    border-top: 1px solid var(--c-border);
}

.avis-origine {
    font-size: 0.75rem;
    letter-spacing: 0.02em;
    color: var(--c-text-muted);
}

/* ⚠️ DEUX REPÈRES SOBRES, ET PAS DE LOGO GOOGLE : réutiliser la marque d'un
   tiers dans une puce colorée engage sur ses règles d'usage, là où une mention
   textuelle suffit à satisfaire l'attribution. Le mot fait le travail. */
.avis-origine--google::before {
    content: '';
    display: inline-block;
    width: 0.45rem;
    height: 0.45rem;
    margin-right: 0.35rem;
    border-radius: 50%;
    background-color: var(--c-text-muted);
    vertical-align: middle;
}

.avis-origine--site {
    color: var(--c-accent, var(--c-text-muted));
}

/* ÉTAT DU JOUR DU LANCEMENT : aucun avis sur le site. Un encart plein, pas un
   carrousel vide — il porte l'explication du badge plutôt qu'un blanc. */
.avis-vide {
    margin: 0;
    padding: 1.5rem;
    background-color: var(--c-bg);
    border: 1px dashed var(--c-border);
    border-radius: var(--radius);
    color: var(--c-text-muted);
    line-height: 1.7;
}

.avis-vide .avis-badge {
    /* Cité dans une phrase : ne doit pas décaler l'interligne. */
    vertical-align: baseline;
}

/* ---- Formulaire de dépôt d'avis (espace client) ---- */
.avis-note {
    border: none;
    margin: 0 0 1.2rem;
    padding: 0;
}

.avis-note legend {
    font-size: 0.85rem;
    letter-spacing: 0.5px;
    color: var(--c-text-muted);
    margin-bottom: 0.5rem;
    padding: 0;
}

.avis-note__choix {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

/* Une ligne par note : cible tactile pleine largeur, et le libellé « 3 sur 5 »
   reste lisible à côté des étoiles (celles-ci sont décoratives). */
.avis-note__option {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    padding: 0.55rem 0.8rem;
    background-color: var(--c-bg-elev);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    cursor: pointer;
}

.avis-note__option:hover {
    border-color: var(--c-accent-texte);
}

/* Le bouton radio reste un VRAI radio, visible et focusable : c'est lui qui
   porte la sélection au clavier comme au lecteur d'écran. */
.avis-note__option input {
    accent-color: var(--c-accent-texte);
    width: 1.1rem;
    height: 1.1rem;
    flex: none;
}

.avis-note__option:has(input:checked) {
    border-color: var(--c-accent-texte);
}

.avis-note__option:has(input:focus-visible) {
    outline: 2px solid var(--c-accent);
    outline-offset: 2px;
}

.avis-note__etoiles {
    color: var(--c-accent-texte);
    letter-spacing: 2px;
}

.avis-note__libelle {
    font-size: 0.85rem;
    color: var(--c-text-muted);
}

.avis-form__aide {
    margin: 0.4rem 0 0;
    font-size: 0.82rem;
    color: var(--c-text-muted);
}

/* La piste est focusable (tabindex) pour que les flèches ← → du clavier la
   fassent défiler nativement, y compris sans JavaScript : il lui faut donc un
   anneau de focus visible, comme à n'importe quel élément atteignable. */
.avis-carousel__track:focus-visible {
    outline: 2px solid var(--c-accent);
    outline-offset: 4px;
    border-radius: var(--radius);
}

@media (max-width: 560px) {
    /* Cible tactile d'au moins 44 px : à 390 px, la flèche est le seul point
       d'appui précis, la carte occupant toute la largeur. */
    .avis-carousel__arrow {
        width: 2.9rem;
        height: 2.9rem;
    }

    /* En mobile les flèches sont ramenées SUR les bords (règle du design
       d'origine, plus haut) et recouvrent alors les 46 premiers pixels de la
       carte — donc le début de chaque ligne de la citation, mesuré à 390 px.
       On écarte le contenu de la carte plutôt que de déplacer les flèches :
       les garder à hauteur du pouce, de part et d'autre, est ce qui rend le
       carrousel utilisable d'une seule main. */
    .avis-carousel__track > .avis-card {
        padding-left: 3.4rem;
        padding-right: 3.4rem;
    }

    .avis-resume__note {
        font-size: 1.7rem;
    }
}

/* =========================================================================
   Apparition en fondu des sections au défilement (section_reveal.js)
   L'état masqué n'est appliqué que si le JS a posé .reveal-enabled sur <html>
   ET que l'utilisateur ne demande pas de mouvement réduit : sans JS, tout
   reste visible.
   ========================================================================= */
.reveal-enabled .section.reveal {
    opacity: 0;
    transform: translateY(24px);
    transition: opacity 0.6s ease, transform 0.6s ease;
    will-change: opacity, transform;
}

.reveal-enabled .section.reveal.is-visible {
    opacity: 1;
    transform: none;
}

@media (prefers-reduced-motion: reduce) {
    .reveal-enabled .section.reveal {
        opacity: 1;
        transform: none;
        transition: none;
    }
}

/* =========================================================================
   Mes données & suppression du compte (O8)

   Une page longue, consultée pour répondre à « qu'est-ce que ce salon sait de
   moi ? ». Elle réutilise ce qui existe déjà dans l'espace client (.client-table
   dans .table-scroll, .badge, .tunnel-recap) ; ce bloc n'ajoute que ce qui lui
   est propre : la barre d'actions, le sommaire chiffré, la fiche en deux
   colonnes, les cartes d'avis et de séries, et la phrase à recopier.
   ========================================================================= */
.donnees-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-bottom: 0.8rem;
}

.donnees-aide {
    color: var(--c-text-muted);
    font-size: 0.88rem;
    margin-bottom: 1.2rem;
}

.donnees-aide a {
    color: var(--c-accent-texte);
}

/* =========================================================================
   LES PORTES DE L'ESPACE CLIENT — « où puis-je aller d'ici ? »

   Les quatre accès (rendez-vous réguliers, fiche, mot de passe, données
   personnelles) étaient des liens noyés dans trois paragraphes de prose : rien
   ne signalait ce qui était cliquable, et l'intitulé annonçait un geste
   (« Changer mon mot de passe ») là où on cherche un endroit.

   Des CARTES, donc, et rien de neuf sous le soleil de la charte : mêmes
   variables de couleur, même rayon, même accent au survol que le reste du
   site. Aucune bibliothèque, aucune icône — un titre, une phrase.

   `auto-fit` + `minmax` plutôt qu'un nombre de colonnes en dur : les cartes
   passent d'elles-mêmes de quatre à deux puis à une seule, sans point de
   bascule à maintenir. Mesuré : une colonne à 390 px, deux à 768, quatre
   au-delà de 1100 — et aucun débordement à 320 px, où la carte la plus
   étroite descend à 280 px.
   ========================================================================= */
/* =========================================================================
   LOT 26 — LE RAPPEL DE RÈGLEMENT, DANS L'ESPACE CLIENT

   Un CONSTAT, pas une mise en demeure : le salon a noté quelque chose, on le
   dit, et on dit où ça se règle. Le cadre reprend l'accent du site plutôt
   qu'un rouge d'alerte — une dette de coiffeur n'est pas un incident, et une
   bannière alarmante sur une page qu'on ouvre pour vérifier l'heure de son
   rendez-vous serait disproportionnée.

   ⚠️ AUCUNE LIGNE DE TOTAL, et ce n'est pas une omission de style : le moteur
   ne publie `totalDu` que sur ses routes d'administration, et le composer ici
   serait une créance inventée. Voir le gabarit, qui porte le raisonnement.

   Les montants sont alignés à droite sur écran large et repassent dans le flux
   sous 30rem : une colonne de nombres qui déborde vaut moins qu'une ligne qui
   se replie.
   ========================================================================= */
.rappel-reglement {
    margin: 0 0 1.5rem;
    padding: 1rem 1.15rem;
    background: var(--c-bg-elev);
    border: 1px solid var(--c-accent-interface);
    border-left-width: 4px;
    border-radius: var(--radius);
}

.rappel-reglement__titre {
    margin: 0 0 0.35rem;
    font-size: 1.05rem;
    color: var(--c-accent-texte);
}

.rappel-reglement__texte {
    margin: 0 0 0.5rem;
}

.rappel-reglement__liste {
    margin: 0 0 0.75rem;
    padding: 0;
    list-style: none;
}

.rappel-reglement__liste li {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.15rem 0.5rem;
    padding: 0.35rem 0;
    border-top: 1px solid var(--c-border);
}

.rappel-reglement__date {
    font-weight: 600;
}

.rappel-reglement__qui {
    color: var(--c-text-muted);
    font-size: 0.9rem;
}

/* `margin-left: auto` pousse le montant à droite QUAND la ligne tient ; dès
   qu'elle passe à la ligne, il redevient un mot parmi les autres. */
.rappel-reglement__montant {
    margin-left: auto;
    white-space: nowrap;
}

/* LE TOTAL DÛ, publié par le moteur (`compte.totalDu`). Il se détache de la
   liste par un filet, parce qu'il n'est pas une ligne de plus : c'est la somme
   que le salon réclamera, et c'est le seul nombre que la personne retiendra.

   ⚠️ IL N'EST JAMAIS COMPOSÉ ICI NI AILLEURS DANS LA VITRINE — voir
   ImpayesClient::total et tests/Gabarits/GardeTotalImpayeTest. */
.rappel-reglement__total {
    margin: 0 0 0.75rem;
    padding-top: 0.75rem;
    border-top: 1px solid var(--c-border, rgba(0, 0, 0, 0.12));
    text-align: right;
}

.rappel-reglement__aide {
    margin: 0;
    color: var(--c-text-muted);
    font-size: 0.9rem;
    line-height: 1.5;
}

.espace-portes {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    gap: 0.75rem;
    margin-bottom: 2rem;
}

.espace-porte {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    padding: 0.9rem 1rem;
    background: var(--c-bg-elev);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    color: var(--c-text);
    /* La carte ENTIÈRE est la cible : viser un mot souligné au doigt est ce
       qui rendait ces accès pénibles sur un téléphone. */
    text-decoration: none;
    transition: border-color 0.15s ease, background-color 0.15s ease;
}

.espace-porte:hover,
.espace-porte:focus-visible {
    background: var(--c-bg-elev-2);
    border-color: var(--c-accent-texte);
}

.espace-porte__titre {
    font-family: var(--police-titre);
    font-weight: 500;
    letter-spacing: 0.5px;
    color: var(--c-accent-texte);
}

.espace-porte__sous {
    color: var(--c-text-muted);
    font-size: 0.85rem;
    line-height: 1.35;
}

/* Sommaire : autant de liens que de sections, chacun avec son compte. Sur
   téléphone, les puces passent à la ligne d'elles-mêmes plutôt que de forcer
   un défilement horizontal. */
.donnees-sommaire {
    list-style: none;
    margin: 0 0 2rem;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.donnees-sommaire a {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    padding: 0.45rem 0.8rem;
    border: 1px solid var(--c-border);
    border-radius: 99px;
    background: var(--c-bg-elev);
    color: var(--c-text-muted);
    font-size: 0.85rem;
}

.donnees-sommaire a:hover {
    border-color: var(--c-accent-texte);
    color: var(--c-accent-texte);
}

.donnees-sommaire span {
    color: var(--c-accent-texte);
    font-family: var(--police-titre);
}

/* Compteur repris dans les titres de section. */
.donnees-compteur {
    color: var(--c-text-muted);
    font-size: 0.85rem;
}

/* Fiche d'identité : libellé au-dessus de la valeur, en grille auto-ajustée —
   une colonne à 390 px, deux ou trois au-delà, sans media query. */
.donnees-fiche {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 1rem;
    margin: 0 0 2rem;
    padding: 1.1rem 1.25rem;
    background: var(--c-bg-elev);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
}

.donnees-fiche dt {
    font-size: 0.75rem;
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--c-text-muted);
    margin-bottom: 0.25rem;
}

.donnees-fiche dd {
    margin: 0;
    /* Une adresse e-mail longue ne doit pas élargir la grille. */
    overflow-wrap: anywhere;
}

.donnees-liste {
    list-style: none;
    margin: 0 0 2rem;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.donnees-carte {
    padding: 1.1rem 1.25rem;
    background: var(--c-bg-elev);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
}

.donnees-carte__head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
}

.donnees-carte__titre {
    margin: 0;
    color: var(--c-accent-texte);
    font-family: var(--police-titre);
    letter-spacing: 1px;
}

.donnees-carte p {
    margin: 0 0 0.4rem;
}

.donnees-carte .aide,
.client-table .aide {
    display: block;
    font-size: 0.8rem;
    color: var(--c-text-muted);
}

.donnees-annule {
    color: #e88;
}

/* Conséquences de la suppression : une liste qu'on lit, pas qu'on survole. */
.donnees-consequences {
    margin: 0;
    padding-left: 1.2rem;
}

.donnees-consequences li {
    margin-bottom: 0.6rem;
    line-height: 1.6;
}

.donnees-consequences li:last-child {
    margin-bottom: 0;
}

/* La phrase à recopier : lisible, sélectionnable, mais posée comme une
   citation — surtout PAS un bouton « copier », la friction est le garde-fou. */
.donnees-phrase {
    display: block;
    margin-top: 0.4rem;
    padding: 0.5rem 0.8rem;
    background: var(--c-bg-elev-2);
    border: 1px dashed var(--c-border);
    border-radius: var(--radius);
    color: var(--c-accent-texte);
    font-family: var(--police-titre);
    letter-spacing: 2px;
    text-align: center;
}

/* ═══ ⚠️ LES RÈGLES `.rdv-jours*` ONT ÉTÉ RETIRÉES *(27 août 2026)* ═══
   Elles habillaient une LISTE DE LIENS — un par jour, vers ce même écran. Ces
   liens ne marchaient pas : ils envoyaient `?jour=` quand le contrôleur lit
   `date`, donc la page se rechargeait à l'identique. La liste a été remplacée
   par l'ACCORDÉON du tunnel (`.creneau-accordion` / `.creneau-day`), qui
   déplie les créneaux sur place — la donnée des sept jours est déjà en page.

   ⚠️ ON LES SUPPRIME PLUTÔT QUE DE LES LAISSER DORMIR. Une règle qui ne
   correspond plus à aucun gabarit ne coûte rien à l'écran et beaucoup à la
   lecture : elle donne à croire qu'un motif existe, et invite à le
   « restaurer ». C'est le pendant du contrôle inverse que ce dépôt fait déjà
   — une classe écrite sans règle —, et il vaut dans les deux sens. */

/* ═══ L'ACCUEIL D'UN SITE D'ENSEIGNE — « Nos salons » en onglets (lot V1c) ═══
   Ces regles ne servent QUE l'accueil d'une enseigne multi-etablissements.
   Un mono-salon rend `home/index.html.twig` et n'en voit aucune : il n'y a pas
   un selecteur ici qui touche une classe existante.

   ⚠️ ET ELLES SONT ECRITES PARCE QUE LE GABARIT LES NOMMAIT SANS QU'AUCUNE
   N'EXISTE. Le depot a deja paye ce defaut : trois classes de la vue Semaine
   calculees, gardees par un test, posees dans le DOM — et invisibles, faute de
   regle. Un `grep -c` de chaque classe du gabarit est le controle le moins
   cher qui soit. */

.enseigne {
    max-width: 1100px;
    margin: 0 auto;
    padding: 3rem 1rem;
}

.enseigne__entete {
    text-align: center;
    margin-bottom: 2rem;
}

.enseigne__surtitre {
    margin: 0 0 .35rem;
    font-size: .8rem;
    letter-spacing: .18em;
    text-transform: uppercase;
    color: var(--c-accent-texte);
}

.enseigne__titre {
    margin: 0;
    font-size: clamp(1.9rem, 5vw, 2.8rem);
    line-height: 1.15;
}

.enseigne__baseline {
    margin: .6rem auto 0;
    max-width: 46ch;
    color: var(--c-text-muted);
}

/* ── La barre d'onglets ──
   ⚠️ MASQUEE PAR DEFAUT, REVELEE PAR LE MODULE. Sans JavaScript ces boutons
   ne feraient rien : tous les panneaux sont ouverts, et il n'y a rien a
   basculer. Le module pose `is-enhanced` au moment meme ou il replie les
   panneaux — les deux gestes sont indissociables, et c'est le CSS qui rend
   l'absence du JS propre plutot que cassee. */
.enseigne-onglets {
    display: none;
}

.enseigne-salons.is-enhanced .enseigne-onglets {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: .5rem;
    margin-bottom: 2rem;
}

.enseigne-onglet {
    padding: .6rem 1.4rem;
    border: 1px solid var(--c-border);
    border-radius: 999px;
    background: var(--c-bg-elev);
    color: var(--c-text);
    font: inherit;
    font-weight: 600;
    cursor: pointer;
    transition: background .15s ease, border-color .15s ease, color .15s ease;
}

.enseigne-onglet:hover {
    border-color: var(--c-accent-texte);
    color: var(--c-accent-texte);
}

.enseigne-onglet.is-actif {
    background: var(--c-accent);
    border-color: var(--c-accent-texte);
    color: var(--c-accent-contrast);
}

/* ⚠️ L'ANNEAU DE FOCUS RESTE VISIBLE : ces onglets se parcourent aux fleches,
   et sans lui on ne saurait pas lequel a le curseur. */
.enseigne-onglet:focus-visible {
    outline: 2px solid var(--c-accent);
    outline-offset: 2px;
}

/* ── Un panneau de salon ──
   ⚠️ LE SELECTEUR PORTE SON `:not([hidden])`, ET C'EST UNE REGLE DU DEPOT.
   `hidden` n'est qu'un `display: none` de la feuille du NAVIGATEUR : toute
   declaration d'auteur qui touche `display` l'emporte, sans le moindre
   signal. Un `display: grid` nu ici rendrait `panneau.hidden = true`
   parfaitement inoperant — et le module aurait l'air juste. Le depot l'a paye
   sur un bouton « debloquer » indenicable, signale en production. */
.enseigne-panneau:not([hidden]) {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr);
    gap: 1.5rem 2.5rem;
    align-items: start;
    padding: 1.75rem;
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    background: var(--c-bg-elev);
    box-shadow: var(--shadow);
}

/* Le panneau des AVIS n'a qu'UNE colonne — 7 septembre 2026.

   Celui d'un salon en a deux : coordonnees a gauche, prestations a droite.
   Ici il n'y a qu'un flux — la note, puis les cartes, qui portent DEJA leur
   propre grille (`.avis-list`, auto-fit). Le laisser en deux colonnes
   couperait cette grille en deux et rendrait des cartes de 260 px dans une
   moitie de panneau.

   Ecrit en surcharge du bloc plutot qu'en dupliquant ses neuf declarations :
   le cadre, le fond et l'ombre restent ceux de son voisin, et un changement
   de theme les suit tous les deux. */
.enseigne-panneau--avis:not([hidden]) {
    grid-template-columns: minmax(0, 1fr);
}

/* ⚠️⚠️ ET SES ENFANTS RETROUVENT LEUR PLACE — sans ces deux lignes, la regle
   ci-dessus NE SERT A RIEN, et c'est mesure : le panneau rendait toujours
   « 740px 221px ».

   `.enseigne-panneau__lien` porte `grid-column: 2` pour le panneau d'un SALON,
   ou il vit sous la colonne des prestations. Repris ici, il RECREE une seconde
   colonne — implicite, donc invisible dans la declaration du parent — et la
   note se retrouvait comprimee dans un couloir de 221 px, « sur 7 avis » casse
   sur trois lignes.

   ⚠️ C'EST LE DEFAUT DU 25 AOUT, A L'IDENTIQUE : la mediatheque avait repris
   `equipe-pastille`, le repere d'une LIGNE de tableau, dans un cadre de 9 rem.
   *Tout emprunt d'une classe concue pour un autre contexte se regarde a
   l'ecran* — aucun test de ce depot ne mesure un pixel, et le HTML servi
   portait la bonne classe. */
/* ⚠️ LA CLASSE EST DOUBLEE — `.enseigne-panneau.enseigne-panneau--avis` —, ET
   CE N'EST PAS UNE COQUETTERIE. Ecrit `.enseigne-panneau--avis > *`, ce
   selecteur pese (0,1,0), autant que `.enseigne-panneau__lien` qui est declare
   PLUS BAS dans ce fichier : a specificite egale c'est l'ordre qui tranche, et
   la regle perdait. MESURE, pas relu — le panneau rendait encore « 740px
   221px » avec les deux regles pourtant chargees.

   ⚠️ ET ON GARDE `> *` PLUTOT QUE DE NOMMER `__lien` : la propriete est
   *« aucun enfant de ce panneau ne part en colonne 2 »*, pas *« ce lien-la
   revient »*. Un enfant ajoute demain suivrait, une liste ecrite a la main
   non. */
.enseigne-panneau.enseigne-panneau--avis > * {
    grid-column: 1;
}

/* Empiles (sans JavaScript), les panneaux ont besoin d'air entre eux. */
.enseigne-panneau + .enseigne-panneau {
    margin-top: 1.25rem;
}

.enseigne-salons.is-enhanced .enseigne-panneau + .enseigne-panneau {
    margin-top: 0;
}

.enseigne-panneau__titre {
    margin: 0 0 .5rem;
    font-size: 1.35rem;
}

.enseigne-panneau__sous-titre {
    margin: 0 0 .6rem;
    font-size: .78rem;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--c-text-muted);
}

.enseigne-panneau__adresse {
    margin: 0 0 .4rem;
    color: var(--c-text-muted);
    line-height: 1.5;
}

.enseigne-panneau__tel {
    margin: 0;
    font-weight: 600;
}

.enseigne-panneau__tel a {
    color: var(--c-accent-texte);
    text-decoration: none;
}

.enseigne-panneau__tel a:hover {
    text-decoration: underline;
}

.enseigne-panneau__horaires {
    margin-top: 1.25rem;
}

/* La colonne de gauche empile coordonnees et horaires ; les prestations et le
   bouton occupent la droite. */
.enseigne-panneau__coordonnees,
.enseigne-panneau__horaires {
    grid-column: 1;
}

.enseigne-panneau__prestations,
.enseigne-panneau__lien {
    grid-column: 2;
}

.enseigne-horaires {
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: .92rem;
}

.enseigne-horaires__jour {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    padding: .3rem 0;
    border-bottom: 1px solid var(--c-border);
}

.enseigne-horaires__jour:last-child {
    border-bottom: 0;
}

.enseigne-horaires__nom {
    font-weight: 600;
}

.enseigne-horaires__valeur {
    color: var(--c-text-muted);
    text-align: right;
}

/* ── Les prestations, cliquables ──
   ⚠️ CHACUNE EST UN LIEN VERS LE TUNNEL DE CE SALON, et le lien doit AVOIR
   L'AIR cliquable : c'est le geste central de cette page (demande d'Alexandre,
   « quand on clique sur une prestation ca enclenche le tunnel »). Une ligne de
   catalogue inerte enverrait le visiteur chercher un bouton ailleurs. */
.enseigne-prestations {
    margin: 0;
    padding: 0;
    list-style: none;
    display: grid;
    gap: .5rem;
}

.enseigne-prestation {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 1rem;
    padding: .7rem .9rem;
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    background: var(--c-bg);
    color: var(--c-text);
    text-decoration: none;
    transition: border-color .15s ease, transform .15s ease;
}

a.enseigne-prestation:hover {
    border-color: var(--c-accent-texte);
    transform: translateX(2px);
}

a.enseigne-prestation:focus-visible {
    outline: 2px solid var(--c-accent);
    outline-offset: 2px;
}

/* Sans `chemin`, aucun lien n'est possible : la ligne reste LISIBLE et cesse
   d'avoir l'air cliquable. On montre le trou, on ne le cache pas. */
.enseigne-prestation--sans-lien {
    opacity: .7;
}

.enseigne-prestation__nom {
    font-weight: 600;
}

.enseigne-prestation__meta {
    color: var(--c-text-muted);
    font-size: .88rem;
    white-space: nowrap;
}

.enseigne-panneau__lien {
    margin: 1rem 0 0;
}

/* ⚠️⚠️ `.hero--marque` A ÉTÉ RETIRÉE — décision d'Alexandre, 8 septembre 2026.
   NE PAS LA RECRÉER SANS LIRE CE QUI SUIT.

   Elle valait `min-height: 62vh` et raccourcissait le hero de l'accueil
   d'enseigne. Son motif, écrit au lot B-3 : *« sur l'accueil d'un salon le hero
   EST la page ; sur celui d'une enseigne, le geste est de CHOISIR SA VILLE, et
   cette liste est juste dessous. Un hero pleine hauteur la repousserait hors de
   l'écran. »* Elle ajoutait : *« ce n'est pas on harmonisera plus tard »*.

   ⚠️ **LE MOTIF A ÉTÉ ENTENDU ET LA DÉCISION RENVERSÉE** : *« le hero avec
   l'image de fond ne prend pas toute la place sur l'écran, on veut le même
   comportement que sur l'accueil d'un salon »*. Le hero de l'enseigne hérite
   donc de `.hero`, pleine hauteur, **exactement comme celui d'un salon**.

   ⚠️ **CE QUE ÇA COÛTE, ET QUI EST ASSUMÉ** : le sélecteur de ville passe sous
   la ligne de flottaison — il faut défiler pour choisir son établissement, sur
   un site dont c'est la fonction principale. *Le fondu du bas de `.hero::after`
   reste le seul indice qu'il y a quelque chose dessous.*

   ⚠️ **ET LA CLASSE EST RETIRÉE DU GABARIT AVEC LA RÈGLE**, plutôt que gardée
   avec un `min-height` qui répéterait celui du parent : *une classe qui ne
   correspond à aucune règle est invisible à tout ce qui lit le HTML*, et une
   règle qui ne fait que redire son parent est un futur « nettoyage » qui
   changerait le rendu sans que personne comprenne pourquoi. */

/* ── Le bloc « concept » de la marque ── */
.enseigne-concept {
    max-width: 62ch;
    margin: 0 auto;
    padding: 0 1rem 4rem;
}

/* ⚠️ ILLUSTRÉ, LE BLOC S'ÉLARGIT — et sans cette règle il ne servirait à rien.
   `.enseigne-concept` est borné à 62ch, la largeur d'une colonne de texte
   confortable ; une grille de photos y tiendrait dans un couloir. La classe est
   posée par le gabarit UNIQUEMENT quand il y a des photos, donc le texte seul
   garde exactement la mise en page qu'il avait avant ce lot. */
.enseigne-concept--illustre {
    max-width: 72rem;
}

.enseigne-concept__titre {
    font-size: clamp(1.5rem, 4vw, 2.1rem);
    margin: 0 0 1rem;
}

.enseigne-concept__texte p {
    margin: 0 0 1rem;
    line-height: 1.7;
    color: var(--c-text-muted);
}

/* ⚠️ UNE SEULE COLONNE SOUS 860 px. Le panneau est en deux colonnes, dont une
   liste de prestations : a l'etroit, les metadonnees (« 30 min · 25 € »)
   passeraient sous le nom et la ligne perdrait sa lisibilite. */
@media (max-width: 860px) {
    .enseigne-panneau:not([hidden]) {
        grid-template-columns: minmax(0, 1fr);
        padding: 1.25rem;
    }

    .enseigne-panneau__coordonnees,
    .enseigne-panneau__horaires,
    .enseigne-panneau__prestations,
    .enseigne-panneau__lien {
        grid-column: 1;
    }
}

/* =========================================================================
   LES VIDÉOS DE L'ACCUEIL — `home/_video.html.twig` *(lot CMS étape 3,
   10 septembre 2026)*.

   ⚠️ TROIS MODES, UNE SEULE BOÎTE. Façade, « en pause » et « automatique »
   rendent des enfants différents — un `<button>` ou une `<iframe>` — dans le
   MÊME cadre, aux MÊMES dimensions. C'est ce qui fait que le clic sur la
   façade ne déplace rien : le lecteur prend exactement la place du bouton.
   ========================================================================= */
.video-accueil {
    position: relative;
    width: 100%;
    margin: 0 auto;
    aspect-ratio: var(--video-ratio);
    max-width: var(--video-largeur);
    overflow: hidden;
    border-radius: var(--radius);
    background: var(--c-bg-elev);
    box-shadow: var(--shadow);
}

/* ═══ ⚠️⚠️ LES DEUX FORMATS PORTENT CHACUN SON CADRE, ET LA BOÎTE N'EN PORTE
   AUCUN ═══
   C'est le seul endroit du produit où `16 / 9` et `9 / 16` sont écrits : le
   moteur publie un **nom** de format, jamais un cadre — *le format est une mise
   en page, et elle appartient au consommateur*. Il n'y a donc pas deux sources
   qui pourraient diverger ; il y en a une de chaque côté du fil, pour deux
   questions différentes.

   ⚠️ **LA VALEUR DE REPLI A ÉTÉ RETIRÉE DE `.video-accueil`, ET C'EST VOULU.**
   Écrite `var(--video-ratio, 16 / 9)`, elle faisait exister `16 / 9` DEUX fois
   — et surtout elle rendait `video-accueil--paysage` **inutile**, donc une
   classe posée par le gabarit et correspondant à aucune règle. *Une classe
   écrite dans un gabarit qui ne peint rien est invisible à tout ce qui lit le
   HTML*, et ce dépôt l'a payé le 25 août. Ici, chaque format DÉCLARE le sien.

   ⚠️ **LA LARGEUR BAISSE AVEC LE RATIO, ET IL LE FAUT** : un 9/16 laissé à
   900 px ferait **1 600 px de haut**, c'est-à-dire une section plus haute qu'un
   écran, au milieu d'une page d'accueil. `min()` la borne aussi à la hauteur
   visible, sans quoi un écran bas la coupe encore.

   ⚠️ **`svh`, PAS `vh`** — même correctif que la hauteur plein écran du 6
   septembre : `100vh` mesure l'écran **sans** la barre du navigateur, donc sur
   téléphone le bloc déborde de ce qu'on voit. */
.video-accueil--paysage {
    --video-ratio: 16 / 9;
    --video-largeur: 900px;
}

.video-accueil--portrait {
    --video-ratio: 9 / 16;
    --video-largeur: min(420px, 56svh);
}

/* ⚠️ LES DEUX ENFANTS REMPLISSENT LE CADRE, ET LA RÈGLE EST COMMUNE. Écrite
   deux fois — une par enfant — elle divergerait le jour où l'on ajuste l'un
   des deux, et le lecteur ne prendrait plus la place du bouton. */
.video-accueil__facade,
.video-accueil__lecteur {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border: 0;
    display: block;
}

.video-accueil__facade {
    padding: 0;
    background: none;
    cursor: pointer;
}

/* ⚠️ `cover` PLUTÔT QUE `contain` : YouTube sert `hqdefault.jpg` en 4/3 avec
   ses propres bandes noires. En `contain`, on verrait les bandes de Google
   DANS notre cadre, plus les nôtres autour. */
.video-accueil__apercu {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

/* ⚠️⚠️ LE VOILE EST PLEIN CADRE, ET CE N'EST PAS UN CHOIX D'ESTHÉTIQUE — LA
   GARDE DES FONDS L'A IMPOSÉ, ET ELLE AVAIT RAISON.
   Première version : une pastille ronde de 4,5 rem en `rgba(0,0,0,.55)`, posée
   au centre par un `transform`. `testAucuneFeuilleNeFigeUneCouleurSombre` l'a
   refusée — un noir écrit à la main qui ne dérive pas du thème —, et
   l'exemption « SURCOUCHE » ne la couvrait pas : elle demande `position` **et**
   `inset`, c'est-à-dire un voile qui recouvre ce qu'il assombrit.

   ⚠️ **LE REMÈDE N'A PAS ÉTÉ D'ÉLARGIR L'EXEMPTION.** Dériver de `--c-bg`
   serait FAUX ici : sur un thème clair, la pastille deviendrait blanche avec un
   ▶ blanc sur une photo lumineuse. *Ce qui se pose sur une PHOTO ne suit pas la
   palette* — même raison que `.gallery__caption`. Le bloc est donc devenu ce
   qu'il aurait dû être dès le départ : **un vrai voile**, plein cadre, que la
   propriété reconnaît sans qu'on touche à la garde. *La borne d'une garde se
   trouve en la posant trop large et en regardant ce qu'elle casse — ici, c'est
   le CODE qui avait tort.*

   ⚠️ Et le voile est utile pour lui-même : un ▶ blanc sur une vignette claire
   est illisible, et une vignette YouTube peut être n'importe quoi. */
.video-accueil__lire {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    /* ⚠️ LES DEUX TEINTES DU VOILE SONT DÉCLARÉES ICI, ET PAS DANS LA RÈGLE DE
       SURVOL. Ce n'est pas de la cosmétique : la règle de survol n'est pas
       positionnée, donc la garde des fonds ne peut pas la reconnaître comme
       une surcouche et la refuse — sa propre limite, écrite sur elle. Les
       poser sur le voile met les deux valeurs là où vit la propriété qui
       les justifie, et le voile devient le point unique de sa palette. */
    --voile-video: rgba(0, 0, 0, 0.28);
    --voile-video-survol: rgba(0, 0, 0, 0.45);
    background: var(--voile-video);
    color: #ffffff;
    font-size: 3.5rem;
    line-height: 1;
    padding-left: 0.12em; /* le glyphe ▶ n'est pas centré dans sa chasse */
    text-shadow: 0 2px 12px rgba(0, 0, 0, 0.6);
}

@media (prefers-reduced-motion: no-preference) {
    .video-accueil__lire {
        transition: background 0.2s ease;
    }

    .video-accueil__lire > span {
        display: block;
        transition: transform 0.2s ease;
    }

    .video-accueil__facade:hover .video-accueil__lire,
    .video-accueil__facade:focus-visible .video-accueil__lire {
        background: var(--voile-video-survol);
    }

    .video-accueil__facade:hover .video-accueil__lire > span,
    .video-accueil__facade:focus-visible .video-accueil__lire > span {
        transform: scale(1.12);
    }
}

/* LE BOUTON « ŒIL » D'UN MOT DE PASSE (16 septembre 2026) — posé par `assets/mot_de_passe_visible.js`.
   Le bouton est dans le champ, à droite ; le champ garde sa place à gauche pour le texte. Cible de 44 px au doigt. */
.mdp-visible {
    position: relative;
    display: block;
}

/* ⚠️ Même sélecteur que la règle générale des champs, plus `.mdp-visible` : sinon son `padding` l'emporte et le texte passe sous le bouton. */
.client-form .mdp-visible input:not([type="checkbox"]):not([type="radio"]):not([type="color"]) {
    padding-right: 3rem;
}

.mdp-visible__bouton {
    position: absolute;
    top: 50%;
    right: 0.25rem;
    transform: translateY(-50%);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.75rem;
    height: 2.75rem;
    padding: 0;
    border: 0;
    background: none;
    color: var(--c-text-muted);
    cursor: pointer;
}

.mdp-visible__bouton:hover,
.mdp-visible__bouton:focus-visible {
    color: var(--c-text);
}
