/* assets/polices/polices.css */
/* IVY JEWELS — polices auto-hébergées : plus aucune requête vers Google (docs/SUISSE.md § 1.10, LPD).
   Fichiers woff2 VARIABLES, sous-ensemble latin, tels que Google Fonts les sert à un navigateur moderne
   (Cormorant Garamond v21 : axe wght 300–700 ; Jost v20 : axe wght 100–900), vérifiés par lecture de la
   table fvar. Un seul fichier couvre donc les graisses 300 et 400 (Cormorant) ou 400 et 500 (Jost) que
   demande src/css/tokens.css ; les noms de famille sont ceux que tokens.css attend, et ses polices de
   repli calibrées (« … Fallback », size-adjust) restent en jeu pendant le swap.
   Les url() sont relatives à ce fichier : il faut le servir depuis assets/polices/ (ou en réécrire les
   chemins à la construction, comme pour src/css/*.css).
   Cormorant Garamond, retouche du 26.09.2026 : dans les fichiers de Google, les diacritiques des bas-de-casse
   (â ê î ô û, aigus, graves, trémas, tildes) flottent 76/1000 em au-dessus de la lettre — le circonflexe de
   « même » paraissait détaché à toutes les tailles. Les contours d'accent de ces glyphes ont été abaissés de
   35/1000 em (fontTools, contours entièrement au-dessus de 430 unités, déltas de graisse conservés) dans les
   deux fichiers ; les capitales sont intactes. Contrôle : site-test/police (page d'atelier) ou tout titre
   contenant « même ». */

