/* =============================================================
   THÈME CHALEUREUX — theme.css
   Composants. QUE des var(). Aucune couleur, police, taille,
   espacement, rayon ou durée en littéral. La structure ne change
   jamais d'un client à l'autre — seul tokens.css est réécrit.

   ---- CE QUI EST ICI, ET CE QUI EST EN UTILITAIRE --------------
   Depuis le chantier 2, la mise en page (grilles, gouttières,
   marges, tailles de texte) est écrite en utilitaires Tailwind
   DANS LE MARKUP. Ce fichier ne garde que quatre choses, et la
   frontière est mécanique, donc outillable :

     base        les éléments nus et le focus
     rythme      wrap / measure / stack / section — la grammaire de
                 densité, mesurée par test/density.test.js sur CE
                 fichier : elle ne peut pas partir en utilitaire
     composants  les six blocs qui portent un MÉCANISME — menu sans
                 JS, fusion multiply du média, compteur des étapes,
                 décalage d'aplat au survol, statut de l'enseigne
     roles       la liste fermée des rôles de couleur

   Toute déclaration qui produit color / background / border-color /
   fill vit dans ce fichier, jamais dans une classe du markup. C'est
   ce qui garde les couples avant-plan/arrière-plan ÉNUMÉRABLES, donc
   vérifiables par test/contrast.test.js (CLAUDE.md §4).
   ============================================================= */

@layer base {

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

  /* Posé sur le conteneur de défilement, donc valable pour TOUTES les
     ancres d'un coup — y compris le lien d'évitement, qui déposait sa
     cible sous la barre collante. Un scroll-margin par section aurait
     laissé passer la prochaine section ajoutée. */
  html{ -webkit-text-size-adjust:100%; scroll-padding-top:var(--ancre-degagement); }

  body{
    margin:0;
    background:var(--surface);
    color:var(--ink);
    font-family:var(--font-text);
    font-size:var(--text-base);
    line-height:var(--lh-base);
    text-rendering:optimizeLegibility;
    -webkit-font-smoothing:antialiased;

    /* Pied collé en bas sur les pages COURTES — la 404 en est une, et il
       y en aura d'autres. Sans ça le pied flotte au milieu de l'écran et
       la page paraît inachevée.
       Vérifié après coup : les barres collantes de l'enseigne et de
       l'en-tête continuent de fonctionner, un élément flex pouvant être
       position:sticky tant que le défilement reste celui du document. */
    min-height:var(--page-min-h);
    display:flex;
    flex-direction:column;
  }
  main{ flex:1 0 auto; }

  a{ color:var(--brand-strong); text-decoration:none; }
  /* Le survol se signale par le soulignement, pas par un changement de couleur :
     la marque brute n'est pas lisible en texte sur une marque claire (trou §6 n°4). */
  a:hover{ text-decoration:underline; text-underline-offset:var(--underline-offset); }

  :focus-visible{
    outline:var(--focus-ring-w) solid var(--focus);
    outline-offset:var(--space-1);
    border-radius:var(--radius-sm);
  }

  h1,h2,h3{
    font-family:var(--font-title);
    font-weight:var(--weight-bold);
    line-height:var(--lh-tight);
    letter-spacing:var(--tracking-title);
    margin:0;
    text-wrap:balance;
  }
  p{ margin:0; text-wrap:pretty; }

}

/* =============================================================
   RYTHME — familles serrées, respiration entre groupes

   Ces quatre classes ne partent PAS en utilitaires, et la raison
   n'est pas esthétique : test/density.test.js lit `.section
   { padding-block }` et `.stack > * + * { margin-top }` dans ce
   fichier, exige que ce soit une var() seule, et vérifie que
   l'écart RENDU entre deux sections vaut 3× l'intra-groupe (trou
   §6 n°6). Dispersées dans le markup, ces valeurs redeviendraient
   invérifiables — c'est la lecture en token qui avait masqué le
   défaut la première fois.
   ============================================================= */

@layer rythme {

  .wrap{ width:100%; max-width:var(--page-max); margin-inline:auto; padding-inline:var(--space-4); }
  .measure{ max-width:var(--measure); }
  .stack > * + *{ margin-top:var(--rhythm-intra); }  /* intra-groupe 1× */
  .section{ padding-block:var(--section-pad); }      /* × 2 rendu = inter-groupe 3× */
  .hero{ padding-block:var(--section-pad); }         /* le hero respire comme une section */
  .section + .section{ border-top:var(--hair) solid var(--line); }

}

