/* ═══════════════════════════════════════════════════════════════════════════
   Alazard et Roux — aperçu de site
   Sartor — Data & AI Studio

   Le parti pris tient en une image : la Camargue est le seul paysage français
   sans relief, tout s'y joue sur une horizontale. La page est construite sur
   une ligne d'horizon répétée de section en section, tenue au pixel, et le
   libellé de chaque section est posé à cheval dessus.

   Fond clair, chaux et soleil. Une version précédente était en fond nuit, au
   motif que leurs photos produit sont shootées sur velours noir : l'argument
   était juste sur la photo, faux sur la page. Vingt écrans de suite en noir,
   ça ne se lit plus comme une galerie, ça se lit comme une cave. Le bon
   raisonnement est l'inverse : sur un mur clair, chaque photo sombre devient
   un tableau accroché, et la lumière vient du papier. Il reste exactement
   deux moments sombres, la bande du geste et le bloc professionnels, qui
   valent parce qu'ils sont rares.

   Le rouge est le leur, relevé sur l'aplat de leur enseigne. Il ne fait jamais
   de surface : le wordmark, les numéros de section, les boutons, le prix d'une
   fiche produit. Assez pour qu'on reconnaisse la maison, jamais assez pour
   qu'il concurrence la viande — qui est déjà rouge, et qui doit rester le seul
   rouge dont on ait envie.

   Deux polices, les deux variables et auto-hébergées (94 Ko à elles deux) :
   Bodoni Moda, le didot de l'affiche de course camarguaise, et Archivo, dont
   l'axe de chasse rend le linéal condensé des mêmes affiches.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Polices ──────────────────────────────────────────────────────────────
   Sous-ensemblées au latin et bornées aux axes réellement utilisés
   (fontTools varLib.instancer), d'où les 94 Ko. Pas de CDN : une requête vers
   fonts.gstatic.com, c'est un tiers de plus et un aller-retour DNS. */
@font-face {
  font-family: 'Bodoni Moda';
  src: url('fonts/bodoni-latin.woff2') format('woff2');
  font-weight: 400 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Archivo';
  src: url('fonts/archivo-latin.woff2') format('woff2');
  font-weight: 400 700;
  font-stretch: 70% 105%;
  font-style: normal;
  font-display: swap;
}

/* ── Jetons ─────────────────────────────────────────────────────────────── */
:root {
  --papier:   #FAF6EE;  /* chaux, fond dominant */
  --papier-2: #F1EBDD;  /* sections alternées, survols */
  --encre:    #1A1613;  /* texte */
  --encre-2:  #4B443B;  /* texte secondaire, chapôs */
  /* Numérotation, légendes, mentions. #8B8172 sortait à 3,55:1 sur le papier et
     3,22:1 sur les sections alternées, sous le seuil de 4,5. Ce jeton porte tout
     le second niveau de lecture : sur-titres, légendes, « N références », poids,
     fil d'Ariane, liens de la barre une fois posée. On l'assombrit sans quitter
     la famille chaude : 5,25:1 et 4,76:1. Curiosité qui vaut d'être notée, le
     même gris tenait déjà sur les blocs sombres (4,92:1), c'est la version
     claire qui décrochait. */
  --encre-3:  #6E665A;
  --trait-c:  #DED5C3;  /* filet 1px sur papier */
  --nuit:     #14100E;  /* les deux blocs sombres, et eux seuls */
  --sang:     #A81225;  /* le rouge de leur enseigne */
  /* La même, éclaircie pour tenir sur fond nuit — sauf qu'elle n'y tenait pas
     tout à fait : #C7303F sortait à 3,53:1 sur le nuit, sous le seuil de 4,5.
     Le jeton existe précisément pour régler ce problème, il ne montait pas
     assez haut. #D9525F donne 4,79:1 et reste la même famille de rouge.
     Le rouge de l'enseigne, lui, ne bouge pas : c'est --sang. */
  --sang-2:   #D9525F;

  /* Le fond et l'encre courants d'une section. Une section sombre ne fait que
     réassigner ces variables : plus aucune règle ne code une couleur en dur, et
     le libellé posé sur le filet reprend toujours le bon fond. C'est exactement
     ce qui manquait avant, et qui peignait un rectangle noir sur le bloc
     professionnels. */
  --fond:   var(--papier);
  --texte:  var(--encre);
  --texte-2:var(--encre-2);
  --texte-3:var(--encre-3);
  --trait:  var(--trait-c);
  --accent: var(--sang);

  --serif: 'Bodoni Moda', 'Didot', 'Palatino Linotype', Georgia, serif;
  --sans:  'Archivo', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;

  /* Bodoni est un didot : ses déliés sont des cheveux. Sur un didot variable,
     l'axe qui règle ça n'est pas la graisse mais la taille optique — `opsz`
     bas veut dire « dessin prévu pour du petit texte », donc déliés épaissis et
     contreformes ouvertes. On garde donc un `opsz` bas MÊME en très grand : on
     y gagne un titre qui se lit à l'écran, on n'y perd que le maniérisme d'un
     Bodoni d'affiche. C'est le réglage que corrige la version précédente, où
     les titres étaient en `opsz` 96 et disparaissaient à la lecture. */
  --ti-display: 'opsz' 16, 'wght' 600;
  --ti-titre:   'opsz' 16, 'wght' 600;
  --ti-sous:    'opsz' 18, 'wght' 600;
  --ti-chiffre: 'opsz' 40, 'wght' 500;

  --fs-display: clamp(2.75rem, 1.4rem + 5.6vw, 5.25rem);
  --fs-h2:      clamp(1.75rem, 1.1rem + 2.7vw, 2.875rem);
  --fs-h3:      clamp(1.15rem, 1rem + 0.8vw, 1.5rem);
  --fs-lead:    clamp(1.0625rem, 0.98rem + 0.5vw, 1.3125rem);
  --fs-body:    clamp(1rem, 0.96rem + 0.2vw, 1.0625rem);
  --fs-small:   clamp(0.875rem, 0.85rem + 0.1vw, 0.9375rem);
  --fs-eyebrow: clamp(0.6875rem, 0.66rem + 0.12vw, 0.8125rem);

  --pad:    clamp(1.25rem, 4vw, 4.5rem);
  --gutter: clamp(1rem, 2.2vw, 2rem);

  --sp-tight: clamp(3.5rem, 5vw, 5rem);
  --sp:       clamp(5rem, 7.5vw, 8rem);
  --sp-wide:  clamp(6.5rem, 10vw, 11rem);
}