@font-face {
  font-family: "Cormorant Garamond";
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("/assets/polices/cormorant-garamond-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Cormorant Garamond";
  font-style: italic;
  font-weight: 300 700;
  font-display: swap;
  src: url("/assets/polices/cormorant-garamond-italique-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Jost";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/assets/polices/jost-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* src/css/tokens.css */
/* IVY JEWELS — tokens (SEULE source des couleurs, polices, échelles, espaces, ombres, motion).
   Direction artistique V2 « Pierre et Lumière » — docs/DIRECTION-ARTISTIQUE-V2.md § 2. Une dalle de travertin
   à midi : la page EST la pierre, tout ce qui s'y trouve est posé dessus. Trois matières et aucune quatrième :
   la pierre (--fond, la page), le papier (--papier, tout ce qu'on manipule), la photo (les objets).

   Couches de cascade, déclarées ici parce que cette feuille est la première chargée (l'ordre des couches est
   figé par la première déclaration rencontrée) : tokens < base < composants < vues < utilitaires.
   Le détail de la règle, et ce qu'elle implique pour les feuilles sans @layer, est en tête de base.css. */
@layer tokens, base, composants, vues, utilitaires;

/* Propriétés enregistrées : hors couche (une déclaration @property ne cascade pas). Une fois typées, elles
   s'animent et se transitionnent comme des nombres. Sans @property (Safari < 16.4, Firefox < 128) la valeur
   reste lue mais saute d'un point de l'animation à l'autre : les animations sont écrites pour que ce saut
   soit invisible (mêmes extrémités). */
@property --angle-or { syntax: "<angle>"; inherits: false; initial-value: 90deg; }
@property --progression { syntax: "<number>"; inherits: false; initial-value: 0; }

@layer tokens {

:root {
  /* « only light » et non « light » : la page est une dalle claire, et le navigateur ne doit pas la noircir lui-même.
     Le mode sombre FORCÉ de Chromium (Opera GX « Forcer les pages sombres », Chrome Android, Samsung Internet en
     mode Chromium) n'épargne que les éléments en « only light » : avec « light » seul, il inversait le marbre en une
     pierre presque noire. Mesuré (Chromium 153, prefers-color-scheme: dark + forçage) : luminance moyenne de
     l'accueil 131 → 200, celle d'origine. Ce réglage-ci l'emporte sur la balise <meta name="color-scheme"> ; tout
     élément qui redéclare color-scheme doit garder « only » (base.css, cases à cocher). */
  color-scheme: only light;

  /* ---------- Couleurs (§ 2.1) ----------
     Contrastes mesurés sur --fond (pire cas, WCAG) : encre 14,2:1 · encre-2 5,95:1 · encre-3 3,0:1 (décoratif) ·
     or-3 5,1:1 · eau 7,1:1 · corail-texte 4,6:1. Sur --papier : encre 16,6:1 · encre-2 7,0:1 · or-3 6,0:1.
     Sur --profond et --anthracite, la nacre tient 13,6:1 et 14,0:1. */
  --fond: #F7F1E7;          /* ivoire crème : la page, sous le marbre */
  --papier: #EFE5D3;        /* beige champagne : certains blocs — tiroir, champs, modales, cartels, badge, récapitulatif */
  --blanc: #FFFFFF;          /* intérieur des cases et boutons de choix non cochés */
  --nacre: #FBF8F2;         /* texte clair sur le brun cacao et sur --profond */
  --fond-2: #EEE3D1;        /* LQIP et fond des .visuel en attente */
  --fond-3: #E5D6BF;        /* idem, un cran plus soutenu */
  --chauffe: color-mix(in srgb, var(--encre) 6%, var(--fond));   /* surface de survol des zones cliquables sur la pierre (encre 12,4:1) */
  --anthracite: #4A3427;    /* brun cacao : fond du bouton plein (nacre 10,9:1) */
  --encre: #2E211A;         /* cacao profond : texte, titres, logo (13,9:1 sur l'ivoire) */
  --encre-2: #6A574A;       /* secondaire, légendes (6,1:1 ; 5,4:1 au pire pixel du marbre) */
  --encre-3: #9C8A7B;       /* chevrons seulement, jamais du texte */
  --or: #C9AE74;            /* or champagne : filets des .lien--cta, petites touches ; jamais sous du texte */
  --or-2: #E7D6AE;
  --or-3: #7A6034;          /* or champagne textuel : « JEWELS », numéros 01–05 (5,3:1) */
  --numero: var(--or-3);    /* les chiffres des pièces */
  --profond: #0E2A4A;       /* une seule section : étape 3 de la section épinglée */
  --profond-2: #081A30;
  --eau: #1F4E7A;           /* étapes 1–2 de la section épinglée ; « Gardez-le » de la table gravée (7,1:1) */
  --ciel: #CFE3F5;
  --ciel-2: #A9CDEB;
  --verre: #6FB3E8;
  --corail: #D9653D;
  --corail-texte: #A63F26;
  --pitaya: #C9407A;
  --ligne: #E3D5BF;         /* filets gravés (règles, table, pied) */
  --bordure-champ: #8C7867;
  --succes: #4D6B4F;        /* vert sauge foncé : texte de réussite (5,3:1) */
  --sauge: #9DB09A;         /* vert sauge : accent seulement (pastille « En stock ») ; jamais du texte */
  --erreur: #9B2F1F;
  --voile: rgba(46, 33, 26, .38);  /* tiroir et modales seulement (arrière-plan) ; JAMAIS sur une photo */
  /* Anneau de focus en deux couleurs (base.css) : --focus tient 9,4:1 sur le beige, --nacre 13,9:1 sur --profond ;
     l'un des deux est toujours visible, quelle que soit la surface. */
  --focus: #4A3427;
  --ombre-teinte: 60 42 30;        /* triplet sRGB : teinte de TOUTES les ombres, brun chaud, jamais bleu ni noir */
  --cartel-fond: color-mix(in srgb, var(--papier) 94%, transparent);   /* fond du cartel posé sur une photo (encre ≥ 15:1) */

  /* ---------- Ombres (§ 2.2) : une famille, deux états, plus une feuille ----------
     Courte, quasi nette, chaude : l'ombre d'un objet réel à midi. --ombre-contact pour un cartel, un badge ou un
     champ ; --ombre-posee = repos de tout objet photographique ; --ombre-levee = son survol (avec translateY(-2px)),
     portée par .objet::before / ::after (base.css) — on anime opacity et transform, jamais box-shadow ;
     --ombre-feuille (tiroir) et --ombre-modale (modales, feuille de paiement, récapitulatif) pour le papier tenu au-dessus
     de la table. Aucune ombre sur le texte, les filets, les boutons, les liens. */
  --ombre-contact: 0 1px 1px rgb(var(--ombre-teinte) / .10);
  --ombre-posee: 0 1px 1px rgb(var(--ombre-teinte) / .10), 4px 7px 3px 0 rgb(var(--ombre-teinte) / .16);
  --ombre-levee: 0 1px 1px rgb(var(--ombre-teinte) / .08), 6px 12px 6px -2px rgb(var(--ombre-teinte) / .18);
  --ombre-feuille: -16px 0 40px -16px rgb(var(--ombre-teinte) / .32);
  --ombre-modale: 0 24px 48px -24px rgb(var(--ombre-teinte) / .32);
  --ombre-focus: 0 0 0 2px var(--nacre);   /* le halo clair de l'anneau de focus en deux couleurs (base.css) */
  /* Alias de TRANSITION (lots 2 et 3 : composants.css et entete.css les demandent encore) — à retirer à la fusion. */
  --ombre-1: var(--ombre-contact);
  --ombre-2: var(--ombre-modale);

  /* ---------- Marbre ----------
     Le fond de page n'est pas un aplat : c'est la tuile de marbre veiné sans couture (assets/identite/marbre/,
     fabriquée depuis assets/photos/source/marbre.jpg, amplitude 0,7), répétée par base.css et recouverte d'un VOILE
     de --fond qui règle sa force. La tuile est calée sur l'ivoire crème : sa moyenne vaut --fond à moins d'un niveau
     près, le veinage n'est qu'une modulation autour de lui. 55 % : le marbre se voit, le texte reste lisible
     (encre-2 ≥ 5,4:1 au pire pixel). 0 % rend l'aplat --fond au pixel près ; prefers-contrast: more le remet à 0. */
  --marbre-force: 55%;
  --marbre-voile: color-mix(in srgb, var(--fond) calc(100% - var(--marbre-force)), transparent);

  /* ---------- Laque (§ 2.6, la dalle vernie) ----------
     Deux couches fixes au hublot (base.css « La laque ») posent sur le marbre un vernis qui accroche des rayons de soleil
     quand on défile, en mode de fusion « overlay » : la lumière n'y change pas la teinte, elle creuse le contraste du
     veinage (une laque) et n'éclaircit que vers l'éclat — un blanc chaud tiré de --nacre — ; le seul assombrissement est
     le brun des ombres, à 7 % au plus (contraste de --encre-2 mesuré ≥ 4,9:1 au pixel le plus sombre). */
  --laque-eclat: color-mix(in srgb, white 72%, var(--nacre));             /* le blanc chaud de la lumière */
  --laque-vernis: color-mix(in srgb, var(--laque-eclat) 82%, transparent);  /* cœur de la bande spéculaire */
  --laque-vernis-2: color-mix(in srgb, var(--laque-eclat) 38%, transparent); /* ses bords */
  --laque-rayon: color-mix(in srgb, var(--laque-eclat) 66%, transparent);   /* un rayon de soleil */
  --laque-rayon-2: color-mix(in srgb, var(--laque-eclat) 40%, transparent); /* un rayon plus faible */
  --laque-ombre: rgb(var(--ombre-teinte) / .07);                            /* la profondeur : les bords de la dalle */
  --laque-flanc: rgb(var(--ombre-teinte) / .05);                            /* le creux de part et d'autre du vernis */

  /* ---------- Typographie (§ 2.3) ----------
     Deux familles variables auto-hébergées (assets/polices/polices.css). Graisses AUTORISÉES : Cormorant 400 (500
     tolérée sous 20 px sur la pierre), Jost 400 et 500. font-weight: 300 est interdit partout (tools/verifie.mjs
     --da-v2). Compensation optique partout où les deux familles voisinent : Cormorant = Jost × 1,25. */
  --police-display: "Cormorant Garamond", "Cormorant Garamond Fallback", Georgia, "Times New Roman", serif;
  --police-texte: "Jost", "Jost Fallback", "Helvetica Neue", Arial, system-ui, sans-serif;
  --police-ui: var(--police-texte);

  /* Échelle : jeton → interligne / approche / emploi (le détail est au § 2.3 du brief). */
  --t-affiche: clamp(4rem, 2rem + 6vw, 8rem);              /* 64–128 px · .9 · -.02em · numéro de planche, « Ivy » du pied, « 404 » */
  --t-hero: clamp(2.75rem, 1.2rem + 4.2vw, 5.25rem);        /* 44–84 px · 1.0 · -.015em · h1 accueil, boutique, L'eau */
  --t-immersion: clamp(2.5rem, 1rem + 4.5vw, 4.5rem);       /* 40–72 px · 1.02 · -.01em · les quatre mots de la section épinglée */
  --t-1: clamp(2.25rem, 1.3rem + 2.6vw, 3.5rem);            /* 36–56 px · 1.06 · -.01em · h1 de fiche, h2 de section, h1 éditoriaux */
  --t-2: clamp(1.75rem, 1.3rem + 1.2vw, 2.5rem);            /* 28–40 px · 1.12 · nom sur planche, h2 de chapitre */
  --t-3: 1.875rem;                                          /* 30 px · 1.18 · titre du tiroir, mots de la table gravée, citation */
  --t-4: 1.5rem;                                            /* 24 px · 1.25 · nom sur carte, nom dans le panier */
  --t-question: 1.375rem;                                   /* 22 px · 1.3 · question d'accordéon */
  --t-etiquette: 1.125rem;                                  /* 18 px · 1.3 · Cormorant italique : étiquette de règle, titres du pied */
  --t-cta: 1.25rem;                                         /* 20 px · 1.3 · Cormorant italique : .lien--cta */
  --t-chapo: 1.25rem;                                       /* 20 px · 1.45 · Jost : chapô, accroche de fiche, consigne d'étape */
  --t-corps: 1rem;                                          /* 16 px fixe · 1.6 · tout paragraphe */
  --t-ui: .875rem;                                          /* 14 px · 1.4 · .01em · navigation, bouton, prix de carte */
  --t-petit: .8125rem;                                      /* 13 px · 1.5 · légendes, méta, libellés, stepper, cartel */
  --t-label: .6875rem;                                      /* 11 px · 1.2 · .12em capitales · DEUX emplois : .ligne-matiere, badge */
  --t-matiere: var(--t-label);                              /* alias de transition (.ligne-matiere lisait --t-matiere) */

  --interligne-serre: 1.0;                                  /* alias gardés (§ 2.3) */
  --interligne-titre: 1.12;
  --interligne-corps: 1.6;
  --espacement-capitales: .12em;                            /* seule valeur d'approche des capitales */

  /* Mesures */
  --largeur-texte: 34em;                                    /* ≈ 66 signes */
  --largeur-chapo: 26em;
  --largeur-h1: 18ch;                                       /* le h1 de l'accueil tient sur une ligne à ≥ 1280 px */

  /* ---------- Espacement, grille (§ 2.4) ---------- */
  --e-1: 4px;
  --e-2: 8px;
  --e-12: 12px;
  --e-3: 16px;
  --e-4: 24px;
  --e-5: 32px;
  --e-6: 48px;
  --rythme-1: 40px;                          /* entre une règle et son contenu ; entre deux blocs d'un chapitre */
  --rythme-2: clamp(64px, 8vw, 112px);       /* entre deux sections */
  --rythme-3: clamp(112px, 14vw, 192px);     /* avant et après la section épinglée ; entrée d'une page sans héros */
  --e-7: var(--rythme-1);                    /* alias, ne rien casser */
  --e-8: var(--rythme-2);
  --e-9: var(--rythme-3);
  --e-10: var(--rythme-3);
  --e-section: var(--rythme-2);

  --marge-page: clamp(20px, 5vw, 96px);
  --gouttiere: clamp(16px, 2vw, 32px);
  --largeur-max: 1440px;

  /* ---------- Forme (§ 2.5) : une pierre taillée et une feuille ont des angles ---------- */
  --rayon: 0;
  --rayon-pilule: 999px;                     /* compteur du panier, boutons-icônes, pastille de galerie */
  --filet: 1px;

  /* ---------- Motion (4 durées, 3 easings, une presse) ---------- */
  --ease-sortie: cubic-bezier(.16, 1, .3, 1);
  --ease-luxe: cubic-bezier(.22, 1, .36, 1);
  --ease-entree-sortie: cubic-bezier(.65, 0, .35, 1);
  --duree-1: .18s;
  --duree-2: .45s;
  --duree-3: .9s;
  --duree-4: 1.4s;
  --duree-presse: .09s;

  /* ---------- Empilement ---------- */
  --z-entete: 50;
  --z-achat: 60;
  --z-drawer: 80;
  --z-modale: 90;
  --z-toasts: 95;
  --z-preloader: 110;

  /* ---------- Zones sûres (viewport-fit=cover) ----------
     Nulles dans un navigateur ; non nulles en application installée (display-mode: standalone) sur un iPhone à
     encoche, ou en paysage. Elles sont intégrées aux hauteurs d'en-tête ci-dessous : tout ce qui se cale sur
     --h-entete (scroll-padding, hero, têtes collantes) suit sans rien savoir de l'encoche. */
  --safe-haut: env(safe-area-inset-top, 0px);
  --safe-bas: env(safe-area-inset-bottom, 0px);
  --safe-gauche: env(safe-area-inset-left, 0px);
  --safe-droite: env(safe-area-inset-right, 0px);

  /* ---------- Hauteurs (§ 2.5) ---------- */
  --h-bandeau: 0px;                          /* le bandeau n'existe plus (dans tous les blocs) */
  --h-entete-mobile: 56px;
  --h-entete-paysage: 48px;
  --h-entete: calc(64px + var(--safe-haut));
  --h-entete-defile: calc(56px + var(--safe-haut));
  --h-achat: 64px;
}

/* Mobile : en-tête plus bas. */
@media (max-width: 767px) {
  :root { --h-entete: calc(var(--h-entete-mobile) + var(--safe-haut)); --h-entete-defile: calc(var(--h-entete-mobile) + var(--safe-haut)); }
}

/* Mobile paysage / viewport court : tout se compacte. */
@media (orientation: landscape) and (max-height: 520px) {
  :root {
    --h-entete: calc(var(--h-entete-paysage) + var(--safe-haut));
    --h-entete-defile: calc(var(--h-entete-paysage) + var(--safe-haut));
    --rythme-2: clamp(40px, 6vw, 64px);
    --rythme-3: clamp(56px, 8vw, 96px);
    --marge-page: 32px;
  }
}

/* Contraste renforcé demandé par le système : les jetons montent d'un cran, tout le site suit.
   Mesuré sur l'ivoire : encre-2 10,1:1 · encre-3 6,1:1 · or-3 7,6:1 · eau 9,3:1 · ligne 2,8:1 (filets). */
@media (prefers-contrast: more) {
  :root {
    --encre-2: #3E2F26;
    --encre-3: #6A574A;
    --or-3: #5A4524;
    --eau: #173C5E;
    --corail-texte: #8C3320;
    --ligne: #9C8A73;
    --bordure-champ: #6A574A;
    --anthracite: #2E211A;
    --voile: rgba(46, 33, 26, .65);
    /* Le marbre est éteint : c'est la seule chose du site qui fasse DESCENDRE un contraste sous sa valeur nominale.
       Quand le système demande plus de contraste, le fond redevient l'aplat ivoire exact. */
    --marbre-force: 0%;
  }
}

}

/* Polices de repli calibrées (CLS ≈ 0 au swap). Hors couche : les descripteurs @font-face ne cascadent pas. */
@font-face {
  font-family: "Cormorant Garamond Fallback";
  src: local("Georgia"), local("Times New Roman");
  size-adjust: 88%;
  ascent-override: 92%;
  descent-override: 26%;
  line-gap-override: 0%;
}
@font-face {
  font-family: "Jost Fallback";
  src: local("Arial"), local("Helvetica Neue");
  size-adjust: 94%;
  ascent-override: 96%;
  descent-override: 24%;
  line-gap-override: 0%;
}

/* src/css/base.css */
/* IVY JEWELS — base : reset, la pierre (fond marbré), primitives de matière (.objet, .cartel, .regle, .numero,
   .lien--cta), typographie, utilitaires, images, formulaires, focus, motion réduite, formats.
   Direction artistique V2 « Pierre et Lumière » — docs/DIRECTION-ARTISTIQUE-V2.md § 2.6, § 3.1.

   ---------- Couches de cascade ----------
   L'ordre est déclaré en tête de tokens.css (première feuille chargée) : @layer tokens, base, composants, vues, utilitaires.
   Chaque feuille du système ouvre sa couche : tokens.css → tokens ; base.css → base ; composants.css, entete.css et
   innovations.css → composants. Entre deux couches, c'est la couche la plus tardive qui gagne, quelle que soit la
   spécificité : un composant surcharge le socle sans !important ni sélecteur alourdi.
   Invariant à connaître : une déclaration SANS couche l'emporte sur toutes les couches (elle est traitée comme une
   couche finale implicite). Les feuilles vues/*.css, pied.css et paiement.css ne déclarent pas de couche : elles
   gardent donc leur priorité naturelle sur le système, comme aujourd'hui. Corollaire pour !important : l'ordre
   s'inverse (une couche précoce gagne), et un !important sans couche perd face à un !important en couche — d'où
   l'exception d'impression posée ici même, à côté de [hidden]. impression.css reste hors couche, à charger en dernier. */

@layer base {

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

html {
  font-size: 100%;
  -webkit-text-size-adjust: 100%;
  scroll-padding-top: calc(var(--h-entete) + var(--e-4));
  /* La gouttière de l'ascenseur est réservée en permanence : ouvrir le tiroir (overflow: hidden) ne décale plus la page. */
  scrollbar-gutter: stable;
  hanging-punctuation: first last;
  /* Au doigt, Android voile en gris tout lien ou bouton effleuré au début d'un défilement — un « appui » que l'on n'a
     pas voulu. Boutons et cartes ont leur propre retour (:active, :focus-visible) : le voile est retiré. */
  -webkit-tap-highlight-color: transparent;

  /* ---------- Fond marbré ----------
     La couleur d'abord, l'image ensuite : --fond (ivoire crème) reste peint quoi qu'il arrive, l'image n'est qu'une
     modulation posée dessus. Deux couches, la première étant la plus haute :
       1. le voile — un dégradé plat de --fond à --marbre-force près, qui règle la force du veinage sans déplacer
          la moyenne (tokens.css) ;
       2. la tuile — sans couture, servie en AVIF avec repli WebP par image-set().
     DÉFILANTE, pas figée : background-attachment reste à « scroll ». Le fixed collerait le marbre au hublot au lieu
     de la page, coûte une repeinte à chaque image sur mobile, et iOS l'ignore de toute façon.
     Posé sur la RACINE, donc propagé au canevas : le marbre couvre aussi la gouttière d'ascenseur et le rebond de fin
     de page, et se répète à l'infini vers le bas.
     Sans color-mix ou sans image-set()/type(), la déclaration tombe et il reste background-color : l'aplat ivoire. */
  background-color: var(--fond);
  background-image:
    linear-gradient(var(--marbre-voile), var(--marbre-voile)),
    image-set(
      url("/assets/identite/marbre/tuile-1024.avif") type("image/avif"),
      url("/assets/identite/marbre/tuile-1024.webp") type("image/webp"));
  background-repeat: repeat;
  background-attachment: scroll;
  background-position: 0 0;
}

/* Mobile : la tuile de 512, quatre fois moins de mémoire décodée — ce qui compte sur un téléphone. */
@media (max-width: 767px) {
  html {
    background-image:
      linear-gradient(var(--marbre-voile), var(--marbre-voile)),
      image-set(
        url("/assets/identite/marbre/tuile-512.avif") type("image/avif"),
        url("/assets/identite/marbre/tuile-512.webp") type("image/webp"));
  }
}

/* Tablette (768 à 1023 px) : la tuile de 768, par le même raisonnement. */
@media (min-width: 768px) and (max-width: 1023px) {
  html {
    background-image:
      linear-gradient(var(--marbre-voile), var(--marbre-voile)),
      image-set(
        url("/assets/identite/marbre/tuile-768.avif") type("image/avif"),
        url("/assets/identite/marbre/tuile-768.webp") type("image/webp"));
  }
}
/* ---------- La laque ----------
   La dalle est vernie. Deux couches fixes au hublot (html::before, html::after), SOUS tout le contenu (z-index négatif
   dans le contexte d'empilement de la racine) et AU-DESSUS du marbre peint par la racine : le vernis ne défile pas avec
   la page — c'est un reflet, il appartient à la lumière, pas à la pierre. Aucune image, aucun flou : des dégradés dans
   le ton du fond (tokens.css, --laque-*) fusionnés en « overlay » avec le marbre — la lumière y creuse le contraste du
   veinage au lieu de le voiler, ce qui fait la laque — ; seuls opacity, rotate et translate bougent (compositeur).
   Les couches débordent du hublot (inset négatif) pour que la rotation et le glissement ne découvrent jamais un bord.
     ::before — les rayons : un éventail de quatre rayons de largeurs inégales issus d'un soleil hors champ, en haut à
                droite (le midi de la DA), bords feutrés, sur un vignettage brun chaud très faible (la profondeur de la
                laque). Pendant le défilement il pivote de huit degrés autour du soleil et respire en opacité : la
                lumière tourne sur la dalle.
     ::after  — le vernis : une large bande spéculaire oblique, un cœur d'éclat étroit et feutré (jamais un trait), un
                creux brun très léger de part et d'autre (le relief d'un vernis), qui glisse dans le sens du contenu
                mais deux fois moins vite (parallaxe du reflet) : la surface semble accrocher le soleil.
   Pilotage : animation-timeline: scroll(root) sous @supports ; sinon la variable --defilement (0 à 1), posée sur la
   racine par src/js/noyau/laque.js, entre dans les mêmes calc(). Mouvement réduit (préférence système ou réglage du
   pied, html.motion-reduite) : rien ne bouge, la laque reste posée dans son état médian, toujours vernie. Contraste
   renforcé, contraste forcé, impression : la laque disparaît, comme le marbre.
   Lisibilité : la fusion « overlay » d'une lumière claire n'éclaircit que ; les seuls assombrissements sont --laque-ombre
   (7 %) et --laque-flanc (5 %), mesurés sur le fond nu : --encre-2 garde ≥ 4,9:1 et --encre ≥ 11:1 au pixel le plus
   sombre. */
html::before, html::after {
  content: "";
  position: fixed;
  z-index: -1;
  pointer-events: none;
  contain: strict;
  mix-blend-mode: overlay;
}
html::before {
  inset: -14%;
  transform-origin: 92% 0;
  background-image:
    radial-gradient(120% 90% at 50% 40%, transparent 55%, var(--laque-ombre) 100%),
    conic-gradient(from 168deg at 92% -22%,
      transparent 0deg 3deg, var(--laque-rayon) 9deg 13deg, transparent 20deg 26deg,
      var(--laque-rayon-2) 31deg 33deg, transparent 38deg 45deg,
      var(--laque-rayon) 52deg 59deg, transparent 67deg 74deg,
      var(--laque-rayon-2) 78deg 79deg, transparent 84deg 360deg);
  rotate: calc((.5 - var(--defilement, .5)) * 8deg);
  opacity: .9;
}
html::after {
  inset: -40% 0;
  background-image:
    linear-gradient(112deg, transparent 48.6%, var(--laque-vernis) 50%, transparent 51.4%),
    linear-gradient(112deg,
      transparent 26%, var(--laque-flanc) 33%, transparent 39%,
      var(--laque-vernis-2) 46%, var(--laque-vernis) 50%, var(--laque-vernis-2) 54%,
      transparent 61%, var(--laque-flanc) 67%, transparent 74%);
  translate: 0 calc((.5 - var(--defilement, .5)) * 60%);
}
@supports (animation-timeline: scroll()) {
  html::before { rotate: none; animation: laque-rayons linear both; animation-timeline: scroll(root); }
  html::after { translate: none; animation: laque-vernis linear both; animation-timeline: scroll(root); }
  @keyframes laque-rayons { from { rotate: 4deg; opacity: .8; } 30% { opacity: 1; } 55% { opacity: .75; } 80% { opacity: 1; } to { rotate: -4deg; opacity: .85; } }
  @keyframes laque-vernis { from { translate: 0 30%; } to { translate: 0 -30%; } }
}
/* Écran tactile (html.tactile, posé par motion.js ; any-pointer: coarse dès le premier rendu) : la même laque, sans
   fusion « overlay ». Une couche fusionnée oblige le compositeur à relire toute la page derrière elle à chaque image du
   défilement — deux relectures plein écran par image sur un téléphone, de quoi figer l'écran pendant l'élan. Or sur
   cette pierre claire (luminance ≥ 0,5 partout), « overlay » d'un blanc chaud vaut un mélange normal de ce même blanc
   (fond + α × (1 − fond)) : rayons et vernis ne changent pas. Seules les ombres, qu'« overlay » n'assombrit qu'à peine
   sur un fond clair, descendent à 1 % et 0,7 % pour garder le même effet. */
@media (any-pointer: coarse) {
  html::before, html::after { mix-blend-mode: normal; }
  html { --laque-ombre: rgb(var(--ombre-teinte) / .01); --laque-flanc: rgb(var(--ombre-teinte) / .007); }
}
html.tactile::before, html.tactile::after { mix-blend-mode: normal; }
/* Compositeur fragile (Opera Android et GX Mobile, motion.js) : la laque est retirée — deux calques plein écran de
   moins à composer à chaque image de l'élan, que ce navigateur ne présente déjà pas toutes. Le marbre reste. */
html.compositeur-fragile::before, html.compositeur-fragile::after { display: none; }
/* Diagnostic (?diag, src/js/noyau/diag.js) : la bascule « Laque : non ». */
html.diag-sans-laque::before, html.diag-sans-laque::after { display: none; }
html.tactile { --laque-ombre: rgb(var(--ombre-teinte) / .01); --laque-flanc: rgb(var(--ombre-teinte) / .007); }
@media (prefers-reduced-motion: reduce) { html::before, html::after { animation: none; rotate: none; translate: none; } }
html.motion-reduite::before, html.motion-reduite::after { animation: none; rotate: none; translate: none; }
@media (prefers-contrast: more), (forced-colors: active), print { html::before, html::after { display: none; } }

html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
.lenis.lenis-stopped { overflow: hidden; }
html.est-bloque { overflow: hidden; }

body {
  margin: 0;
  min-height: 100svh;
  /* Transparent : le marbre est peint par la racine (propagé au canevas, ci-dessus). Un aplat opaque ici
     le recouvrirait sur toute la page — et un second exemplaire de la tuile, calé sur la boîte du body,
     s'arrêterait à la gouttière d'ascenseur. */
  background: transparent;
  color: var(--encre);
  font-family: var(--police-texte);
  font-size: var(--t-corps);
  line-height: var(--interligne-corps);
  font-synthesis: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-feature-settings: "kern" 1, "liga" 1;
  overflow-x: clip;
}

::selection { background: var(--ciel); color: var(--profond); }

img, svg, video, canvas { display: block; max-width: 100%; }
img { height: auto; }
figure { margin: 0; }
button, input, select, textarea { font: inherit; color: inherit; letter-spacing: inherit; }
button { cursor: pointer; background: none; border: 0; padding: 0; }
a { color: inherit; text-decoration: none; }
p { margin: 0 0 1em; }
p:last-child { margin-bottom: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
address { font-style: normal; }
hr { border: 0; border-top: var(--filet) solid var(--ligne); margin: var(--e-6) 0; }
small { font-size: var(--t-petit); }
strong, b { font-weight: 500; }
em, i { font-style: italic; }
abbr[title] { text-decoration: none; }
[hidden] { display: none !important; }
/* Impression : les panneaux repliés s'impriment ouverts (accordéons des fiches, bulles d'information).
   L'exception vit ici et pas dans impression.css : un !important de la couche base ne se lève que depuis la couche base. */
@media print { .accordeon__panneau[hidden], .infobulle__texte[hidden] { display: block !important; } }

/* ---------- Focus : anneau en deux couleurs ----------
   Un seul anneau sombre tombait à 1,26:1 sur les surfaces bleu profond (section épinglée, lightbox, toasts). Le halo
   nacre (--ombre-focus, collé à l'élément) est lu sur le sombre, l'anneau --focus (outline, 2 px plus loin) sur le
   beige : 13,9:1 et 9,4:1, sans variante par surface. Un composant qui pose son propre box-shadow (modale, tiroir)
   le garde — sa couche gagne — et conserve l'anneau sombre. */
:focus { outline: none; }
:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; box-shadow: var(--ombre-focus); border-radius: var(--rayon); }
/* Surfaces sombres qui l'annoncent (toasts, composants/toasts.js) : anneau et halo nacre, une seule couleur claire. */
.sur-sombre :focus-visible { outline-color: var(--nacre); }
/* Le focus programmatique du routeur sur <main> ne dessine rien. */
main:focus-visible, .vue:focus-visible { outline: none; box-shadow: none; }

/* ---------- Lien d'évitement ---------- */
.lien-evitement {
  position: fixed; top: calc(var(--e-3) + var(--safe-haut)); left: max(var(--e-3), var(--safe-gauche)); z-index: calc(var(--z-preloader) + 1);
  padding: var(--e-2) var(--e-4); background: var(--anthracite); color: var(--nacre);
  font-family: var(--police-ui); font-size: var(--t-ui); font-weight: 500;
  transform: translateY(-200%); transition: transform var(--duree-1) var(--ease-luxe);
}
.lien-evitement:focus { transform: none; }
/* Contenu masqué visuellement mais focusable (boutons « Agrandir la photo », « Fermer le menu ») : au focus
   clavier, il se montre. La variante nommée .u-visuellement-cache-focalisable (entete.js) partage la règle. */
.u-visuellement-cache:focus-visible, .u-visuellement-cache-focalisable:focus-visible {
  position: static; width: auto; height: auto; margin: 0; padding: var(--e-2) var(--e-3); overflow: visible;
  clip: auto; clip-path: none; white-space: normal; background: var(--anthracite); color: var(--nacre);
}

/* ---------- Typographie (§ 2.3) : l'encre est posée en 400, jamais en filet gris ---------- */
h1, h2, h3, h4 {
  font-family: var(--police-display);
  font-weight: 400;
  line-height: var(--interligne-titre);
  margin: 0;
  letter-spacing: 0;
  hyphens: manual;
  text-wrap: balance;
}
h1 { font-size: var(--t-1); line-height: 1.06; letter-spacing: -.01em; }
h2 { font-size: var(--t-1); line-height: 1.06; letter-spacing: -.01em; }
h3 { font-size: var(--t-2); }
h4 { font-size: var(--t-3); line-height: 1.18; }
/* Titres équilibrés (balance, Baseline), paragraphes sans ligne orpheline (pretty : Chrome 117, Safari 26 ; ignoré ailleurs). */
.u-titre-hero, .u-titre-1, .u-titre-2, .u-titre-3, .u-affiche { text-wrap: balance; }
p, li, dd, dt, blockquote, figcaption { text-wrap: pretty; }
.u-italique { font-style: italic; font-weight: 400; }
.u-titre-hero { font-size: var(--t-hero); font-weight: 400; line-height: 1; letter-spacing: -.015em; max-width: var(--largeur-h1); }
.u-titre-1 { font-size: var(--t-1); font-weight: 400; line-height: 1.06; letter-spacing: -.01em; }
.u-titre-2 { font-size: var(--t-2); font-weight: 400; line-height: 1.12; letter-spacing: 0; }
.u-titre-3 { font-size: var(--t-3); font-weight: 400; line-height: 1.18; letter-spacing: 0; }
/* Le numéro de planche, « Ivy » du pied, « 404 » : l'affiche, chiffres alignés. */
.u-affiche { font-family: var(--police-display); font-weight: 400; font-size: var(--t-affiche); line-height: .9; letter-spacing: -.02em; font-variant-numeric: lining-nums tabular-nums; }
.u-chapo { font-family: var(--police-texte); font-size: var(--t-chapo); line-height: 1.45; max-width: var(--largeur-chapo); color: var(--encre); }
.u-texte { max-width: var(--largeur-texte); }
.u-petit { font-size: var(--t-petit); line-height: 1.5; color: var(--encre-2); }
.u-secondaire { color: var(--encre-2); }
.u-centre { text-align: center; }

/* Ligne matière — geste de maison, UNE fois par page : le premier des deux emplois des capitales espacées (§ 2.3),
   l'autre étant le badge d'état. .surtitre est conservé comme ALIAS (même rendu) : les vues le demandent encore
   jusqu'à la fusion des lots 2 à 7 ; après, il n'a plus d'usage. */
.ligne-matiere, .surtitre {
  display: block;
  font-family: var(--police-ui);
  font-size: var(--t-label);
  font-weight: 500;
  letter-spacing: var(--espacement-capitales);
  text-transform: uppercase;
  color: var(--encre-2);
  line-height: 1.2;
}
.surtitre { margin-bottom: var(--e-4); }
.surtitre--or { color: var(--or-3); }
.surtitre--nacre, .ligne-matiere--nacre { color: var(--nacre); }
.ligne-matiere .est-eau { color: var(--eau); }

/* Prix : Jost tabulaire, jamais gras, jamais colorés, jamais barrés. */
.prix {
  font-family: var(--police-ui);
  font-weight: 400;
  font-variant-numeric: lining-nums tabular-nums;
  color: var(--encre);
  white-space: nowrap;
}

/* Numéro de pièce (§ 3.1) : Cormorant 400, or textuel, chiffres alignés ; la taille vient du contexte (14 px sur
   carte, --t-affiche sur planche, 16 px dans le cartel, 18 px dans le pied). La vue pose view-transition-name:
   numero-<id> : le chiffre voyage avec la photo de la carte à la fiche. */
.numero {
  font-family: var(--police-display);
  font-weight: 400;
  font-style: normal;
  color: var(--numero);
  font-variant-numeric: lining-nums tabular-nums;
  letter-spacing: 0;
  line-height: 1;
}
.numero--planche { font-size: var(--t-affiche); line-height: .9; letter-spacing: -.02em; }
.numero--carte { font-size: .875rem; }
.numero--cartel { font-size: 1rem; }
.numero--pied { font-size: 1.125rem; }

/* Lien souligné animé (inchangé). */
.lien {
  position: relative;
  display: inline-block;
  color: inherit;
  padding-bottom: 2px;
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-size: 100% 1px;
  background-position: 0 100%;
  transition: background-size var(--duree-2) var(--ease-luxe), color var(--duree-1);
}
.lien:hover, .lien:focus-visible { background-size: 0 1px; }
.lien--eau { color: var(--eau); background-image: linear-gradient(var(--eau), var(--eau)); }
/* Lien d'appel (§ 3.1) : tout ce qui découvre — « Voir la pièce », « L'histoire de la maison », « Écrire à Ivy »,
   « Retour à l'accueil ». Cormorant italique, encre ; au survol le filet or se DESSINE de gauche à droite (l'inverse
   de .lien, qui se rétracte) et la flèche « → » — en texte, pas d'icône — avance de 4 px. Le padding bas laisse le
   filet sous les descendantes. Un .icone qui traînerait encore (vues d'avant la fusion) est réglé par composants.css. */
.lien--cta {
  font-family: var(--police-display); font-style: italic; font-weight: 400; font-size: var(--t-cta); line-height: 1.3;
  letter-spacing: 0; text-transform: none; color: var(--encre);
  background-image: linear-gradient(var(--or), var(--or)); background-size: 0 1px; background-position: 0 100%; background-repeat: no-repeat;
  padding-bottom: 4px;
  transition: background-size var(--duree-2) var(--ease-luxe), color var(--duree-1);
}
.lien--cta:hover, .lien--cta:focus-visible { background-size: 100% 1px; }
.lien--cta__fleche { display: inline-block; margin-left: .25em; transition: transform var(--duree-2) var(--ease-luxe); }
.lien--cta:hover .lien--cta__fleche, .lien--cta:focus-visible .lien--cta__fleche { transform: translateX(4px); }

/* Texte courant. */
.texte { max-width: var(--largeur-texte); }
.texte p + p { margin-top: 1em; }
.texte a { text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--or); }
.texte blockquote { margin: var(--e-5) 0; padding-left: var(--e-4); border-left: var(--filet) solid var(--or); font-family: var(--police-display); font-style: italic; font-size: var(--t-3); line-height: 1.18; }

/* ---------- Conteneurs ---------- */
.u-conteneur {
  width: 100%;
  max-width: var(--largeur-max);
  margin-inline: auto;
  padding-inline: max(var(--marge-page), var(--safe-gauche)) max(var(--marge-page), var(--safe-droite));
}
/* Sans !important sur position : la variante :focus-visible ci-dessus doit pouvoir la lever. */
.u-visuellement-cache {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}
.u-cache-mobile { display: initial; }
@media (max-width: 767px) { .u-cache-mobile { display: none !important; } }

/* ---------- Sections (§ 2.4) : padding haut seulement, jamais de padding bas ----------
   La grille se voit par ses horizontales (règles, filets, filet du pied), jamais par ses colonnes ; la dernière
   section de main reçoit 48 px avant le pied, qui suit à margin-top: 0 (cible : ≤ 64 px entre le dernier contenu
   et le filet du pied). Aucun aplat de section : la pierre continue sous tout (.section--lin ne peint plus rien). */
.section { padding-block: var(--rythme-2) 0; }
.section--serree { padding-block: var(--rythme-1) 0; }
.section--large { padding-block: var(--rythme-3) 0; }
.section--lin { background: transparent; }
main > .section:last-child, .vue > .section:last-child { padding-bottom: var(--e-6); }
.section__tete { margin-bottom: var(--e-6); max-width: 720px; }
.section__tete--centre { margin-inline: auto; text-align: center; }
.section__tete--centre .u-chapo { margin-inline: auto; }
.section__tete .u-chapo { margin-top: var(--e-4); }
.section__tete .surtitre { margin-bottom: var(--e-3); }
.section__pied { margin-top: var(--e-6); }
.filet { border: 0; border-top: var(--filet) solid var(--ligne); margin: 0; }

/* Règle gravée (§ 3.1) : un filet 1 px --ligne et une étiquette Cormorant italique 18 px bas-de-casse --encre-2,
   sans numéro ; un .lien--cta facultatif à droite. UNE par section, jamais deux dans un même écran de 900 px,
   aucune sur la fiche ni dans le tunnel (contrôlé par tools/verifie.mjs --da-v2). */
.regle {
  display: flex; justify-content: space-between; align-items: baseline; gap: var(--e-4);
  padding-top: var(--e-3); border-top: var(--filet) solid var(--ligne); margin-bottom: var(--rythme-1);
}
.regle__etiquette { font-family: var(--police-display); font-style: italic; font-weight: 400; font-size: var(--t-etiquette); line-height: 1.3; color: var(--encre-2); margin: 0; }
.regle__lien { flex: none; }

/* Séparateur lierre : monogramme entre deux hairlines. */
.separateur-lierre {
  display: flex; align-items: center; justify-content: center; gap: var(--e-3);
  color: var(--or); margin-block: var(--rythme-1);
}
.separateur-lierre::before, .separateur-lierre::after { content: ""; width: 48px; height: 1px; background: var(--or-2); }
.separateur-lierre .icone { width: 16px; height: 16px; }

/* ---------- L'objet posé (§ 3.1) ----------
   Tout objet photographique posé sur la pierre : carte, planche, portrait, photo de la section eau, image
   éditoriale. L'ombre est un pseudo-élément — ::after porte l'ombre POSÉE (repos), ::before l'ombre LEVÉE (survol),
   et l'on n'anime que leur opacity et le transform de l'objet, jamais box-shadow (qui repeint à chaque image).
   Survol / focus-within sur pointeur fin seulement : l'objet se soulève de 2 px en --duree-1, redescend en 300 ms.
   Tactile : aucun changement d'état. Mouvement réduit : l'ombre change, l'objet ne bouge pas.
   Le view-transition-name: produit-<id> reste sur le .visuel intérieur, jamais sur .objet : un transform sur
   l'élément au départ du morph ferait sauter la transition.
   Deux classes posées par motion.js pour le geste « se poser » (§ 4.9) : .est-leve (ombre levée, avant de se
   poser) et .est-pose (le retour à l'ombre posée dure 700 ms au lieu de 300). */
.objet { position: relative; border-radius: 0; transform: translateY(0); transition: transform 300ms var(--ease-luxe); }
.objet::before, .objet::after {
  content: ""; position: absolute; inset: 0; z-index: -1; border-radius: 0; pointer-events: none;
  transition: opacity 300ms var(--ease-luxe);
}
.objet::after { box-shadow: var(--ombre-posee); opacity: 1; }
.objet::before { box-shadow: var(--ombre-levee); opacity: 0; }
.objet.est-leve::after { opacity: 0; }
.objet.est-leve::before { opacity: 1; }
.objet.est-pose, .objet.est-pose::before, .objet.est-pose::after { transition-duration: 700ms; transition-timing-function: var(--ease-sortie); }
@media (hover: hover) and (pointer: fine) {
  .objet:hover, .objet:focus-within { transform: translateY(-2px); transition-duration: var(--duree-1); }
  .objet:hover::before, .objet:hover::after, .objet:focus-within::before, .objet:focus-within::after { transition-duration: var(--duree-1); }
  .objet:hover::after, .objet:focus-within::after { opacity: 0; }
  .objet:hover::before, .objet:focus-within::before { opacity: 1; }
}

/* ---------- Le cartel (§ 3.1) ----------
   Étiquette de papier posée en bas à gauche d'une photo, mordant sur la pierre de la moitié de sa hauteur — comme
   au musée : « 03 — Céleste, collier papillon · CHF 28.– ». C'est un <a> vers la fiche (ou un <span>). Règle
   absolue : du texte ne recouvre jamais une photo autrement que par un cartel. Le parent (.objet) est positionné. */
.cartel {
  --hauteur-cartel: 40px;
  position: absolute; left: var(--e-4); bottom: calc(-1 * var(--hauteur-cartel) / 2); z-index: 1;
  display: inline-flex; align-items: center; gap: var(--e-2);
  min-height: var(--hauteur-cartel); padding: 0 var(--e-12);
  background: var(--cartel-fond); border-bottom: var(--filet) solid var(--encre); border-radius: 0;
  box-shadow: var(--ombre-contact);
  font-family: var(--police-ui); font-size: var(--t-petit); font-weight: 400; line-height: 1.5; letter-spacing: 0; text-transform: none;
  color: var(--encre); white-space: nowrap; max-width: calc(100% - 2 * var(--e-4)); overflow: hidden; text-overflow: ellipsis;
  transition: background-color var(--duree-1);
}
.cartel:hover, .cartel:focus-visible { background: var(--papier); }
.cartel__numero { font-family: var(--police-display); font-size: 1rem; color: var(--numero); font-variant-numeric: lining-nums tabular-nums; line-height: 1; }
@media (max-width: 767px) { .cartel { left: var(--marge-page); max-width: calc(100% - 2 * var(--marge-page)); } }

/* ---------- Images (IVY.utils.imageHtml) ----------
   La classe de ratio est dérivée de l'option ratio (« 4:5 » → .visuel--4-5) : la famille reste déclarée en entier.
   Aucune image 21:9 (§ 1.2, arbitrage 6). Le fond --fond-3 est le seul aplat toléré : l'attente du LQIP. */
.visuel {
  position: relative;
  overflow: hidden;
  background-color: var(--fond-3);
  background-size: cover;
  background-position: center;
  aspect-ratio: 4 / 5;
  border-radius: 0;
}
.visuel--1-1 { aspect-ratio: 1; }
.visuel--4-5 { aspect-ratio: 4 / 5; }
.visuel--5-4 { aspect-ratio: 5 / 4; }
.visuel--2-1 { aspect-ratio: 2 / 1; }
.visuel--3-4 { aspect-ratio: 3 / 4; }
.visuel--3-2 { aspect-ratio: 3 / 2; }
.visuel--2-3 { aspect-ratio: 2 / 3; }
.visuel--16-9 { aspect-ratio: 16 / 9; }
.visuel--9-16 { aspect-ratio: 9 / 16; }
/* Une vidéo posée se comporte comme une image : elle remplit son cadre, sans transition d'opacité (la vignette
   d'affiche est déjà là pendant le chargement). */
.visuel video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.visuel img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: var(--focal, center);
  opacity: 0; transition: opacity var(--duree-2) ease-out, transform var(--duree-4) var(--ease-sortie);
}
.visuel.est-charge img, .visuel--brut img { opacity: 1; }
.visuel--brut img { transition: none; }
.visuel--vide { display: grid; place-items: center; background: linear-gradient(135deg, var(--fond-2), var(--fond-3)); }
/* Image absente : un simple anneau or (la goutte-illustration d'avant est retirée, § 7.8). */
.visuel__vide { width: 28%; aspect-ratio: 1; border-radius: var(--rayon-pilule); border: var(--filet) solid var(--or-2); opacity: .7; }

/* ---------- Formulaires (§ 3.5) : le papier ----------
   Libellé Jost 500 13 px bas-de-casse --encre au-dessus du champ ; champ 48 px sur --papier, filet --bordure-champ,
   angles droits, --ombre-contact ; focus = filet --profond + anneau deux couleurs ; erreur = filet --erreur. */
.champ { display: grid; gap: var(--e-2); }
.champ__libelle { font-family: var(--police-ui); font-size: var(--t-petit); font-weight: 500; letter-spacing: 0; text-transform: none; line-height: 1.5; color: var(--encre); transition: color var(--duree-1); }
.champ__libelle .champ__facultatif { font-weight: 400; color: var(--encre-2); }
.champ__erreur { font-size: var(--t-petit); color: var(--erreur); display: flex; gap: var(--e-2); align-items: center; }
input:not([type=radio]):not([type=checkbox]):not([type=range]), textarea, select {
  width: 100%; min-height: 48px; padding: var(--e-2) var(--e-3);
  background: var(--papier); border: var(--filet) solid var(--bordure-champ); border-radius: var(--rayon);
  box-shadow: var(--ombre-contact);
  color: var(--encre); font-size: 1rem; line-height: 1.4;
  transition: border-color var(--duree-1), box-shadow var(--duree-1);
}
textarea { min-height: 120px; resize: vertical; }
input::placeholder, textarea::placeholder { color: var(--encre-2); }
/* Au focus, la saisie garde l'anneau en deux couleurs de :focus-visible (ci-dessus) et fonce sa bordure : le halo ciel
   d'avant (1,2:1 sur le papier du champ) ne marquait presque rien. */
input:not([type=radio]):not([type=checkbox]):focus-visible, textarea:focus-visible, select:focus-visible { border-color: var(--profond); }
input[aria-invalid="true"], textarea[aria-invalid="true"] { border-color: var(--erreur); }
/* Cases et boutons de choix dessinés : blancs à l'intérieur tant qu'ils ne sont pas cochés, quel que soit le réglage clair ou
   sombre du téléphone (le contrôle natif d'iOS en mode sombre les peignait en noir). Coché : un point --profond au centre
   du rond, une coche blanche sur fond --profond dans la case. En contraste forcé, le contrôle natif revient. */
input[type=checkbox], input[type=radio] {
  -webkit-appearance: none; appearance: none; flex: none; display: inline-grid; place-content: center;
  width: 20px; height: 20px; margin: 0; padding: 0; cursor: pointer;
  background: var(--blanc); border: 1.5px solid var(--encre-2); border-radius: var(--rayon); color-scheme: only light;
  transition: border-color var(--duree-1) ease, background-color var(--duree-1) ease;
}
input[type=radio] { border-radius: var(--rayon-pilule); }
input[type=radio]::before { content: ""; width: 10px; height: 10px; border-radius: var(--rayon-pilule); background: var(--profond); transform: scale(0); transition: transform var(--duree-1) ease; }
input[type=radio]:checked { border-color: var(--profond); }
input[type=radio]:checked::before { transform: scale(1); }
input[type=checkbox]::before { content: ""; width: 10px; height: 5px; margin-top: -3px; border: solid var(--blanc); border-width: 0 0 2px 2px; transform: rotate(-45deg) scale(0); transition: transform var(--duree-1) ease; }
input[type=checkbox]:checked { background: var(--profond); border-color: var(--profond); }
input[type=checkbox]:checked::before { transform: rotate(-45deg) scale(1); }
input[type=checkbox]:hover:not(:checked), input[type=radio]:hover:not(:checked) { border-color: var(--encre); }
input[type=checkbox]:disabled, input[type=radio]:disabled { opacity: .45; cursor: not-allowed; }
input[type=checkbox][aria-invalid="true"] { border-color: var(--erreur); }
@media (prefers-reduced-motion: reduce) { input[type=checkbox], input[type=radio], input[type=checkbox]::before, input[type=radio]::before { transition: none; } }
@media (forced-colors: active) { input[type=checkbox], input[type=radio] { -webkit-appearance: auto; appearance: auto; } input[type=checkbox]::before, input[type=radio]::before { content: none; } }
.case { display: flex; gap: var(--e-3); align-items: flex-start; cursor: pointer; line-height: 1.4; }
.case input { margin-top: 2px; }
select { appearance: none; background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%231C1A17' stroke-width='1.25'><path d='M6 9l6 6 6-6'/></svg>"); background-repeat: no-repeat; background-position: right var(--e-3) center; padding-right: var(--e-6); }
fieldset { border: 0; padding: 0; margin: 0; min-width: 0; }
legend { padding: 0; }
/* État du champ lu sur le parent : le libellé suit la saisie qu'il nomme. :user-invalid n'apparaît qu'après
   interaction (jamais de rouge avant la première saisie) ; aria-invalid est posé par les vues à la validation. */
.champ:has(:focus-visible) .champ__libelle { color: var(--profond); }
.champ:has([aria-invalid="true"]) .champ__libelle, .champ:has(:user-invalid) .champ__libelle { color: var(--erreur); }
.champ:has(:user-invalid) :is(input, textarea, select) { border-color: var(--erreur); }

/* ---------- Transitions de vue ----------
   Morphing carte → fiche : les noms sont posés dans composants.css (un nom par pièce). Ici, le fondu de page et
   les groupes nommés « produit », auxquels s'applique la classe de transition .produit. */
.vue { display: block; min-height: 100svh; outline: none; } /* un écran réservé avant le premier rendu : le pied ne saute pas (CLS) */
.vue--entre { animation: vue-entre var(--duree-2) var(--ease-sortie) both; }
@keyframes vue-entre { from { opacity: 0; } to { opacity: 1; } }
::view-transition-old(root) { animation: 250ms ease-out both vt-sortie; }
::view-transition-new(root) { animation: 250ms ease-out both vt-entree; }
@keyframes vt-sortie { to { opacity: 0; } }
@keyframes vt-entree { from { opacity: 0; } }
::view-transition-old(produit), ::view-transition-new(produit),
::view-transition-old(.produit), ::view-transition-new(.produit) { animation-duration: .3s; object-fit: cover; mix-blend-mode: normal; }
::view-transition-group(.produit) { animation-timing-function: var(--ease-luxe); }
::view-transition-group(entete) { animation-duration: 0s; }
@media (max-width: 767px) {
  ::view-transition-old(root), ::view-transition-new(root) { animation-duration: 150ms; }
}

/* ---------- États ---------- */
/* Désactivé : 60 % d'opacité, soit 4,3:1 pour le texte d'un bouton plein sur le beige (2,8:1 à 45 %). */
[aria-disabled="true"] { opacity: .6; pointer-events: none; }

/* ---------- Motion réduite : vrai mode ----------
   Le !important en couche base l'emporte sur tout, couches et feuilles sans couche comprises.
   L'objet posé garde son ombre (elle change d'état) mais ne bouge pas. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; transition-delay: 0s !important; scroll-behavior: auto !important; }
  .visuel img { opacity: 1; }
  .objet, .objet:hover, .objet:focus-within { transform: none !important; }
  ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }
}
html.motion-reduite *, html.motion-reduite *::before, html.motion-reduite *::after {
  animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; transition-delay: 0s !important;
}
html.motion-reduite .visuel img { opacity: 1; }
html.motion-reduite .objet, html.motion-reduite .objet:hover, html.motion-reduite .objet:focus-within { transform: none !important; }

/* ---------- Sans JavaScript (aperçu iOS « Coup d'œil », lecteurs sans script) ---------- */
/* Les pages pré-rendues du site public contiennent un vrai en-tête et un vrai pied : on ne masque que les vides. */
html:not(.js) .preloader, html:not(.js) [data-entete]:empty, html:not(.js) [data-pied]:empty { display: none !important; }
html.js .sans-js { display: none; }
.sans-js { display: grid; gap: var(--e-4); padding-block: var(--rythme-1); max-width: 720px; }
.sans-js__logo { font-family: var(--police-display); font-size: 2.5rem; line-height: 1; color: var(--encre); }
.sans-js__logo small { display: block; font-family: var(--police-ui); font-size: .6875rem; font-weight: 500; letter-spacing: .28em; text-transform: uppercase; color: var(--or-3); margin-top: 6px; }
.sans-js__note { padding: var(--e-3) var(--e-4); background: var(--papier); color: var(--encre); border-radius: var(--rayon); box-shadow: var(--ombre-contact); font-size: var(--t-petit); }
.sans-js__produits { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--e-3); }
.sans-js__produits li { display: grid; gap: 4px; font-family: var(--police-display); font-size: 1rem; }
.sans-js__produits img { width: 100%; height: auto; aspect-ratio: 4 / 5; object-fit: cover; }
.sans-js__produits span:last-child { font-family: var(--police-ui); font-size: .875rem; }

/* ---------- Contraste forcé (Windows « Contraste élevé ») ----------
   Les couleurs sont remplacées par celles du système ; ne restent que les bordures et les soulignements. */
@media (forced-colors: active) {
  /* Le marbre est retiré : en contraste forcé, le fond est celui du système (Canvas), et une image de
     fond posée dessous resterait peinte sans que les couleurs du texte y soient accordées. */
  html { background-image: none; }
  .visuel img { opacity: 1; }
  .lien, .lien--cta { background-image: none; text-decoration: underline; text-underline-offset: 3px; }
  .lien-evitement, .u-visuellement-cache:focus-visible, .u-visuellement-cache-focalisable:focus-visible { border: 1px solid CanvasText; }
  :focus-visible { outline: 2px solid Highlight; outline-offset: 2px; box-shadow: none; }
  .filet, hr, .regle { border-top-color: CanvasText; }
  .separateur-lierre::before, .separateur-lierre::after { background: CanvasText; }
  .visuel__vide { border-color: CanvasText; }
  /* Les ombres n'existent pas : objets et cartels sont bordés. */
  .objet::before, .objet::after { display: none; }
  .objet, .cartel { border: 1px solid CanvasText; }
  .cartel { background: Canvas; color: CanvasText; }
}

/* ---------- Impression : filet de sécurité ----------
   La feuille d'impression complète est impression.css (hors couche, chargée en dernier). Ces lignes garantissent,
   même sans elle, qu'aucune surcouche interactive ne se retrouve sur le papier, et que la pierre — grain compris —
   ne part jamais à l'encre. */
@media print {
  .entete, .bandeau, .pied, .panier, .toasts, .preloader, .achat-fixe, .lecture, .lien-evitement, .menu-mobile, .recherche, .modale, .lightbox { display: none !important; }
  /* html autant que body : le marbre est peint par la RACINE. La forme courte remet aussi background-image à none. */
  html, body { background: #fff; color: #000; }
  .objet::before, .objet::after { display: none; }
}

}

/* Langue autre que le français (amorce d'index.html, IVY.langue) : la vue, l'en-tête et le pied restent invisibles
   jusqu'au premier rendu traduit — jamais un éclair de français — 4 s au plus. */
.langue-attente [data-vue], .langue-attente [data-entete], .langue-attente [data-pied] { visibility: hidden; }

/* Allemand : les mots composés (« Grössenumtausch », « Datenschutzerklärung ») se coupent dans le texte courant ;
   un titre ne déborde jamais de sa colonne, quelle que soit la langue (<html lang> posé par IVY.langue). */
:lang(de) p, :lang(de) li, :lang(de) dd, :lang(de) td, :lang(de) th { hyphens: auto; }
h1, h2, h3, h4 { overflow-wrap: break-word; }

/* src/css/composants.css */
/* IVY JEWELS — composants. Direction artistique V2 « Pierre et Lumière », docs/DIRECTION-ARTISTIQUE-V2.md § 3.3 à 3.7 et 3.9 :
   carte produit et rail, boutons (quatre rangs), badges, accordéon, champs, prix, stepper, tiroir panier, barre
   d'achat, table gravée de l'eau, onglets, modale, lightbox, toasts, galerie, boîte « Taille unique », fil d'Ariane,
   infobulle, mini-carte, préchargeur, contraste forcé. Couche « composants » (ordre déclaré dans tokens.css, règle
   expliquée en tête de base.css).
   Trois matières, aucune quatrième : la pierre (la page), le papier (--papier : tiroir, modales, champs, badge,
   cartel, toasts, barre d'achat) et la photo (.objet, ombre posée par base.css). Le seul aplat sombre est le bouton
   plein (--anthracite). Tout est dans les jetons (contrôle 6 de tools/verifie.mjs --da-v2) : aucune font-size,
   ombre, couleur ni rayon littéral ; les durées qui ne sont pas des jetons (350 ms) sont celles que le brief fixe.
   Supprimés (§ 5.2, § 7) : .chip, .chips, .reassurance*, .bloc-eau*, .bouton--instagram / --grand / --pleine /
   --petit / --lien, le disque « + » et le cœur des cartes, .carrousel*, .caracteristiques,
   .pastille-icone, .titre-eau, le sélecteur de tailles.
   Alias de TRANSITION, à retirer à la fusion des lots 4 à 7 (marqués « alias ») : .drawer* / .ligne-panier* /
   .quantite* (panier.js d'avant le lot 4) et .bouton--secondaire (= .bouton--contour). */

@layer composants {

/* ---------- Boutons (§ 3.4) : quatre rangs ----------
   1. .bouton — la plaque --anthracite, sans ombre, sans icône, jamais étirée ; quatre libellés, un seul par écran.
   2. .bouton--contour — mêmes cotes, filet --encre, survol --chauffe (« Continuer mes achats », « Me prévenir »).
   3. .lien--cta (base.css) — tout ce qui découvre.
   4. .bouton--icone — 44 px rond (favoris, partage, fermer). */
.bouton, .bouton--contour {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--e-2);
  min-height: 56px; padding: var(--e-2) var(--e-5);
  width: max-content; max-width: min(100%, 360px);
  font-family: var(--police-ui); font-size: var(--t-ui); font-weight: 500; letter-spacing: .01em; text-transform: none; line-height: 1.3;
  font-variant-numeric: lining-nums tabular-nums;
  color: var(--nacre); background: var(--anthracite); border: var(--filet) solid var(--anthracite); border-radius: 0; box-shadow: none;
  text-align: center; white-space: normal; cursor: pointer; text-decoration: none;
  transition: background-color var(--duree-1) var(--ease-luxe), border-color var(--duree-1) var(--ease-luxe), color var(--duree-1) var(--ease-luxe), transform var(--duree-presse);
}
.bouton:hover, .bouton:focus-visible { background: var(--encre); border-color: var(--encre); color: var(--nacre); }
.bouton:active, .bouton--contour:active { transform: translateY(1px); }
.bouton--contour, .bouton--secondaire /* alias */ { color: var(--encre); background: transparent; border-color: var(--encre); }
.bouton--contour:hover, .bouton--contour:focus-visible, .bouton--secondaire:hover, .bouton--secondaire:focus-visible { background: var(--chauffe); border-color: var(--encre); color: var(--encre); }
/* « Ajouté ✓ » : le libellé change, la plaque reste sombre. */
.bouton.est-succes, .bouton.est-succes:hover { background: var(--encre); border-color: var(--encre); color: var(--nacre); }
/* Désactivé : plaque --chauffe, texte --encre-2 (5,3:1), aucune transparence. */
.bouton[disabled], .bouton[aria-disabled="true"] { background: var(--chauffe); border-color: var(--chauffe); color: var(--encre-2); opacity: 1; cursor: not-allowed; pointer-events: none; transform: none; }
.bouton .icone { flex: none; }
.bouton--icone {
  display: inline-grid; place-items: center; width: 44px; height: 44px; min-height: 0; padding: 0; border-radius: var(--rayon-pilule);
  background: transparent; border: 0; color: var(--encre); box-shadow: none;
  transition: background-color var(--duree-1);
}
.bouton--icone:hover, .bouton--icone:focus-visible { background: var(--chauffe); color: var(--encre); }
.bouton--icone.est-actif .icone { fill: currentColor; }
.bouton--icone .icone { transition: transform var(--duree-1) var(--ease-luxe); }
.bouton--icone:active .icone { transform: scale(1.15); }
.boutons { display: flex; flex-wrap: wrap; gap: var(--e-3); align-items: center; }

/* Icônes */
.icone { display: inline-block; width: 24px; height: 24px; fill: none; stroke: currentColor; stroke-width: 1.25; stroke-linecap: round; stroke-linejoin: round; vertical-align: middle; flex: none; }
.lien .icone, .lien--cta .icone { vertical-align: -3px; margin-right: 4px; }
/* Lien d'appel en 13 px (tiroir, fiche : « ou commander par message Instagram »). */
.lien--cta--petit { font-size: var(--t-petit); line-height: 1.5; padding-bottom: 2px; }

/* ---------- Badges (§ 3.5) : une étiquette de papier — « Résistant à l'eau » (cartes), « Nouveau », « Épuisé » ---------- */
.badge {
  display: inline-flex; align-items: center; gap: 6px; padding: 4px 8px;
  font-family: var(--police-ui); font-size: var(--t-label); font-weight: 500; letter-spacing: var(--espacement-capitales); text-transform: uppercase; line-height: 1.2;
  color: var(--encre); background: var(--papier); border-radius: 0; box-shadow: var(--ombre-contact);
}
.badge--nouveau::before { content: ""; width: 6px; height: 6px; border-radius: var(--rayon-pilule); background: var(--corail); }
.badge--epuise, .badge--erreur { color: var(--erreur); }
/* L'eau : la même étiquette, plus discrète — papier translucide, texte --encre-2, goutte en or champagne. */
.badge--eau { gap: 5px; color: var(--encre-2); background: var(--cartel-fond); }
.badge--eau .icone { width: 12px; height: 12px; color: var(--or-3); }

/* ---------- Carte produit (§ 3.3) : un objet posé ----------
   Numéro au-dessus, photo 4:5 dans un <a class="objet"> (ombre posée, se soulève au survol — base.css), second
   visuel imbriqué en fondu croisé et zoom de 4 % en --duree-3 sur pointeur fin, badges empilés au coin. Sous la
   photo : nom (Cormorant) + prix (Jost), sous-titre (Jost 13), puis le bouton filaire pleine largeur (44 px) qui se
   remplit de cacao au survol et au succès (« Ajouté ✓ »). */
.carte-produit { position: relative; display: block; }
.carte-produit__numero { display: block; margin-bottom: var(--e-2); }
.carte-produit__lien { display: block; color: inherit; }
.carte-produit__visuel { position: relative; }
.carte-produit__visuel img { transition: opacity var(--duree-2) ease-out, transform var(--duree-3) var(--ease-luxe); }
.carte-produit__second { position: absolute; inset: 0; height: 100%; aspect-ratio: auto; opacity: 0; transition: opacity 350ms cubic-bezier(.4, 0, .2, 1); }
@media (hover: hover) and (pointer: fine) {
  .carte-produit__lien:hover .carte-produit__second, .carte-produit__lien:focus-visible .carte-produit__second { opacity: 1; }
  .carte-produit__lien:hover .carte-produit__visuel img, .carte-produit__lien:focus-visible .carte-produit__visuel img { transform: scale(1.04); }
}
@media (prefers-reduced-motion: reduce) { .carte-produit__lien .carte-produit__visuel img { transform: none; } }
.carte-produit__badges { position: absolute; top: var(--e-2); left: var(--e-2); z-index: 2; display: flex; flex-direction: column; align-items: flex-start; gap: var(--e-1); pointer-events: none; }
/* Le cœur d'une carte (page Favoris) : pastille de papier en haut à droite de la photo, 44 px. */
.carte-produit__favori { position: absolute; top: var(--e-2); right: var(--e-2); z-index: 2; background: var(--cartel-fond); box-shadow: var(--ombre-contact); color: var(--encre); }
.carte-produit__favori.est-actif { color: var(--corail-texte); }
.carte-produit--epuise .carte-produit__visuel { opacity: .7; }
.carte-produit__infos { padding-top: var(--e-3); display: grid; gap: var(--e-1); }
.carte-produit__ligne { display: flex; justify-content: space-between; align-items: baseline; gap: var(--e-3); }
.carte-produit__nom { font-family: var(--police-display); font-size: var(--t-4); font-weight: 400; line-height: 1.25; letter-spacing: 0; margin: 0; }
.carte-produit__prix, .carte-produit .prix { font-size: var(--t-ui); font-weight: 500; }
.carte-produit__soustitre { margin: 0; font-family: var(--police-ui); font-size: var(--t-petit); line-height: 1.5; color: var(--encre-2); min-width: 0; }
.carte-produit__ajout {
  display: flex; align-items: center; justify-content: center; width: 100%; min-height: 44px; margin-top: var(--e-12); padding: 0 var(--e-3);
  font-family: var(--police-ui); font-size: var(--t-petit); font-weight: 500; line-height: 1.3; letter-spacing: .01em; text-transform: none; text-decoration: none;
  color: var(--encre); background: transparent; border: var(--filet) solid var(--encre); border-radius: 0; cursor: pointer;
  transition: background-color var(--duree-1) var(--ease-luxe), color var(--duree-1) var(--ease-luxe), border-color var(--duree-1) var(--ease-luxe), transform var(--duree-presse);
}
.carte-produit__ajout:active { transform: translateY(1px); }
.carte-produit__ajout.est-succes { background: var(--anthracite); border-color: var(--anthracite); color: var(--nacre); }
@media (hover: hover) and (pointer: fine) {
  .carte-produit__ajout:hover, .carte-produit__ajout:focus-visible { background: var(--anthracite); border-color: var(--anthracite); color: var(--nacre); }
}
/* Morphing carte → fiche : les noms sont inline (produit-<id> sur la figure, numero-<id> sur le chiffre, planche et
   tête de fiche comprises) ; la classe de transition partagée (.produit, base.css) règle la courbe du groupe. */
.carte-produit__visuel, .galerie__principale { view-transition-class: produit; }
.numero { view-transition-class: numero; }
::view-transition-group(.numero) { animation-timing-function: var(--ease-luxe); }

/* Rail mobile (§ 3.3), SOUS 768 px seulement : 1,4 tuile visible, accroche au début de chaque tuile. Le rail déborde
   du conteneur (marge négative) et garde la marge de page en retrait intérieur : la première tuile s'aligne sur le
   texte, la piste file jusqu'aux bords. Au-dessus, l'élément reste un bloc et c'est la grille de la vue qui s'applique
   (accueil-rangee, produit__autres, introuvable__rangee, grille-produits). */
@media (max-width: 767px) {
  .rail-objets, .grille-produits.rail-objets {
    display: flex; gap: var(--e-4); overflow-x: auto; scroll-snap-type: x mandatory; scroll-padding-inline: var(--marge-page);
    padding-inline: var(--marge-page); margin-inline: calc(-1 * var(--marge-page)); scrollbar-width: none; overscroll-behavior-x: contain; -webkit-overflow-scrolling: touch;
  }
  .rail-objets::-webkit-scrollbar { display: none; }
  .rail-objets > * { flex: 0 0 68vw; min-width: 0; scroll-snap-align: start; }
  /* Les deux flèches d'un rail (composants/rail.js) : boutons de papier de 44 px, ‹ › en Cormorant, éteints au bout. */
  .rail-boutons { display: inline-flex; gap: var(--e-2); vertical-align: middle; }
  /* Ligne de flèches seule sous un rail (fiche, 404) : à droite ; elle n'existe que sous 768 px. */
  .rail-ligne { display: flex; justify-content: flex-end; margin: var(--e-3) 0 0; }
  .rail-boutons[hidden] { display: none; }
  .rail-bouton {
    display: grid; place-items: center; width: 44px; height: 44px; background: var(--cartel-fond); color: var(--encre);
    box-shadow: var(--ombre-contact); border: 0; cursor: pointer; font-family: var(--police-display); font-size: var(--t-3); line-height: 1;
    transition: opacity var(--duree-1) var(--ease-sortie), background var(--duree-1) var(--ease-sortie);
  }
  .rail-bouton span { display: block; transform: translateY(-2px); }
  .rail-bouton:hover { background: var(--papier); }
  .rail-bouton[aria-disabled="true"] { opacity: .35; cursor: default; }
}

/* Grille de cartes standard (favoris : 4 colonnes ; 404 et accueil : cinq) — la grille se voit par ses horizontales. */
.grille-produits { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--e-6) var(--e-4); }
.grille-produits--5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
@media (max-width: 1023px) { .grille-produits, .grille-produits--5 { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 767px) { .grille-produits, .grille-produits--5 { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--e-5) var(--e-3); } }

/* ---------- Accordéon (§ 3.5) ----------
   Filets --ligne, question Cormorant --t-question bas-de-casse sur 64 px, « + » 20 px --encre-2 qui tourne de 45°,
   contenu Jost --t-corps sur --largeur-texte, ouverture grid-template-rows 0fr → 1fr en --duree-2. */
.accordeon { border-top: var(--filet) solid var(--ligne); }
.accordeon__item { border-bottom: var(--filet) solid var(--ligne); }
.accordeon__titre { margin: 0; font-size: inherit; font-weight: 400; line-height: inherit; letter-spacing: 0; }
.accordeon__bouton {
  display: flex; align-items: center; justify-content: space-between; gap: var(--e-3); width: 100%; min-height: 64px; padding: var(--e-2) 0;
  font-family: var(--police-display); font-size: var(--t-question); font-weight: 400; line-height: 1.3; letter-spacing: 0; text-transform: none; text-align: left; color: var(--encre);
}
.accordeon__icone { flex: none; display: grid; place-items: center; width: 20px; height: 20px; font-family: var(--police-ui); font-size: var(--t-cta); font-weight: 400; line-height: 1; color: var(--encre-2); transition: transform var(--duree-2) var(--ease-luxe); }
.accordeon__item.est-ouvert .accordeon__icone { transform: rotate(45deg); }
.accordeon__panneau { display: grid; grid-template-rows: 0fr; transition: grid-template-rows var(--duree-2) var(--ease-sortie); }
.accordeon__item.est-ouvert .accordeon__panneau { grid-template-rows: 1fr; }
.accordeon__contenu { min-height: 0; overflow: hidden; max-width: var(--largeur-texte); font-family: var(--police-texte); font-size: var(--t-corps); line-height: 1.6; color: var(--encre); }
.accordeon__corps { padding-bottom: var(--e-4); }
.accordeon__corps p { margin-bottom: .75em; }
.accordeon__corps p:last-child { margin-bottom: 0; }

/* ---------- Champs (§ 3.5) : compléments à base.css ----------
   Le champ lui-même (papier, 48 px, filet, --ombre-contact, focus, erreur) est en couche base. Ici : le formulaire
   à 34em, deux champs sur une ligne dès 360 px (Prénom | Nom, NPA | Localité), Rue + Numéro en 3fr 1fr, la note
   « Pays : Suisse » en 12 px. */
.formulaire { display: grid; gap: var(--e-4); max-width: var(--largeur-texte); }
.champs { display: grid; gap: var(--e-4) var(--e-3); }
@media (min-width: 360px) { .champs { grid-template-columns: 1fr 1fr; } .champs--rue { grid-template-columns: 3fr 1fr; } }
.champ__note { font-family: var(--police-ui); font-size: .75rem; line-height: 1.5; color: var(--encre-2); }
.champ__facultatif { font-weight: 400; color: var(--encre-2); }

/* ---------- Prix (§ 3.5) : Jost tabulaire --encre, jamais gras ni coloré ni barré ----------
   14 px 500 sur carte (ci-dessus), 20 px 500 sur fiche, 16 px 400 dans le panier, 18 px Cormorant dans le pied. */
.prix--fiche { font-size: 1.25rem; font-weight: 500; }
.prix--panier { font-size: 1rem; font-weight: 400; }
.prix--pied { font-family: var(--police-display); font-size: 1.125rem; font-weight: 400; }

/* ---------- Stepper du tunnel (§ 3.5) : une ligne Jost 13 px bas-de-casse, « 2 / 4 · Coordonnées » ---------- */
.stepper {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 var(--e-2); margin: 0; padding: 0; list-style: none;
  font-family: var(--police-ui); font-size: var(--t-petit); line-height: 1.5; letter-spacing: 0; text-transform: none; color: var(--encre-2);
  font-variant-numeric: lining-nums tabular-nums;
}
.stepper__compte, .stepper__etape.est-active { color: var(--encre); font-weight: 500; }
.stepper__etape { display: inline-flex; gap: var(--e-1); align-items: baseline; }
.stepper__etape::before { content: none; }
.stepper__numero { font-variant-numeric: lining-nums tabular-nums; color: var(--encre); font-weight: 500; }

/* ---------- Tiroir panier (§ 3.6) : une feuille soulevée ----------
   Papier de 440 px (100 % sous 599 px), --ombre-feuille, glissement 450 ms --ease-sortie, fermeture 350 ms, voile en
   --duree-1. Tête « Votre panier » --t-3 + compte 13 px tabulaire ; ligne article : vignette 72 px, nom --t-4, méta
   13 px, stepper 32 px et « Retirer » sur la même ligne, prix 16 px à droite ; la jauge de franco juste sous la
   liste ; pied : « Vous aimerez aussi » → caisse (totaux → un seul .bouton, jamais étiré, § 3.4 → lien Instagram
   13 px → ligne 12 px). Bureau : le corps défile, le pied reste au bas de la feuille. Téléphone (≤ 767 px) : toute
   la feuille défile, la caisse est une barre de papier collée en bas (sticky, filet au-dessus, safe-area). Le
   markup vient du lot 4 (panier.js : racine .tiroir, enfants .panier__*) ; .ligne-panier* reste en alias pour le
   récapitulatif de commande.js d'avant le lot 4. */
.tiroir { position: fixed; inset: 0; z-index: var(--z-drawer); }
.tiroir[hidden] { display: none; }
.tiroir:not(.est-ouvert) { pointer-events: none; }
.panier__voile, .tiroir__voile { position: absolute; inset: 0; background: var(--voile); opacity: 0; transition: opacity var(--duree-1) ease-out; }
.panier__feuille, .tiroir__feuille {
  position: absolute; top: 0; right: 0; bottom: 0; width: min(100%, 440px);
  display: flex; flex-direction: column; background: var(--papier); color: var(--encre); box-shadow: var(--ombre-feuille); border-radius: 0;
  transform: translateX(100%); transition: transform var(--duree-2) var(--ease-sortie);
  overscroll-behavior: contain;
}
.tiroir.est-ouvert .panier__voile, .tiroir.est-ouvert .tiroir__voile { opacity: 1; }
.tiroir.est-ouvert .panier__feuille, .tiroir.est-ouvert .tiroir__feuille { transform: none; }
.tiroir.est-fermant .panier__feuille, .tiroir.est-fermant .tiroir__feuille { transition-duration: 350ms; transition-timing-function: var(--ease-entree-sortie); }
@media (max-width: 599px) { .panier__feuille, .tiroir__feuille { width: 100%; } }
.panier__tete { display: flex; align-items: center; gap: var(--e-2); padding: calc(var(--e-3) + var(--safe-haut)) max(var(--e-3), var(--safe-droite)) var(--e-3) var(--e-5); border-bottom: var(--filet) solid var(--ligne); }
.panier__titre { display: flex; align-items: baseline; gap: var(--e-2); margin: 0; font-family: var(--police-display); font-size: var(--t-3); font-weight: 400; line-height: 1.18; letter-spacing: 0; }
.panier__compte, .panier__titre .u-secondaire { flex: 1; font-family: var(--police-ui); font-size: var(--t-petit); font-weight: 400; line-height: 1.5; color: var(--encre-2); font-variant-numeric: lining-nums tabular-nums; }
.panier__fermer { flex: none; margin-left: auto; }
.panier__corps { flex: 1; overflow: auto; padding: var(--e-2) max(var(--e-5), var(--safe-droite)) var(--e-4) var(--e-5); overscroll-behavior: contain; }
.panier__corps > .panier__jauge { padding-top: var(--e-3); }
.panier__pied { padding: var(--e-4) max(var(--e-5), var(--safe-droite)) calc(var(--e-4) + var(--safe-bas)) var(--e-5); border-top: var(--filet) solid var(--ligne); display: grid; gap: var(--e-3); background: var(--papier); }
.panier__caisse { display: grid; gap: var(--e-3); }
@media (max-width: 599px) { .panier__tete, .panier__corps, .panier__pied { padding-left: max(var(--e-4), var(--safe-gauche)); } }
/* Téléphone (≤ 767 px) et fenêtre basse (paysage, 844 × 390) : toute la feuille défile, le corps ne s'étire plus
   (aucun papier nu entre la liste et le pied), et la caisse (totaux + bouton) est une barre collée en bas de la
   feuille — sous le pouce, visible pendant le défilement, poussée au bas de l'écran par le pied étiré quand le
   contenu est court (§ 3.6). Le pied perd son filet, porté par la caisse. */
@media (max-width: 767px), (orientation: landscape) and (max-height: 520px) {
  .panier__feuille, .tiroir__feuille { overflow: auto; }
  .panier__corps { flex: none; overflow: visible; padding-bottom: var(--e-3); }
  .panier__pied { flex: 1; display: flex; flex-direction: column; gap: 0; padding: 0; border-top: 0; }
  .panier__complement { padding: var(--e-3) max(var(--e-5), var(--safe-droite)) var(--e-4) var(--e-5); border-top: var(--filet) solid var(--ligne); }
  .panier__caisse {
    position: sticky; bottom: 0; margin-top: auto;
    padding: var(--e-3) max(var(--e-5), var(--safe-droite)) calc(var(--e-3) + var(--safe-bas)) var(--e-5);
    background: var(--papier); border-top: var(--filet) solid var(--ligne);
  }
}
@media (max-width: 599px) { .panier__complement, .panier__caisse { padding-left: max(var(--e-4), var(--safe-gauche)); } }
/* Fenêtre basse seulement (téléphone couché, 390 px de haut) : la suggestion attend le portrait, la tête et les
   interlignes se compactent, la caisse se réduit aux totaux et au bouton (Instagram et la mention attendent aussi),
   et RIEN n'est collé : une caisse collée de 234 px recouvrait la ligne d'article (quantité, Retirer) ; ici la
   feuille défile simplement, la ligne est entière et le bouton à quelques pixels du pli. Le sélecteur de la
   suggestion est précisé pour l'emporter sur sa règle de base, écrite plus bas. */
@media (orientation: landscape) and (max-height: 520px) {
  .panier__corps { padding-bottom: var(--e-2); }
  .panier__tete { padding-top: calc(var(--e-2) + var(--safe-haut)); padding-bottom: var(--e-2); }
  .panier__pied { flex: none; position: static; }
  .panier__pied .panier__complement { display: none; }
  .panier__caisse { position: static; padding-top: var(--e-2); padding-bottom: calc(var(--e-2) + var(--safe-bas)); gap: var(--e-2); }
  .panier__caisse .panier__instagram, .panier__caisse .panier__mention { display: none; }
}

/* Lignes d'article */
.panier__lignes { margin: 0; padding: 0; list-style: none; }
.panier__ligne, .ligne-panier { display: grid; grid-template-columns: 72px minmax(0, 1fr) auto; gap: var(--e-3); padding: var(--e-3) 0; border-bottom: var(--filet) solid var(--ligne); align-items: start; }
.panier__vignette, .ligne-panier > a:first-child { display: block; width: 72px; }
.panier__infos { min-width: 0; }
.panier__nom, .ligne-panier__nom { display: inline-block; font-family: var(--police-display); font-size: var(--t-4); font-weight: 400; line-height: 1.25; letter-spacing: 0; color: var(--encre); }
.panier__meta, .ligne-panier__meta { margin: 2px 0 0; font-family: var(--police-ui); font-size: var(--t-petit); line-height: 1.5; color: var(--encre-2); }
.panier__actions { display: flex; align-items: center; gap: var(--e-3); margin-top: var(--e-2); }
.panier__stepper { display: inline-flex; align-items: stretch; height: 32px; border: var(--filet) solid var(--ligne); font-family: var(--police-ui); font-size: var(--t-ui); }
.panier__stepper-bouton { position: relative; width: 32px; display: grid; place-items: center; color: var(--encre); transition: background-color var(--duree-1); }
.panier__stepper-bouton::after { content: ""; position: absolute; inset: -7px -6px; }   /* cible tactile 44 px */
.panier__stepper-bouton:hover, .panier__stepper-bouton:focus-visible { background: var(--chauffe); }
.panier__quantite, .panier__stepper-valeur { display: grid; place-items: center; min-width: 28px; text-align: center; font-size: var(--t-petit); font-variant-numeric: lining-nums tabular-nums; }
.panier__retirer { position: relative; font-family: var(--police-ui); font-size: var(--t-petit); line-height: 1.5; color: var(--encre-2); }
.panier__retirer::after { content: ""; position: absolute; inset: -12px -8px; }
.panier__retirer:hover, .panier__retirer:focus-visible { color: var(--encre); }
.panier__prix, .ligne-panier__prix { text-align: right; font-size: 1rem; font-weight: 400; }
.panier__vide, .panier-vide { display: grid; gap: var(--e-3); justify-items: start; padding: var(--rythme-1) 0; }
.panier__vide-titre { margin: 0; font-family: var(--police-display); font-size: var(--t-3); font-weight: 400; line-height: 1.18; letter-spacing: 0; }
.panier__vide-texte { margin: 0; font-family: var(--police-ui); font-size: var(--t-petit); line-height: 1.5; color: var(--encre-2); max-width: var(--largeur-chapo); }

/* Jauge franco (sous les lignes) ; pied : suggestions, caisse (totaux, bouton, Instagram, moyens de paiement) */
.panier__jauge, .panier__franco { display: grid; gap: var(--e-2); font-family: var(--police-ui); font-size: var(--t-petit); line-height: 1.5; color: var(--encre-2); }
.panier__jauge-texte { margin: 0; }
.panier__jauge-barre { height: 2px; background: var(--ligne); overflow: hidden; }
.panier__jauge-barre > i, .panier__jauge-barre > span { display: block; height: 100%; width: 0; background: var(--or); transition: width var(--duree-2) var(--ease-sortie); }
.panier__complement { display: grid; gap: var(--e-1); }
.panier__complement-titre, .panier__complement .pied__titre { margin: 0; font-family: var(--police-display); font-style: italic; font-weight: 400; font-size: var(--t-etiquette); line-height: 1.3; letter-spacing: 0; text-transform: none; color: var(--encre-2); }
.panier__totaux { display: grid; gap: var(--e-2); font-family: var(--police-ui); font-size: var(--t-ui); line-height: 1.4; }
.panier__total-ligne, .panier__totaux .ligne { display: flex; justify-content: space-between; align-items: baseline; gap: var(--e-3); margin: 0; }
.panier__total-ligne--total, .panier__totaux .ligne--total { font-weight: 500; padding-top: var(--e-2); border-top: var(--filet) solid var(--ligne); }
.panier__totaux .prix { font-size: 1rem; font-weight: 400; }
.panier__total-ligne--total .prix, .panier__totaux .ligne--total .prix { font-weight: 500; }
.panier__instagram { margin: 0; }
.panier__mention, .panier__moyens { margin: 0; font-family: var(--police-ui); font-size: .75rem; line-height: 1.5; color: var(--encre-2); }

/* Marques de paiement (composants/logos-paiement.js) : badges 40 × 26 au format de la marque Apple Pay, tous à la même
   hauteur — la règle d'Apple : jamais plus petite que les marques voisines. 24 px par défaut ; chaque contexte règle
   --hauteur-logo. Images en data-URI : nettes à toute densité, aucune requête. */
.logos-paiement { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--e-1) 6px; line-height: 0; vertical-align: middle; }
.logo-paiement { display: block; height: var(--hauteur-logo, 24px); width: auto; aspect-ratio: 40 / 26; border-radius: 0; }
.panier__mention--logos { display: grid; gap: var(--e-2); justify-items: start; --hauteur-logo: 22px; }
@media (forced-colors: active) { .logo-paiement { forced-color-adjust: none; } }