/* =============================================================
   RÔLES DE COULEUR — liste fermée

   Huit classes, et huit seulement. Chacune correspond à une entrée
   de test/lib/couples.js ; les combinaisons possibles (4 rôles de
   texte × 2 surfaces) sont toutes déjà couvertes par la matrice.
   test/markup.test.js refuse toute classe de rôle qui n'est pas
   dans cette liste.

   Ce qui n'est PAS ici, délibérément : --brand-contrast. Il ne
   vit que dans .btn--primary, .media--empty et .marker-badge, où
   l'aplat est posé par la même règle. Lui donner une classe libre
   permettrait de le poser sur n'importe quel fond — c'est le trou
   §6 n°2, rouvert.
   ============================================================= */

@layer roles {

  .on-raised{ background:var(--surface-raised); }

  .t-ink{ color:var(--ink); }
  .t-muted{ color:var(--ink-muted); }
  .t-brand{ color:var(--brand-strong); }

  .rule-t{ border-top:var(--hair) solid var(--line); }
  .rule-b{ border-bottom:var(--hair) solid var(--line); }
  /* Filets PORTEURS DE SENS — 3:1 exigé, à ne pas confondre avec --line. */
  .rule-brand-t{ border-top:var(--hair-2) solid var(--brand-line); }
  .rule-brand-l{ border-left:var(--hair-3) solid var(--brand-line); }

}