.sombre {
  --fond:    var(--nuit);
  --texte:   #F4F0E8;
  --texte-2: #C6BFB2;
  --texte-3: #8A8171;
  --trait:   #38312A;
  --accent:  var(--sang-2);
  background: var(--fond);
  color: var(--texte);
}
.alterne { --fond: var(--papier-2); background: var(--fond); }

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; scroll-padding-top: 5.5rem; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--papier);
  color: var(--encre);
  font-family: var(--sans);
  font-size: var(--fs-body);
  line-height: 1.65;
  font-optical-sizing: auto;
  font-feature-settings: 'pnum' 1, 'lnum' 1;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* `height: auto` n'est pas cosmétique : sans lui, l'attribut HTML `height`
   d'une image l'emporte sur son `aspect-ratio` CSS, et les vignettes sortaient
   à 875 px de haut sur mobile. Les rares images qui ont besoin d'une hauteur
   imposée la déclarent elles-mêmes. */
img { max-width: 100%; height: auto; display: block; }
a { color: inherit; }

::selection { background: var(--sang); color: #FAF6EE; }
:focus-visible { outline: 1px solid var(--accent); outline-offset: 3px; }

/* ── Typographie ────────────────────────────────────────────────────────── */
h1, h2, h3, p, ol, ul, figure, dl { margin: 0; }
ol, ul { padding: 0; list-style: none; }

h1 {
  font-family: var(--serif);
  font-variation-settings: var(--ti-display);
  font-size: var(--fs-display);
  line-height: 0.94;
  letter-spacing: -0.015em;
  color: var(--texte);
  text-wrap: balance;
}
h2 {
  font-family: var(--serif);
  font-variation-settings: var(--ti-titre);
  font-size: var(--fs-h2);
  line-height: 1.1;
  letter-spacing: -0.01em;
  color: var(--texte);
  text-wrap: balance;
}
h3 {
  font-family: var(--sans);
  font-size: var(--fs-h3);
  font-weight: 600;
  font-stretch: 94%;
  line-height: 1.25;
  letter-spacing: -0.005em;
  color: var(--texte);
}

/* Le sur-titre : capitales condensées très espacées, la lettre de l'affiche
   clouée sur le platane du village. Réservé aux libellés, jamais un titre. */
.eyebrow {
  font-family: var(--sans);
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  font-stretch: 78%;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  line-height: 1;
  color: var(--texte-3);
}
/* Le numéro de section est rouge. Sept occurrences dans la page, chacune de la
   taille d'un timbre : c'est le fil qui coud la marque à la mise en page. */
.eyebrow.num { color: var(--accent); }

.lead {
  font-size: var(--fs-lead);
  line-height: 1.5;
  color: var(--texte-2);
  max-width: 46ch;
}
h2 + .lead, h2 + .prose, h1 + .lead { margin-top: 1.3rem; }
.prose { max-width: 62ch; color: var(--texte-2); }
.prose + .prose { margin-top: 1.1em; }

.legende {
  font-family: var(--sans);
  font-size: var(--fs-small);
  font-weight: 500;
  line-height: 1.5;
  letter-spacing: 0.01em;
  color: var(--texte-3);
}

/* Le chiffre remarquable. Seule chose, avec les titres, à passer en Bodoni :
   c'est cette réserve qui en fait un ornement plutôt qu'une donnée. */
.chiffre {
  font-family: var(--serif);
  font-variation-settings: var(--ti-chiffre);
  font-feature-settings: 'lnum' 1, 'tnum' 1;
  letter-spacing: -0.005em;
  color: var(--texte);
}

/* Lien de texte souligné. Jamais de bouton fantôme. */
.lien {
  text-decoration: underline;
  text-underline-offset: 0.22em;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--texte-3);
  transition: text-decoration-color 0.2s, color 0.2s;
}
.lien:hover { color: var(--accent); text-decoration-color: var(--accent); }

/* Le bouton. Un aplat rouge, jamais plus d'un par écran. */
.bouton {
  display: inline-block;
  padding: 1rem 2.2rem;
  background: var(--sang);
  color: #FAF6EE;
  font-family: var(--sans);
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  font-stretch: 78%;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  text-decoration: none;
  transition: background-color 0.25s;
}
.bouton:hover { background: color-mix(in oklab, var(--sang) 84%, black); }

/* ── Grille ─────────────────────────────────────────────────────────────── */
.band {
  display: grid;
  grid-template-columns:
    [full-start] minmax(var(--pad), 1fr)
    [wide-start] minmax(0, 1360px)
    [wide-end]   minmax(var(--pad), 1fr)
    [full-end];
  position: relative;
}
.band > * { grid-column: wide-start / wide-end; }
.band > .bleed { grid-column: full-start / full-end; }

.g12 { display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--gutter); }

/* L'horizon : un filet traverse la page en pleine largeur au sommet de chaque
   section numérotée, et le libellé est posé à cheval dessus, son fond mangeant
   la ligne. Une section sans libellé ne porte pas l'attribut : un filet seul,
   sans rien à tenir, se lit comme un trait oublié. */
.band[data-num]::before {
  content: '';
  position: absolute;
  inset: 0 0 auto 0;
  height: 1px;
  background: var(--trait);
}