/* ---------- Barre d'achat fixe (§ 3.7, < 1024 px) : une bande de papier ----------
   Vignette 40 px, nom Cormorant 16 px, prix Jost 14 px 500, .bouton de 44 px (seule exception aux 56 px). */
.achat-fixe {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: var(--z-achat);
  display: flex; align-items: center; gap: var(--e-3); min-height: var(--h-achat);
  padding: var(--e-2) max(var(--e-3), var(--safe-droite)) calc(var(--e-2) + var(--safe-bas)) max(var(--e-3), var(--safe-gauche));
  background: var(--papier); border-top: var(--filet) solid var(--ligne);
  transform: translateY(100%); transition: transform var(--duree-2) var(--ease-sortie);
}
.achat-fixe.est-visible { transform: none; }
.achat-fixe .visuel, .achat-fixe__vignette { width: 40px; flex: none; }
.achat-fixe__infos { flex: 1; min-width: 0; display: grid; gap: 2px; }
.achat-fixe__nom { font-family: var(--police-display); font-size: 1rem; font-weight: 400; line-height: 1.2; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.achat-fixe__prix, .achat-fixe__meta { font-family: var(--police-ui); font-size: var(--t-ui); font-weight: 500; line-height: 1.4; color: var(--encre); font-variant-numeric: lining-nums tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.achat-fixe .bouton, .achat-fixe .bouton--contour { min-height: 44px; padding: 0 var(--e-4); flex: none; }
@media (min-width: 1024px) { .achat-fixe { display: none; } }
html.est-bloque .achat-fixe { transform: translateY(100%); }

/* ---------- Table gravée de l'eau (§ 3.9) : deux colonnes sur la pierre, sans fond, sans pastille ---------- */
.table-eau { display: grid; grid-template-columns: 1fr 1fr; gap: 0 var(--e-6); }
.table-eau__tete { margin: 0 0 var(--e-2); font-family: var(--police-display); font-style: italic; font-weight: 400; font-size: 1.625rem; line-height: 1.2; letter-spacing: 0; color: var(--encre); }
.table-eau__tete--oui { color: var(--eau); }
.table-eau__liste { margin: 0; padding: 0; list-style: none; }
.table-eau__ligne { display: grid; gap: var(--e-1); padding-block: var(--e-3); }
.table-eau__ligne + .table-eau__ligne { border-top: var(--filet) solid var(--ligne); }
.table-eau__mot { font-family: var(--police-display); font-size: var(--t-3); font-weight: 400; line-height: 1.18; letter-spacing: 0; color: var(--encre); }
.table-eau__consigne { font-family: var(--police-texte); font-size: .9375rem; line-height: 1.5; color: var(--encre-2); }
.table-eau__note { margin: var(--e-4) 0 0; font-family: var(--police-ui); font-size: var(--t-petit); line-height: 1.5; color: var(--encre); }
@media (max-width: 599px) {
  .table-eau { grid-template-columns: 1fr; }
  .table-eau__colonne + .table-eau__colonne { margin-top: var(--e-5); }
}

/* ---------- Onglets : Jost bas-de-casse, filet or sous l'onglet actif ---------- */
.onglets__liste { position: relative; display: flex; gap: var(--e-5); border-bottom: var(--filet) solid var(--ligne); overflow-x: auto; scrollbar-width: none; }
.onglets__liste::after { content: ""; position: absolute; bottom: -1px; left: 0; height: 1px; width: var(--indicateur-largeur, 0); background: var(--or); transform: translateX(var(--indicateur-x, 0)); transition: transform var(--duree-2) var(--ease-luxe), width var(--duree-2) var(--ease-luxe); }
.onglets__onglet { padding: var(--e-3) 0; font-family: var(--police-ui); font-size: var(--t-ui); font-weight: 400; letter-spacing: 0; text-transform: none; color: var(--encre-2); white-space: nowrap; transition: color var(--duree-1); }
.onglets__onglet:hover, .onglets__onglet.est-actif { color: var(--encre); }
.onglets__panneau { padding-top: var(--e-4); }

/* ---------- Modale : une feuille de papier tenue au-dessus de la table ----------
   Peut vivre dans la couche supérieure (popover « auto », composants/modale.js) : la mise en page ne change pas.
   Entrée translateY(16px) scale(.98) → 1 en --duree-2 --ease-sortie, fermeture 350 ms. */
.modale { position: fixed; inset: 0; z-index: var(--z-modale); display: grid; place-items: center; padding: var(--e-4); }
.modale:not(.est-ouverte) { pointer-events: none; }
.modale__voile { position: absolute; inset: 0; background: var(--voile); opacity: 0; transition: opacity var(--duree-1) ease-out; }
.modale__boite {
  position: relative; width: min(100%, 560px); max-height: calc(100svh - 2 * var(--e-4)); overflow: auto; overscroll-behavior: contain;
  background: var(--papier); color: var(--encre); padding: var(--e-6) var(--e-5) var(--e-5); border-radius: 0; box-shadow: var(--ombre-modale);
  opacity: 0; transform: translateY(16px) scale(.98); transition: opacity var(--duree-2) var(--ease-sortie), transform var(--duree-2) var(--ease-sortie);
}
.modale__boite--petite { width: min(100%, 440px); }
.modale__boite--grande { width: min(100%, 800px); }
.modale__boite--pleine { width: 100%; height: 100%; max-height: none; }
.modale.est-ouverte .modale__voile { opacity: 1; }
.modale.est-ouverte .modale__boite { opacity: 1; transform: none; }
.modale.est-fermante .modale__boite, .modale.est-fermante .modale__voile { transition-duration: 350ms; }
.modale__fermer { position: absolute; top: var(--e-2); right: var(--e-2); }
.modale__titre { margin: 0 var(--e-6) var(--e-4) 0; font-family: var(--police-display); font-size: var(--t-3); font-weight: 400; line-height: 1.18; letter-spacing: 0; }
.modale__contenu { display: grid; gap: var(--e-4); }

/* ---------- Lettre d'information (composants/newsletter.js) : une feuille de papier, un seul bouton plein ---------- */
.modale--newsletter .modale__titre { font-size: var(--t-2); line-height: 1.12; font-variant-numeric: lining-nums; }
.newsletter { display: grid; gap: var(--e-3); }
.newsletter__surtitre { margin: 0; font-family: var(--police-display); font-style: italic; font-size: var(--t-etiquette); line-height: 1.3; color: var(--encre-2); }
.newsletter__texte { margin: 0; font-size: var(--t-corps); line-height: 1.6; color: var(--encre); }
.newsletter__form { display: grid; gap: var(--e-3); }
.newsletter__form .case { font-size: var(--t-petit); line-height: 1.5; color: var(--encre-2); }
.newsletter__bouton { width: 100%; max-width: none; }
.newsletter__mention { margin: 0; font-size: var(--t-petit); line-height: 1.5; color: var(--encre-2); }
.newsletter__code { display: flex; align-items: baseline; justify-content: space-between; gap: var(--e-3); margin: 0; padding: var(--e-3) var(--e-4); border: var(--filet) dashed var(--or-3); font-family: var(--police-display); font-size: var(--t-2); line-height: 1.1; letter-spacing: .01em; color: var(--encre); font-variant-numeric: lining-nums tabular-nums; }
.newsletter__code .lien { font-family: var(--police-ui); font-size: var(--t-petit); letter-spacing: 0; }
.panier__total-ligne--remise { color: var(--succes); }
@media (max-width: 767px) {
  .modale { padding: 0; align-items: end; }
  .modale__boite { width: 100%; max-height: 92svh; padding-bottom: calc(var(--e-5) + var(--safe-bas)); }
}
@media (orientation: landscape) and (max-height: 520px) {
  .modale { padding: 0; }
  .modale__boite { width: 100%; height: 100%; max-height: none; }
}

/* Apparition et disparition des surcouches sans minuteur : @starting-style fournit l'état de départ quand l'élément
   est rendu déjà ouvert (display: none → visible), transition-behavior: allow-discrete retarde le display: none de
   sortie jusqu'à la fin du glissement. Aujourd'hui les scripts posent la classe une image plus tard et diffèrent le
   masquage : ces règles sont alors sans effet, et deviennent actives dès qu'ils s'en passeront. Chrome 117, Safari 17.5. */
@supports (transition-behavior: allow-discrete) {
  .tiroir, .drawer, .modale, .toast { transition-behavior: allow-discrete; }
  .tiroir, .drawer, .modale { transition-property: display; transition-duration: var(--duree-2); }
  @starting-style {
    .tiroir.est-ouvert .tiroir__voile, .drawer.est-ouvert .drawer__voile, .modale.est-ouverte .modale__voile { opacity: 0; }
    .tiroir.est-ouvert .tiroir__feuille, .drawer.est-ouvert .drawer__panneau { transform: translateX(100%); }
    .modale.est-ouverte .modale__boite { opacity: 0; transform: translateY(16px) scale(.98); }
    .toast.est-visible { opacity: 0; transform: translateY(12px); }
    .lightbox { opacity: 0; }
    .infobulle__texte { opacity: 0; translate: 0 -4px; }
  }
}

/* ---------- Toasts : un billet de papier posé en bas de l'écran ----------
   Le conteneur peut être un popover « manual » (composants/toasts.js) : mêmes règles, couche supérieure en plus. */
.toasts { position: fixed; left: 50%; bottom: calc(var(--e-4) + var(--safe-bas)); z-index: var(--z-toasts); display: grid; gap: var(--e-2); width: min(calc(100% - 2 * var(--e-4)), 420px); transform: translateX(-50%); pointer-events: none; }
.toast {
  display: flex; align-items: center; gap: var(--e-3); padding: var(--e-12) var(--e-2) var(--e-12) var(--e-3);
  background: var(--fond); color: var(--encre); border: var(--filet) solid var(--ligne); border-bottom: var(--filet) solid var(--encre); border-radius: 0; box-shadow: var(--ombre-modale); pointer-events: auto;
  font-family: var(--police-ui); font-size: var(--t-petit); line-height: 1.5;
  opacity: 0; transform: translateY(12px); transition: opacity var(--duree-2) var(--ease-sortie), transform var(--duree-2) var(--ease-sortie);
}
.toast.est-visible { opacity: 1; transform: none; }
.toast.est-sortant { opacity: 0; transform: translateY(8px); }
.toast--erreur { border-bottom-color: var(--erreur); }
.toast__message { flex: 1; margin: 0; }
.toast__action { flex: none; font-size: var(--t-petit); color: var(--encre); }
.toast__fermer { position: relative; width: 32px; height: 32px; color: var(--encre-2); }
.toast__fermer::after { content: ""; position: absolute; inset: -6px; }   /* cible tactile 44 px */
.toast__fermer:hover { color: var(--encre); }
@media (max-width: 1023px) { .toasts { bottom: calc(var(--h-achat) + var(--e-3) + var(--safe-bas)); } }

/* ---------- Galerie produit (§ 4.3) ----------
   Desktop : deux objets posés empilés (portrait, zoom) puis le tour 360. Sous 1024 px : carrousel scroll-snap,
   photos 1:1 bord à bord, compteur « 1 / 2 » en badge papier en bas à droite (28 px). Lightbox sur feuille. */
.galerie { position: relative; }
.galerie__piste { display: grid; gap: var(--e-4); }
.galerie__item { position: relative; cursor: zoom-in; }
.galerie__compteur, .galerie__fleche { display: none; }
/* Dès 768 px les rangées sont des grilles : ni flèches de rail ni leur ligne (les règles du rail, plus haut, valent sous 768 px). */
@media (min-width: 768px) { .rail-ligne, .rail-boutons { display: none; } }
/* La vidéo d'une pièce n'est plus une tuile du carrousel : vues/produit.js la pose sous la galerie par composants/film.js
   (.produit__film, produit.css). */

/* ---------- Film posé (composants/film.js) : la vidéo se pose sur la photo, jamais l'inverse ----------
   La photo (imageHtml) reste le poster et l'image LCP ; la vidéo, au-dessus, n'apparaît qu'en fondu (--duree-4) quand
   elle joue vraiment (.est-joue), et n'existe pas en mouvement réduit. Le bouton Pause / Lire reprend celui de la
   galerie, mais en bas à DROITE : le cartel d'un objet posé mord le bas gauche du cadre (ui.cartel) ; 44 px, papier ;
   discret au pointeur (survol, focus), toujours visible au doigt. */
.film__video { opacity: 0; transition: opacity var(--duree-4) var(--ease-luxe); pointer-events: none; }
.film.est-joue .film__video { opacity: 1; }
.film__bouton {
  position: absolute; right: var(--e-2); bottom: var(--e-2); z-index: 2;
  min-height: 44px; padding: var(--e-12) var(--e-3);
  font-family: var(--police-ui); font-size: var(--t-petit); line-height: 1.5; letter-spacing: 0;
  color: var(--encre); background: var(--cartel-fond); border: var(--filet) solid var(--ligne); cursor: pointer;
  opacity: 0; transition: opacity var(--duree-2) var(--ease-sortie), background-color var(--duree-1) ease-out;
}
.film__bouton:hover { background: var(--papier); }
.film:hover .film__bouton, .film:focus-within .film__bouton, .film__bouton:focus-visible { opacity: 1; }
@media (hover: none) { .film__bouton { opacity: 1; } }
/* Un cartel occupe le bas de la photo : sous 768 px le bouton monte en haut à droite, jamais deux papiers qui se touchent. */
@media (max-width: 767px) { .objet:has(> .cartel) .film__bouton { bottom: auto; top: var(--e-2); } }
@media (prefers-reduced-motion: reduce) { .film__video, .film__bouton { display: none; } }
html.motion-reduite .film__video, html.motion-reduite .film__bouton { display: none; }
@media (max-width: 1023px) {
  .galerie__piste { grid-auto-flow: column; grid-auto-columns: 100%; gap: 0; overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
  .galerie__piste::-webkit-scrollbar { display: none; }
  .galerie__item { scroll-snap-align: start; }
  /* 4:5 entier (la photo n'est plus rognée en carré, 29.09.2026) : la hauteur disponible le permet sur tous les téléphones. */
  .galerie__item .visuel { aspect-ratio: 4 / 5; }
  /* Deux flèches de papier à mi-hauteur, 44 px, ‹ › en Cormorant ; éteintes au bout de la piste, jamais retirées. */
  .galerie__fleche {
    display: grid; place-items: center; position: absolute; top: 50%; z-index: 2; width: 44px; height: 44px; margin-top: -22px;
    background: var(--cartel-fond); color: var(--encre); box-shadow: var(--ombre-contact); border: 0; cursor: pointer;
    font-family: var(--police-display); font-size: var(--t-3); line-height: 1; transition: opacity var(--duree-1) var(--ease-sortie), background var(--duree-1) var(--ease-sortie);
  }
  .galerie__fleche span { display: block; transform: translateY(-2px); }
  .galerie__fleche--prec { left: var(--e-3); }
  .galerie__fleche--suiv { right: var(--e-3); }
  .galerie__fleche:hover { background: var(--papier); }
  .galerie__fleche[aria-disabled="true"] { opacity: .35; cursor: default; }
  .galerie__compteur {
    display: inline-flex; align-items: center; position: absolute; right: var(--e-3); bottom: var(--e-3); z-index: 2;
    min-height: 28px; padding: 0 var(--e-2); background: var(--papier); box-shadow: var(--ombre-contact);
    font-family: var(--police-ui); font-size: var(--t-petit); line-height: 1; color: var(--encre); font-variant-numeric: lining-nums tabular-nums;
  }
}

/* Lightbox : voile, puis la photo sur une feuille de papier (--ombre-modale), boutons en pastilles de papier. */
.lightbox { position: fixed; inset: 0; z-index: var(--z-modale); background: var(--voile); display: grid; place-items: center; padding: var(--e-4); color: var(--encre); opacity: 1; transition: opacity var(--duree-2) ease-out; }
.lightbox__feuille { position: relative; display: grid; gap: var(--e-2); justify-items: center; max-width: 100%; padding: var(--e-3); background: var(--papier); box-shadow: var(--ombre-modale); }
.lightbox__feuille picture { display: contents; }
.lightbox img { display: block; max-width: 100%; max-height: calc(100svh - 2 * var(--e-4) - 2 * var(--e-3) - 28px); width: auto; height: auto; object-fit: contain; }
.lightbox__compteur { font-family: var(--police-ui); font-size: var(--t-petit); line-height: 1; color: var(--encre-2); font-variant-numeric: lining-nums tabular-nums; }
.lightbox .bouton--icone { background: var(--papier); box-shadow: var(--ombre-contact); color: var(--encre); }
.lightbox .bouton--icone:hover, .lightbox .bouton--icone:focus-visible { background: var(--papier); color: var(--encre); }
.lightbox__fermer { position: absolute; top: calc(var(--e-3) + var(--safe-haut)); right: max(var(--e-3), var(--safe-droite)); }
/* Champ de recherche : la croix native d'effacement (Blink/WebKit) doublait le bouton Fermer. */
.recherche__champ::-webkit-search-cancel-button, .recherche__champ::-webkit-search-decoration { -webkit-appearance: none; appearance: none; display: none; }
/* Téléphone : la feuille prend toute la largeur et la photo va bord à bord — « agrandir » agrandit vraiment. */
@media (max-width: 599px) {
  .lightbox { padding: 0; }
  .lightbox__feuille { width: 100%; padding: calc(var(--e-6) + var(--safe-haut)) 0 calc(var(--e-3) + var(--safe-bas)); }
  .lightbox img { max-width: 100%; width: 100%; max-height: calc(100svh - var(--e-6) - var(--e-3) - 28px - var(--safe-haut) - var(--safe-bas)); }
}
.lightbox__nav { position: absolute; top: 50%; transform: translateY(-50%); }
.lightbox__nav--prec { left: max(var(--e-3), var(--safe-gauche)); }
.lightbox__nav--suiv { right: max(var(--e-3), var(--safe-droite)); }

/* Tour 360 (tour-360.js) : « Tourner » est un lien souligné sous le cadre (le cadre est stylé par la vue). */
.tour360__lecture { font-family: var(--police-ui); font-size: var(--t-petit); line-height: 1.5; }
.tour360__lecture[aria-pressed="true"] { color: var(--encre-2); }

/* ---------- Boîte « Taille unique · chaîne de réglage — Guide » (§ 4.3, boîte gardée, 40 px) ---------- */
.taille-unique { display: flex; align-items: center; gap: var(--e-3); min-height: 40px; padding: var(--e-1) var(--e-3); background: var(--papier); border: var(--filet) solid var(--ligne); border-radius: 0; box-shadow: var(--ombre-contact); font-family: var(--police-ui); font-size: var(--t-petit); line-height: 1.5; }
.taille-unique__schema { width: 32px; height: 32px; flex: none; color: var(--encre-2); }
.taille-unique__schema .arc { transform-origin: 24px 24px; animation: taille-ouvre 4s ease-in-out infinite; }
.taille-unique__schema .arc--gauche { animation-name: taille-ouvre-gauche; }
@keyframes taille-ouvre { 0%, 100% { transform: translateX(0); } 50% { transform: translateX(2px); } }
@keyframes taille-ouvre-gauche { 0%, 100% { transform: translateX(0); } 50% { transform: translateX(-2px); } }
.taille-unique:hover .arc { animation-play-state: paused; }
.taille-unique__texte { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 var(--e-2); }
.taille-unique__texte strong { font-size: var(--t-petit); font-weight: 500; }
/* Sélecteur de tailles (aucune pièce ne l'emploie : les cinq sont en taille unique) — gardé pour le balisage de la fiche. */
.tailles { display: flex; flex-wrap: wrap; gap: var(--e-2); }
.taille { position: relative; }
.taille input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.taille span { display: grid; place-items: center; min-width: 44px; height: 44px; padding: 0 var(--e-3); background: var(--papier); border: var(--filet) solid var(--bordure-champ); border-radius: 0; font-family: var(--police-ui); font-size: var(--t-ui); font-variant-numeric: lining-nums tabular-nums; transition: border-color var(--duree-1), background-color var(--duree-1), color var(--duree-1); }
.taille input:checked + span { background: var(--anthracite); color: var(--nacre); border-color: var(--anthracite); }
.taille input:focus-visible + span { outline: 2px solid var(--focus); outline-offset: 2px; box-shadow: var(--ombre-focus); }

/* ---------- Fil d'Ariane (« ‹ Les bijoux », 13 px) ---------- */
.fil-ariane { display: flex; flex-wrap: wrap; gap: var(--e-2); margin: 0; padding: 0; list-style: none; font-family: var(--police-ui); font-size: var(--t-petit); line-height: 1.5; color: var(--encre-2); }
.fil-ariane li { display: flex; align-items: center; gap: var(--e-2); }
.fil-ariane li + li::before { content: "›"; color: var(--encre-3); }
.fil-ariane a:hover, .fil-ariane a:focus-visible { color: var(--encre); }
.fil-ariane [aria-current] { color: var(--encre); }

/* ---------- Infobulle (page L'eau) : un papier sous le mot ---------- */
.infobulle { position: relative; display: inline-flex; align-items: center; gap: 6px; }
.infobulle__bouton { display: inline-grid; place-items: center; width: 24px; height: 24px; border-radius: var(--rayon-pilule); color: var(--encre-2); }
.infobulle__bouton:hover, .infobulle__bouton:focus-visible { color: var(--encre); background: var(--chauffe); }
.infobulle__texte {
  position: absolute; top: calc(100% + 8px); left: 0; z-index: 5; width: min(320px, 80vw); padding: var(--e-3) var(--e-4);
  background: var(--papier); color: var(--encre); font-family: var(--police-texte); font-size: var(--t-petit); line-height: 1.5; letter-spacing: 0; text-transform: none; font-weight: 400;
  border-radius: 0; box-shadow: var(--ombre-modale); border-bottom: var(--filet) solid var(--encre);
  transition: opacity var(--duree-1) ease-out, translate var(--duree-1) var(--ease-luxe);
}
.infobulle__texte[hidden] { display: none; }

/* ---------- Mini-carte du tiroir (« Vous aimerez aussi ») ---------- */
.mini-carte { display: grid; grid-template-columns: 72px minmax(0, 1fr); gap: var(--e-3); align-items: center; padding: var(--e-2) 0; color: inherit; }
.mini-carte__lien { display: block; width: 72px; }
.mini-carte__infos { display: grid; gap: 2px; }
.mini-carte__nom { font-family: var(--police-display); font-size: var(--t-4); font-weight: 400; line-height: 1.25; letter-spacing: 0; color: var(--encre); }
.mini-carte__ligne { display: flex; align-items: baseline; gap: var(--e-3); }
.mini-carte__prix { font-size: 1rem; font-weight: 400; }
.mini-carte__ajout { position: relative; font-family: var(--police-ui); font-size: var(--t-petit); line-height: 1.5; color: var(--encre); }
.mini-carte__ajout::after { content: ""; position: absolute; inset: -12px -8px; }   /* cible tactile 44 px */
.mini-carte[aria-current="true"] .mini-carte__nom { font-style: italic; }

/* ---------- Préchargeur : le logo seul sur la pierre ---------- */
.preloader { position: fixed; inset: 0; z-index: var(--z-preloader); display: grid; place-items: center; background: var(--fond); }
.preloader__logo { display: grid; justify-items: center; gap: var(--e-2); }
.preloader__mot { font-family: var(--police-display); font-size: var(--t-hero); font-weight: 400; line-height: 1; letter-spacing: -.015em; color: var(--encre); overflow: hidden; }
/* Le filet or tient la lumière : l'angle du dégradé tourne (--angle-or, enregistré dans tokens.css). Sans @property,
   l'animation saute de 90° à 450°, deux angles identiques : le filet reste simplement or. */
.preloader__filet { width: 120px; height: 1px; background: linear-gradient(var(--angle-or), var(--or-2), var(--or) 50%, var(--or-2)); animation: eclat-or 2.4s linear infinite; }
@keyframes eclat-or { to { --angle-or: 450deg; } }
/* « JEWELS » sous le mot : le troisième et dernier emploi des capitales (§ 2.3, exempté du compte) — c'est le logo. */
.preloader__logo .preloader__sous { font-family: var(--police-ui); font-size: .6875rem; font-weight: 500; letter-spacing: .28em; text-transform: uppercase; color: var(--or-3); }
html.a-preloader { overflow: hidden; }

/* ---------- Contraste forcé (Windows « Contraste élevé ») ----------
   Les fonds et ombres disparaissent : chaque surface de papier reçoit une bordure, chaque état une marque autre que
   la couleur (Highlight sur les onglets et tailles actifs, remplissage sur les icônes actives). */
@media (forced-colors: active) {
  .bouton, .bouton--contour, .bouton--secondaire, .taille span, .panier__stepper, .quantite, .badge, .galerie__compteur, .galerie__fleche, .rail-bouton { border: 1px solid ButtonText; }
  .bouton[disabled], .bouton[aria-disabled="true"] { opacity: 1; color: GrayText; border-color: GrayText; }
  .taille input:checked + span, .onglets__onglet.est-actif, .stepper__etape.est-active { outline: 2px solid Highlight; outline-offset: -2px; }
  .modale__boite, .tiroir__feuille, .drawer__panneau, .toast, .infobulle__texte, .achat-fixe, .taille-unique, .lightbox__feuille, .lightbox .bouton--icone { border: 1px solid CanvasText; }
  .modale__voile, .tiroir__voile, .drawer__voile, .lightbox { background: Canvas; }
  .modale__voile, .tiroir__voile, .drawer__voile { opacity: .85; }
  .preloader { background: Canvas; }
  .accordeon, .accordeon__item, .panier__ligne, .ligne-panier, .panier__totaux .ligne--total, .table-eau__ligne, .fil-ariane { border-color: CanvasText; }
  .onglets__liste::after, .badge--nouveau::before, .panier__jauge-barre > i, .panier__jauge-barre > span { background: CanvasText; forced-color-adjust: none; }
  .bouton--icone.est-actif .icone { forced-color-adjust: none; }
}

}

/* Mention de taxe (IVY.utils.tva) : n'existe que si la maison est assujettie — sous le prix de la fiche, sous le total du
   panier et du récapitulatif. Discrète, alignée à droite sous un total, à gauche sous le prix de la fiche. */
.mention-tva { margin: var(--e-1) 0 0; font-family: var(--police-ui); font-size: var(--t-petit); line-height: 1.5; color: var(--encre-2); }
.panier__totaux .mention-tva, .commande__totaux .mention-tva { text-align: right; }

/* src/css/entete.css */
/* IVY JEWELS — en-tête : logo, nav, actions (dont « Rechercher » ≥ 1024 px), régime commande, menu mobile,
   recherche plein écran (déclenchée par l'action de bureau, le menu mobile ou le clavier — recherche.js).
   Direction artistique V2 « Pierre et Lumière » — docs/DIRECTION-ARTISTIQUE-V2.md § 3.2.
   Couche « composants » (ordre déclaré dans tokens.css, règle expliquée en tête de base.css).
   Supprimés (§ 7.1, 7.2) : le bandeau utilitaire, le méga-menu « Bijoux », le champ de recherche dans la barre de
   bureau (un simple déclencheur en texte le remplace), le régime transparent-sur-photo (plus aucune photo sous
   l'en-tête). Tout ce qui est taille, couleur, ombre ou durée vient des jetons ; les seules capitales sont « JEWELS »
   (troisième et dernier emploi, § 2.3). */

@layer composants {

/* ---------- En-tête ----------
   64 px (56 mobile, 48 paysage : --h-entete, tokens.css). Transparent sur la pierre ; dès 24 px de défilement
   .est-solide (entete.js) : --fond à 92 % + flou + filet bas --ligne + 56 px, en --duree-2. Se cache en descendant,
   revient en remontant (existant). Le filet bas est une bordure, transparente au repos : jamais une box-shadow. */
.entete {
  position: sticky; top: 0; z-index: var(--z-entete); height: var(--h-entete);
  padding-top: var(--safe-haut);
  background: transparent; color: var(--encre);
  border-bottom: var(--filet) solid transparent;
  transition: background-color var(--duree-2) var(--ease-luxe), border-color var(--duree-2), height var(--duree-2) var(--ease-luxe), transform var(--duree-2) var(--ease-luxe);
  view-transition-name: entete;
}
.entete__interieur { height: 100%; display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: var(--e-4); }
.entete.est-solide {
  background: color-mix(in srgb, var(--fond) 92%, transparent);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  height: var(--h-entete-defile); border-bottom-color: var(--ligne);
}
/* Écran tactile : pas de flou d'arrière-plan. Le flouter obligeait le compositeur à relire la page sous l'en-tête à
   chaque image du défilement ; le papier, presque opaque, n'en laissait de toute façon paraître qu'un voile. */
@media (any-pointer: coarse) {
  .entete.est-solide { background: color-mix(in srgb, var(--fond) 97%, transparent); backdrop-filter: none; -webkit-backdrop-filter: none; }
}
html.tactile .entete.est-solide { background: color-mix(in srgb, var(--fond) 97%, transparent); backdrop-filter: none; -webkit-backdrop-filter: none; }
.entete.est-cache { transform: translateY(-100%); }
.entete.est-menu-ouvert { transform: none; }

/* ---------- Logo typographique ----------
   « Ivy » Cormorant 400 2.5rem --encre (2rem mobile et paysage), « JEWELS » Jost 500 11 px .28em --or-3 sur la
   ligne de base. Le menu mobile reprend le logo : un nom de transition ne peut exister qu'une fois par page. */
.entete__logo { display: inline-flex; align-items: baseline; gap: var(--e-12); color: var(--encre); text-decoration: none; justify-self: center; view-transition-name: entete-logo; }
.menu-mobile .entete__logo { view-transition-name: none; }
.entete__logo .logo-ivy { font-family: var(--police-display); font-size: 2.5rem; font-weight: 400; line-height: 1; letter-spacing: -.02em; }
.entete__logo small { font-family: var(--police-ui); font-size: .6875rem; font-weight: 500; line-height: 1; letter-spacing: .28em; text-transform: uppercase; color: var(--or-3); }
@media (max-width: 767px) { .entete__logo .logo-ivy { font-size: 2rem; } }
@media (orientation: landscape) and (max-height: 520px) { .entete__logo .logo-ivy { font-size: 2rem; } }

/* ---------- Navigation (bureau) ----------
   Jost --t-ui 400 bas-de-casse .02em : « Les bijoux · La Maison · L'eau · Contact ». Actif ou survol = filet or 1 px
   sous le mot (dessiné de gauche à droite, existant). Les actions en texte (≥ 1024 px) partagent ce dessin. */
.entete__nav { display: flex; gap: var(--e-5); align-items: center; height: 100%; }
.entete__nav > li { height: 100%; display: flex; align-items: center; }
.entete__lien, .entete__action-texte {
  display: inline-flex; align-items: center; height: 44px; padding: 0 2px;
  font-family: var(--police-ui); font-size: var(--t-ui); font-weight: 400; letter-spacing: .02em; text-transform: none; line-height: 1;
  color: inherit; white-space: nowrap;
  background-image: linear-gradient(var(--or), var(--or)); background-repeat: no-repeat; background-size: 0 1px; background-position: 0 calc(100% - 12px);
  transition: background-size var(--duree-2) var(--ease-luxe);
}
.entete__lien:hover, .entete__lien:focus-visible, .entete__lien.est-actif,
.entete__action:hover .entete__action-texte, .entete__action:focus-visible .entete__action-texte, .entete__action.est-actif .entete__action-texte { background-size: 100% 1px; }
@media (max-width: 1200px) { .entete__nav { gap: var(--e-4); } }
@media (max-width: 1023px) { .entete__nav { display: none; } }

/* ---------- Actions ----------
   ≥ 1024 px : « Rechercher », « Compte », « Favoris » et « Panier · 1 » en texte, compteur tabulaire, espace réservé
   même à zéro (« Panier » seul, la place de « · 0 » reste prise). < 1024 px : icônes 22 px compte, favoris et panier
   avec pastille, puis le burger ; « Rechercher » disparaît (le menu mobile porte la recherche). */
.entete__actions { display: flex; justify-content: flex-end; align-items: center; gap: var(--e-4); }
.entete__action {
  position: relative; min-width: 44px; height: 44px; display: inline-flex; align-items: center; justify-content: center; padding: 0;
  color: inherit; border-radius: var(--rayon-pilule); transition: background-color var(--duree-1);
}
.entete__action .icone { width: 22px; height: 22px; }
.entete__action.est-actif .icone { fill: currentColor; }
.entete__action-texte { display: none; }
.entete__compte { display: inline-block; min-width: 3ch; font-variant-numeric: lining-nums tabular-nums; white-space: pre; }
.entete__compte.est-vide { visibility: hidden; }
.entete__burger { display: none; }
.entete__burger .icone { width: 24px; height: 24px; }
/* Pastille de compte (< 1024 px) : nacre sur --anthracite (14,0:1), la seule surface sombre admise hors section
   épinglée (§ 5.8) — l'or n'existe qu'en filet et en chiffres (§ 1.1), jamais en aplat. */
.compteur {
  position: absolute; top: 4px; right: 2px; min-width: 16px; height: 16px; padding: 0 4px; display: grid; place-items: center;
  font-family: var(--police-ui); font-size: .6875rem; font-weight: 500; line-height: 1; letter-spacing: 0;
  font-variant-numeric: lining-nums tabular-nums;
  color: var(--nacre); background: var(--anthracite); border-radius: var(--rayon-pilule);
  transform: scale(0); transition: transform var(--duree-2) var(--ease-luxe);
}
.compteur.est-visible { transform: scale(1); }
.compteur.est-pulse { animation: compteur-pulse var(--duree-2) var(--ease-luxe); }
@keyframes compteur-pulse { 50% { transform: scale(1.25); } }
@media (min-width: 1024px) {
  .entete__action--texte { border-radius: 0; }
  .entete__action--texte .icone, .entete__action--texte .compteur { display: none; }
  .entete__action-texte { display: inline-flex; }
}
@media (hover: hover) and (pointer: fine) {
  .entete__action:not(.entete__action--texte):hover { background: var(--chauffe); }
}
@media (max-width: 1023px) {
  .entete__interieur { grid-template-columns: auto 1fr auto; }
  .entete__logo { grid-column: 1; justify-self: start; }
  .entete__actions { grid-column: 3; gap: var(--e-1); }
  .entete__action--recherche { display: none; }
  .entete__burger { display: inline-flex; }
  .entete__action:hover { background: var(--chauffe); }
}
/* Téléphone : trois icônes au plus dans l'en-tête — le compte vit dans le menu. */
@media (max-width: 767px) { .entete__action--compte { display: none; } }

/* Positionnement par ancre (Chrome 125, Safari 26 ; l'absolu ci-dessus reste le repli exact) : la pastille de
   compte s'accroche au coin haut-droit de l'icône qu'elle compte (icônes affichées sous 1024 px seulement). */
@supports (anchor-name: --x) {
  .entete__action .icone { anchor-name: --icone-action; }
  .compteur { position-anchor: --icone-action; top: calc(anchor(top) - 6px); left: calc(anchor(right) - 8px); right: auto; }
}

/* ---------- Régime commande (§ 3.2, § 4.7) ----------
   entete.js pose .entete--commande sur la vue « commande » : logo à gauche, « ‹ Retour au panier », « Paiement sécurisé ·
   TWINT, Apple Pay, Google Pay, carte » 13 px --encre-2 à droite ; ni favoris, ni recherche, ni nav, ni burger. */
.entete__retour { display: none; justify-self: start; white-space: nowrap; font-family: var(--police-ui); font-size: var(--t-ui); font-weight: 400; letter-spacing: .02em; line-height: 1; color: var(--encre); }
.entete__retour[hidden] { display: none; }
.entete__note { display: none; margin: 0; justify-self: end; white-space: nowrap; font-family: var(--police-ui); font-size: var(--t-petit); font-weight: 400; line-height: 1.5; color: var(--encre-2); }
.entete__note[hidden] { display: none; }
.entete--commande .entete__interieur { grid-template-columns: auto 1fr auto; }
.entete--commande .entete__nav, .entete--commande .entete__actions, .entete--commande .entete__burger { display: none; }
.entete--commande .entete__logo { grid-column: 1; grid-row: 1; justify-self: start; }
.entete--commande .entete__retour { display: inline-flex; align-items: center; grid-column: 2; grid-row: 1; min-height: 44px; padding: 0 2px; }
.entete--commande .entete__note { display: block; grid-column: 3; grid-row: 1; }
.entete__retour-chevron { margin-right: var(--e-2); }
.entete__retour:hover .entete__retour-texte, .entete__retour:focus-visible .entete__retour-texte { text-decoration: underline; text-underline-offset: 4px; text-decoration-color: var(--or); }
@media (max-width: 599px) { .entete--commande .entete__note { display: none; } }
@media (max-width: 359px) {
  /* Cible 44 × 44 avec la flèche seule ; le libellé est masqué à la manière de .u-visuellement-cache (toujours lu). */
  .entete__retour-texte { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
  .entete--commande .entete__retour { width: 44px; justify-content: center; }
  .entete__retour-chevron { margin: 0; }
}

/* ---------- Menu mobile plein écran (gardé, § 3.2) ----------
   Une feuille de papier qui descend sur la table : --papier (seule surface opaque, § 1.1). Mots Cormorant 400 (plus
   de 300), le mot d'appel en italique --or-3 ; la recherche ne vit plus qu'ici. */
.menu-mobile { position: fixed; inset: 0; z-index: var(--z-drawer); background: var(--papier); color: var(--encre); clip-path: inset(0 0 100% 0); transition: clip-path var(--duree-3) var(--ease-sortie); overflow: auto; overscroll-behavior: contain; visibility: hidden; }
.menu-mobile.est-ouvert { clip-path: inset(0 0 0 0); visibility: visible; }
.menu-mobile.est-fermant { transition-duration: var(--duree-2); transition-timing-function: var(--ease-entree-sortie); }
.menu-mobile__tete { display: flex; align-items: center; justify-content: space-between; height: var(--h-entete); padding: var(--safe-haut) max(var(--marge-page), var(--safe-droite)) 0 max(var(--marge-page), var(--safe-gauche)); }
.menu-mobile__corps { display: grid; gap: var(--e-6); padding: var(--e-5) max(var(--marge-page), var(--safe-droite)) calc(var(--rythme-1) + var(--safe-bas)) max(var(--marge-page), var(--safe-gauche)); }
.menu-mobile__liens { display: grid; gap: var(--e-3); }
.menu-mobile__lien { font-family: var(--police-display); font-size: var(--t-1); font-weight: 400; line-height: 1.05; letter-spacing: -.01em; color: var(--encre); display: block; }
.menu-mobile__lien .u-italique { color: var(--or-3); }
.menu-mobile__lien.est-actif .u-italique, .menu-mobile__lien.est-actif { text-decoration: underline; text-underline-offset: 6px; text-decoration-thickness: 1px; text-decoration-color: var(--or); }
.menu-mobile__sous { display: grid; gap: var(--e-2); padding-top: var(--e-2); }
.menu-mobile__sous a { font-family: var(--police-ui); font-size: var(--t-corps); color: var(--encre-2); padding: 6px 0; }
.menu-mobile__pied { display: grid; gap: var(--e-3); border-top: var(--filet) solid var(--ligne); padding-top: var(--e-5); font-family: var(--police-ui); font-size: var(--t-petit); color: var(--encre-2); }
.menu-mobile__pied a, .menu-mobile__pied button { display: inline-flex; align-items: center; gap: var(--e-2); min-height: 44px; }
.menu-mobile .est-revele { opacity: 1; }
@media (orientation: landscape) and (max-height: 520px) {
  .menu-mobile__corps { grid-template-columns: 1fr 1fr; gap: var(--e-5); }
  .menu-mobile__lien { font-size: var(--t-2); }
}

/* ---------- Recherche plein écran (action « Rechercher » ≥ 1024 px, menu mobile, « / » et Ctrl+K) ----------
   Le papier, à 96 %, avec le flou de l'en-tête solide ; le champ hérite du socle des saisies (base.css) et la couche
   composants le dépouille sans !important. */
.recherche { position: fixed; inset: 0; z-index: var(--z-modale); background: color-mix(in srgb, var(--papier) 96%, transparent); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); display: grid; grid-template-rows: auto 1fr; opacity: 0; visibility: hidden; transition: opacity var(--duree-2), visibility 0s linear var(--duree-2); overflow: auto; }
.recherche.est-ouverte { opacity: 1; visibility: visible; transition-delay: 0s; }
.recherche__tete { display: flex; align-items: center; gap: var(--e-3); padding: calc(var(--e-4) + var(--safe-haut)) max(var(--marge-page), var(--safe-droite)) var(--e-4) max(var(--marge-page), var(--safe-gauche)); border-bottom: var(--filet) solid var(--ligne); }
.recherche__champ { flex: 1; min-height: 56px; border: 0; background: transparent; box-shadow: none; font-family: var(--police-display); font-size: var(--t-2); padding-inline: 0; }
.recherche__corps { padding: var(--e-5) max(var(--marge-page), var(--safe-droite)) calc(var(--rythme-1) + var(--safe-bas)) max(var(--marge-page), var(--safe-gauche)); }
.recherche__resultats { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: var(--e-4); }
.recherche__vide { color: var(--encre-2); }
.recherche__suggestions { display: flex; flex-wrap: wrap; gap: var(--e-2); margin-bottom: var(--e-5); }

/* ---------- Contraste renforcé et forcé ---------- */
@media (prefers-contrast: more) {
  .entete.est-solide { background: var(--fond); backdrop-filter: none; -webkit-backdrop-filter: none; }
  .recherche { background: var(--papier); backdrop-filter: none; -webkit-backdrop-filter: none; }
}
@media (forced-colors: active) {
  .entete { border-bottom: 1px solid CanvasText; }
  .entete.est-solide { backdrop-filter: none; -webkit-backdrop-filter: none; }
  .entete__lien, .entete__action-texte, .menu-mobile__lien { background-image: none; }
  .entete__lien.est-actif, .entete__action.est-actif .entete__action-texte { text-decoration: underline; text-underline-offset: 6px; }
  .menu-mobile, .recherche { border: 1px solid CanvasText; backdrop-filter: none; -webkit-backdrop-filter: none; }
  .compteur { border: 1px solid ButtonText; }
}

}

/* ---------- Langue (entete.js) ----------
   Un drapeau 21 × 14 à gauche du cœur, discret : 85 % d'opacité, un filet d'encre à 14 % pour que le blanc des
   drapeaux ne se perde pas sur la pierre, pleine opacité au survol et ouvert. La liste est une feuille de papier
   posée sous le drapeau, alignée à sa droite : quatre lignes de 44 px, drapeau et nom de la langue dans sa langue ;
   la langue courante porte le filet or sous son nom. */
.langue { position: relative; display: inline-flex; }
.langue__drapeau { position: relative; display: inline-block; flex: none; width: 21px; height: 14px; overflow: hidden; border-radius: var(--rayon); }
.langue__drapeau::after { content: ""; position: absolute; inset: 0; border: var(--filet) solid color-mix(in srgb, var(--encre) 14%, transparent); border-radius: inherit; pointer-events: none; }
.langue__drapeau .drapeau { display: block; width: 100%; height: 100%; }
.langue__bouton .langue__drapeau { opacity: .85; transition: opacity var(--duree-1); }
.langue__bouton:hover .langue__drapeau, .langue__bouton:focus-visible .langue__drapeau, .langue__bouton[aria-expanded="true"] .langue__drapeau { opacity: 1; }
@media (min-width: 1024px) { .langue__bouton { min-width: 32px; } }
.langue__liste {
  position: absolute; top: calc(100% + var(--e-1)); right: 0; z-index: 5; min-width: 176px; margin: 0; padding: var(--e-1) 0; list-style: none;
  background: var(--papier); border: var(--filet) solid var(--ligne); border-radius: var(--rayon); box-shadow: var(--ombre-posee);
}
.langue__liste[hidden] { display: none; }
.langue__choix {
  display: flex; align-items: center; gap: var(--e-3); min-height: 44px; padding: 0 var(--e-4);
  font-family: var(--police-ui); font-size: var(--t-ui); font-weight: 400; letter-spacing: .02em; line-height: 1; color: var(--encre); white-space: nowrap;
  transition: background-color var(--duree-1);
}
.langue__choix:hover, .langue__choix:focus-visible { background: var(--chauffe); }
.langue__choix[aria-current="true"] span:last-child { text-decoration: underline; text-decoration-color: var(--or); text-decoration-thickness: 1px; text-underline-offset: 6px; }
@media (forced-colors: active) { .langue__drapeau::after { border-color: CanvasText; } .langue__liste { border-color: CanvasText; } }

/* src/css/pied.css */
/* IVY JEWELS — pied de page : une signature gravée, pas un annuaire.
   Direction artistique V2 « Pierre et Lumière » — docs/DIRECTION-ARTISTIQUE-V2.md § 3.8.
   Transparent (la pierre continue), un filet haut --ligne, 64 px avant la signature, margin-top: 0 (la dernière
   section de main porte ses 48 px, base.css). Bureau : la signature, la devise et la ligne de coordonnées à gauche,
   les deux colonnes à droite, sur une même rangée — c'est ce qui tient le pied sous 420 px ; sous 600 px, une
   colonne (≤ 900 px). Hors couche, comme les vues (règle expliquée en tête de base.css). */
.pied { background: transparent; color: var(--encre); margin-top: 0; padding: 64px 0 calc(var(--e-5) + var(--safe-bas)); border-top: var(--filet) solid var(--ligne); }
.pied__haut { display: grid; grid-template-columns: minmax(0, 6fr) minmax(0, 3fr) minmax(0, 3fr); gap: var(--e-6); align-items: start; }

/* 1. Signature : « Ivy » Cormorant 400 --t-affiche --encre, « Jewels » italique 24 px sur la ligne de base, à droite du mot. */
.pied__marque { display: grid; gap: var(--e-3); align-content: start; }
.pied__entete { display: flex; align-items: center; gap: clamp(24px, 4vw, 64px); flex-wrap: wrap; }
.pied__medaillon { flex: none; width: clamp(132px, 14vw, 200px); }
.pied__medaillon .visuel { aspect-ratio: 1; }
.pied__signature { display: inline-flex; align-items: baseline; gap: var(--e-3); color: var(--encre); width: max-content; max-width: 100%; }
.pied__ivy { font-family: var(--police-display); font-weight: 400; font-size: var(--t-affiche); line-height: .9; letter-spacing: -.02em; }
.pied__jewels { font-family: var(--police-display); font-style: italic; font-weight: 400; font-size: 1.5rem; line-height: 1; letter-spacing: 0; }
/* 2. « Des bijoux qui vivent avec vous. » Cormorant italique 22 px ; le mot d'appel ressort en romain, comme il se doit dans une italique. */
.pied__devise { font-family: var(--police-display); font-style: italic; font-weight: 400; font-size: 1.375rem; line-height: 1.3; color: var(--encre); margin: 0; max-width: var(--largeur-chapo); }
.pied__devise em { font-style: normal; }
/* 3. Une ligne Jost 14 px --encre-2 : ville · e-mail · téléphone · Instagram · envoi offert (valeurs de IVY.MARQUE). */
.pied__ligne { font-family: var(--police-ui); font-size: var(--t-ui); font-weight: 400; line-height: 1.6; letter-spacing: 0; color: var(--encre-2); margin: 0; max-width: var(--largeur-texte); }
.pied__ligne a { color: var(--encre); white-space: nowrap; text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--or); }
.pied__ligne a:hover, .pied__ligne a:focus-visible { text-decoration-color: currentColor; }
.pied__ligne .pied__separateur { color: var(--encre-2); padding-inline: var(--e-1); }

/* 4. Deux colonnes titrées en Cormorant italique --t-etiquette : « Les cinq pièces » (numero 18 px · nom Cormorant 18 px ·
   prix tabulaire à droite, filets --ligne entre les lignes) et « La Maison » (Jost 14 px). Mêmes hauteurs de ligne (40 px). */
.pied__colonne { display: grid; gap: var(--e-2); align-content: start; }
.pied__titre { font-family: var(--police-display); font-style: italic; font-weight: 400; font-size: var(--t-etiquette); line-height: 1.3; letter-spacing: 0; text-transform: none; color: var(--encre-2); margin: 0; }
.pied__liste { display: grid; }
.pied__liste li { border-top: var(--filet) solid var(--ligne); }
.pied__liste li:first-child { border-top: 0; }
/* Survol et focus : un filet or sous le mot (l'or n'existe qu'en filet et en chiffres, § 1.1) — jamais le texte en or. */
.pied__liste a { display: flex; align-items: baseline; gap: var(--e-2); min-height: 36px; padding-block: var(--e-1); color: var(--encre); text-decoration: underline; text-underline-offset: 4px; text-decoration-color: transparent; transition: text-decoration-color var(--duree-1); }
.pied__liste a:hover, .pied__liste a:focus-visible { text-decoration-color: var(--or); }
.pied__lien-bouton { display: flex; align-items: baseline; min-height: 36px; padding: var(--e-1) 0; background: none; border: 0; color: var(--encre); font: inherit; text-align: left; text-decoration: underline; text-underline-offset: 4px; text-decoration-color: transparent; cursor: pointer; transition: text-decoration-color var(--duree-1); }
.pied__lien-bouton:hover, .pied__lien-bouton:focus-visible { text-decoration-color: var(--or); }
.pied__pieces a { font-family: var(--police-display); font-weight: 500; font-size: 1.125rem; line-height: 1.3; }
.pied__pieces .numero { flex: none; min-width: 2ch; font-variant-numeric: lining-nums tabular-nums; }
.pied__pieces .pied__nom { flex: 1; min-width: 0; }
/* Prix du pied : 18 px Cormorant, chiffres alignés à droite (§ 2.3, § 3.5). Le rendu .prix (Jost) de base.css est repris ici. */
.pied__pieces .pied__prix { flex: none; font-family: var(--police-display); font-weight: 500; font-size: 1.125rem; font-variant-numeric: lining-nums tabular-nums; color: inherit; margin-left: auto; }
.pied__maison a { font-family: var(--police-ui); font-size: var(--t-ui); font-weight: 400; line-height: 1.4; letter-spacing: 0; }

/* 5. Les sept pages légales sur une ligne Jost 12 px séparées par « · » ; chaque lien garde une cible de 32 px.
   En encre : --encre-2 ne descend jamais sous 13 px (§ 2.1). */
.pied__legal { display: flex; flex-wrap: wrap; align-items: center; margin-top: var(--e-3); padding-top: var(--e-12); border-top: var(--filet) solid var(--ligne); font-family: var(--police-ui); font-size: .75rem; line-height: 1.5; color: var(--encre); }
.pied__legal a { display: inline-flex; align-items: center; min-height: 32px; color: var(--encre); text-decoration: underline; text-underline-offset: 3px; text-decoration-color: transparent; transition: text-decoration-color var(--duree-1); }
.pied__legal a:hover, .pied__legal a:focus-visible { text-decoration-color: var(--or); }
.pied__legal .pied__separateur { padding-inline: var(--e-1); }

/* 6. © année et « Réduire les animations », Jost 12 px, en encre (même raison). L'état pressé se lit à l'icône pleine
   et au filet or — pas en bleu : --eau n'existe que dans la section épinglée et la table gravée (§ 2.1). */
.pied__bas { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: var(--e-2) var(--e-5); margin-top: 0; font-family: var(--police-ui); font-size: .75rem; line-height: 1.5; color: var(--encre); }
.pied__bas p { margin: 0; }
/* Moyens de paiement acceptés, entre le © et « Réduire les animations » : les six marques à 20 px. */
.pied__paiement { --hauteur-logo: 20px; }
.pied__reduire { display: inline-flex; align-items: center; gap: var(--e-2); min-height: 32px; color: var(--encre); font-family: var(--police-ui); font-size: inherit; text-decoration: underline; text-underline-offset: 3px; text-decoration-color: transparent; transition: text-decoration-color var(--duree-1); }
.pied__reduire .icone { width: 16px; height: 16px; }
.pied__reduire:hover, .pied__reduire:focus-visible { text-decoration-color: var(--or); }
.pied__reduire[aria-pressed="true"] { text-decoration-color: var(--or); }
.pied__reduire[aria-pressed="true"] .icone { fill: currentColor; }
/* Dans le menu mobile (entete.js), le même bouton prend la taille du pied du menu. */
.menu-mobile__pied .pied__reduire { font-size: var(--t-petit); min-height: 44px; }

@media (max-width: 1023px) {
  .pied__haut { grid-template-columns: 1fr 1fr; }
  .pied__marque { grid-column: 1 / -1; }
}
/* Téléphone : une colonne, rangées de 32 px, écarts resserrés — le pied tient sous 900 px (§ 3.8). */
@media (max-width: 599px) {
  .pied__haut { grid-template-columns: 1fr; gap: var(--e-4); }
  .pied__marque { grid-column: auto; gap: var(--e-12); }
  .pied__liste a { min-height: 32px; }
}

@media (forced-colors: active) {
  .pied, .pied__liste li, .pied__legal { border-color: CanvasText; }
  .pied__ligne a, .pied__legal a, .pied__liste a { text-decoration: underline; text-decoration-color: CanvasText; }
  .pied__reduire[aria-pressed="true"] { text-decoration-color: CanvasText; }
}

/* src/css/vues/accueil.css */
/* IVY JEWELS — vue Accueil. Ordre fixé par la fondatrice (2026-09-13) : le médaillon et la promesse, la photo des
   cinq pièces aux fruits, la collection, puis la mosaïque Instagram et la fondatrice. La section épinglée de l’eau revient
   juste avant la fondatrice (et sur la page L’eau). Feuille de vue, hors couche : elle prime sur composants et base (voir base.css).
   Aucune font-size, couleur, ombre ni durée hors jetons ; aucun uppercase ; aucun rayon ; aucune box-shadow. */

/* ---------- 1. Héros : le médaillon à gauche, la promesse à droite ----------
   Bureau (≥ 1024 px) : deux colonnes ; le médaillon est un carré plafonné à la hauteur de l’écran (en-tête et marges
   déduits), la promesse centrée à côté — h1, ligne matière, trois faits sur filets gravés, un bouton plein.
   Téléphone et tablette : le médaillon bord à bord (tablette : 560 px au plus), la promesse dessous. */
.accueil-hero { padding-top: var(--e-5); }
.accueil-hero__grille { display: grid; grid-template-columns: minmax(0, 11fr) minmax(0, 9fr); column-gap: clamp(32px, 5vw, 96px); align-items: center; }
.accueil-hero__objet { width: min(100%, calc(100svh - var(--h-entete) - 2 * var(--e-5))); justify-self: end; }
.accueil-hero__visuel { aspect-ratio: 1; }
.accueil-hero__texte { display: grid; justify-items: start; gap: var(--e-4); max-width: 34rem; }
.accueil-hero__titre { margin: 0; }
.accueil-hero__matiere { margin: 0; }
.accueil-promesse { list-style: none; margin: 0; padding: 0; width: 100%; border-bottom: var(--filet) solid var(--ligne); }
.accueil-promesse__fait { padding-block: var(--e-12); border-top: var(--filet) solid var(--ligne); font-family: var(--police-ui); font-size: var(--t-corps); line-height: 1.5; color: var(--encre); font-variant-numeric: lining-nums tabular-nums; }
.accueil-hero__bouton { margin-top: var(--e-2); }

@media (max-width: 1023px) {
  .accueil-hero { padding-top: 0; }
  .accueil-hero__grille { grid-template-columns: 1fr; row-gap: var(--e-5); }
  .accueil-hero__objet { width: min(100%, 560px); justify-self: center; }
  .accueil-hero__texte { max-width: var(--largeur-texte); }
}
@media (max-width: 767px) {
  .accueil-hero__objet { width: auto; justify-self: stretch; margin-inline: calc(-1 * var(--marge-page)); }
  .accueil-hero__texte { gap: var(--e-3); }
  .accueil-hero__bouton { width: 100%; max-width: none; }
  /* Téléphone : la promesse tient en une ligne de méta — les MÊMES trois faits, séparés par « · », sans filets
     (Jost 13 px, encre-2) : la première carte arrive un écran plus tôt. Le séparateur est collé au fait qui le suit
     (espace insécable) : si la ligne se replie, aucun « · » ne reste seul en bout de ligne. */
  .accueil-promesse { border-bottom: 0; font-family: var(--police-ui); font-size: var(--t-petit); line-height: 1.5; color: var(--encre-2); font-variant-numeric: lining-nums tabular-nums; }
  .accueil-promesse__fait { display: inline; padding: 0; border-top: 0; font-size: inherit; line-height: inherit; color: inherit; }
  .accueil-promesse__fait + .accueil-promesse__fait::before { content: " ·\00a0"; }
}
/* Écran couché (844 × 390) : médaillon à gauche à la hauteur de la fenêtre, promesse à droite. */
@media (orientation: landscape) and (max-height: 520px) {
  .accueil-hero { padding-top: var(--e-3); }
  .accueil-hero__grille { grid-template-columns: auto minmax(0, 1fr); column-gap: var(--e-5); }
  .accueil-hero__objet { width: calc(100svh - var(--h-entete) - 2 * var(--e-3)); margin-inline: 0; justify-self: start; }
  .accueil-hero__texte { gap: var(--e-2); }
  .accueil-promesse__fait { padding-block: var(--e-1); font-size: var(--t-petit); }
}

/* ---------- 2. Les cinq pièces réunies ----------
   Le film des cinq pièces (composants/film.js) et la photo en poster, au cadre natif 2:1 de la photo (le film garde ce
   cadre : jamais recadré, comme le veut la fondatrice) : sur ordinateur, aussi large que le film de La Maison (1200 px,
   la vidéo en fait 1440) sans jamais dépasser 72 % de la hauteur de l'écran ; 880 px au plus en dessous, à la largeur
   du conteneur sur tablette et téléphone. Posé, cartel des cinq noms. */
.accueil-cinq { padding-top: var(--rythme-1); }
.accueil-cinq__objet { width: min(100%, 880px); margin-inline: auto; margin-bottom: var(--e-4); }
.accueil-cinq__visuel { aspect-ratio: 2 / 1; }
@media (min-width: 1024px) {
  .accueil-cinq__objet { width: min(100%, 1200px, calc(72svh * 2)); }
}

/* ---------- 3. La collection ----------
   Bureau et paysage : les cinq cartes en grille (jamais un rail) ; téléphone et tablette : le rail bord à bord. */
@media (min-width: 1024px), ((orientation: landscape) and (max-height: 520px)) {
  .accueil-rangee { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: var(--e-4); overflow: visible; padding-inline: 0; margin-inline: 0; scroll-snap-type: none; }
  .accueil-rangee > * { flex: none; scroll-snap-align: none; min-width: 0; }
}
/* L'indice sous le rail (« 5 pièces · faire glisser → ») n'existe que là où le rail existe : téléphone debout.
   Partout ailleurs les cinq cartes sont en grille et il n'y a rien à faire glisser. */
/* Sous le rail (téléphone) : « 5 pièces » à gauche, les deux flèches du rail à droite (composants/rail.js). */
.accueil-rangee__indice { display: none; align-items: center; justify-content: space-between; gap: var(--e-3); margin: var(--e-3) 0 0; font-family: var(--police-ui); font-size: var(--t-petit); line-height: 1.5; color: var(--encre-2); font-variant-numeric: lining-nums tabular-nums; }
@media (max-width: 767px) and (not ((orientation: landscape) and (max-height: 520px))) {
  .accueil-rangee { margin-inline: calc(-1 * var(--marge-page)); }
  .accueil-rangee__indice { display: flex; }
}
@media (min-width: 768px) and (max-width: 1023px) and (not ((orientation: landscape) and (max-height: 520px))) {
  .accueil-rangee { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--e-5) var(--e-4); margin-inline: 0; }
  .accueil-rangee .carte-produit__ligne { flex-wrap: wrap; row-gap: 0; }
}
@media (orientation: landscape) and (max-height: 520px) {
  .accueil-rangee { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--e-3); }
  .accueil-rangee .carte-produit__ligne { flex-wrap: wrap; row-gap: 0; }
}