@layer composants {

  /* ================= LIEN D'ÉVITEMENT ======================
     Premier élément focalisable de la page. Au clavier, sans lui, on
     traverse l'enseigne, le logo, l'interrupteur de menu, cinq liens et
     un bouton avant d'atteindre le texte — à chaque page.

     Il n'est PAS masqué par `display:none` ni `visibility:hidden` :
     l'un et l'autre le retireraient de l'ordre de tabulation, donc de
     l'existence. Il est déplacé hors cadre et revient au focus. */
  .skip{
    position:absolute; z-index:var(--z-skip);
    inset-inline-start:var(--space-2); inset-block-start:var(--space-2);
    padding:var(--space-2) var(--space-3);
    border-radius:var(--radius);
    background:var(--surface-raised); color:var(--ink);
    transform:translateY(var(--skip-hidden-y));
  }
  .skip:focus{ transform:none; }

  /* ================= ENSEIGNE (signature) ==================
     Barre collante, point qui respire, mono utilitaire.
     4 déclinaisons : horaires / rendez-vous / delai / absent.
     ABSENT → la barre n'est pas rendue par le composant (pas de règle ici). */
  /* La barre entière est collante ; ni l'enseigne ni l'en-tête ne le sont
     séparément. Collants tous deux à top:0, ils se superposaient dès le
     premier défilement — voir src/components/Barre.astro. */
  .barre{ position:sticky; top:0; z-index:var(--z-barre); }

  .enseigne{
    background:var(--surface-raised);
    border-bottom:var(--hair) solid var(--line);
    color:var(--ink);
  }
  .enseigne__dot{
    inline-size:var(--dot-size); block-size:var(--dot-size); border-radius:var(--radius-pill);
    flex:0 0 auto; background:var(--neutral-dot);
    animation:breathe var(--dur-breath) var(--ease) infinite;
  }
  .enseigne[data-status="open"]  .enseigne__dot{ background:var(--positive); }
  .enseigne[data-status="closed"] .enseigne__dot{ background:var(--negative); }
  .enseigne__sep{ opacity:var(--opacity-sep); }

  /* ================= EN-TÊTE / NAV =========================
     Le fond de l'en-tête est translucide : color-mix est une opération
     sur la couleur, elle reste donc ici et jamais dans le markup. */
  .header{
    background:color-mix(in oklab, var(--surface) var(--header-tint), transparent);
    /* Safari exige encore le préfixe : sans lui, l'en-tête y reste
       simplement translucide, sans flou, et le défaut revient. */
    -webkit-backdrop-filter:blur(var(--header-blur)) saturate(var(--header-saturate));
    backdrop-filter:blur(var(--header-blur)) saturate(var(--header-saturate));
    border-bottom:var(--hair) solid var(--line);
  }
  /* Menu dépliant en CSS pur, sans JavaScript.
     Le <details> ne sert que d'INTERRUPTEUR : le panneau est son FRÈRE,
     pas son enfant. Sinon le navigateur masque lui-même le contenu d'un
     <details> fermé, et aucune règle d'auteur ne le ramène au desktop.
     Sous 52 rem l'en-tête reste sur une ligne et le panneau se déploie
     dessous ; au-dessus, l'interrupteur disparaît et le panneau redevient
     une rangée. Le balisage ne change pas.
     (Un vrai bouton avec aria-expanded demandera du JS : chantier 2.)

     Ce bloc est un MÉCANISME, pas de la mise en page : l'état ouvert
     pilote un frère et deux icônes. Il ne se dit pas en utilitaires. */
  .nav__toggle{
    display:inline-flex; align-items:center; justify-content:center;
    inline-size:var(--tap-target); block-size:var(--tap-target);
    cursor:pointer; list-style:none; color:var(--ink);
    border-radius:var(--radius-sm);
  }
  .nav__toggle::-webkit-details-marker{ display:none; }
  .nav__menu[open] .nav__toggle .icon--menu{ display:none; }
  .nav__menu:not([open]) .nav__toggle .icon--fermer{ display:none; }
  /* Le panneau se déploie SOUS l'en-tête, sur toute la largeur, plutôt que
     de pousser le logo sur une deuxième ligne — c'est ce décalage-là qui
     rendait mal. */
  .nav__panel{ display:none; }
  .nav__menu[open] ~ .nav__panel{
    position:absolute; inset-inline:0; top:100%;
    display:flex; flex-direction:column; align-items:stretch;
    gap:var(--space-2);
    padding:var(--space-3) var(--space-4);
    background:var(--surface-raised);
    border-bottom:var(--hair) solid var(--line);
  }
  /* Site à une seule route : rien à déplier, l'interrupteur disparaît. Le
     panneau reste masqué sous 52 rem — la variante qui l'y rendait statique
     a été essayée et REJETÉE en capture : `flex-wrap` absorbait bien le
     débordement, mais le libellé du bouton passait à la ligne et la barre
     collante faisait deux lignes. Elle est d'un seul bloc par décision.
     Le bouton n'est pas perdu : le hero le porte juste dessous, et la bande
     de contact ferme la page. Au-dessus de 52 rem, la règle du média le
     ramène dans la barre. */
  .header[data-solo] .nav__menu{ display:none; }

  .nav__links a{ color:var(--ink); }
  .nav__links a:hover{ color:var(--brand-strong); }

  @media (min-width:52rem){
    .nav__menu{ display:none; }
    .nav__panel{
      position:static; display:flex; flex-direction:row; align-items:center;
      gap:var(--space-3); padding:0; margin-inline-start:auto;
      background:transparent; border-bottom:0;
    }
  }

  /* ================= APPEL À L'ACTION ANCRÉ ================
     Sous 52 rem, le bouton de la barre est masqué avec le panneau, et sur
     un site à route unique l'interrupteur disparaît aussi : une fois le
     hero passé, AUCUNE action n'était atteignable sur toute la hauteur
     restante — ~6 000 px sur Boréal, dont la bande de contact terminale
     qui ne rend pas de bouton faute d'email. `docs/02-5` §48 exige un
     appel « persistant ». Ramener le bouton dans la barre a été essayé et
     REJETÉ en capture (elle passait à deux lignes) ; raccourcir le libellé
     est interdit (§5). Ancré en bas, les trois contraintes tiennent.

     `fixed` et non `sticky` : sticky ne colle qu'à l'intérieur de son
     conteneur, la barre disparaîtrait donc en arrivant sur le pied —
     exactement là où elle sert le plus.

     Le conteneur porte la surface et le filet parce qu'un bouton posé nu
     laisse le texte de la page défiler sous ses coins arrondis. La mise en
     page reste en utilitaires, comme partout. */
  .cta-bas{
    position:fixed; inset-block-end:0; inset-inline:0; z-index:var(--z-cta);
    background:var(--surface-raised);
    border-top:var(--hair) solid var(--line);
  }
  /* La barre est FIXE : elle sort du flux, donc rien ne réserve sa place.
     Sans dégagement, elle recouvre la fin du pied de page et le dernier lien
     du site devient incliquable — un défaut qu'une capture pleine page ne
     montre pas, puisqu'elle ne rend un élément fixe qu'une seule fois.

     Ce dégagement N'EST PAS ICI : il est porté par le pied de page, en
     utilitaire, et seulement là où la barre est rendue
     (`src/components/PiedDePage.astro`). Première version : `padding-block-end`
     sur `body`. Elle marchait et produisait deux défauts — une couture de
     surface claire entre le pied et la barre, tous deux sur surface remontée,
     et 8 rem de vide sous le pied de la 404, qu'aucune barre ne justifiait.
     La VALEUR reste une majoration de tokens.css, MESURÉE au navigateur par
     `npm run shots`. */

  @media (min-width:52rem){
    /* Le panneau redevient une rangée dans la barre, avec son bouton : la
       même action serait offerte deux fois, à deux endroits de l'écran. */
    .cta-bas{ display:none; }
  }

  /* ================= ICÔNES ================================
     Tracé seul, currentColor : la couleur vient du contexte, jamais de
     l'icône. Une icône ne porte jamais le sens seule — le texte à côté
     le porte. Le jeu est fermé et vit dans theme/icons.mjs. */
  .icon{
    inline-size:var(--icon-size); block-size:var(--icon-size);
    flex:0 0 auto;
    fill:none; stroke:currentColor; stroke-width:var(--icon-stroke);
    stroke-linecap:round; stroke-linejoin:round;
  }
  .icon--lg{ inline-size:var(--icon-size-lg); block-size:var(--icon-size-lg); }

  /* ================= BOUTONS =============================== */
  .btn{
    display:inline-flex; align-items:center; justify-content:center; gap:var(--space-1);
    padding:var(--space-2) var(--space-3);
    border:var(--hair) solid transparent; border-radius:var(--radius);
    font-family:var(--font-text); font-size:var(--text-sm); font-weight:var(--weight-medium);
    cursor:pointer; text-decoration:none; white-space:nowrap;
    min-height:var(--tap-target);   /* ≥ cible tactile */
    transition:transform var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
  }
  .btn:hover{ text-decoration:none; }
  .btn:active{ transform:translateY(var(--press-y)); }
  /* Le survol DÉCALE l'aplat, il ne change pas de token : le texte reste
     --brand-contrast sur un aplat plafonné (trou §6 n°2). */
  .btn--primary{ background:var(--brand-fill); color:var(--brand-contrast); }
  .btn--primary:hover{ background:var(--brand-fill-hover); }
  /* Le filet est la SEULE affordance de ce bouton : fond transparent,
     couleur d'encre du corps de texte. Il porte donc du sens, et WCAG 2.1
     SC 1.4.11 lui demande 3:1 — d'ou `--brand-line`, le token que le socle
     designe pour les filets porteurs de sens (trou §6 n°4), et non `--line`,
     qui est un separateur decoratif range dans OBSERVES et asserte a rien.
     Mesure avant correction, sur les pixels de la capture : 1,30:1. */
  .btn--ghost{ background:transparent; color:var(--ink); border-color:var(--brand-line); }
  .btn--ghost:hover{ background:var(--surface-raised); }

  /* ================= BANDE MÉDIA ===========================
     Deux modes : photo présente (image-slot) OU état digne sans photo.
     L'état vide = aplat dérivé de la marque + traitement typographique.
     JAMAIS de rectangle gris barré. */
  .media{
    position:relative; overflow:hidden;
    border-radius:var(--radius);
    aspect-ratio:var(--ratio-media);
  }
  .media--tall{ aspect-ratio:var(--ratio-media-tall); }
  .media--wide{ aspect-ratio:var(--ratio-media-wide); }
  .media__img{ position:absolute; inset:0; width:100%; height:100%; }
  /* object-fit:cover — la photo remplit le cadre quel que soit son format.
     Le client envoie du carré, du portrait, du panoramique : le cadre tient. */
  .media__img img{ width:100%; height:100%; object-fit:cover; display:block;
                   transition:transform var(--dur-slow) var(--ease); }
  a:hover .media__img img{ transform:scale(var(--zoom)); }

  /* État vide digne */
  .media--empty{
    display:grid; place-content:center; text-align:center;
    padding:var(--space-5);
    background:var(--brand-fill);
    color:var(--brand-contrast);
  }
  .media--empty .media__mono{ opacity:var(--opacity-mono); }
  .media--soft.media--empty{ background:var(--surface-raised); color:var(--ink); }

  /* Variante « objet » : visuel détouré sur fond blanc.
     Le fichier ne remplit plus le cadre — il s'y pose, à distance des bords,
     et son blanc fusionne avec la surface au lieu de percer la grille.
     Déclarée par le contenu (kind:"objet"), jamais devinée : voir theme/media.mjs.
     La surface DOIT rester claire, multiply en dépend. */
  /* isolation : la fusion s'arrête au bloc média. Sans elle, mix-blend-mode
     remonte au premier contexte d'empilement trouvé — donc à ce qu'un futur
     fond de .card ou de section viendrait poser. Le résultat dépendrait de
     l'entourage au lieu du token. */
  .media--objet{ background:var(--surface-raised); isolation:isolate; }
  .media--objet .media__img{ inset:var(--media-objet-pad); width:auto; height:auto; }
  .media--objet .media__img img{
    object-fit:contain;
    mix-blend-mode:var(--blend-objet);
  }
  /* Pas de zoom : agrandir un objet posé le fait sortir de sa gouttière. */
  a:hover .media--objet .media__img img{ transform:none; }

  /* ================= PORTES (offre) ======================== */
  .door{
    border:var(--hair) solid var(--line); border-radius:var(--radius);
    background:var(--surface-raised);
    /* Reprise à l'identique de l'ancien `p-4` du markup : ce thème rend
       245 px de colonne dans une porte à 390 px, une valeur VUE et validée.
       Elle descend ici parce que Sobre en veut une autre sous 44 rem, et
       qu'un markup partagé ne peut pas porter deux valeurs. */
    padding:var(--space-4);
  }
  .door[data-marked="true"]{ border-color:var(--brand-line); border-width:var(--hair-2); }
  /* `align-self:flex-start` retiré : la pastille partage désormais sa
     rangée avec l'étiquette de la carte, où elle se centre. En colonne,
     elle occupait une rangée à elle et désalignait la carte marquée. */
  /* Même rembourrage vertical que `.marker-badge`, depuis le même token :
     la rangée d'étiquette mesure alors pareil sur les deux portes, qu'elle
     porte une pastille ou non. Invisible (fond transparent), et c'est le
     but — la seule chose qui change est la PHASE des rangées suivantes. */
  .door__tag{ padding-block:var(--badge-pad-y); }

  .marker-badge{
    background:var(--brand-fill); color:var(--brand-contrast);
    padding:var(--badge-pad-y) var(--space-2); border-radius:var(--radius-pill);
  }

  /* ================= ÉTAPES (séquence réelle) ==============
     Le numéro est un COMPTEUR CSS, pas un contenu : la séquence se
     renumérote seule quand une étape est ajoutée ou retirée. */
  .steps{ counter-reset:step; }
  .step__n{ counter-increment:step; }
  .step__n::before{ content:counter(step,decimal-leading-zero); }

  /* ================= FAQ ==================================
     Le « + » qui pivote en « × » est un état du <details> : mécanisme. */
  .faq__item summary{ cursor:pointer; list-style:none; }
  .faq__item summary::-webkit-details-marker{ display:none; }
  .faq__item summary::after{ content:"+"; font-family:var(--font-mono); color:var(--brand-strong); transition:transform var(--dur) var(--ease); }
  .faq__item[open] summary::after{ transform:rotate(var(--rotate-open)); }

  /* ================= RÉVÉLATION À L'APPARITION =============
     Contenu VISIBLE par défaut. La classe .anim-on (posée par JS, sauf
     reduced-motion) active seule l'état masqué-puis-révélé. Sans JS,
     sans observateur, ou en reduced-motion : tout reste visible. */
  .reveal{ opacity:100%; transform:none; }
  .reveal.will-reveal{ opacity:0; transform:translateY(var(--reveal-y)); transition:opacity var(--dur-slow) var(--ease), transform var(--dur-slow) var(--ease); }
  .reveal.will-reveal.is-in{ opacity:100%; transform:none; }

  @keyframes breathe{
    0%,100%{ opacity:100%;   transform:scale(1); }
    50%    { opacity:0.45; transform:scale(0.82); }
  }

  @media (prefers-reduced-motion: reduce){
    .reveal.will-reveal{ opacity:100%; transform:none; }
    .enseigne__dot{ animation:none; }
  }

}