.sombre[data-num]::before, .alterne[data-num]::before { content: none; }
.sombre .tete, .alterne .tete {
  transform: none;
  background: none;
  padding-top: clamp(2.5rem, 4vw, 3.5rem);
}

.tete {
  grid-column: wide-start / wide-end;
  display: flex;
  align-items: baseline;
  gap: 1.1rem;
  transform: translateY(-0.62em);
  padding-right: 1.4rem;
  background: var(--fond);
  width: max-content;
  max-width: 100%;
}

/* ── Barre de navigation ────────────────────────────────────────────────── */
.nav {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gutter);
  padding: 0.7rem var(--pad);
  /* Le wordmark est blanc : sur le ciel clair de la photo d'ouverture il
     disparaît sans ce voile. Le voile est uniforme, la photo ne l'est pas :
     elle est nettement plus claire à gauche, si bien que « Les viandes » et
     « Le savoir-faire » sortaient à 3,50:1 et 4,00:1 quand « Professionnels »
     tenait à 6,11:1. La lisibilité se dégradait en dégradé, de gauche à droite.
     On appuie donc partout plutôt que de rattraper lien par lien. */
  padding-bottom: 1.5rem;
  background: linear-gradient(to bottom,
    color-mix(in srgb, var(--nuit) 82%, transparent) 0%,
    color-mix(in srgb, var(--nuit) 55%, transparent) 60%,
    transparent 100%);
  transition: transform 0.3s ease, background 0.3s ease, box-shadow 0.3s ease;
}
.nav.posee {
  padding-bottom: 0.7rem;
  background: var(--papier);
  box-shadow: inset 0 -1px 0 var(--trait-c);
}
.nav.cachee { transform: translateY(-100%); }
/* Le wordmark est presque carré : on le cale en hauteur, sinon il fait une
   barre de navigation de 140 px de haut. */
.nav a > img { height: clamp(38px, 4.6vw, 52px); width: auto; }
/* Sur le papier, c'est la version rouge de leur enseigne qui prend la place.
   Une version noircie au filtre `invert` faisait de leur marque une marque
   anonyme : le rouge la rend immédiatement à eux. */