/* Partagée avec la page L’eau (composants/section-eau.js) : cette feuille est chargée sur toutes les pages. */
/* ---------- Section épinglée « L'or qui aime l'eau » ----------
   Mécanique intacte : motion.js (data-epingle 50 vh × 4 étapes, classes est-etape-* posées par accueil.js sur ivy:etape),
   gouttes.js (l'eau sur tout le fond). Ce qui change autour : AUCUN aplat — le canvas est transparent à l'étape 0, les
   gouttes perlent sur la pierre, et la teinte ciel arrive à l'étape 1 sous le masque vertical de .eau-fond__cadre
   (innovations.css : aucune ligne horizontale) ; la photo est un objet posé (ombre posée, base.css) ; plus de verre
   dépoli (gouttes.js calme l'eau derrière le texte, REGLAGES.calmeTexte .92) ; cinq styles de texte.
   Contrastes, calculés sur les fonds plats des étapes (gouttes.js REGLAGES) : encre 14,2:1 sur la pierre, 12,4:1 sur
   --ciel ; --encre-2 tombe à 4,38:1 sur --ciel-2 → --profond aux étapes 1-2 (8,7:1) ; sur --profond (étape 3) : nacre
   13,6:1, --ciel 11,0:1, --or-2 9,1:1 ; le verdict en --eau tient 7,1:1 sur la pierre, 6,6:1 sur --ciel, 5,2:1 sur
   --ciel-2 (encre à l'étape 0 : le bleu n'arrive qu'avec l'eau). Les textes changent de couleur au rythme du fondu du
   canvas (--duree-4 = REGLAGES.fondu), sinon l'or-2 du verdict tomberait à 1,3:1 sur le ciel encore présent. */
.eau-section { position: relative; isolation: isolate; margin-top: var(--rythme-3); color: var(--encre); transition: color var(--duree-4) var(--ease-luxe); }
.eau-section.est-etape-3 { color: var(--nacre); }
.eau-section__interieur { position: relative; z-index: 1; display: grid; grid-template-columns: 7fr 5fr; gap: var(--rythme-1); align-items: center; min-height: 100svh; padding-block: var(--e-6); }
.eau-section__visuel { position: relative; }
/* 84svh, pas 80 : centrée dans l'écran, la photo laisse alors 72 px de pierre au-dessus d'elle — avec les 192 px du
   --rythme-3, la bande nue avant l'épinglage reste sous les 120 px du contrôle 8 (mesuré 125 à 80svh, 1440 × 900). */
/* L'OBJET (qui porte l'ombre) prend la largeur qu'impose la hauteur : 84 svh × 4/5, bornée par la colonne — sinon
   l'ombre se dessinait pour toute la colonne, 119 px au-delà de la photo. */
.eau-section__visuel .objet { width: min(100%, calc(84svh * 4 / 5)); }
.eau-section__visuel .visuel { aspect-ratio: 4 / 5; }
/* Preuve d'eau : la vidéo remplit le même cadre que la photo ; le poster couvre en attendant. */
.eau-section__preuve { background-color: var(--fond-3); }
/* ---------- Épinglage collant (motion.js, epingleColle) ----------
   Sur tout écran tactile (le compositeur tient la scène : aucun hoquet à l'entrée ni à la sortie), et sans GSAP
   (fichier unique hors ligne, bloqueur, aperçu), motion.js enveloppe la section dans .epingle-espace — haut
   de la course (--epingle-longueur) plus un écran — et la section, collante en haut, vaut un écran : l'eau plein fond
   couvre l'écran entier, texte sous la photo compris, pendant toute la course. Deux hauteurs comme sur téléphone
   (barre d'adresse) : la section 100lvh, le contenu 100svh. */
.epingle-espace { margin-top: var(--rythme-3); height: calc(var(--epingle-longueur, 0px) + var(--epingle-hauteur, 100vh)); height: calc(var(--epingle-longueur, 0px) + var(--epingle-hauteur, 100lvh)); }
/* La section garde sa hauteur naturelle (un écran, plus si un format l'exige : rien n'est jamais rogné) ; motion.js la
   relit dans --epingle-hauteur pour dimensionner la course. */
.eau-section.epingle--colle { position: sticky; top: 0; margin-top: 0; min-height: 100vh; min-height: 100svh; }
.eau-section.epingle--colle .eau-section__interieur { min-height: 100vh; min-height: 100svh; }
.epingle-espace + .accueil-fondatrice { padding-top: var(--rythme-3); }
/* Page L'eau : la section remplace la photo de tête — plus proche du titre, visuel plus haut (jusqu'à 92 svh). */
.eau-section--pleine { margin-top: var(--rythme-1); }
.epingle-espace:has(> .eau-section--pleine) { margin-top: var(--rythme-1); }
.eau-section--pleine .eau-section__visuel .objet { width: min(100%, calc((100svh - 2 * var(--e-6)) * 4 / 5)); }
.eau-section__texte { position: relative; display: grid; gap: var(--e-4); align-content: center; justify-items: start; }
.eau-section__titre { margin: 0; font-family: var(--police-display); font-weight: 400; font-size: var(--t-2); line-height: 1.12; letter-spacing: 0; }
.eau-etapes { position: relative; width: 100%; min-height: 200px; }
/* Mot d'étape : changé par fondu + translateY(8px) en --duree-2 (§ 4.9). */
.eau-etape { position: absolute; inset: 0; display: grid; gap: var(--e-2); opacity: 0; transform: translateY(8px); transition: opacity var(--duree-2) var(--ease-sortie), transform var(--duree-2) var(--ease-sortie); }
.eau-etape.est-active { position: relative; opacity: 1; transform: none; }
.eau-etape__mot { margin: 0; font-family: var(--police-display); font-weight: 400; font-size: var(--t-immersion); line-height: 1.02; letter-spacing: -.01em; }
.eau-etape__verdict { margin: 0; font-family: var(--police-ui); font-size: var(--t-petit); font-weight: 500; line-height: 1.5; letter-spacing: 0; text-transform: none; }
.eau-etape__consigne { margin: 0; font-family: var(--police-texte); font-size: var(--t-chapo); line-height: 1.45; color: var(--encre-2); max-width: var(--largeur-chapo); }
/* « Brillent, même mouillés. » et le paragraphe : à l'étape 3 seulement (la place est réservée, rien ne saute). */
.eau-section__fin { display: grid; gap: var(--e-3); opacity: 0; visibility: hidden; transition: opacity var(--duree-2) var(--ease-sortie), visibility 0s linear var(--duree-2); }
.eau-section.est-etape-3 .eau-section__fin { opacity: 1; visibility: visible; transition-delay: 0s; }
.eau-section__signature { margin: 0; font-family: var(--police-display); font-weight: 400; font-size: var(--t-3); line-height: 1.18; letter-spacing: 0; }
.eau-section__phrase { margin: 0; font-size: var(--t-corps); line-height: 1.6; color: var(--encre-2); max-width: var(--largeur-texte); }
.eau-section__lien { transition: background-size var(--duree-2) var(--ease-luxe), color var(--duree-4) var(--ease-luxe); }
/* Couleurs par étape (voir le calcul en tête de section). */
.eau-section :is(.eau-etape__verdict, .eau-etape__consigne, .eau-section__phrase) { transition: color var(--duree-4) var(--ease-luxe); }
.eau-section.est-etape-1 :is(.eau-etape__consigne, .eau-section__phrase), .eau-section.est-etape-2 :is(.eau-etape__consigne, .eau-section__phrase) { color: var(--profond); }
.eau-section.est-etape-1 .eau-etape__verdict, .eau-section.est-etape-2 .eau-etape__verdict { color: var(--eau); }
.eau-section.est-etape-3 :is(.eau-etape__consigne, .eau-section__phrase) { color: var(--ciel); }
.eau-section.est-etape-3 .eau-etape__verdict { color: var(--or-2); }
.eau-section.est-etape-3 .eau-section__lien { color: var(--nacre); }
/* Entre les étapes 2 et 3 le fond TRAVERSE la luminance moyenne (ciel-2 → profond, et retour). Un fondu du texte
   simultané passerait par la couleur du fond : mesuré 1,3:1 à mi-course, le texte se dissout puis renaît. Le texte
   bascule donc d'un coup, au croisement, là où encre et nacre valent la même chose (≈ 4:1, jamais moins) : délai
   --duree-2 à l'aller (le fond s'assombrit lentement, ease-out sur 1,4 s), --duree-1 au retour (il s'éclaircit vite).
   Les autres passages (pierre ↔ ciel) ne croisent rien et gardent le fondu de 1,4 s. */
.eau-section.est-etape-3, .eau-section.est-etape-3 :is(.eau-etape__verdict, .eau-etape__consigne, .eau-section__phrase) { transition: color 0s linear var(--duree-2); }
.eau-section.est-etape-3 .eau-section__lien { transition: background-size var(--duree-2) var(--ease-luxe), color 0s linear var(--duree-2); }
.eau-section.est-etape-2, .eau-section.est-etape-2 :is(.eau-etape__verdict, .eau-etape__consigne, .eau-section__phrase) { transition: color 0s linear var(--duree-1); }
.eau-section.est-etape-2 .eau-section__lien { transition: background-size var(--duree-2) var(--ease-luxe), color 0s linear var(--duree-1); }
/* Empilée (mouvement réduit, ScrollTrigger absent, fenêtre trop basse) : les quatre étapes se suivent, la fin est
   visible, le canvas est collant et clair (gouttes.js paletteEmpilee) — couleurs du ciel. Déclaré APRÈS les étapes :
   quoi qu'il reste d'une classe est-etape-*, c'est ce régime qui gagne. */
.eau-section.epingle--empilee { color: var(--encre); }
.epingle--empilee .eau-section__interieur { min-height: 0; }
.epingle--empilee .eau-etapes { display: grid; gap: var(--e-5); min-height: 0; }
.epingle--empilee .eau-etape { position: relative; inset: auto; opacity: 1; transform: none; }
.eau-section.epingle--empilee :is(.eau-etape__consigne, .eau-section__phrase) { color: var(--profond); }
.eau-section.epingle--empilee .eau-etape__verdict { color: var(--eau); }
.eau-section.epingle--empilee .eau-section__lien { color: var(--encre); }
.eau-section.epingle--empilee .eau-section__fin { opacity: 1; visibility: visible; }

@media (max-width: 1023px) {
  .eau-section__interieur { grid-template-columns: 1fr; gap: var(--e-5); min-height: 0; }
  /* ---------- Épinglée sur petit écran (téléphone, tablette) ----------
     La scène est l'écran : la section vaut une fenêtre pleine, jamais plus. Deux hauteurs, et c'est exprès — sur un
     téléphone, la barre d'adresse se rétracte au défilement et la fenêtre GRANDIT, alors que l'épinglage a figé la
     hauteur de la section une fois pour toutes :
       — la SECTION (donc le canvas, qui la remplit) est haute de 100lvh, la fenêtre barre RÉTRACTÉE : elle couvre
         l'écran dans les deux états, sans laisser paraître le fond de page sous elle ;
       — le CONTENU tient dans 100svh, la fenêtre barre DÉPLOYÉE, et se cale en haut : rien ne passe jamais sous le
         pli, quel que soit l'état de la barre.
     Repli en vh partout où svh/lvh ne sont pas connus. */
  .eau-section.epingle { min-height: 100vh; min-height: 100lvh; }
  .eau-section.epingle .eau-section__interieur { min-height: 100vh; min-height: 100svh; align-content: center; }
  /* Les quatre étapes partagent une cellule de grille : la hauteur du bloc est celle de la PLUS LONGUE, la même à
     chaque étape. Sinon la section grandirait en cours d'épinglage et la scène sauterait. */
  .eau-section.epingle .eau-etapes { display: grid; grid-template-areas: "etape"; min-height: 0; }
  .eau-section.epingle .eau-etape { position: relative; inset: auto; grid-area: etape; }
  /* Quatre blocs de texte au plus par écran (§ 4.1) : la fin (signature, paragraphe) ne prend sa place qu'à l'étape 3. */
  .eau-section.epingle:not(.est-etape-3) .eau-section__fin { display: none; }
}

/* ---------- Téléphone debout, section épinglée ----------
   Tout tient dans une fenêtre, en-tête collant compris. Le texte (titre, étape, fin, lien) prend ce qu'il lui faut ; le
   VISUEL prend ce qui reste, plafonné à 40 svh — c'est la seule pièce élastique. Sur un grand téléphone c'est une belle
   photo ; sur un écran court elle devient un bandeau plutôt que de pousser une ligne de texte sous le pli (l'eau du
   canvas tient alors l'image à elle seule). Aucun texte n'est coupé nulle part. */
@media (max-width: 1023px) and (orientation: portrait) {
  .eau-section.epingle .eau-section__interieur { grid-template-rows: minmax(0, 1fr) auto; align-items: stretch; align-content: stretch; }
  /* Colonne flex de hauteur DÉFINIE (100 % de la cellule de grille) : c'est elle qui donne à l'objet un référent pour
     son min(100%, 40svh) — dans une grille aux lignes automatiques, ce pourcentage retomberait à zéro. */
  .eau-section.epingle .eau-section__visuel { display: flex; flex-direction: column; justify-content: center; min-height: 0; height: 100%; }
  /* La photo garde son cadre 4:5, jamais rognée : sa hauteur est la pièce élastique (jusqu'à 52 svh, ou ce qui reste),
     sa largeur en découle, centrée. Un cadre pleine largeur × 40 svh coupait la moitié de l'image sur un écran large et
     court (panneau latéral, tablette). */
  .eau-section.epingle .eau-section__visuel .objet { height: min(100%, 52svh); width: auto; aspect-ratio: 4 / 5; max-width: 100%; min-height: 0; margin-inline: auto; }
  .eau-section.epingle .eau-section__visuel .visuel { aspect-ratio: auto; height: 100%; max-height: none; width: 100%; }
  .eau-section.epingle .eau-section__texte { align-content: center; }
}
@media (max-width: 767px) and (orientation: portrait) {
  /* Padding haut = en-tête collant (56 px + encoche) : même quand il réapparaît au premier geste vers le haut, il ne
     recouvre pas le titre de la section épinglée. */
  .eau-section.epingle .eau-section__interieur { gap: var(--e-3); padding-block: calc(var(--h-entete) + var(--e-3)) calc(var(--e-6) + var(--e-4)); }
  .eau-section.epingle .eau-section__texte { gap: var(--e-3); }
  .eau-section.epingle .eau-section__titre { font-size: var(--t-2); }
  .eau-section.epingle .eau-etape { gap: var(--e-1); }
  /* À l'étape 3, quatre blocs au plus : le mot et son verdict, la signature, le paragraphe, le lien — le titre (lu
     pendant trois étapes) et la consigne (que le paragraphe redit) s'effacent. */
  .eau-section.epingle.est-etape-3 :is(.eau-section__titre, .eau-etape__consigne) { display: none; }
  .eau-section.epingle .eau-section__phrase { font-size: var(--t-petit); line-height: 1.5; }
}
/* Écran couché (844 × 390) : la section est épinglée là aussi — motion.js n'empile qu'en dessous de 380 px de fenêtre
   stable — et doit donc tenir ENTIÈRE dans 100svh. Trois colonnes : photo | titre + étape + lien | fin ; display:
   contents fait des blocs du texte des items de la grille. Empilée (mouvement réduit), la section grandit librement. */