.nav .mark-rouge { display: none; }
.nav.posee .mark-clair { display: none; }
.nav.posee .mark-rouge { display: block; }
.nav ul { display: flex; gap: clamp(1rem, 2.4vw, 2.2rem); }
.nav a { text-decoration: none; }
.nav .eyebrow { color: #E8E2D6; transition: color 0.2s; }
.nav.posee .eyebrow { color: var(--encre-3); }
.nav a:hover .eyebrow, .nav .eyebrow.actif { color: var(--sang); }
/* Sous 900 px, les cinq entrées ne tiennent pas. Elles étaient simplement
   masquées, et il ne restait qu'une barre de 60 px portant le logo et aucun
   lien : la boutique n'était plus atteignable qu'après dix écrans de
   défilement, sur le format où le site sera justement ouvert. On garde donc la
   seule entrée qui mène au catalogue, en aplat rouge et à la taille d'une
   cible tactile. Le sélecteur vise le lien par son href plutôt que par son
   rang : les trois gabarits de page (accueil, boutique, fiche) le posent au
   même endroit mais rien ne le garantit dans le temps. */
@media (max-width: 900px) {
  .nav { padding-block: 0.5rem; }
  .nav.posee { padding-bottom: 0.5rem; }
  /* On masque d'abord, on révèle ensuite. Écrit dans l'autre sens
     (`li:not(:has(…))`), un navigateur sans `:has()` jetterait la règle et
     laisserait les cinq entrées déborder de la barre ; ici il retombe
     simplement sur le comportement d'avant, sans menu. */
  .nav ul li { display: none; }
  .nav ul li:has(a[href$="boutique.html"]) { display: block; }
  .nav ul a {
    display: flex;
    align-items: center;
    min-height: 44px;
    padding-inline: 1rem;
    background: var(--sang);
  }
  /* Deux règles à trois classes visent ce libellé et l'emporteraient sur un
     sélecteur plus court : `.actif` le peindrait rouge sur rouge sur la page
     boutique, et `.nav.posee` le peindrait gris sur rouge dès que la barre se
     pose au défilement. On repasse donc au-dessus des deux. */
  .nav ul a .eyebrow,
  .nav.posee ul a .eyebrow,
  .nav ul a .eyebrow.actif,
  .nav ul a:hover .eyebrow { color: var(--papier); }
}

/* ── Ouverture ──────────────────────────────────────────────────────────── */
.hero { padding-bottom: var(--sp); }
.hero-img {
  aspect-ratio: 21 / 9;
  /* 46vh mangeaient près de la moitié du premier écran pour un paysage, et
     repoussaient le bouton d'entrée sous le pli. 40vh suffisent à ce que le
     troupeau porte, et rendent la place au titre, au chapô et à la porte. */
  max-height: 40vh;
  width: 100%;
  object-fit: cover;
  object-position: center 62%;
  filter: contrast(1.06) brightness(0.98);
}
.hero-corps { margin-top: clamp(2.5rem, 5vw, 4rem); align-items: end; }
.hero h1 { margin-top: 1.4rem; grid-column: 1 / 8; }
/* Le chapô se cale en pied du titre, dans la colonne de droite : la ligne de
   base commune tient les deux blocs, et le haut de page cesse d'être un titre
   posé sur du vide. */
.hero .lead { grid-column: 8 / -1; padding-bottom: 0.5rem; }
/* La porte, sur toute la largeur, sous les deux colonnes. */
.hero-cta { grid-column: 1 / -1; margin-top: clamp(1.6rem, 3vw, 2.4rem); }
@media (max-width: 900px) {
  .hero h1, .hero .lead { grid-column: 1 / -1; }
  .hero .lead { margin-top: 1.8rem; }
  .hero-cta { margin-top: 1.6rem; }
}
/* En 21:9 sur un téléphone, la bande d'ouverture tombe à 167 px : le troupeau
   devient un filet et l'image ne porte plus rien. On la redresse. */
@media (max-width: 700px) {
  .hero-img { aspect-ratio: 16 / 10; object-position: center 58%; }
}

/* ── Le fil ─────────────────────────────────────────────────────────────── */
.fil-bloc { background: var(--papier-2); }
/* Cette bande a été écrite pour tenir sur une ligne, et n'y tenait à aucune
   largeur : ses trois blocs totalisent 1 583 px alors que la colonne de texte
   est plafonnée à 1 360 px. Elle passait donc à la ligne partout, avec le point
   rouge orphelin en bout de première ligne et une coupe de 491 px contre
   1 092 px. `text-wrap: balance` ne pouvait rien : les blocs sont en
   `white-space: nowrap`, il n'y a rien à équilibrer à l'intérieur.
   On arrête de faire semblant. Deux lignes assumées, réparties par le flex, et
   le séparateur passe en tête du bloc suivant : s'il y a un retour, il ouvre la
   ligne au lieu de pendre au bout de la précédente. La hauteur d'interligne
   redescend à 1,5, le rembourrage de la bande faisant déjà la respiration —
   elle occupait 373 px pour quatorze mots. */
.fil {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: clamp(1rem, 2vw, 2rem);
  row-gap: 0.2rem;
  padding: var(--sp-tight) 0;
  font-family: var(--sans);
  font-weight: 600;
  font-stretch: 78%;
  text-transform: uppercase;
  letter-spacing: 0.13em;
  font-size: clamp(0.85rem, 0.6rem + 1vw, 1.25rem);
  line-height: 1.5;
  color: var(--encre-2);
}
.fil .chiffre {
  font-size: clamp(1.9rem, 1rem + 3.2vw, 3.25rem);
  letter-spacing: 0;
  text-transform: none;
  vertical-align: -0.12em;
  margin: 0 0.1em;
}
/* Le séparateur vivait en fin de bloc, donc il restait accroché à la fin de la
   ligne quand le bloc suivant passait dessous. Il est maintenant porté par le
   bloc qui suit. */
.fil .sep { display: none; }
.fil .bloc + .bloc::before {
  content: '·';
  color: var(--sang);
  margin-right: clamp(1rem, 2vw, 2rem);
}
.fil .bloc { white-space: nowrap; }
@media (max-width: 640px) { .fil .bloc { white-space: normal; } }

/* ── Bandes en pleine image ─────────────────────────────────────────────── */
.pleine { position: relative; }
.pleine img {
  width: 100%;
  height: clamp(300px, 50vh, 540px);
  object-fit: cover;
}
.pleine .surimpression {
  position: absolute;
  inset: auto 0 0 0;
  padding: clamp(2rem, 5vw, 4rem) var(--pad);
  /* Le voile moyen était très confortable (10:1 sous la légende), mais l'avant-bras
     du boucher passe juste derrière la deuxième ligne et y faisait tomber le pire
     pixel à 2,75:1. Une moyenne ne se lit pas, une ligne se lit. On monte le palier
     du milieu, là où la légende tombe, plutôt que d'assombrir toute la photo. */
  background: linear-gradient(to top,
    color-mix(in srgb, var(--nuit) 96%, transparent) 12%,
    color-mix(in srgb, var(--nuit) 82%, transparent) 50%,
    transparent 100%);
  color: #F4F0E8;
}
.pleine .surimpression h2 { color: #F4F0E8; max-width: 18ch; }
.pleine .surimpression .legende { margin-top: 0.9rem; max-width: 52ch; color: #D2CCC0; }

/* ── 01 · Les viandes ───────────────────────────────────────────────────── */
/* Quatre espèces sur une ligne, à égalité. La version précédente ne montrait
   que le taureau : le bœuf est pourtant le rayon le plus fourni du catalogue,
   et l'agneau et le cochon ont chacun leur filière. */
.viandes { padding: var(--sp) 0; }
.v-intro { margin-bottom: clamp(2.5rem, 5vw, 4rem); }
.v-intro h2 { grid-column: 1 / 7; }
.v-intro .lead { grid-column: 7 / -1; align-self: end; }

.v-liste, .s-liste {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--gutter);
}
/* Même colonne que les cartes de la boutique, même raison : « Agneau de Crau et
   des Alpilles » passe à la ligne là où les trois autres titres tiennent sur
   une, et sa mention de références décrochait de 30 px. */
.v-liste a, .s-liste a { text-decoration: none; display: flex; flex-direction: column; height: 100%; }
.v-liste img, .s-liste img {
  aspect-ratio: 4 / 5;
  object-fit: cover;
  width: 100%;
  flex: none;
  transition: filter 0.4s ease;
}
.v-liste a:hover img, .s-liste a:hover img { filter: brightness(1.08); }
.v-liste h3, .s-liste h3 { margin-top: 1.1rem; }
.v-liste p, .s-liste p {
  margin-top: 0.6rem;
  color: var(--texte-2);
  font-size: var(--fs-small);
  line-height: 1.6;
}
.v-liste .refs, .s-liste .refs {
  display: inline-block;
  /* `auto` colle la mention au fond de la carte ; `align-self` la ramène à la
     largeur de son texte, sinon le filet du dessous traverserait la colonne. */
  margin-top: auto;
  padding-top: 0.9rem;
  align-self: start;
  color: var(--texte-3);
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  font-stretch: 78%;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  border-bottom: 1px solid var(--trait);
  padding-bottom: 0.25rem;
  transition: color 0.2s, border-color 0.2s;
}
.v-liste a:hover .refs, .s-liste a:hover .refs {
  color: var(--accent);
  border-color: var(--accent);
}
/* Le sigle de l'appellation, seul mot rouge d'un titre dans toute la page. */
.aop { color: var(--sang); }

@media (max-width: 1000px) {
  .v-liste { grid-template-columns: repeat(2, 1fr); row-gap: clamp(2rem, 5vw, 3rem); }
}
@media (max-width: 900px) {
  .v-intro h2, .v-intro .lead { grid-column: 1 / -1; }
}
@media (max-width: 560px) { .v-liste { grid-template-columns: 1fr; } }

/* L'encart : le seul endroit de la page où l'on argumente. Un filet rouge à
   gauche, le sceau AOP à droite, et rien d'autre. */
.encart {
  margin-top: clamp(3rem, 6vw, 5rem);
  padding: clamp(1.6rem, 3vw, 2.4rem) 0 clamp(1.6rem, 3vw, 2.4rem) clamp(1.4rem, 2.5vw, 2.2rem);
  border-left: 3px solid var(--sang);
  display: flex;
  align-items: center;
  gap: clamp(1.5rem, 4vw, 3.5rem);
}
.encart .citation {
  font-family: var(--serif);
  font-variation-settings: var(--ti-sous);
  font-size: clamp(1.2rem, 0.95rem + 1.1vw, 1.75rem);
  line-height: 1.3;
  letter-spacing: -0.005em;
  max-width: 34ch;
}
.encart .legende { margin-top: 1rem; max-width: 60ch; }
/* Le sceau est fourni sur fond blanc : `multiply` le fond dans le papier. */
.encart .sceau {
  width: clamp(78px, 8vw, 104px);
  flex: none;
  order: 2;
  mix-blend-mode: multiply;
}
@media (max-width: 700px) {
  .encart { flex-direction: column; align-items: flex-start; }
  .encart .sceau { order: 0; }
}

/* ── 02 · Le savoir-faire ───────────────────────────────────────────────── */
/* La moitié du catalogue n'est pas de la viande crue. L'ancien site n'en
   parlait nulle part ; c'est pourtant ce qui ne s'achète qu'ici. */
.savoir { padding: clamp(1.5rem, 2.5vw, 2rem) 0 var(--sp); }
.s-intro { margin-bottom: clamp(2.5rem, 5vw, 4rem); }
.s-intro h2 { grid-column: 1 / 7; }
.s-intro .lead { grid-column: 7 / -1; align-self: end; }
.s-liste { grid-template-columns: repeat(3, 1fr); }
.s-liste .eyebrow { display: block; margin-top: 1.2rem; }
.s-liste h3 { margin-top: 0.7rem; }
@media (max-width: 900px) {
  .s-intro h2, .s-intro .lead { grid-column: 1 / -1; }
  .s-liste { grid-template-columns: 1fr; row-gap: clamp(2rem, 5vw, 3rem); }
}

/* ── 03 · La boutique (sommaire) ────────────────────────────────────────── */
/* Le geste signature. Une liste pleine largeur, pas une grille de cartes :
   une grille se scanne, un sommaire de revue se lit. */
.gammes-bloc { padding: var(--sp) 0; }
.b-intro { margin-bottom: clamp(2rem, 4vw, 3rem); }
.b-intro h2 { grid-column: 1 / 8; }
.b-intro .lead { grid-column: 8 / -1; align-self: end; }
@media (max-width: 900px) { .b-intro h2, .b-intro .lead { grid-column: 1 / -1; } }
/* La liste et la vitrine sont deux colonnes, pas une image posée sur l'autre.
   La version précédente calait la vitrine en absolu au milieu de la liste : la
   photo tombait entre deux rangées et les filets lui passaient dessous, ce qui
   se lit comme une image lâchée par erreur. Chacune sa colonne, plus aucun
   croisement possible. */
.gammes-wrap {
  display: grid;
  grid-template-columns: minmax(0, 1fr) clamp(220px, 27vw, 390px);
  gap: clamp(1.5rem, 3.5vw, 3.5rem);
}
.b-cta { margin-top: clamp(2.5rem, 5vw, 3.5rem); }

.gammes li { border-top: 1px solid var(--trait); }
.gammes li:last-child { border-bottom: 1px solid var(--trait); }
.gammes a {
  display: grid;
  grid-template-columns: clamp(3rem, 5vw, 4.5rem) 1fr auto;
  align-items: center;
  gap: var(--gutter);
  min-height: clamp(4.5rem, 7.5vw, 7rem);
  padding: 0.9rem clamp(0.5rem, 1.5vw, 1.25rem);
  text-decoration: none;
  transition: background-color 0.35s cubic-bezier(0.2, 0.6, 0.2, 1);
}
.gammes a:hover { background: var(--papier-2); }
.gammes .nom {
  font-family: var(--serif);
  font-variation-settings: var(--ti-titre);
  font-size: clamp(1.35rem, 0.9rem + 1.7vw, 2.375rem);
  line-height: 1.05;
  letter-spacing: -0.01em;
}
.gammes .refs { color: var(--texte-3); font-size: var(--fs-small); white-space: nowrap; }

/* La vitrine : les huit vignettes empilées au même endroit, une seule visible.
   Le survol pilote l'échange en CSS pur (`:has`), sans une ligne de script.
   La colonne occupe toute la hauteur de la liste, le cadre y est collant : la
   photo descend avec le regard au lieu d'attendre en bas de page. */
/* Le cadre était en 4/5 dans une colonne de 290 px : 363 px de photo pour 873 px
   de liste, soit 510 px de vide sous elle, 58 % de la colonne. Le collant est
   une bonne idée mais il ne sert à rien ici, la section tient dans un écran et
   la photo restait garée en haut. Elle changeait donc six cents pixels au-dessus
   de la ligne survolée, hors du champ de vision.
   Deux gestes : la colonne s'élargit, et le cadre se centre sur la hauteur de la
   liste. Le vide restant se lit alors comme une marge, et la photo tombe à
   hauteur des rangées du milieu. On garde `aspect-ratio` plutôt qu'une hauteur
   fixe : entre 901 et 1200 px la colonne se resserre, et une hauteur en dur y
   donnerait un cadre étroit qui rognerait la viande. */
.vitrine {
  align-self: stretch;
  pointer-events: none;
  display: flex;
  align-items: center;
}
.vitrine-cadre {
  position: sticky;
  top: clamp(5.5rem, 8vw, 7.5rem);
  width: 100%;
  aspect-ratio: 3 / 4;
}
.vitrine img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 0.4s ease, transform 0.5s cubic-bezier(0.2, 0.6, 0.2, 1);
}
.vitrine-cadre img:first-child { opacity: 1; transform: none; }
.gammes-wrap:has(a:hover) .vitrine-cadre img:first-child { opacity: 0; transform: translateY(10px); }
.gammes-wrap:has(li:nth-child(1) a:hover) .vitrine-cadre img:nth-child(1),
.gammes-wrap:has(li:nth-child(2) a:hover) .vitrine-cadre img:nth-child(2),
.gammes-wrap:has(li:nth-child(3) a:hover) .vitrine-cadre img:nth-child(3),
.gammes-wrap:has(li:nth-child(4) a:hover) .vitrine-cadre img:nth-child(4),
.gammes-wrap:has(li:nth-child(5) a:hover) .vitrine-cadre img:nth-child(5),
.gammes-wrap:has(li:nth-child(6) a:hover) .vitrine-cadre img:nth-child(6),
.gammes-wrap:has(li:nth-child(7) a:hover) .vitrine-cadre img:nth-child(7),
.gammes-wrap:has(li:nth-child(8) a:hover) .vitrine-cadre img:nth-child(8) {
  opacity: 1;
  transform: none;
}
/* Sur mobile, pas de survol : la vignette est posée dans la rangée. */
.gammes .vig-mob { display: none; }
@media (max-width: 900px) {
  .gammes-wrap { grid-template-columns: 1fr; }
  .vitrine { display: none; }
  /* Le compte de références était masqué faute de place sur la ligne. C'est
     pourtant l'argument que le titre de la section annonce (« Quatre-vingt-huit
     morceaux ») et la preuve que la maison a de la profondeur : « 20 » sous
     Bœuf, « 17 » sous Charcuterie. Il s'effaçait donc sur le format où il compte
     le plus. La rangée passe à deux lignes dans sa colonne du milieu : le numéro
     et la vignette enjambent les deux, le nom prend la première, le compte la
     seconde. */
  .gammes a {
    grid-template-columns: clamp(2.4rem, 8vw, 3rem) 1fr 60px;
    row-gap: 0.35rem;
  }
  .gammes .num     { grid-area: 1 / 1 / 3 / 2; }
  .gammes .nom     { grid-area: 1 / 2 / 2 / 3; }
  .gammes .refs    { grid-area: 2 / 2 / 3 / 3; }
  .gammes .vig-mob { grid-area: 1 / 3 / 3 / 4; display: block; width: 60px; aspect-ratio: 4 / 5; object-fit: cover; }
}

/* ── 04 · La maison ─────────────────────────────────────────────────────── */
.maison { padding: var(--sp) 0; }
.maison .col-img { grid-column: 1 / 6; }
.maison .col-img img { aspect-ratio: 4 / 5; object-fit: cover; object-position: center 22%; width: 100%; }
.maison .col-txt { grid-column: 7 / -1; align-self: center; }
.maison h2 { margin-top: 1.4rem; }
.maison .prose { margin-top: 1.4rem; }
.maison .frise { grid-column: 1 / -1; margin-top: clamp(3rem, 6vw, 5rem); }
@media (max-width: 900px) {
  .maison .col-img, .maison .col-txt { grid-column: 1 / -1; }
  .maison .col-txt { margin-top: 2.5rem; }
}

.frise li {
  display: grid;
  grid-template-columns: minmax(5rem, 6.5rem) 1fr;
  gap: clamp(1rem, 2.5vw, 2rem);
  align-items: baseline;
  padding: 1.15rem 0;
  border-top: 1px solid var(--trait);
}
.frise li:last-child { border-bottom: 1px solid var(--trait); }
.frise .chiffre { font-size: clamp(1.7rem, 1.2rem + 1.6vw, 2.5rem); line-height: 1; }
.frise p { color: var(--texte-2); }

/* ── 05 · Professionnels (bloc sombre) ──────────────────────────────────── */
.pro .dedans { padding: clamp(1.5rem, 2.5vw, 2rem) 0 var(--sp); }
.pro .intro { grid-column: 1 / 7; }
.pro .cols {
  grid-column: 1 / -1;
  margin-top: clamp(2.2rem, 4vw, 3rem);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 0;
}
.pro .cols > div { padding: 1.6rem clamp(1rem, 2vw, 2rem) 0 0; border-top: 1px solid var(--trait); }
.pro .cols h3 { margin-bottom: 0.7rem; }
.pro .cols p { color: var(--texte-2); font-size: var(--fs-small); line-height: 1.6; }
.pro .apres {
  grid-column: 1 / -1;
  margin-top: clamp(2.2rem, 4vw, 3rem);
  color: var(--texte-2);
}
@media (max-width: 900px) { .pro .intro { grid-column: 1 / -1; } }

/* ── 06 · Venir ─────────────────────────────────────────────────────────── */
.venir { padding: var(--sp-wide) 0; text-align: center; }
.venir h2 { margin: 1.6rem auto 0; max-width: 16ch; }
.venir .tel {
  display: inline-block;
  margin-top: clamp(1.8rem, 3.5vw, 2.5rem);
  font-size: clamp(2.1rem, 1.2rem + 3.6vw, 3.75rem);
  line-height: 1;
  text-decoration: none;
  transition: color 0.2s;
}
.venir .tel:hover { color: var(--sang); }
.venir .adresse { margin-top: 1.4rem; }
.venir .adresse span { display: block; }
.venir .bouton { margin-top: clamp(2rem, 3.5vw, 2.75rem); }

/* ═══ La boutique ═══════════════════════════════════════════════════════════
   Pages construites par `fetch_shop.py` à partir de leur API WooCommerce :
   89 références, leurs photos, leurs prix, leurs codes article. Aucun panier —
   l'aperçu montre la vitrine, pas la caisse. */
.page-boutique { padding-top: clamp(4.5rem, 6vw, 6rem); }
.tete-boutique .dedans { padding: clamp(2rem, 4vw, 3.5rem) 0 clamp(2rem, 4vw, 3rem); }
.tete-boutique h1 { max-width: 14ch; }
.tete-boutique .lead { max-width: 52ch; }

/* Le rail des rayons. Collant sous la barre, il défile horizontalement sur
   téléphone plutôt que de se replier en pavé de liens. */
.rayons {
  position: sticky;
  top: clamp(3.6rem, 5vw, 4.8rem);
  z-index: 20;
  background: var(--papier);
  box-shadow: inset 0 -1px 0 var(--trait-c), inset 0 1px 0 var(--trait-c);
}
.rayons ul {
  display: flex;
  gap: clamp(1rem, 2.4vw, 2.2rem);
  padding: 0.85rem var(--pad);
  overflow-x: auto;
  scrollbar-width: none;
}
.rayons ul::-webkit-scrollbar { display: none; }
/* Au large, le rail tient sur deux lignes plutôt que de sortir du cadre : un
   défilement horizontal sans amorce visible se lit comme un texte coupé. */
@media (min-width: 901px) { .rayons ul { flex-wrap: wrap; overflow: visible; row-gap: 0.6rem; } }
/* Et sous 901 px, où il défile vraiment : le rail mesure 1 873 px pour une
   fenêtre de 390, soit huit rayons sur neuf hors champ. La barre de défilement
   est masquée juste au-dessus et le dégradé n'existait pas : le deuxième rayon
   se lisait donc comme un mot tronqué, pas comme la promesse d'une suite.
   C'est le défaut que le commentaire ci-dessus décrit, corrigé seulement du
   côté large. */
@media (max-width: 900px) {
  .rayons ul {
    mask-image: linear-gradient(to right, #000 calc(100% - 2.75rem), transparent);
    scroll-snap-type: x proximity;
    scroll-padding-inline: var(--pad);
  }
  .rayons li { scroll-snap-align: start; }
  /* 22 px de haut au pouce, c'est la moitié du minimum utilisable. On agrandit
     la zone cliquable sans toucher à la mise en page : le rail garderait sinon
     68 px de haut, et la barre plus le rail mangeraient 15 % de l'écran. */
  .rayons a { position: relative; }
  .rayons a::after { content: ''; position: absolute; inset: -11px 0; }
}
.rayons li { flex: none; }
.rayons a {
  display: flex;
  align-items: baseline;
  gap: 0.45rem;
  text-decoration: none;
  padding-bottom: 0.15rem;
  border-bottom: 2px solid transparent;
  transition: border-color 0.2s;
}
.rayons a:hover { border-color: var(--sang); }
.rayons a:hover .eyebrow { color: var(--encre); }
.rayons .n {
  font-family: var(--sans);
  font-size: 0.68rem;
  font-weight: 600;
  color: var(--sang);
  letter-spacing: 0.04em;
}

.rayon { padding: clamp(3rem, 5vw, 4.5rem) 0 0; scroll-margin-top: 7rem; }
.rayon-tete { padding-bottom: clamp(1.5rem, 3vw, 2.2rem); border-bottom: 1px solid var(--trait); }
.rayon-tete .compte { margin-top: 0.9rem; }
.rayon-tete .lead { margin-top: 0.9rem; }

.grille {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: clamp(1.4rem, 2.6vw, 2.4rem) var(--gutter);
  padding-top: clamp(1.8rem, 3vw, 2.6rem);
}
/* Le nom du produit tient sur une ou deux lignes selon sa longueur, et le bloc
   du bas portait son filet 1 px juste dessous : sur une même rangée, les prix
   n'étaient pas à la même hauteur et les filets ne se rejoignaient pas. Sur 89
   cartes, ça casse la seule règle que cette page s'est donnée, une ligne
   d'horizon tenue au pixel. La carte devient une colonne, le bas est poussé
   contre le fond, les filets se rejoignent. */
.produit a { text-decoration: none; display: flex; flex-direction: column; height: 100%; }
.produit img {
  aspect-ratio: 4 / 5;
  object-fit: cover;
  width: 100%;
  flex: none;          /* la photo ne se laisse pas comprimer par la colonne */
  background: var(--papier-2);
  transition: filter 0.35s ease;
}
.produit a:hover img { filter: brightness(1.1); }
.p-nom {
  display: block;
  margin-top: 0.85rem;
  font-family: var(--sans);
  font-size: var(--fs-small);
  font-weight: 600;
  font-stretch: 94%;
  line-height: 1.35;
  color: var(--encre);
  /* L'écart sous le nom passe ici : il était porté par la marge haute de
     `.p-bas`, qui sert maintenant à pousser le bloc contre le fond de carte. */
  margin-bottom: 0.5rem;
}
.p-bas {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.6rem;
  margin-top: auto;
  padding-top: 0.5rem;
  border-top: 1px solid var(--trait);
}
.p-poids { font-size: var(--fs-eyebrow); color: var(--encre-3); letter-spacing: 0.04em; }
.p-prix { font-size: 1.15rem; color: var(--sang); white-space: nowrap; }
/* Deux colonnes sur téléphone : à une seule, le rayon bœuf faisait vingt écrans
   et la boutique entière en dépassait cinquante mille pixels. */
@media (max-width: 620px) { .grille { grid-template-columns: repeat(2, 1fr); } }

/* Le bandeau livraison, repris mot pour mot de leur page « Livraison ». */
.livraison { background: var(--papier-2); margin-top: var(--sp); }
.livraison .dedans {
  padding: clamp(2.5rem, 4vw, 3.5rem) 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: clamp(1.8rem, 3vw, 3rem);
}
.livraison .montant { font-size: clamp(1.6rem, 1.2rem + 1.2vw, 2.25rem); line-height: 1.1; margin-top: 0.7rem; }
.livraison .legende { margin-top: 0.6rem; max-width: 34ch; }

/* ── Fiche produit ──────────────────────────────────────────────────────── */
.fiche { padding-bottom: 0; }
.fil-ariane { padding: clamp(1.2rem, 2.5vw, 2rem) 0 clamp(1.5rem, 3vw, 2.5rem); }
.fil-ariane a { text-decoration: none; }
.fil-ariane a:hover { color: var(--sang); }
.fiche-corps { align-items: start; }
.fiche-corps .f-img { grid-column: 1 / 6; margin: 0; }
.fiche-corps .f-img img { aspect-ratio: 4 / 5; object-fit: cover; width: 100%; background: var(--papier-2); }
.fiche-corps .f-txt { grid-column: 7 / -1; }
.f-txt h1 { font-size: clamp(1.9rem, 1.3rem + 2.2vw, 3rem); margin-top: 1rem; }
.f-prix {
  font-variation-settings: 'opsz' 18, 'wght' 600;
  font-size: clamp(2rem, 1.4rem + 2.2vw, 3rem);
  line-height: 1;
  color: var(--sang);
  margin-top: 1.4rem;
}
.f-txt .prose { margin-top: 1.4rem; }
.specs {
  margin-top: clamp(1.8rem, 3vw, 2.5rem);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 0;
}
.specs > div { border-top: 1px solid var(--trait); padding: 0.9rem 1rem 0.9rem 0; }
.specs dt {
  font-family: var(--sans);
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  font-stretch: 78%;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--encre-3);
}
.specs dd { margin: 0.4rem 0 0; font-size: var(--fs-small); font-variant-numeric: tabular-nums; }
.f-txt .bouton { margin-top: clamp(1.8rem, 3vw, 2.5rem); }
.f-note { margin-top: 1.2rem; max-width: 46ch; }

.suite { padding: var(--sp) 0 0; }
.tete-suite { border-top: 1px solid var(--trait); padding-top: 1.2rem; }
.nav-fiche {
  display: flex;
  justify-content: space-between;
  gap: var(--gutter);
  margin-top: clamp(2.5rem, 4vw, 3.5rem);
  padding-top: 1.3rem;
  border-top: 1px solid var(--trait);
  font-size: var(--fs-small);
}
.nav-fiche a { text-decoration: none; color: var(--encre-3); }
.nav-fiche a:hover { color: var(--sang); }
@media (max-width: 900px) {
  .fiche-corps .f-img, .fiche-corps .f-txt { grid-column: 1 / -1; }
  .fiche-corps .f-img { max-width: 22rem; }
  .fiche-corps .f-txt { margin-top: 2rem; }
}

/* ── Pied ───────────────────────────────────────────────────────────────── */
.pied .dedans {
  padding: clamp(3rem, 5vw, 4rem) 0 clamp(2rem, 3vw, 2.5rem);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: clamp(2rem, 4vw, 3rem);
  align-items: start;
}
.pied img { height: 82px; width: auto; }
.pied p { color: var(--texte-3); font-size: var(--fs-small); line-height: 1.7; }
.pied .eyebrow { display: block; margin-bottom: 0.85rem; }
.pied .bas {
  grid-column: 1 / -1;
  border-top: 1px solid var(--trait);
  padding-top: 1.3rem;
  margin-top: clamp(0.5rem, 1.5vw, 1.5rem);
}

/* Bandeau d'aperçu. Il dit que la page n'est pas encore la leur. En bas et
   dans le flux : en position fixe il recouvrait le contenu en permanence, ce
   qui se lit comme un défaut et non comme une mention. */
.apercu {
  background: var(--papier-2);
  box-shadow: inset 0 1px 0 var(--trait-c);
  padding: 0.85rem var(--pad);
  text-align: center;
}
.apercu .eyebrow { color: var(--encre-3); }
.apercu strong { color: var(--sang); font-weight: 600; }

/* ── Cibles tactiles ────────────────────────────────────────────────────── */
/* Trois liens tombaient sous les 44 px au pouce. Le plus coûteux est le numéro
   du bloc Professionnels : sur le site d'une maison de viandes, le téléphone
   est la conversion, et ce bloc-là vise les bouchers et les restaurateurs.
   On agrandit la zone cliquable sans rien déplacer — le lien reste dans le fil
   de sa phrase, seule sa zone déborde. Même geste que sur le rail des rayons. */
@media (max-width: 900px) {
  .pro .apres .lien, .fil-ariane a, .nav a:has(> img) { position: relative; }
  .pro .apres .lien::after,
  .fil-ariane a::after { content: ''; position: absolute; inset: -15px 0; }
  .nav a:has(> img)::after { content: ''; position: absolute; inset: -4px -7px; }
}

/* ── Apparition au défilement ───────────────────────────────────────────── */
/* Une seule animation dans tout le site, et elle ne se rejoue jamais.
   `.js` est posé par une ligne dans le <head> : sans elle, les seize blocs de
   l'accueil restent à `opacity: 0` pour toujours si le script de fin de page
   ne s'exécute pas. La probabilité est faible, la conséquence est totale, et
   le garde-fou coûte une classe. */
@media (prefers-reduced-motion: no-preference) {
  .js .monte { opacity: 0; transform: translateY(14px); }
  .monte.vu {
    opacity: 1;
    transform: none;
    transition: opacity 0.7s ease-out, transform 0.7s ease-out;
  }
}

@media print {
  .nav, .apercu, .rayons { display: none; }
  body { background: #fff; color: #000; }
}