@media (orientation: landscape) and (max-height: 520px) {
  .eau-section__interieur { grid-template-columns: 4fr 5fr 4fr; grid-template-areas: "visuel titre fin" "visuel etapes fin" "visuel lien fin"; grid-template-rows: auto 1fr auto; gap: var(--e-2) var(--e-5); padding-block: var(--h-entete) var(--e-3); align-items: stretch; }
  .eau-section__texte { display: contents; }
  .eau-section__visuel { grid-area: visuel; align-self: center; }
  .eau-section__visuel .visuel { aspect-ratio: 4 / 5; max-height: calc(100svh - var(--h-entete) - var(--e-3)); }
  .eau-section__titre { grid-area: titre; font-size: var(--t-2); }
  .eau-etapes { grid-area: etapes; min-height: 0; }
  .eau-section__lien { grid-area: lien; align-self: end; justify-self: start; }
  .eau-section__fin { grid-area: fin; align-self: center; gap: var(--e-2); }
  .eau-section.epingle:not(.est-etape-3) .eau-section__fin { display: grid; }
  /* L'étape : mot et verdict sur une même ligne, la consigne juste dessous (la cellule est haute : rien ne s'étire). */
  .eau-etape { grid-template-columns: auto 1fr; align-items: baseline; align-content: start; column-gap: var(--e-3); row-gap: var(--e-1); }
  .eau-etape__consigne { grid-column: 1 / -1; font-size: var(--t-corps); line-height: 1.5; }
  .eau-etape__mot { font-size: var(--t-2); }
  .eau-section__signature { font-size: var(--t-question); }
  .eau-section__phrase { font-size: var(--t-petit); line-height: 1.5; }
}

/* ---------- Sur @ivy_jewels_geneva ----------
   La mosaïque des publications réelles : six vignettes carrées, chacune un objet posé (ombre courte, base.css) et
   un bouton qui ouvre la visionneuse (stories.js) ; 3 × 2 dès la tablette, 2 × 3 sur téléphone, gap --e-3. Le
   cartel de papier mord de 20 px sous la planche, en bas à gauche, comme sous une photo : la marge le contient,
   pour que la règle de la section suivante garde son rythme. */
.accueil-insta { padding-top: var(--rythme-2); }
/* Après la section épinglée, la fondatrice respire comme avant : --rythme-3. */
.eau-section + .accueil-fondatrice { padding-top: var(--rythme-3); }
.accueil-insta__planche { position: relative; margin-bottom: var(--e-4); }
.accueil-insta__grille { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--e-3); list-style: none; margin: 0; padding: 0; }
.accueil-insta__objet { min-width: 0; }
.accueil-insta__bouton { display: block; width: 100%; padding: 0; margin: 0; border: 0; background: none; color: inherit; cursor: pointer; }
.accueil-insta__bouton .visuel { aspect-ratio: 1; }
@media (max-width: 767px) {
  .accueil-insta__planche { margin-inline: calc(-1 * var(--marge-page)); }
  .accueil-insta__grille { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* Écran couché : trois colonnes tiennent, la planche reste dans les marges. */
@media (orientation: landscape) and (max-height: 520px) {
  .accueil-insta__planche { margin-inline: 0; }
  .accueil-insta__grille { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ---------- Derrière Ivy Jewels ----------
   Une personne réelle derrière la marque (docs/SUISSE.md § 5.2), en dernier, sans lin : le portrait posé sur 5 colonnes,
   « Je m'appelle Ivana. », sa phrase, la maison, deux liens d'appel. Les pastilles « à compléter » restent visibles :
   un trou assumé vaut mieux qu'une invention. */
.accueil-fondatrice__grille { display: grid; grid-template-columns: 5fr 7fr; column-gap: var(--rythme-1); row-gap: var(--e-6); align-items: center; }
.accueil-fondatrice__visuel { min-width: 0; }
.accueil-fondatrice__texte { display: grid; gap: var(--e-4); max-width: var(--largeur-texte); justify-items: start; }
.accueil-fondatrice__titre { margin: 0; }
.accueil-fondatrice__parole { margin: 0; font-family: var(--police-display); font-style: italic; font-weight: 400; font-size: var(--t-3); line-height: 1.18; letter-spacing: 0; }
.accueil-fondatrice__parole p { margin: 0; }
.accueil-fondatrice__parole--vide { font-style: italic; color: var(--encre-2); }
.accueil-fondatrice__lieu { margin: 0; font-size: var(--t-corps); line-height: 1.6; text-wrap: balance; }
.accueil-fondatrice__liens { display: flex; flex-wrap: wrap; gap: var(--e-3) var(--e-5); }
/* Sans portrait (à la demande de la fondatrice) : la logique de l'en-tête de La Maison (.maison__tete, en 4 / 8) — le titre
   en grand à gauche, la parole en italique comme .maison__citation, la maison et les liens à droite —, posée sur la grille
   5 / 7 de l'accueil (celle de « À venir »), alignée en haut. Une colonne sous 1024 px, paysage court compris. */
.accueil-fondatrice--sans-portrait .accueil-fondatrice__grille { align-items: start; }
.accueil-fondatrice--sans-portrait .accueil-fondatrice__titre { max-width: 9em; text-wrap: balance; }
/* ---------- À venir : un grand mot posé sur la pierre, le titre et la phrase à côté ----------
   Grille 5 / 7 alignée en bas : « Bientôt » en Cormorant italique --t-affiche (le corps d'un numéro de planche), le
   texte en colonne (titre --t-1, phrase --t-chapo, note 13 px, deux liens). Une seule colonne sous 1024 px, le mot
   au-dessus du titre. Rien d'autre sur la pierre : ni cadre ni aplat. */
.accueil-avenir { padding-bottom: var(--e-6); }
.accueil-avenir__grille { display: grid; grid-template-columns: 5fr 7fr; column-gap: var(--rythme-1); row-gap: var(--e-4); align-items: end; margin-top: var(--e-5); }
.accueil-avenir__mot { margin: 0; font-family: var(--police-display); font-style: italic; font-weight: 400; font-size: var(--t-affiche); line-height: .9; letter-spacing: -.02em; color: var(--encre); }
.accueil-avenir__texte { display: grid; gap: var(--e-4); max-width: var(--largeur-texte); justify-items: start; }
.accueil-avenir__titre { margin: 0; }
.accueil-avenir__phrase { margin: 0; font-family: var(--police-ui); font-size: var(--t-chapo); line-height: 1.45; }
.accueil-avenir__note { margin: 0; font-family: var(--police-ui); font-size: var(--t-petit); line-height: 1.5; color: var(--encre-2); }
.accueil-avenir__liens { display: flex; flex-wrap: wrap; gap: var(--e-3) var(--e-5); }
.accueil-avenir__liens .lien--cta { background: none; border: 0; padding: 0; cursor: pointer; }
@media (max-width: 1023px) { .accueil-avenir__grille { grid-template-columns: 1fr; row-gap: var(--e-5); align-items: start; } }

/* Pastille « à compléter » : Jost 13 px 500 bas-de-casse, filet pointillé or-3 (5,1:1 sur la pierre), angles droits. */
.a-completer { display: inline-block; padding: var(--e-1) var(--e-2); border: var(--filet) dashed var(--or-3); border-radius: 0; color: var(--or-3); font-family: var(--police-ui); font-style: normal; font-size: var(--t-petit); font-weight: 500; letter-spacing: 0; text-transform: none; line-height: 1.5; vertical-align: middle; white-space: nowrap; }
@media (max-width: 1023px) {
  .accueil-fondatrice__grille { grid-template-columns: 1fr; row-gap: var(--e-5); }
  /* Tablette : le portrait prend le bord gauche du texte, à 360 px, plutôt que de flotter au milieu. */
  .accueil-fondatrice__visuel { width: min(100%, 360px); }
}
@media (max-width: 767px) {
  /* Téléphone : le portrait bord à bord ; la ligne d'ombre de l'objet suit le bord bas. */
  .accueil-fondatrice__visuel { width: auto; margin-inline: calc(-1 * var(--marge-page)); }
}
@media (orientation: landscape) and (max-height: 520px) {
  .accueil-fondatrice__grille { grid-template-columns: 2fr 3fr; column-gap: var(--e-5); }
  .accueil-fondatrice__visuel { width: min(100%, 240px); margin-inline: 0; }
}
@media (max-width: 1023px) {
  /* Deux classes : l'emporte sur la grille 2fr / 3fr du paysage court, prévue pour le portrait de 240 px. */
  .accueil-fondatrice--sans-portrait .accueil-fondatrice__grille { grid-template-columns: minmax(0, 1fr); row-gap: var(--e-4); }
  .accueil-fondatrice--sans-portrait .accueil-fondatrice__titre { max-width: none; }
}

/* src/css/vues/boutique.css */
/* IVY JEWELS — vue Boutique : la règle, le titre, cinq planches alternées 7/5 (DA V2 § 3.10, § 4.2). Le catalogue —
   bannière, chips, tri, compteur, barre collante, bloc eau, réassurances — a disparu (§ 7.7). Les favoris (lot 4)
   sont stylés dans paiement.css : plus rien d'eux ici.
   Feuille HORS couche : priorité naturelle sur composants.css (règle expliquée en tête de base.css).
   Rien hors jetons (§ 6.0) : tailles var(--t-*) et les fixées du § 2.3, ombres portées par .objet (base.css), aucune
   capitale, aucun rayon, aucune durée littérale. Le bouton plein et le numéro sont ceux du lot 1-2 (.bouton, .numero). */

/* ---------- Tête : règle « Les bijoux », h1 --t-hero à gauche, chapô et ligne matière à droite ---------- */
.boutique__tete { display: grid; grid-template-columns: 7fr 5fr; gap: var(--e-4) var(--e-6); align-items: end; }
.boutique__titre { max-width: var(--largeur-h1); }
.boutique__chapo { display: grid; gap: var(--e-3); justify-items: start; }
.boutique__chapo .u-chapo { margin: 0; }

/* ---------- La planche (§ 3.10) ----------
   Photo 4:5 posée (.objet, ombre par pseudo-éléments) sur 7 colonnes, colonne de texte sur 5, centrées l'une sur
   l'autre, 80 svh au moins ; --rythme-2 avant chaque planche (donc entre la tête et la première, et entre deux).
   .planche--inverse : la photo passe à droite par order, la grille devient 5/7. scroll-margin-top : l'ancre
   « #/boutique/bagues » s'arrête sous l'en-tête. Le morph planche → fiche : produit-<id> sur le .visuel (jamais sur
   .objet, qui se soulève au survol), numero-<id> sur le chiffre ; les classes de transition règlent la courbe (base.css). */
.planche {
  display: grid; grid-template-columns: 7fr 5fr; gap: var(--e-6); align-items: center;
  min-height: 80svh; padding-top: var(--rythme-2); scroll-margin-top: var(--h-entete);
}
.planche--inverse { grid-template-columns: 5fr 7fr; }
.planche--inverse .planche__lien { order: 2; }
.planche__lien { display: block; color: inherit; }
.planche__visuel { width: 100%; view-transition-class: produit; }
.planche__numero { view-transition-class: numero; }
/* Pièce épuisée : image à 70 % (§ 3.3), une fois chargée (l'opacité de chargement passe de 0 à 1 avant). */
.planche--epuisee .visuel.est-charge img { opacity: .7; }

/* La colonne : numéro --t-affiche (.numero--planche, base.css : 64–128 px, chiffres alignés), puis le texte. */
.planche__colonne { display: block; }
.planche__numero { display: block; margin-bottom: var(--e-3); }
.planche__texte { display: grid; gap: var(--e-3); justify-items: start; }
.planche__nom { font-size: var(--t-2); line-height: 1.12; letter-spacing: 0; margin: 0; }
.planche__soustitre { font-family: var(--police-ui); font-size: var(--t-petit); line-height: 1.5; color: var(--encre-2); margin: 0; }
/* L'accroche de la fondatrice, au mot près : Cormorant italique --t-3. */
.planche__accroche { font-family: var(--police-display); font-style: italic; font-weight: 400; font-size: var(--t-3); line-height: 1.18; letter-spacing: 0; max-width: var(--largeur-chapo); margin: 0; }
/* Prix : Jost 20 px 500 tabulaire (taille fixée du § 2.3), jamais coloré. */
.planche__prix { font-size: 1.25rem; font-weight: 500; line-height: 1.3; }
.planche__bouton { margin-top: var(--e-2); }
.planche__voir { margin-top: var(--e-2); }

/* Tablette : deux colonnes égales, le texte respire. */
@media (max-width: 1023px) {
  .planche, .planche--inverse { grid-template-columns: 1fr 1fr; gap: var(--e-5); }
}

/* Téléphone (§ 4.2) : numéro 64 px, photo bord à bord, puis le texte ; min-height auto ; le bouton plein reste
   (une planche = un écran). Le numéro quitte la colonne (display: contents) pour passer au-dessus de la photo. */
@media (max-width: 767px) {
  .boutique__tete { grid-template-columns: 1fr; gap: var(--e-4); }
  .planche, .planche--inverse { grid-template-columns: 1fr; gap: 0; min-height: auto; align-items: start; }
  .planche__colonne { display: contents; }
  .planche__numero { order: -1; font-size: 4rem; margin-bottom: var(--e-3); }
  .planche__lien, .planche--inverse .planche__lien { order: 0; margin-inline: calc(-1 * var(--marge-page)); }
  .planche__texte { order: 1; padding-top: var(--e-4); }
}

/* Paysage court : la photo est bornée par la hauteur, la planche ne dépasse pas l'écran de beaucoup. */
@media (orientation: landscape) and (max-height: 520px) {
  .planche, .planche--inverse { min-height: auto; grid-template-columns: 1fr 1fr; }
  .planche__lien { width: min(100%, calc(80svh * 4 / 5)); justify-self: center; }
}

/* src/css/vues/produit.css */
/* IVY JEWELS — vue Produit : tête « ‹ Les bijoux » / « 03 / 05 », galerie 7/12 (deux objets posés + tour 360°),
   panneau collant 5/12, boîte taille, bouton plein, disponibilité, accordéons, note « photos réelles », « Les quatre
   autres. », barre d'achat fixe, guide des tailles (DA V2 § 4.3, § 3.7).
   Feuille HORS couche : priorité naturelle sur composants.css (règle expliquée en tête de base.css). Les composants
   (galerie, accordéon, bouton, boîte taille, barre fixe, rail, cartes) sont ceux du lot 2 : ici, seulement la mise
   en page de la fiche et ce qui lui est propre.
   Rien hors jetons (§ 6.0) : tailles var(--t-*) et les fixées du § 2.3, ombres var(--ombre-*), aucune capitale (la
   ligne matière est la seule de la page), rayons 0 ou pilule, aucune durée littérale. Aucune .regle sur la fiche. */

.produit { padding-top: var(--e-5); }

/* ---------- Tête : « ‹ Les bijoux » 13 px à gauche, « 03 / 05 » Cormorant 16 px --numero à droite ---------- */
.produit__tete { display: flex; justify-content: space-between; align-items: baseline; gap: var(--e-4); min-height: 24px; margin-bottom: var(--e-5); }
.produit__retour { font-size: var(--t-petit); line-height: 1.5; }
.produit__compte { font-family: var(--police-display); font-size: 1rem; line-height: 1; color: var(--numero); font-variant-numeric: lining-nums tabular-nums; white-space: nowrap; }
.produit__compte .numero { view-transition-class: numero; }

/* ---------- Corps : galerie 7 col (rangée 1) et note (rangée 2) à gauche, panneau collant 5 col sur les deux ---------- */
.produit__corps { display: grid; grid-template-columns: 7fr 5fr; grid-template-rows: auto auto; gap: var(--e-3) clamp(32px, 5vw, 96px); align-items: start; }
.produit__galerie { grid-column: 1; grid-row: 1; position: relative; }
.produit__panneau {
  grid-column: 2; grid-row: 1 / span 2; align-self: start;
  position: sticky; top: calc(var(--h-entete-defile) + var(--e-5)); max-width: 480px;
  display: grid; gap: var(--e-3);
}
.produit__photos-note { grid-column: 1; grid-row: 2; font-size: var(--t-petit); line-height: 1.5; color: var(--encre-2); margin: 0; max-width: var(--largeur-texte); }
/* Le film de la pièce (vues/produit.js, composants/film.js) : un objet posé sous la galerie, à son cadre natif 9:16,
   420 px de large sur bureau (une pièce filmée au téléphone, tenue comme un téléphone), pleine largeur sur téléphone ;
   son cartel mord sur la pierre, d'où la marge basse. Le bouton Pause / Lire du film reste en bas à droite. */
/* --hauteur-cartel vit sur .cartel (base.css), pas ici : la marge basse compte la moitié du cartel (20 px, --e-4 la couvre) plus un pas. */
.produit__film { position: relative; width: min(100%, 420px); margin-top: var(--e-5); margin-bottom: calc(var(--e-4) + var(--e-3)); }
.produit__film .visuel { aspect-ratio: 9 / 16; }
@media (max-width: 599px) { .produit__film { width: 100%; } }
/* Le bouton « Agrandir » est visuellement caché : au focus clavier il devient une étiquette de papier sur la photo. */
.galerie__item .galerie__agrandir:focus-visible {
  position: absolute; inset: auto var(--e-3) var(--e-3) auto; width: auto; height: auto; margin: 0; padding: var(--e-2) var(--e-3);
  clip: auto; clip-path: none; overflow: visible; white-space: nowrap; z-index: 3;
  background: var(--papier); color: var(--encre); font-family: var(--police-ui); font-size: var(--t-petit); letter-spacing: 0; text-transform: none; box-shadow: var(--ombre-contact);
}

/* ---------- Panneau, dans l'ordre du § 4.3 ---------- */
/* Nom --t-1 (le plus grand texte de la page) et prix 20 px 500 sur la même ligne. */
.produit__titre { display: flex; align-items: baseline; justify-content: space-between; gap: var(--e-4); }
.produit__titre h1 { font-size: var(--t-1); line-height: 1.06; letter-spacing: -.01em; margin: 0; }
.produit__prix { font-size: 1.25rem; font-weight: 500; line-height: 1.3; }
/* Sous-titre 13 px --encre-2 à gauche ; favori et partage (boutons-icônes 44 px) à droite, débordant de 12 px pour
   que l'icône reste alignée sur le bord du panneau. */
.produit__sousligne { display: flex; align-items: center; justify-content: space-between; gap: var(--e-3); margin-top: calc(-1 * var(--e-2)); }
.produit__sous-titre { font-family: var(--police-ui); font-size: var(--t-petit); line-height: 1.5; letter-spacing: 0; color: var(--encre-2); margin: 0; }
.produit__actions { display: flex; flex: none; margin-right: calc(-1 * var(--e-12)); }
.produit__favori.est-actif { color: var(--or-3); }
.produit__panneau .ligne-matiere { margin: 0; }
/* Accroche de la fondatrice, au mot près : Cormorant italique --t-chapo. */
.produit__accroche { font-family: var(--police-display); font-style: italic; font-weight: 400; font-size: var(--t-chapo); line-height: 1.45; letter-spacing: 0; margin: 0; max-width: var(--largeur-chapo); }

/* Le formulaire d'achat : boîte taille, mention d'hygiène, bouton, disponibilité. */
.produit__achat { display: grid; gap: var(--e-3); justify-items: start; }
/* Boîte taille (.taille-unique du lot 2, 40 px de papier) : le texte à gauche, « Guide » à droite. */
.produit__taille { justify-content: space-between; width: 100%; color: var(--encre); }
.produit__taille strong { font-weight: 500; }
.produit__taille-texte { min-width: 0; }
.produit__guide { flex: none; font-size: var(--t-petit); }
.produit__selection { display: grid; gap: var(--e-3); width: 100%; }
.produit__erreur { font-size: var(--t-petit); line-height: 1.5; color: var(--erreur); margin: 0; }
.produit__hygiene { font-size: var(--t-petit); line-height: 1.5; color: var(--encre); margin: 0; max-width: var(--largeur-chapo); }
/* Marques de paiement sous le bouton d'achat : discrètes, 22 px, à la suite de la disponibilité. */
.produit__paiement { margin: var(--e-3) 0 0; --hauteur-logo: 22px; }

/* « ● En stock — expédié 1 à 2 jours ouvrables après réception du paiement » : un point 6 px, le texte en 13 px. */
.produit__dispo { position: relative; padding-left: var(--e-3); font-size: var(--t-petit); line-height: 1.5; color: var(--encre-2); margin: 0; }
.produit__dispo::before { content: ""; position: absolute; left: 0; top: .55em; width: 6px; height: 6px; border-radius: var(--rayon-pilule); background: var(--sauge); }
.produit__dispo--epuise::before { background: var(--erreur); }
/* « ou commander par message Instagram → » : lien d'appel à 13 px (§ 4.3), aligné à gauche dans le panneau. */
.produit__instagram { justify-self: start; text-align: left; }

/* Accordéons (lot 2) : seulement l'espace avant, les tirets d'entretien et les liens légaux en ligne. */
.produit__accordeons { margin-top: var(--e-3); }
.produit__entretien { list-style: none; margin: 0 0 .75em; padding: 0; display: grid; gap: var(--e-1); }
.produit__entretien li { padding-left: var(--e-3); text-indent: calc(-1 * var(--e-3)); }
.produit__entretien li::before { content: "— "; }
.produit__liens-legaux { display: flex; flex-wrap: wrap; gap: var(--e-2) var(--e-4); font-size: var(--t-petit); }
.produit__liens-legaux .lien { padding-block: 2px 3px; }

/* ---------- « Les quatre autres. » : Cormorant italique 26 px, quatre cartes standard 4 × 1fr, sans flèches ;
   sous 768 px le .rail-objets du lot 2 prend le relais (aucune règle ici). ---------- */
.produit__autres-titre { font-family: var(--police-display); font-style: italic; font-weight: 400; font-size: 1.625rem; line-height: 1.18; letter-spacing: 0; margin: 0 0 var(--e-5); }
@media (min-width: 768px) {
  .produit__autres { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--e-4); padding-inline: 0; overflow: visible; }
}

/* ---------- Tablette et téléphone (§ 4.3 mobile) ----------
   Tête de 32 px, galerie 1:1 bord à bord (le ratio et le compteur en badge papier sont ceux du lot 2), puis le
   panneau (nom + prix ≤ 560 px, bouton ≤ 950 px), note photos après les accordéons (ordre du DOM). La barre d'achat
   fixe couvre les 64 derniers pixels de l'écran jusqu'au bout de la page : la page réserve cette hauteur SOUS le
   pied (pierre nue), jamais entre main et le pied (§ 2.4 : ≤ 64 px avant le filet). */
@media (max-width: 1023px) {
  .produit { padding-top: 0; }
  .produit__tete { min-height: 32px; align-items: center; margin-bottom: 0; }
  .produit__corps { grid-template-columns: 1fr; grid-template-rows: none; gap: var(--e-3); }
  .produit__galerie, .produit__panneau, .produit__photos-note { grid-column: auto; grid-row: auto; }
  .produit__galerie { margin-inline: calc(-1 * var(--marge-page)); }
  .produit__film { margin-top: var(--e-4); margin-bottom: calc(var(--e-4) + var(--e-5)); }
  .produit__panneau { position: static; max-width: none; }
  .produit__photos-note { margin-top: var(--e-3); }
  .tour360 { padding-inline: var(--marge-page); }
  html:has(main.vue--produit) body { padding-bottom: calc(var(--h-achat) + var(--safe-bas)); }
}
@media (orientation: landscape) and (max-height: 520px) {
  .produit__corps { grid-template-columns: 1fr 1fr; gap: var(--e-5); }
  .produit__galerie { margin-inline: 0; }
  .produit__galerie .galerie__item .visuel { aspect-ratio: 4 / 5; }
  .produit__panneau { position: static; }
  .tour360 { padding-inline: 0; }
}

/* ---------- Tour à 360° (composants/tour-360.js) : inerte sans données ---------- */
.tour360 { display: grid; gap: var(--e-3); margin-top: var(--e-4); }
.tour360__cadre { position: relative; touch-action: pan-y; user-select: none; -webkit-user-select: none; cursor: grab; outline-offset: -3px; }
.tour360__cadre.est-saisi { cursor: grabbing; }
/* Les vues s'enchaînent sans fondu : c'est une rotation, pas un diaporama. */
.tour360__cadre .visuel img { transition: none; }
.tour360__angle { position: absolute; right: var(--e-3); bottom: var(--e-3); padding: var(--e-1) var(--e-2); font-family: var(--police-ui); font-size: var(--t-petit); letter-spacing: 0; font-variant-numeric: lining-nums tabular-nums; color: var(--encre); background: var(--papier); box-shadow: var(--ombre-contact); border-radius: 0; pointer-events: none; }
.tour360__barre { display: flex; align-items: center; flex-wrap: wrap; gap: var(--e-3); }
.tour360__aide { margin: 0; }
@media (hover: none) { .tour360__cadre { cursor: default; } }

/* ---------- Tailles & ajustement (modale du guide) : tableau construit depuis le catalogue, ligne de la pièce marquée ---------- */
.guide { display: grid; gap: var(--e-4); }
.guide table { width: 100%; border-collapse: collapse; font-size: var(--t-petit); font-variant-numeric: lining-nums tabular-nums; }
.guide th, .guide td { text-align: left; padding: var(--e-2); border-bottom: var(--filet) solid var(--ligne); vertical-align: top; }
.guide th { font-weight: 500; color: var(--encre-2); letter-spacing: 0; text-transform: none; }
.guide tr.est-actif td { background: var(--chauffe); }
.guide tr.est-actif td:first-child { border-left: 2px solid var(--or); }

/* src/css/vues/pages.css */
/* IVY JEWELS — vues éditoriales et utilitaires : La Maison, L'eau, Contact, 404, pages légales, Commande.
   Direction artistique V2 « Pierre et Lumière » (docs/DIRECTION-ARTISTIQUE-V2.md § 4.4 à 4.8) : la page EST la pierre,
   les photos sont des objets posés dessus (.objet, base.css § 3.1), le texte est posé à côté — jamais sur une photo
   autrement que par un cartel. Feuille SANS couche (règle expliquée en tête de base.css) : elle garde sa priorité
   naturelle sur le système. Rien hors jetons : tailles var(--t-*) ou tailles fixées du § 2.3, ombres var(--ombre-*),
   couleurs des jetons, durées des jetons, angles droits. Aucune capitale espacée : la ligne matière et le badge sont
   les deux seuls emplois du site, et aucun n'est ici. */

/* ---------- Entrée de page sans héros (§ 4.6, 4.7) : 48 px sur bureau, 24 sur téléphone ---------- */
.page-tete { padding-top: var(--e-6); }
@media (max-width: 767px) { .page-tete { padding-top: var(--e-4); } }

/* ---------- Le chapitre (§ 4.4) : tête collante sur 4 colonnes, corps sur 8 ----------
   Titré, jamais numéroté (§ 1.2, arbitrage 3 : on numérote les objets, pas les rubriques). Le filet haut est une
   horizontale de la grille — pas une règle gravée (.regle, une seule par section) : les chapitres se suivent sous des
   filets --ligne. La tête colle sous l'en-tête défilé et s'arrête à la fin de SON chapitre : align-self: start la
   laisse libre dans sa cellule de grille (étirée sur toute la rangée, elle n'aurait aucune marge de mouvement) et la
   cellule borne son déplacement — elle ne glisse jamais sur le chapitre suivant. Sous 1024 px : une colonne, tête
   statique (une tête collante prendrait un tiers d'un écran de téléphone). */
.chapitre { display: grid; grid-template-columns: 4fr 8fr; gap: var(--rythme-1); padding-top: var(--e-4); border-top: var(--filet) solid var(--ligne); }
.chapitre + .chapitre { margin-top: var(--rythme-2); }
.chapitre__tete { align-self: start; position: sticky; top: calc(var(--h-entete-defile) + var(--e-4)); display: grid; gap: var(--e-3); }
.chapitre__tete h2 { font-size: var(--t-2); font-weight: 400; line-height: 1.12; letter-spacing: 0; }
.chapitre__corps { display: grid; gap: var(--rythme-1); align-content: start; }
.chapitre__corps > * { min-width: 0; }
.chapitre__corps .texte, .chapitre__corps .u-petit { max-width: var(--largeur-texte); }
/* La photo d'un chapitre : un objet posé sur 5 des 8 colonnes du corps. */
.chapitre__visuel { width: calc(100% * 5 / 8); }
.chapitre__visuel .visuel { aspect-ratio: 4 / 5; }
.chapitre__liens { display: flex; flex-wrap: wrap; gap: var(--e-3) var(--e-6); }
@media (max-width: 1023px) {
  .chapitre { grid-template-columns: 1fr; gap: var(--e-5); }
  .chapitre__tete { position: static; }
  .chapitre__visuel { width: 100%; max-width: 560px; }
}
/* Téléphone : les photos vont bord à bord (§ 1.2, arbitrage 6) ; l'ombre posée passe sous les marges. */
@media (max-width: 767px) {
  .chapitre__visuel { width: auto; max-width: none; margin-inline: calc(-1 * var(--marge-page)); }
}

/* ---------- La Maison (§ 4.4) ----------
   La photo des cinq pièces est posée dans le conteneur — jamais à bord perdu sur bureau —, plafonnée à 1200 px de large
   et à 72svh de haut (la largeur suit le ratio de la dérivée : --ratio-hero). Le cartel « La Maison — Genève » mord sur
   la pierre ; le titre vient SOUS la photo, avec l'exergue de la fondatrice à sa droite. */
.maison__hero { --ratio-hero: 2; width: min(100%, 1200px, calc(72svh * var(--ratio-hero))); margin-inline: auto; }
.maison__tete { display: grid; grid-template-columns: 4fr 8fr; gap: var(--rythme-1); align-items: start; margin-top: calc(var(--rythme-1) + var(--e-4)); }
.maison__tete h1 { font-size: var(--t-1); }
.maison__exergue { display: grid; gap: var(--e-4); max-width: var(--largeur-chapo); }
.maison__citation { font-family: var(--police-display); font-style: italic; font-weight: 400; font-size: var(--t-3); line-height: 1.18; margin: 0; }
.maison__chapitres { margin-top: var(--rythme-2); }
.maison__pied { display: flex; flex-wrap: wrap; gap: var(--e-3) var(--e-6); margin-top: var(--rythme-2); padding-bottom: var(--e-6); }
/* La liste des cinq : numéro, nom Cormorant 24, sous-titre, prix tabulaire à droite ; filets --ligne entre les lignes,
   --chauffe au survol (surface de survol des zones cliquables sur la pierre, § 2.1). Le prix est en Jost 20 px 500 :
   compensation optique Cormorant = Jost × 1,25 quand les deux familles voisinent (§ 2.3), 24 / 20 = 1,2. */
.maison-pieces { display: grid; }
.maison-pieces li { border-top: var(--filet) solid var(--ligne); }
.maison-pieces li:last-child { border-bottom: var(--filet) solid var(--ligne); }
.maison-pieces a { display: grid; grid-template-columns: auto 1fr auto; gap: var(--e-3); align-items: baseline; padding: var(--e-12) var(--e-2); margin-inline: calc(-1 * var(--e-2)); color: inherit; transition: background-color var(--duree-1); }
.maison-pieces a:hover, .maison-pieces a:focus-visible { background: var(--chauffe); }
.maison-pieces__nom { font-family: var(--police-display); font-size: var(--t-4); line-height: 1.25; }
.maison-pieces__sous { font-size: var(--t-petit); color: var(--encre-2); margin-left: var(--e-2); }
.maison-pieces .prix { font-size: 1.25rem; font-weight: 500; }
@media (max-width: 1023px) { .maison__tete { grid-template-columns: 1fr; gap: var(--e-4); } }
@media (max-width: 767px) {
  .maison__hero { width: auto; margin-inline: calc(-1 * var(--marge-page)); }
  .maison-pieces__sous { display: block; margin-left: 0; }
}

/* ---------- L'eau (§ 4.5) : sur la pierre, l'eau vit sur la photo ----------
   Le bleu profond est réservé à la section épinglée de l'accueil : ici, rien n'est peint. Le h1 et le chapô à gauche,
   la photo 4:5 posée sur 5 colonnes à droite — c'est elle qui porte les gouttes (gouttes.monte, entretien.js). */
.eau-tete { display: grid; grid-template-columns: 7fr 5fr; gap: var(--e-6); align-items: center; }
.eau-tete__texte { display: grid; gap: var(--e-4); align-content: center; }
.eau-tete__texte h1 { font-size: var(--t-hero); line-height: 1; letter-spacing: -.015em; max-width: var(--largeur-h1); }
/* La bulle d'information (ci-dessous) se cale sur le chapô entier, pas sur le mot. */
.eau-tete__chapo { position: relative; }
/* Tête sans photo (28.09.2026) : la photo de la page est celle de la section épinglée qui suit, plus grande, épinglée.
   Le h1 porte lui-même --largeur-h1 (en ch de SA police) : rien à borner ici. */
.eau-tete--texte { grid-template-columns: 1fr; }
/* --rythme-3 avant la table : sa règle « Ce qu'ils supportent » est la seconde de la page, et deux règles ne partagent
   jamais un écran de 900 px (§ 3.1, contrôle 7) — avec --rythme-2 elle tombait à 899 px sur 1440 × 900. */
.eau-table { margin-top: var(--rythme-3); }
.eau-chapitres { margin-top: var(--rythme-2); }
.eau-chapitres .chapitre:last-child { padding-bottom: var(--e-6); }
@media (max-width: 1023px) {
  .eau-tete { grid-template-columns: 1fr; gap: var(--e-5); }
  .eau-tete__visuel { max-width: 480px; }
}

/* La matière, discrètement : un mot du chapô ouvre la bulle d'information (balisage de IVY.ui.infobulle, dont
   composants/ui.js gère le clic et Échap). La bulle se positionne par rapport au CHAPÔ, pas au mot : sur un petit écran,
   un mot en fin de ligne enverrait une bulle de 300 px hors du cadre. Elle s'étale donc sous la phrase, bord à bord. */
.infobulle--mot { position: static; vertical-align: baseline; gap: 0; }
.infobulle--mot .infobulle__texte { top: calc(100% + 10px); left: 0; right: 0; width: auto; max-width: 440px; }
.infobulle--mot .infobulle__bouton {
  display: inline; width: auto; height: auto; border-radius: 0; color: inherit; font: inherit;
  text-decoration: underline dotted; text-decoration-color: currentColor; text-underline-offset: 4px;
}
.infobulle--mot .infobulle__bouton .icone { width: 14px; height: 14px; margin-left: 4px; vertical-align: -2px; opacity: .8; }
.infobulle--mot .infobulle__bouton:hover,
.infobulle--mot .infobulle__bouton[aria-expanded="true"] { color: var(--encre); text-decoration-style: solid; }

/* Les trois étapes de la garantie : chiffres alignés --numero (la seule numérotation de la page : ce sont des étapes,
   pas des rubriques), filets --ligne, gras 500. */
.etapes-garantie { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--e-4); }
.etapes-garantie li { display: grid; align-content: start; gap: var(--e-2); padding-top: var(--e-3); border-top: var(--filet) solid var(--ligne); font-size: var(--t-petit); line-height: 1.5; color: var(--encre-2); }
.etapes-garantie .numero { font-size: var(--t-2); line-height: 1; }
.etapes-garantie strong { color: var(--encre); font-weight: 500; font-size: var(--t-corps); line-height: 1.4; }
@media (max-width: 767px) { .etapes-garantie { grid-template-columns: 1fr; } }

/* ---------- Contact (§ 4.6) ----------
   Titre et chapô sur la grille des chapitres (4 / 8), puis trois chapitres : Nous écrire (liste de définitions, filets
   entre les termes, ni carte ni pastille), Votre message (formulaire 34em, champs papier, un seul bouton plein aligné à
   gauche), Questions (accordéons). En tête de « Nous écrire », qui répond : la ligne « C'est <prénom>, la fondatrice, qui vous
   répond. » en Cormorant italique --t-3 (--t-4 sous 768 px), sur toute la largeur du corps — sans portrait, à la demande de
   la fondatrice (si un portrait était de nouveau déclaré : 3 des 8 colonnes au bureau, 120 px au téléphone, ligne à côté). */
.contact__tete { display: grid; grid-template-columns: 4fr 8fr; gap: var(--rythme-1); align-items: start; }
.contact__tete h1 { font-size: var(--t-1); }
.contact__chapitres { margin-top: var(--rythme-2); }
.contact__chapitres .chapitre:last-child { padding-bottom: var(--e-6); }
.contact__qui { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 5fr); gap: var(--e-4) var(--rythme-1); align-items: end; }
.contact__qui-visuel { min-width: 0; max-width: 220px; }
.contact__qui-visuel .visuel { aspect-ratio: 4 / 5; }
.contact__qui-ligne { margin: 0; max-width: var(--largeur-texte); font-family: var(--police-display); font-weight: 400; font-size: var(--t-3); line-height: 1.18; }
/* Sans portrait (à la demande de la fondatrice) : la ligne seule, sur toute la largeur de la colonne ; en italique, la voix
   d'une citation (comme .maison__citation), pour qu'elle ne se lise pas comme un second titre à côté de « Nous écrire ». */
.contact__qui.contact__qui--sans-portrait { grid-template-columns: minmax(0, 1fr); }
.contact__qui--sans-portrait .contact__qui-ligne { font-style: italic; text-wrap: balance; }
/* Bureau : la ligne et le titre « Nous écrire » côte à côte partagent leur ligne de base. */
@media (min-width: 1024px) { .chapitre:has(> .chapitre__corps > .contact__qui--sans-portrait) > * { align-self: first baseline; } }
.contact-dl { display: grid; margin: 0; }
.contact-dl > div { display: grid; grid-template-columns: minmax(0, 4fr) minmax(0, 8fr); gap: var(--e-2) var(--e-4); align-items: baseline; padding-block: var(--e-3); border-top: var(--filet) solid var(--ligne); }
.contact-dl > div:last-child { border-bottom: var(--filet) solid var(--ligne); }
.contact-dl dt { font-family: var(--police-display); font-weight: 400; font-size: 1.625rem; line-height: 1.2; }
.contact-dl dd { margin: 0; font-size: .9375rem; line-height: 1.5; color: var(--encre-2); }
.contact-dl dd a { color: var(--encre); text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--or); }
.contact__form { display: grid; gap: var(--e-4); max-width: var(--largeur-texte); }
.contact__ligne { display: grid; grid-template-columns: 1fr 1fr; gap: var(--e-3); }
/* Champ piège du formulaire (serveur/contact.mjs) : hors de l'écran, hors du clavier et des lecteurs d'écran. */
.contact__piege { position: absolute; left: -10000px; top: auto; width: 1px; height: 1px; overflow: hidden; }
.contact__form { position: relative; }
.contact__form [data-contact-envoyer][aria-busy="true"] { cursor: progress; opacity: .72; }
/* L'envoi direct a échoué : le même message, prêt dans la messagerie de la cliente ou pour Instagram. */
.contact__repli { display: grid; gap: var(--e-3); padding: var(--e-4); border: var(--filet) solid var(--ligne); background: var(--papier); max-width: var(--largeur-texte); }
.contact__repli p { margin: 0; }
.contact__repli-liens { display: flex; flex-wrap: wrap; align-items: center; gap: var(--e-3) var(--e-4); }
/* Envoyé : la confirmation prend la place du formulaire (focus posé dessus pour le clavier et les lecteurs d'écran). */
.contact__merci { display: grid; gap: var(--e-3); max-width: var(--largeur-texte); outline: none; }
.contact__merci[hidden], .contact__repli[hidden], .contact__form[hidden] { display: none; }
.contact__merci p { margin: 0; }
.contact__merci > .lien, .contact__repli-liens > .lien { justify-self: start; width: auto; }
.contact__merci-titre { font-family: var(--police-display); font-size: var(--t-3); line-height: 1.18; }
.contact__form .bouton { justify-self: start; }
.contact__note { font-size: var(--t-petit); line-height: 1.5; color: var(--encre-2); }
.contact__note a { color: var(--encre); text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--or); }
@media (max-width: 1023px) { .contact__tete { grid-template-columns: 1fr; gap: var(--e-4); } }
@media (max-width: 767px) {
  .contact__qui { grid-template-columns: 120px minmax(0, 1fr); gap: var(--e-4); }
  .contact__qui-visuel { max-width: none; }
  .contact__qui-ligne { font-size: var(--t-4); }
}
@media (max-width: 599px) { .contact-dl > div { grid-template-columns: 1fr; gap: var(--e-1); } }
@media (max-width: 359px) { .contact__ligne { grid-template-columns: 1fr; } }

/* ---------- 404 (§ 4.8) : « 404 » en --numero, la rangée des cinq, un lien d'appel ----------
   La rangée porte .rail-objets (lot 2, § 3.3) : rail à défilement sous 768 px, cinq colonnes au-dessus. */
.introuvable { display: grid; gap: var(--e-4); padding-top: var(--rythme-2); padding-bottom: var(--e-6); }
.introuvable__numero { margin: 0; }
.introuvable h1 { font-size: var(--t-1); }
.introuvable__pied { margin-top: var(--e-4); }
.introuvable__rangee > * { min-width: 0; }
@media (min-width: 768px) { .introuvable__rangee { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--e-5) var(--e-4); margin-top: var(--e-3); } }
@media (min-width: 1024px) { .introuvable__rangee { grid-template-columns: repeat(5, minmax(0, 1fr)); gap: var(--e-4); } }

/* ---------- Pages légales (vues/legal.js, § 4.8) ----------
   Structure conservée : sommaire collant à gauche, sections numérotées par compteur CSS (ce sont des ARTICLES : la seule
   numérotation de rubriques du site), tableaux défilants ou repliés en fiches, marques « à compléter » visibles à l'écran
   comme à l'impression. Chiffres en --numero, lining-nums. Aucun data-revele : la page s'imprime entière. Entrée à
   --rythme-2, 48 px avant le pied (qui suit sans marge). */
.legal { padding-top: var(--rythme-2); padding-bottom: var(--e-6); }
.legal__tete { display: grid; gap: var(--e-3); max-width: var(--largeur-texte); margin-bottom: var(--rythme-1); }
.legal__tete h1 { font-size: var(--t-1); font-weight: 400; }
.legal__meta { display: flex; flex-wrap: wrap; gap: var(--e-2) var(--e-4); align-items: center; font-size: var(--t-petit); color: var(--encre-2); }
.legal__meta .lien { font-size: var(--t-petit); color: var(--encre); }
/* L'avertissement est une feuille de papier (la seule surface opaque admise), filet corail à gauche. */
.legal__avertissement { display: flex; align-items: flex-start; gap: var(--e-2); padding: var(--e-3) var(--e-4); border-left: 3px solid var(--corail); background: var(--papier); box-shadow: var(--ombre-contact); font-size: var(--t-petit); line-height: 1.5; }
.legal__avertissement .icone { flex: none; width: 16px; height: 16px; margin-top: 2px; color: var(--corail-texte); }
/* Page légale lue dans une autre langue que le français : la version française fait foi (langue.js, vues/legal.js). */
.legal__traduction { margin: var(--e-3) 0 0; padding: var(--e-3) var(--e-4); border-left: 3px solid var(--or); background: var(--papier); font-family: var(--police-ui); font-size: var(--t-petit); line-height: 1.5; color: var(--encre-2); }
.legal__corps { display: grid; grid-template-columns: 4fr 8fr; gap: var(--rythme-1); align-items: start; }
/* Les items de grille ont un minimum automatique égal à leur contenu : sans min-width: 0, un tableau
   ou une marque longue élargirait la colonne au-delà de l'écran au lieu de défiler ou de se replier. */
.legal__corps > *, .legal__sections > *, .legal__section > * { min-width: 0; max-width: 100%; }
.legal__sommaire { position: sticky; top: calc(var(--h-entete-defile) + var(--e-4)); display: grid; gap: var(--e-2); }
.legal__sommaire-titre { font-family: var(--police-display); font-style: italic; font-weight: 400; font-size: var(--t-etiquette); line-height: 1.3; color: var(--encre-2); margin: 0; }
.legal__sommaire ol { display: grid; counter-reset: sommaire; }
.legal__sommaire li { counter-increment: sommaire; }
/* Pas de transition sur la couleur : le contrôle de contraste (captures/qa-marbre/2-lisibilite.mjs) relit la couleur
   calculée aussitôt sa feuille de transparence levée, et lirait une valeur en cours d'interpolation. */
.legal__sommaire a { display: flex; gap: var(--e-2); padding: 6px 0; font-size: var(--t-petit); line-height: 1.5; color: var(--encre-2); }
.legal__sommaire a::before { content: counter(sommaire, decimal-leading-zero); flex: none; font-family: var(--police-display); font-size: var(--t-ui); color: var(--numero); font-variant-numeric: lining-nums tabular-nums; }
.legal__sommaire a:hover, .legal__sommaire a:focus-visible { color: var(--encre); }
.legal__sections { display: grid; gap: var(--e-6); max-width: var(--largeur-texte); counter-reset: section; }
.legal__section { display: grid; gap: var(--e-3); padding-top: var(--e-5); border-top: var(--filet) solid var(--ligne); counter-increment: section; }
.legal__section:focus { outline: none; }
.legal__section h2 { display: flex; gap: var(--e-3); align-items: baseline; font-size: var(--t-3); font-weight: 400; line-height: 1.18; letter-spacing: 0; }
.legal__section h2::before { content: counter(section, decimal-leading-zero); flex: none; font-family: var(--police-display); font-size: var(--t-4); color: var(--numero); font-variant-numeric: lining-nums tabular-nums; }
.legal__section p, .legal__section li { line-height: 1.6; }
.legal__section a { text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--or); }
.legal__liste { display: grid; gap: var(--e-2); padding-left: 1.2em; list-style: disc; }
.legal__etapes { display: grid; gap: var(--e-3); padding-left: 1.4em; list-style: decimal; font-variant-numeric: lining-nums tabular-nums; }
.legal__tableau { overflow-x: auto; }
.legal__tableau table { width: 100%; min-width: 480px; border-collapse: collapse; font-size: var(--t-petit); }
.legal__tableau th, .legal__tableau td { text-align: left; vertical-align: top; padding: var(--e-2) var(--e-3) var(--e-2) 0; border-bottom: var(--filet) solid var(--ligne); line-height: 1.5; }
.legal__tableau thead th { font-family: var(--police-ui); font-size: var(--t-petit); font-weight: 500; color: var(--encre-2); }
.legal__tableau tbody th { font-weight: 500; }
.legal__note { font-size: var(--t-petit); color: var(--encre-2); }
.legal__pied { display: grid; gap: var(--e-2); padding-top: var(--e-5); border-top: var(--filet) solid var(--ligne); font-size: var(--t-petit); color: var(--encre-2); }
.legal__autres { display: flex; flex-wrap: wrap; gap: var(--e-2) var(--e-3); }
.legal__autres a { color: var(--encre); }

/* Information que seule la fondatrice peut fournir : surlignée, jamais silencieuse. Une étiquette de papier (la seule
   surface opaque admise) bordée d'un pointillé or : l'ancien pointillé corail (--corail-texte) tombait sous le paragraphe
   et en faisait le pire pixel (encre 2,8:1 ; l'or tient 7,3:1 — contrôle 9, § 6.0). `display: inline` et `white-space:
   normal` sont explicites : un <mark> en inline-block ne se replierait pas et une marque longue déborderait d'un écran
   de 375 px. */
mark.a-completer { display: inline; white-space: normal; background: var(--papier); color: var(--encre); padding: 0 .35em; border-radius: 0; outline: 1px dashed var(--or); outline-offset: 1px; font-style: normal; font-weight: 500; overflow-wrap: anywhere; box-decoration-break: clone; -webkit-box-decoration-break: clone; }

@media (max-width: 1023px) { .legal__corps { grid-template-columns: 1fr; gap: var(--e-5); } .legal__sommaire { position: static; } }
/* Petit écran : chaque ligne du tableau devient une fiche, l'en-tête de colonne est répété devant la cellule
   (attribut data-entete posé par vues/legal.js) — plus rien à faire défiler latéralement. */
@media (max-width: 599px) {
  .legal__tableau table { min-width: 0; }
  .legal__tableau thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .legal__tableau tr { display: grid; gap: 6px; padding: var(--e-3) 0; border-bottom: var(--filet) solid var(--ligne); }
  .legal__tableau th, .legal__tableau td { display: block; padding: 0; border: 0; }
  .legal__tableau td::before { content: attr(data-entete); display: block; font-family: var(--police-ui); font-size: var(--t-petit); font-weight: 500; color: var(--encre-2); }
}
@media print {
  .legal { padding-top: 0; }
  .legal__corps { grid-template-columns: 1fr; }
  .legal__sommaire, .legal__meta .lien, .legal__autres, .legal__avertissement .icone { display: none !important; }
  .legal__section { break-inside: avoid; }
  .legal__tableau table { min-width: 0; }
  mark.a-completer { background: none; outline: 1px dashed var(--encre); }
}

/* ---------- Commande (§ 4.7) ----------
   Le balisage vit dans vues/commande.js (lot 4), sa feuille ici. Padding haut 48 px (24 sur téléphone), formulaire à
   34em, récapitulatif en FEUILLE DE PAPIER posée sur la pierre (--papier, --ombre-modale, filet --ligne, angles droits) ;
   sur téléphone le formulaire vient D'ABORD (plus d'`order`). paiement.css (lot 4) complète et l'emporte sur les
   sélecteurs de même poids : il est chargé après cette feuille. */
.commande { display: grid; grid-template-columns: 7fr 5fr; gap: var(--rythme-1); align-items: start; padding-top: var(--e-6); }
.commande__form { display: grid; gap: var(--e-5); max-width: var(--largeur-texte); }
.commande__groupe { display: grid; gap: var(--e-3); }
.commande__groupe h2 { font-size: var(--t-3); font-weight: 400; }
.commande__ligne { display: grid; grid-template-columns: 1fr 1fr; gap: var(--e-3); }
.commande__recap { position: sticky; top: calc(var(--h-entete-defile) + var(--e-4)); display: grid; gap: var(--e-4); padding: var(--e-5); background: var(--papier); border: var(--filet) solid var(--ligne); border-radius: 0; box-shadow: var(--ombre-modale); }
.commande__recap h2 { font-size: var(--t-3); font-weight: 400; }
.commande__recap .ligne-panier { grid-template-columns: 56px 1fr auto; padding: var(--e-2) 0; }
.commande__recap .ligne-panier .visuel { width: 56px; }
.commande__recap .ligne-panier__nom { font-size: 1rem; }
.commande__choix { display: grid; gap: var(--e-3); }
.commande__choix .case { padding: var(--e-3); border: var(--filet) solid var(--bordure-champ); border-radius: 0; }
.commande__choix .case:has(input:checked) { border-color: var(--profond); background: var(--papier); }
.commande__note { font-size: var(--t-petit); color: var(--encre-2); }
.commande__note a { text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--or); }
.confirmation { display: grid; gap: var(--e-4); max-width: 640px; padding: var(--rythme-2) 0 var(--e-6); }
.confirmation__numero { font-family: var(--police-ui); font-variant-numeric: lining-nums tabular-nums; color: var(--encre-2); }
@media (max-width: 1023px) { .commande { grid-template-columns: 1fr; } .commande__recap { position: static; } }
@media (max-width: 767px) { .commande { padding-top: var(--e-4); } }
@media (max-width: 599px) { .commande__ligne { grid-template-columns: 1fr; } }

/* ---------- Mon compte (#/compte, vues/compte.js) : le formulaire à gauche, la lettre, les commandes et les favoris à droite ---------- */
.compte { padding-block: var(--e-6); }
.compte__tete { display: grid; gap: var(--e-3); margin-bottom: var(--rythme-1); }
.compte__tete h1, .compte__tete .u-chapo { margin: 0; }
.compte__grille { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); gap: var(--rythme-1); align-items: start; }
.compte__form { display: grid; gap: var(--e-4); max-width: var(--largeur-texte); }
.compte__actions { display: grid; justify-items: start; }
.compte__note { margin-top: var(--e-4); }
.compte__cote { display: grid; gap: var(--e-5); }
.compte__bloc { display: grid; gap: var(--e-3); justify-items: start; padding-top: var(--e-3); border-top: var(--filet) solid var(--ligne); }
.compte__bloc p { margin: 0; }
.compte__bloc-titre { margin: 0; font-family: var(--police-display); font-size: var(--t-4); font-weight: 400; line-height: 1.25; }
.compte__code { font-family: var(--police-display); font-size: var(--t-4); font-weight: 400; color: var(--encre); font-variant-numeric: lining-nums; }
.compte .mes-commandes { margin-top: 0; }
@media (max-width: 1023px) { .compte__grille { grid-template-columns: 1fr; } }

/* src/css/innovations.css */
/* IVY JEWELS — innovations : l'eau de la section épinglée (canvas plein fond), gouttes réfractantes sur une photo,
   goutte qui vole vers le panier, visionneuse « stories », barre de lecture.
   Couche « composants » (ordre déclaré dans tokens.css, règle expliquée en tête de base.css).
   DA V2 « Pierre et Lumière » : aucune font-size, couleur, ombre ni durée hors jetons ; aucun rayon hors
   --rayon-pilule ; aucun texte posé sur une photo. */

@layer composants {

/* ---------- L'eau sur tout le fond de la section épinglée (composants/gouttes.js, monteFond) ----------
   Un canvas derrière le contenu de la section ; il est TRANSPARENT à l'étape 0 (les gouttes perlent sur la pierre) et
   peint la teinte de l'étape à partir de l'étape 1. Le cadre porte un masque vertical : la teinte s'efface sur 160 px en
   haut et en bas (64 sous 1024 px, où le texte commence plus près du bord), de sorte qu'aucune ligne horizontale ne
   sépare jamais la pierre du ciel — ni quand la section entre dans la fenêtre, ni quand elle en sort. Épinglée — c'est
   le cas courant, téléphone compris —, la section est l'écran et le canvas la remplit. Empilée (mouvement réduit, ou
   fenêtre trop basse pour la scène), la section fait plusieurs écrans : le canvas est alors COLLANT et haut comme
   l'écran, et le cadre absolu lui donne sa course. Aucun geste ne lui parvient (la section reçoit le pointerdown). */
.eau-fond__cadre {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  -webkit-mask-image: linear-gradient(transparent, #000 160px, #000 calc(100% - 160px), transparent);
  mask-image: linear-gradient(transparent, #000 160px, #000 calc(100% - 160px), transparent);
}
@media (max-width: 1023px) {
  .eau-fond__cadre {
    -webkit-mask-image: linear-gradient(transparent, #000 64px, #000 calc(100% - 64px), transparent);
    mask-image: linear-gradient(transparent, #000 64px, #000 calc(100% - 64px), transparent);
  }
}
.eau-fond { position: sticky; top: 0; display: block; width: 100%; height: 100%; background: transparent; }
.eau-section.epingle--empilee .eau-fond { height: 100svh; }

/* ---------- Gouttes réfractantes sur une photo (composants/gouttes.js, monte) ----------
   La figure qui porte les gouttes reçoit .a-gouttes et les gestes (glissement, tape) ; le canvas la recouvre. */
.gouttes { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 1; pointer-events: none; display: block; }
.a-gouttes { cursor: crosshair; pointer-events: auto; }

/* ---------- Goutte qui vole vers le panier (composants/vol-panier.js) ----------
   Une perle d'or de 16 px (--rayon-pilule), sans ombre : seuls les objets et le papier portent une ombre. */
.goutte-vol {
  position: fixed; left: 0; top: 0; z-index: calc(var(--z-toasts) + 1); width: 16px; height: 16px; pointer-events: none;
  background: radial-gradient(circle at 38% 32%, var(--or-2), var(--or) 55%, var(--or-3) 100%);
  border-radius: var(--rayon-pilule);
  will-change: transform, opacity;
}
.entete__action.est-recu { animation: recu var(--duree-3) var(--ease-luxe); }
.entete__action.est-recu .icone { color: var(--or-3); }
@keyframes recu { 0% { transform: scale(1); } 40% { transform: scale(1.25) rotate(-6deg); } 100% { transform: scale(1); } }

/* ---------- Visionneuse « stories » (composants/stories.js) ----------
   Une feuille de papier tenue au-dessus de la table (--papier, --ombre-modale, angles droits) sur le voile ; la photo
   en 4:5, sans rien dessus ; l'étiquette de la pièce sous la photo, sur le papier. Plein écran sous 768 px. */
.stories { position: fixed; inset: 0; z-index: var(--z-modale); display: grid; place-items: center; opacity: 0; transition: opacity var(--duree-2); }
.stories.est-ouverte { opacity: 1; }
.stories[hidden] { display: none; }
.stories__voile { position: absolute; inset: 0; background: var(--voile); }
.stories__cadre { position: relative; width: min(100%, 440px); height: min(100svh, 800px); background: var(--papier); color: var(--encre); box-shadow: var(--ombre-modale); overflow: hidden; display: grid; grid-template-rows: auto auto 1fr auto; border-radius: 0; }
.stories__barres { display: flex; gap: var(--e-1); padding: calc(var(--e-2) + var(--safe-haut)) var(--e-2) 0; }
.stories__barre { flex: 1; height: 2px; background: var(--ligne); overflow: hidden; }
.stories__barre i { display: block; height: 100%; background: var(--encre); transform-origin: left; transform: scaleX(0); }
.stories__tete { display: flex; align-items: center; gap: var(--e-2); padding: var(--e-2) var(--e-2) var(--e-2) var(--e-3); font-family: var(--police-ui); font-size: var(--t-petit); line-height: 1.5; }
.stories__avatar { display: grid; place-items: center; width: 32px; height: 32px; border-radius: var(--rayon-pilule); background: var(--fond); color: var(--or-3); border: var(--filet) solid var(--or); }
.stories__compte { font-weight: 500; }
.stories__quand { color: var(--encre-2); }
.stories__fermer { margin-left: auto; color: var(--encre); }
.stories__fermer:hover { background: var(--chauffe); color: var(--encre); }
.stories__scene { position: relative; overflow: hidden; }
.stories__scene .visuel { position: absolute; inset: 0; height: 100%; aspect-ratio: auto; }
.stories__scene { background: var(--fond-2); }
.stories__scene .visuel img { object-fit: contain; }
.stories__zone { position: absolute; top: 80px; bottom: 90px; width: 40%; background: transparent; cursor: pointer; }
.stories__zone--prec { left: 0; }
.stories__zone--suiv { right: 0; }
.stories__pied { padding: var(--e-3) var(--e-3) calc(var(--e-3) + var(--safe-bas)); }
.stories__etiquette { display: grid; grid-template-columns: 44px 1fr auto; gap: var(--e-3); align-items: center; padding-top: var(--e-3); border-top: var(--filet) solid var(--ligne); color: var(--encre); }
.stories__etiquette .visuel { width: 44px; }
.stories__nom { display: block; font-family: var(--police-display); font-size: var(--t-4); line-height: 1.25; }
.stories__etiquette .u-petit { color: var(--encre-2); }
.stories__etiquette .lien--cta__fleche { color: var(--encre-2); }
@media (max-width: 767px) { .stories { place-items: stretch; } .stories__cadre { width: 100%; height: 100%; } }
@media (orientation: landscape) and (max-height: 520px) { .stories__cadre { width: min(100%, 520px); height: 100%; grid-template-rows: auto auto 1fr auto; } .stories__scene .visuel img { object-fit: contain; } }

/* ---------- Barre de lecture ----------
   Repli : --progression (nombre enregistré dans tokens.css, donc transitionnable) posée par script sur la barre.
   Avec les animations pilotées par le défilement, plus aucun script : la barre suit le document. Elle ne bouge
   qu'avec le geste de lecture, ce qui la dispense de l'exception de mouvement réduit. */
.lecture { position: fixed; left: 0; right: 0; top: var(--safe-haut); z-index: calc(var(--z-entete) + 2); height: 2px; background: var(--or); transform-origin: left; transform: scaleX(var(--progression, 0)); transition: --progression var(--duree-1) linear; pointer-events: none; }
@supports (animation-timeline: scroll()) {
  .lecture { animation: lecture linear both; animation-timeline: scroll(root); transform: none; transition: none; }
  @keyframes lecture { from { transform: scaleX(0); } to { transform: scaleX(1); } }
}

/* ---------- Motion réduite ---------- */
@media (prefers-reduced-motion: reduce) { .goutte-vol { display: none; } .stories__barre i { transform: scaleX(1); } }
html.motion-reduite .goutte-vol { display: none; }

/* ---------- Contraste forcé ---------- */
@media (forced-colors: active) {
  .stories__cadre, .stories__etiquette { border: 1px solid CanvasText; }
  .stories__barre { border: 1px solid CanvasText; }
  .stories__barre i, .lecture { background: Highlight; forced-color-adjust: none; }
  .eau-fond__cadre { display: none; }
}

}

/* src/css/paiement.css */
/* IVY JEWELS — le panier et le tunnel (lot 4, DA V2 § 3.6, § 4.7, § 4.8) : tunnel de commande (stepper d'une ligne,
   formulaire papier, récapitulatif en feuille et sa barre repliée mobile, bouton collant), confirmation (deux gestes,
   un seul bouton plein), feuille « Paiement » posée sur la pierre, historique « Mes commandes », favoris.
   Le tiroir (.tiroir, .panier__*), les boutons, le stepper et le rail sont stylés par composants.css (contrats § 5.8,
   lot 2) : le markup vit dans composants/panier.js et vues/commande.js, rien n'est redéclaré ici.

   Trois matières, aucune quatrième : la page est la pierre, tout ce qu'on manipule est du papier (--papier), les
   photos sont des objets. Aucune font-size, couleur, ombre ni durée hors jetons ; aucune capitale ; --rayon: 0.
   Feuille SANS couche (règle en tête de base.css) : elle garde sa priorité naturelle sur composants.css et pages.css —
   les quelques règles de pages.css qui visent encore .commande (grille 7/5, .commande__recap) sont recouvertes ici,
   au même sélecteur, par ordre de chargement. */

/* ==================================================================
   Tunnel de commande (§ 4.7)
   ================================================================== */

/* La vue n'a pas de héros : rien ne la force à 60svh, le pied suit le dernier contenu à ≤ 64 px (§ 2.4). */
main.vue--commande, main.vue--favoris { min-height: 0; }
.commande__page { padding-bottom: var(--e-6); }

/* Grille 7 / 5 : la tête (stepper) sur toute la largeur, le formulaire à gauche, la feuille à droite. */
.commande { display: grid; grid-template-columns: 7fr 5fr; gap: var(--rythme-1) var(--rythme-1); align-items: start; padding-top: var(--e-6); }
.commande > * { min-width: 0; }
.commande__tete { grid-column: 1 / -1; grid-row: 1; }
.commande__form { grid-column: 1; grid-row: 2; display: grid; gap: var(--e-5); max-width: var(--largeur-texte); }
.commande__recap { grid-column: 2; grid-row: 2; }

/* Stepper d'une ligne à quatre libellés « 1 Panier · 2 Coordonnées · 3 Vérification · 4 Confirmation » : Jost 13 px
   bas-de-casse, chiffres tabulaires (§ 3.5), sans graisse ; ni cercle, ni pastille, ni fond. L'étape courante en encre
   (aria-current="step"), les autres en encre-2 ; le séparateur « · » est un ::before que composants.css éteint sur
   .stepper__etape et qu'on rallume ici. Une étape passée rejouable est un bouton .lien (base.css : filet d'encre qui
   se rétracte au survol), en encre-2 comme ses voisines. Le h1 des étapes 2 et 3 est visuellement caché
   (.u-visuellement-cache, base.css) : la liste est ce qu'on voit, le titre ce qu'un lecteur d'écran lit. */
.stepper--commande {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 var(--e-2); margin: 0; padding: 0; list-style: none;
  font-family: var(--police-ui); font-size: var(--t-petit); font-weight: 400; line-height: 1.5; letter-spacing: 0; text-transform: none;
  color: var(--encre-2); font-variant-numeric: lining-nums tabular-nums;
}
.stepper--commande .stepper__etape { display: inline-flex; gap: var(--e-1); align-items: baseline; margin: 0; padding: 0; font-weight: 400; color: var(--encre-2); }
.stepper--commande .stepper__etape + .stepper__etape::before { content: "·"; margin-right: var(--e-1); }
.stepper--commande .stepper__etape.est-active { color: var(--encre); font-weight: 400; }
.stepper--commande .stepper__numero { margin: 0; font-weight: 400; color: inherit; font-variant-numeric: lining-nums tabular-nums; }
.stepper--commande .stepper__sur { display: none; }
.stepper--commande .stepper__retour { display: inline-flex; gap: var(--e-1); align-items: baseline; font: inherit; line-height: inherit; color: inherit; }
.stepper--commande .stepper__retour:hover, .stepper--commande .stepper__retour:focus-visible { color: var(--encre); }
/* Téléphone : les quatre libellés numérotés mesurent 348 px en Jost 13 px, soit la largeur utile d'un écran de 390 px
   à 2 px près ; sous 480 px les chiffres s'effacent (« Panier · Coordonnées · Vérification · Confirmation », 300 px,
   le h1 caché garde « Étape 2 sur 4 »). Sous 380 px (à 360 px, 305 px utiles, ils ne tiennent qu'à 3 px près), la
   ligne se réduit à « 2 / 4 · Coordonnées » : seule l'étape courante reste, avec son compte ; un retour reste possible
   par « Modifier le panier » et « Modifier ». */
@media (max-width: 479px) { .stepper--commande .stepper__numero { display: none; } }
@media (max-width: 379px) {
  .stepper--commande .stepper__etape:not(.est-active) { display: none; }
  .stepper--commande .stepper__etape + .stepper__etape::before { content: none; }
  .stepper--commande .stepper__numero, .stepper--commande .stepper__sur { display: inline; }
}

/* Formulaire : groupes, rangées (Prénom | Nom et NPA | Localité dès 360 px, Rue + Numéro en 3fr 1fr), notes. */
.commande__groupe { display: grid; gap: var(--e-3); }
.commande__legende { margin: 0 0 var(--e-1); font-family: var(--police-display); font-size: var(--t-4); font-weight: 400; line-height: 1.25; }
.commande__rangee { display: grid; grid-template-columns: 1fr; gap: var(--e-3); }
@media (min-width: 360px) {
  .commande__rangee { grid-template-columns: 1fr 1fr; row-gap: 0; }
  .commande__rangee--rue { grid-template-columns: minmax(0, 3fr) minmax(9.5rem, 1fr); }
  /* Sous-grille : libellés, champs, notes et erreurs des deux colonnes sur les mêmes lignes — un libellé qui se
     replie (« Numéro (facultatif) » sur téléphone) ou la note « Pays : Suisse » sous un seul champ n'étire ni ne
     décale le champ voisin. Les écarts sont des marges : une ligne vide ne mesure rien. */
  .commande__rangee > .champ { grid-row: span 4; grid-template-rows: subgrid; row-gap: 0; align-content: start; }
  .commande__rangee > .champ > .champ__libelle { align-self: end; margin-bottom: var(--e-2); }
  .commande__rangee > .champ > .commande__pays, .commande__rangee > .champ > .champ__erreur { margin-top: var(--e-2); }
}
.champ--court { max-width: 12rem; }
.commande__pays { margin: 0; font-family: var(--police-ui); font-size: .75rem; line-height: 1.5; color: var(--encre-2); }
.commande__note { margin: 0; font-family: var(--police-ui); font-size: var(--t-petit); line-height: 1.5; color: var(--encre-2); max-width: var(--largeur-texte); }
.commande__note a { text-decoration: none; }
.commande__note strong { color: var(--encre); }
.commande__note--attente { color: var(--encre); }
.commande__reglement { margin: 0; }

/* Choix exclusifs du tunnel (vues/commande.js : pays, mode d'envoi, moyen de paiement) : une ligne à filet par option,
   jamais une boîte — bouton radio natif (accent --profond), nom Jost 14 px, détail 13 px encre-2, prix aligné à droite.
   La ligne cochée prend un filet or et le fond papier : on lit le choix d'un coup d'œil. Cible tactile ≥ 48 px. */
.choix { display: grid; margin: 0; }
.choix__option { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; column-gap: var(--e-3); min-height: 48px; padding: var(--e-3) var(--e-2); border-top: var(--filet) solid var(--ligne); border-radius: 0; cursor: pointer; transition: background-color var(--duree-1) ease, border-color var(--duree-1) ease; }
.choix__option:last-child { border-bottom: var(--filet) solid var(--ligne); }
.choix__option:has(input:checked) { background: var(--papier); border-top-color: var(--or); }
.choix:not(.choix--ligne) > .choix__option:has(input:checked) + .choix__option { border-top-color: var(--or); }
.choix__option:has(input:checked):last-child { border-bottom-color: var(--or); }
.choix__option:has(input:focus-visible) { outline: 2px solid var(--focus); outline-offset: -2px; }
.choix__option input { margin: 0; }
.choix__corps { display: grid; gap: 2px; min-width: 0; }
.choix__nom { font-family: var(--police-ui); font-size: var(--t-ui); line-height: 1.4; color: var(--encre); }
.choix__option:has(input:checked) .choix__nom { font-weight: 500; }
.choix__detail { font-family: var(--police-ui); font-size: var(--t-petit); line-height: 1.5; color: var(--encre-2); }
.choix__prix { font-family: var(--police-ui); font-size: var(--t-ui); line-height: 1.4; color: var(--encre); font-variant-numeric: lining-nums tabular-nums; white-space: nowrap; }
.choix__prix--offert { color: var(--succes); }
/* Pays : deux options côte à côte, sans détail ni prix. */
.choix--ligne { grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr)); gap: 0 var(--e-3); }
.choix--ligne .choix__option { grid-template-columns: auto minmax(0, 1fr); border-bottom: var(--filet) solid var(--ligne); }
.choix--ligne .choix__option:has(input:checked) { border-bottom-color: var(--or); }
/* Le choix du paiement et les trois temps qui le suivent : deux ensembles distincts, jamais deux filets collés. */
.choix[data-moyens] { margin-bottom: var(--e-5); }
/* Moyens de paiement : les marques à droite du nom (Visa et Mastercard côte à côte pour la carte) ;
   sur téléphone, sous le nom, pour ne jamais serrer le texte. */
.choix--moyens .choix__option { --hauteur-logo: 24px; }
.choix__logos { justify-self: end; display: flex; }
@media (max-width: 479px) {
  .choix--moyens .choix__option { grid-template-rows: auto auto; --hauteur-logo: 22px; }
  .choix--moyens .choix__option input { grid-row: 1 / span 2; }
  .choix--moyens .choix__logos { grid-column: 2; grid-row: 2; justify-self: start; margin-top: var(--e-2); }
}
.section-paiement__moyen { display: flex; flex-wrap: wrap; align-items: center; gap: var(--e-2); --hauteur-logo: 22px; }
.commande__champ-groupe { display: grid; gap: var(--e-2); margin-top: var(--e-3); }
.commande__champ-groupe > .champ__libelle { margin: 0; }
@media (forced-colors: active) { .choix__option:has(input:checked) { outline: 2px solid Highlight; outline-offset: -2px; } }
@media (prefers-reduced-motion: reduce) { .choix__option { transition: none; } }
.commande__conditions { gap: var(--e-2); }

/* Le bouton plein, aligné à gauche, jamais étiré ; le libellé du clic qui passe la commande porte le montant et
   l'obligation de payer (long) : il se replie sur deux lignes plutôt que d'élargir la page. */
.commande__action { display: grid; justify-items: start; }
.commande__commander { white-space: normal; text-align: left; padding-block: var(--e-12); }

/* Vérification : des blocs à filet, jamais des boîtes ; « Modifier » en lien 13 px. */
.commande__bloc { display: grid; gap: var(--e-2); padding-top: var(--e-3); border-top: var(--filet) solid var(--ligne); border-radius: 0; }
.commande__bloc-tete { display: flex; align-items: baseline; justify-content: space-between; gap: var(--e-3); }
.commande__bloc-titre { margin: 0; font-family: var(--police-display); font-size: var(--t-4); font-weight: 400; line-height: 1.25; }
.commande__bloc-modifier { font-family: var(--police-ui); font-size: var(--t-petit); color: var(--encre-2); }
.commande__adresse { margin: 0; line-height: 1.5; }

/* Règlement en trois temps (vues/commande.js blocReglement) : chiffres Cormorant or, titre Jost 500, texte 13 px,
   sur filets gravés — le même geste que les étapes de la garantie (pages.css .etapes-garantie). */
.commande__etapes-reglement { list-style: none; margin: var(--e-2) 0 var(--e-3); padding: 0; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--e-4); }
.commande__etapes-reglement li { display: grid; align-content: start; gap: var(--e-1); padding-top: var(--e-12); border-top: var(--filet) solid var(--ligne); font-family: var(--police-ui); font-size: var(--t-petit); line-height: 1.5; color: var(--encre-2); }
.commande__etapes-reglement .numero { font-size: var(--t-4); line-height: 1; }
.commande__etapes-reglement strong { color: var(--encre); font-weight: 500; font-size: var(--t-ui); line-height: 1.4; }
@media (max-width: 767px) { .commande__etapes-reglement { grid-template-columns: 1fr; gap: var(--e-3); } }

/* Code promo : un résumé repliable « Vous avez un code promo ? », un champ et « Appliquer » sur une ligne. */
.commande__code { display: grid; gap: var(--e-2); }
.commande__code-resume { cursor: pointer; font-family: var(--police-ui); font-size: var(--t-ui); line-height: 1.4; color: var(--encre); width: max-content; text-decoration: underline; text-decoration-color: var(--or); text-underline-offset: 4px; }
.commande__code-details[open] .commande__code-resume { margin-bottom: var(--e-3); }
.commande__code-champ { display: flex; gap: var(--e-2); align-items: stretch; max-width: 26rem; }
.commande__code-champ input { flex: 1; min-width: 0; min-height: 48px; letter-spacing: .01em; font-variant-numeric: lining-nums tabular-nums; }
.commande__code-bouton { min-height: 48px; padding-inline: var(--e-4); flex: none; }
.commande__code-actif { margin: 0; font-family: var(--police-ui); font-size: var(--t-ui); line-height: 1.5; color: var(--succes); }
.commande__code-actif .lien { margin-left: var(--e-2); color: var(--encre-2); font-size: var(--t-petit); }
.commande__code-oublie { justify-self: start; margin-top: var(--e-2); font-size: var(--t-petit); color: var(--encre-2); }
.commande__total-ligne--remise { color: var(--succes); }

/* « Continuer mes achats » : sous la colonne de gauche sur écran large, APRÈS le règlement sur téléphone et tablette. */
.confirmation__continuer--etroit { display: none; }

/* Récapitulatif : une feuille de papier posée sur la pierre (--papier, --ombre-modale, filet 1 px, angles droits),
   collante sous l'en-tête. La barre repliée n'existe que sous 1024 px. */
.commande__recap {
  position: sticky; top: calc(var(--h-entete-defile) + var(--e-4));
  display: block; padding: var(--e-5); margin: 0;
  background: var(--papier); color: var(--encre); border: var(--filet) solid var(--ligne); border-radius: 0; box-shadow: var(--ombre-modale);
}
.commande__recap-barre { display: none; }
.commande__recap-corps { display: grid; gap: var(--e-4); }
.commande__recap-titre { margin: 0; font-family: var(--police-display); font-size: var(--t-3); font-weight: 400; line-height: 1.18; letter-spacing: 0; }
.commande__articles { display: grid; margin: 0; padding: 0; list-style: none; }
/* Lignes de 72 px : vignette 48 × 60, nom Cormorant 20 px, méta 13 px, prix tabulaire à droite. */
.commande__article { display: grid; grid-template-columns: 48px minmax(0, 1fr) auto; gap: var(--e-3); align-items: center; min-height: 72px; padding: var(--e-1) 0; border-bottom: var(--filet) solid var(--ligne); }
.commande__article .visuel { width: 48px; }
.commande__article-infos { display: grid; gap: 2px; min-width: 0; }
.commande__article-nom { font-family: var(--police-display); font-size: 1.25rem; line-height: 1.2; }
.commande__article-meta { font-family: var(--police-ui); font-size: var(--t-petit); line-height: 1.5; color: var(--encre-2); }
.commande__totaux { display: grid; gap: var(--e-2); font-family: var(--police-ui); font-size: var(--t-ui); }
.commande__total-ligne { display: flex; justify-content: space-between; gap: var(--e-3); margin: 0; }
.commande__total-ligne--total { font-size: 1rem; font-weight: 500; padding-top: var(--e-2); border-top: var(--filet) solid var(--ligne); }
.commande__total-ligne--total .prix { font-weight: 500; }
.commande__recap-note { color: var(--encre); }

/* Panier vide, commande introuvable. */
.commande__vide { display: grid; gap: var(--e-3); justify-items: start; padding-top: var(--e-6); max-width: var(--largeur-texte); }
.commande__vide-titre { margin: 0; font-size: var(--t-1); line-height: 1.06; letter-spacing: -.01em; }
.commande__vide .commande__note { margin-bottom: var(--e-2); }

/* ==================================================================
   Confirmation (étape 4) : deux gestes, un seul bouton plein, la feuille « Paiement » à droite
   ================================================================== */

.commande--confirmation { grid-template-columns: 7fr 5fr; }
.confirmation--commande {
  grid-column: 1; grid-row: 2;
  display: grid; gap: var(--e-4); justify-items: start; text-align: left;
  max-width: var(--largeur-texte); margin: 0; padding: 0;
}
.confirmation__paiement { grid-column: 2; grid-row: 2; align-self: start; }
.confirmation__numero { margin: 0; font-family: var(--police-ui); font-size: var(--t-petit); line-height: 1.5; letter-spacing: 0; text-transform: none; color: var(--encre-2); font-variant-numeric: lining-nums tabular-nums; }
.confirmation__titre { margin: 0; font-size: var(--t-1); line-height: 1.06; letter-spacing: -.01em; }
.confirmation--commande .u-chapo { margin: 0; }
.confirmation__bloc { display: grid; gap: var(--e-3); width: 100%; justify-items: start; padding-top: var(--e-4); border-top: var(--filet) solid var(--ligne); }
.confirmation__sous-titre { display: flex; align-items: baseline; gap: var(--e-3); margin: 0; font-family: var(--police-display); font-size: var(--t-3); font-weight: 400; line-height: 1.18; letter-spacing: 0; }
.confirmation__sous-titre .numero { font-size: 1.125rem; }
.confirmation__actions { display: grid; gap: var(--e-3); justify-items: start; width: 100%; }
.confirmation__liens { display: flex; flex-wrap: wrap; gap: var(--e-2) var(--e-4); margin: 0; font-family: var(--police-ui); font-size: var(--t-petit); line-height: 1.5; color: var(--encre-2); }
.confirmation__liens .lien { color: var(--encre-2); }
.confirmation__liens .lien.est-succes { color: var(--succes); }
.confirmation__texte { display: grid; gap: var(--e-2); width: 100%; }
.confirmation__texte .champ { width: 100%; }
.dm__texte { width: 100%; min-height: 140px; font-family: var(--police-ui); font-size: var(--t-petit); line-height: 1.5; }
.confirmation__signature { margin: 0; font-family: var(--police-display); font-style: italic; font-size: var(--t-etiquette); line-height: 1.3; color: var(--encre-2); }

/* ==================================================================
   Feuille « Paiement » de la confirmation : une feuille de papier posée sur la pierre (§ 4.7) —
   --papier, --ombre-modale, filet, angles droits. Payé par, date, commande, référence ; ou ce qu'il reste à faire.
   ================================================================== */

.section-paiement {
  display: grid; gap: var(--e-4); width: 100%; min-width: 0; text-align: left;
  padding: var(--e-5);
  background: var(--papier); color: var(--encre); border: var(--filet) solid var(--ligne); border-radius: 0; box-shadow: var(--ombre-modale);
}
.section-paiement__tete { display: grid; gap: var(--e-2); }
.section-paiement__titre { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--e-2) var(--e-3); margin: 0; font-family: var(--police-display); font-size: var(--t-3); font-weight: 400; line-height: 1.18; letter-spacing: 0; text-transform: none; color: var(--encre); }
.section-paiement__titre .numero { font-size: 1.125rem; }
.section-paiement__titre .prix { font-size: 1.25rem; font-weight: 500; }
.section-paiement__note, .section-paiement__aide { margin: 0; font-family: var(--police-ui); font-size: var(--t-petit); line-height: 1.5; color: var(--encre-2); }
.section-paiement__infos { display: grid; gap: var(--e-2); margin: 0; font-family: var(--police-ui); font-size: var(--t-ui); line-height: 1.4; }
.section-paiement__infos > div { display: grid; gap: 2px; }
.section-paiement__infos dt { font-size: var(--t-petit); font-weight: 400; color: var(--encre-2); }
.section-paiement__infos dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
/* Référence de paiement : caractères à recopier un à un, chiffres alignés. */
.section-paiement__code { font-variant-numeric: lining-nums tabular-nums; letter-spacing: .02em; overflow-wrap: anywhere; }

/* ==================================================================
   Historique « Mes commandes » : des lignes à filets, sans boîte
   ================================================================== */

.mes-commandes { display: grid; gap: var(--e-3); max-width: var(--largeur-texte); margin: var(--rythme-1) 0 0; padding-top: var(--e-4); border-top: var(--filet) solid var(--ligne); text-align: left; }
.mes-commandes__titre { margin: 0; font-family: var(--police-display); font-size: var(--t-4); font-weight: 400; line-height: 1.25; }
.mes-commandes__liste { display: grid; margin: 0; padding: 0; list-style: none; }
.mes-commandes__ligne {
  display: grid; grid-template-columns: auto 1fr auto auto; gap: var(--e-2) var(--e-4); align-items: baseline;
  padding: var(--e-3) 0; border-bottom: var(--filet) solid var(--ligne);
  font-family: var(--police-ui); font-size: var(--t-ui); line-height: 1.4; color: var(--encre-2); transition: color var(--duree-1);
}
.mes-commandes__ligne:hover, .mes-commandes__ligne:focus-visible { color: var(--encre); }
.mes-commandes__numero { color: var(--encre); font-variant-numeric: lining-nums tabular-nums; }
.mes-commandes__statut { font-size: var(--t-petit); }

/* ==================================================================
   Favoris (§ 4.8) — la vue n'a pas de feuille propre : ses quelques règles vivent ici
   ================================================================== */

.favoris { padding-top: var(--rythme-2); padding-bottom: var(--e-6); }
.favoris__tete .section__tete { margin-bottom: var(--e-5); }
.favoris__actions { display: flex; flex-wrap: wrap; gap: var(--e-3) var(--e-6); margin-bottom: var(--e-6); }
/* La grille porte aussi .rail-objets (lot 2, un flex à toutes les largeurs) : bureau et tablette = la grille de cartes
   standard (.grille-produits, 4 colonnes) ; sous 768 px = le rail, bord à bord dans le conteneur. */
@media (min-width: 768px) { .favoris__grille { display: grid; padding-inline: 0; overflow: visible; } .favoris__grille > * { flex: none; } }
@media (max-width: 767px) { .favoris__grille { margin-inline: calc(-1 * max(var(--marge-page), var(--safe-gauche))) calc(-1 * max(var(--marge-page), var(--safe-droite))); padding-inline: max(var(--marge-page), var(--safe-gauche)) max(var(--marge-page), var(--safe-droite)); } }
.favoris__vide { display: grid; gap: var(--e-3); justify-items: start; max-width: var(--largeur-texte); }
.favoris__vide-titre { margin: 0; font-family: var(--police-display); font-size: var(--t-3); font-weight: 400; line-height: 1.18; }
.favoris__vide .commande__note { margin-bottom: var(--e-2); }

/* ==================================================================
   Mobile portrait et tablette (< 1024 px)
   ================================================================== */

@media (max-width: 1023px) {
  /* Rythme de 24 (§ 4.7, « 24 mobile ») : barre du récapitulatif → 24 → stepper → 24 → formulaire ; sans barre
     (confirmation), le stepper est à 24 px sous l'en-tête. */
  .commande, .commande--confirmation { grid-template-columns: 1fr; row-gap: 0; column-gap: 0; padding-top: 0; }
  .commande__tete, .commande__form, .commande__recap, .confirmation--commande, .confirmation__paiement { grid-column: auto; grid-row: auto; }
  .commande__tete { padding-block: var(--e-4); }
  .confirmation__paiement { margin-top: var(--e-5); }
  .confirmation__continuer--large { display: none; }
  .confirmation__continuer--etroit { display: inline-flex; justify-self: start; margin-top: var(--e-5); }
  /* Le formulaire d'abord : le récapitulatif n'est plus qu'une barre de papier de 56 px sous l'en-tête, bord à bord,
     qui se déplie en place (pages.css:178 posait déjà order: -1 sur la feuille entière ; ici c'est la barre). */
  .commande__recap {
    order: -1; position: static; padding: 0;
    margin-inline: calc(-1 * max(var(--marge-page), var(--safe-gauche))) calc(-1 * max(var(--marge-page), var(--safe-droite)));
    border: 0; border-bottom: var(--filet) solid var(--ligne);
  }
  .commande__recap-barre {
    display: flex; align-items: center; justify-content: space-between; gap: var(--e-3); width: 100%; min-height: 56px;
    padding: 0 max(var(--marge-page), var(--safe-droite)) 0 max(var(--marge-page), var(--safe-gauche));
    font-family: var(--police-ui); font-size: var(--t-ui); line-height: 1.4; color: var(--encre); text-align: left;
    font-variant-numeric: lining-nums tabular-nums;
  }
  .commande__recap-chevron { flex: none; transition: transform var(--duree-2) var(--ease-luxe); }
  .commande__recap:not(.est-replie) .commande__recap-chevron { transform: rotate(180deg); }
  .commande__recap.est-replie .commande__recap-corps { display: none; }
  .commande__recap-corps { padding: var(--e-2) max(var(--marge-page), var(--safe-droite)) var(--e-4) max(var(--marge-page), var(--safe-gauche)); border-top: var(--filet) solid var(--ligne); }
  /* Bouton collant en bas dès le premier champ touché (classe posée par vues/commande.js) : une bande de papier. */
  .commande__action.est-collant {
    position: sticky; bottom: 0; z-index: 2;
    margin-inline: calc(-1 * max(var(--marge-page), var(--safe-gauche))) calc(-1 * max(var(--marge-page), var(--safe-droite)));
    padding: var(--e-12) max(var(--marge-page), var(--safe-droite)) calc(var(--e-12) + var(--safe-bas)) max(var(--marge-page), var(--safe-gauche));
    background: var(--papier); border-top: var(--filet) solid var(--ligne);
  }
  .confirmation__paiement { width: 100%; }
}
@media (max-width: 599px) {
  .mes-commandes__ligne { grid-template-columns: 1fr auto; }
}

/* ==================================================================
   Mouvement réduit
   ================================================================== */

@media (prefers-reduced-motion: reduce) {
  .commande__recap-chevron, .mes-commandes__ligne { transition: none; }
}

/* ==================================================================
   Contraste forcé (Windows « Contraste élevé ») : les feuilles de papier sont bordées, les filets gardent une couleur
   système (le tiroir, les boutons et le stepper sont couverts par composants.css).
   ================================================================== */

@media (forced-colors: active) {
  .commande__recap, .section-paiement, .commande__action.est-collant { border: 1px solid CanvasText; }
  .commande__article, .commande__bloc, .confirmation__bloc, .mes-commandes__ligne, .choix__option, .commande__total-ligne--total { border-color: CanvasText; }
}

/* src/css/impression.css (media : print) */
@media print {
/* IVY JEWELS — impression : pages légales, fiches, page L'eau, confirmation de commande avec sa section paiement.
   Hors couche (règle en tête de base.css) et liée en DERNIER dans index.html avec media="print" : ces déclarations
   passent devant toutes les couches et devant les feuilles de vues. Les !important ne servent qu'à passer devant
   les styles en ligne (révélations de motion.js, LQIP, --carrousel-item) et devant les rares !important des vues.
   Noir sur blanc, rien d'interactif, adresses des liens en clair, feuille « Paiement » bordée.
   Tailles : les jetons de tokens.css (DA V2 § 6.0), jamais de valeur en points. */

@media print {
  @page { size: A4; margin: 16mm 14mm 18mm; }

  /* ---------- Page ---------- */
  html, body { background: #fff !important; color: #000 !important; overflow: visible !important; scrollbar-gutter: auto; }
  body { min-height: 0; font-size: var(--t-corps); line-height: 1.45; }
  .vue { min-height: 0; }
  .u-conteneur { max-width: none; padding-inline: 0; }
  .section, .section--serree, .section--large { padding-block: 6mm; }
  .page-tete, .legal, .produit, .commande, .commande__tete, .favoris { padding-top: 0 !important; }
  h1, h2, h3, h4 { color: #000; break-after: avoid; }
  p, li, dd { orphans: 3; widows: 3; }
  a { color: #000 !important; text-decoration: underline; text-decoration-color: #000; background-image: none !important; }
  /* Adresses en clair : liens sortants des pages légales, des textes et du contact (jamais sur les boutons). */
  .legal a[href^="http"]:not(.bouton)::after, .texte a[href^="http"]:not(.bouton)::after, .contact a[href^="http"]:not(.bouton)::after,
  .accordeon__contenu a[href^="http"]::after, .legal a[href^="mailto:"]::after {
    content: " (" attr(href) ")"; font-size: var(--t-petit); font-style: normal; word-break: break-all;
  }
  .surtitre, .ligne-matiere, .u-petit, .u-secondaire, .champ__libelle, .fil-ariane, .legal__note, .legal__meta,
  .hero-profond__matiere, .hero-profond__texte .u-chapo, .bloc-eau__texte, .bloc-eau--sombre .bloc-eau__texte,
  .reassurance__texte, .carte-produit__soustitre, .caracteristiques dt, .confirmation__numero,
  .commande__note, .commande__pays, .stepper--commande, .section-paiement__aide, .numero, .regle__etiquette, .cartel { color: #000 !important; }
  .icone { stroke: #000 !important; }
  .pastille-icone { background: none !important; border-color: #000 !important; color: #000 !important; }

  /* ---------- Rien d'interactif sur le papier ---------- */
  .entete, .bandeau, .pied, .panier, .tiroir, .drawer, .toasts, .preloader, .achat-fixe, .lecture, .lien-evitement, .menu-mobile,
  .recherche, .modale, .lightbox, .stories, .goutte-vol, .gouttes, .filtres, .chips, .carte-produit__favori,
  .carte-produit__ajout, .carrousel__nav, .galerie__nav, .galerie__filtres, .tour360, .produit__cta, .produit__titre-actions,
  .produit__instagram, .bouton, .bouton--contour, .boutons, .quantite, .panier__stepper, .panier__retirer, .ligne-panier__retirer,
  .pluie, .eau-section__gouttes, .hero__voile, .hero__rail, .vu-sur,
  .commande__action, .commande__recap-barre, .commande__recap-chevron, .commande__bloc-modifier, [data-tunnel-panier], .confirmation__liens,
  .favoris__actions, video, form button, .accordeon__icone, .infobulle__bouton .icone, .u-visuellement-cache { display: none !important; }

  /* ---------- Tout ce que les révélations retenaient est montré ----------
     motion.js pose opacity: 0, transform et clip-path en ligne, SplitText pose visibility: hidden ; les LQIP sont
     des background-image en ligne sous l'image. */
  [data-revele], [data-revele] *, [data-revele-enfant], [data-revele-css], [data-revele-css] *, .est-revele {
    opacity: 1 !important; transform: none !important; clip-path: none !important; visibility: visible !important; transition: none !important; animation: none !important;
  }
  .visuel img { opacity: 1 !important; }
  .visuel { background-image: none !important; background-color: transparent !important; }
  .objet::before, .objet::after { display: none !important; }
  .accordeon__panneau { height: auto !important; overflow: visible !important; }
  .infobulle { display: block; }
  .infobulle__bouton { display: inline; color: #000 !important; text-decoration: none !important; width: auto; height: auto; }
  .infobulle__texte { position: static !important; width: auto !important; max-width: 68ch; box-shadow: none !important; border: 0 !important; padding: 0 !important; margin-top: 1mm; font-style: italic; opacity: 1 !important; translate: none !important; }

  /* ---------- Surfaces colorées → blanc ---------- */
  .hero-maison, .hero-profond, .hero, .banniere, .bloc-eau, .bloc-eau--sombre, .section--lin, .eau-section, .commande__recap,
  .contact-carte, .taille-unique, .legal__avertissement, .sans-js__note, .section-paiement, 
  .fondatrice, .filtres-feuille, .cartel { background: none !important; color: #000 !important; box-shadow: none !important; }
  .hero-maison::after, .banniere::after, .mega__visuel::after, .vu-sur__legende { display: none !important; }
  .hero-maison, .hero-profond, .hero, .eau-section__interieur, .introuvable { min-height: 0 !important; max-height: none !important; }
  .hero-maison .visuel, .hero__visuel .visuel, .banniere .visuel { position: static !important; height: auto !important; aspect-ratio: 4 / 5 !important; max-width: 70mm; max-height: none !important; }
  .hero__texte, .banniere__texte { position: static !important; color: #000 !important; text-shadow: none !important; padding: 4mm 0 !important; }

  /* ---------- Mises en page à deux colonnes → une seule ---------- */
  .produit__corps, .duo, .duo--inverse, .chapitre, .commande, .commande--confirmation, .contact, .hero-profond__interieur, .eau-section__interieur,
  .reassurance-section, .legal__corps, .hero, .fondatrice__interieur, .planche { grid-template-columns: 1fr !important; gap: 6mm !important; }
  .commande__tete, .commande__form, .commande__recap, .confirmation--commande, .confirmation__paiement { grid-column: auto !important; grid-row: auto !important; }
  .commande__recap.est-replie .commande__recap-corps { display: grid !important; }
  .commande__recap { margin: 0 !important; padding: 0 !important; border: 0 !important; }
  .produit__panneau, .chapitre__tete, .commande__recap, .legal__sommaire, .filtres { position: static !important; max-width: none !important; }
  .produit__galerie { margin-inline: 0 !important; }
  .galerie { display: grid !important; grid-template-columns: repeat(3, 1fr) !important; grid-auto-flow: row !important; grid-auto-columns: auto !important; overflow: visible !important; gap: 3mm !important; }
  .grille-produits, .cinq-pieces, .reassurances, .vu-sur__grille, .rail-objets { display: grid !important; grid-template-columns: repeat(3, minmax(0, 1fr)) !important; gap: 4mm !important; overflow: visible !important; }
  .cinq-pieces > *, .rail-objets > * { flex: none !important; }
  .carrousel__piste { flex-wrap: wrap !important; overflow: visible !important; }
  .carrousel__item { flex: 0 0 30% !important; }
  .accordeon__item, .legal__section, .bloc-eau__item, .reassurance, .ligne-panier, .commande__article, .carte-produit, .galerie__item, .contact__carte, .duo, .confirmation__bloc { break-inside: avoid; }
  .accordeon__contenu { max-width: none; }
  .duo__visuel .visuel, .eau-section__visuel .visuel { max-width: 80mm; }

  /* ---------- Feuille « Paiement » de la confirmation (composants/paiement.js, vues/commande.js) ----------
     Noir sur blanc, bordée, jamais coupée en deux pages : c'est la preuve de paiement que la cliente imprime. */
  .section-paiement { break-inside: avoid; page-break-inside: avoid; border: .25mm solid #000 !important; padding: 5mm !important; margin-block: 6mm; box-shadow: none !important; }
  .section-paiement__titre { font-family: var(--police-display); font-size: var(--t-chapo); font-weight: 500; }
  .section-paiement__infos { font-size: var(--t-petit); }
  .section-paiement__aide, .section-paiement__note { font-size: .75rem; color: #000 !important; }
  mark.a-completer { font-style: italic; }
  mark.a-completer { background: none !important; color: #000 !important; outline: .25mm dashed #000; }
}

}