/* ============================================================================
   LA PAGE D'ACCUEIL

   Le principe de mise en page, relevé sur le site de référence et appliqué ici :

   1. AUCUNE PHOTOGRAPHIE NE TOUCHE LE BORD DE L'ÉCRAN — sauf le ruban, qui
      sort par la droite pour dire qu'il continue, et sauf, au doigt, les deux
      seules photographies qui ne partagent leur bord gauche avec aucun texte :
      le bandeau de la carte et l'ouverture du contact. Voir `.photo-bordABord`
      dans `base.css`.

   2. DEUX PHOTOGRAPHIES NE SONT JAMAIS D'ACCORD. Pas la même largeur, pas le
      même format, pas la même hauteur de départ. Une grille régulière se lit
      comme un tableau ; un décalage se lit comme une page composée.

   3. LE VIDE EST UN ÉLÉMENT. Entre deux blocs, il y a parfois un écran entier
      de crème avec un simple dessin au trait posé au milieu. C'est là que la
      page respire, et c'est ce qui manquait le plus.

   4. LA COLONNE EST ÉTROITE. Sur un grand écran, une large bande de papier
      reste vide de chaque côté.
   ============================================================================ */

/* ============================================================================
   1. LE PREMIER ÉCRAN

   Un écran entier de crème, l'enseigne au milieu, huit dessins au trait autour.
   Rien d'autre. Le texte d'accroche a quitté cet écran : le nom de la maison est
   l'enseigne elle-même.

   LES DESSINS ONT DEUX ENCRES, ET C'EST LE SUJET DE LA PAGE.
   Deux sont en TERRE BATTUE — les verres et le couvert : c'est la table. Six
   sont en VERT SAPIN — le service, la raquette, le joueur, le filet, les
   chaussures, les balles : c'est le club. On lit les deux d'un coup d'œil, et
   on a compris où l'on est avant d'avoir lu une ligne.

   Les deux dessins de table sont DIAGONALEMENT OPPOSÉS : l'ocre traverse
   l'écran, et l'œil fait le trajet. C'est ce qui remplace la règle
   d'alternance du site modèle, devenue impossible à six contre deux.

   TOUT LE MOUVEMENT TIENT DANS UN SEUL CHIFFRE : `--p`, l'avancée du premier
   défilement, de 0 (rien n'a bougé) à 1 (on a quitté l'écran). Le script ne
   fait qu'écrire ce chiffre ; c'est la feuille de style qui en tire la chute
   et la vrille de chaque dessin. Une seule écriture par image de rendu, pas
   huit.

   L'arrivée, elle, ne passe pas par ce chiffre : elle a le sien, `--arrivee`,
   parce que la chute a un signe et que tous les dessins doivent entrer par le
   haut, ceux qui montent en partant comme ceux qui descendent.
   ============================================================================ */

/* Déclarer `--p` lui donne un type : il devient un vrai nombre, interpolable,
   et il vaut 0 tant que personne ne l'a écrit — y compris sans JavaScript. */
@property --p {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}

/* La montée de l'arrivée. Déclarée, elle s'interpole ; et elle vaut zéro tant
   qu'aucune animation ne l'écrit, c'est-à-dire au repos et sans JavaScript. */
@property --arrivee {
  syntax: "<length>";
  inherits: false;
  initial-value: 0px;
}

/* LE PREMIER ÉCRAN PREND L'ÉCRAN ENTIER, ET RIEN D'AUTRE N'Y ENTRE.

   Il valait d'abord un écran entier SANS QUE SA COMPOSITION SUIVE : sur un
   moniteur de 1300 px de haut, cela faisait 1 200 px de crème pour une enseigne
   de 150 et huit dessins de 200. Le client a envoyé la capture en disant « je
   ne veux pas de gros espace comme ça ». On l'a alors plafonné à 820 px, et la
   première rangée de photographies est remontée DANS le premier écran : « la
   héro ne doit pas être coupée par le carrousel, jamais ».

   La correction n'est pas dans la hauteur de la section : c'est que LA SECTION
   ET SA COMPOSITION SONT DEUX CHOSES. La section reprend l'écran entier, de
   sorte que rien ne peut s'y glisser ; et le champ de dessins, posé par-dessus,
   garde sa hauteur propre et se centre dedans.

   ⚠ UNE GRILLE A ÉTÉ ESSAYÉE ICI, PUIS RETIRÉE. Neuf cases, l'enseigne au
   milieu : deux cases ne se recouvrant jamais, le chevauchement devenait
   impossible. Mais réserver une rangée entière à l'enseigne coûte sa hauteur —
   380 px sur un grand écran — et les dessins tombaient à 77 px de bande là où
   ils en avaient 220. La composition se resserrait, et le client l'a vu tout de
   suite : « t'as décalé le titre, la hero ». Le site modèle ne fait pas cela
   non plus : ses dessins sont posés en absolu, et l'enseigne flotte au milieu.

   On garde donc le champ libre, et les recouvrements sont réglés là où ils
   naissent : dans les POSITIONS, les TAILLES et la TAILLE DE L'ENSEIGNE. */
.premier {
  position: relative;
  display: grid;
  place-items: center;
  min-height: calc(100svh - var(--entete-h));
  padding-block: var(--joint-serre);
  /* Au repos, aucun dessin ne sort du cadre — voir `.premier-piece`, qui tient
     chacun à l'intérieur. Le débordement n'est coupé que pour la chute : en
     quittant l'écran, les dessins sortent par le haut ou par le bas, et c'est
     ici qu'ils disparaissent, jamais dans une barre de défilement. */
  overflow: hidden;
}

@media (min-width: 1024px) {
  .premier {
    min-height: calc(100svh - var(--entete-h-lg));
  }
}

/* ---------------------------------------------------------------------------
   L'ENSEIGNE

   Elle occupe une bande centrale que les dessins ne franchissent jamais. Elle
   est composée, pas dessinée : tout son dispositif est dans le composant
   `.enseigne` de `composants.css`, y compris sa taille — qui se règle sur la
   hauteur de la fenêtre autant que sur sa largeur, précisément pour laisser
   cette bande libre. Ici, on ne règle que sa place et son mouvement.
   --------------------------------------------------------------------------- */
.premier-titre {
  position: relative;
  z-index: 1;
  margin: 0;
  transform: translateY(calc(var(--p) * -3rem)) scale(calc(1 - var(--p) * 0.06));
  opacity: calc(1 - var(--p) * 0.85);
}

/* ---------------------------------------------------------------------------
   LE CHAMP DE DESSINS

   Les dessins sont posés en pourcentages de ce champ : si le champ suit la
   fenêtre, ils s'écartent jusqu'aux quatre coins et la couronne se défait —
   c'est exactement le « gros espace » de la première capture. Le champ est donc
   borné à la hauteur pour laquelle la composition a été réglée, et centré dans
   la section, qui, elle, peut être aussi haute qu'on veut.
   --------------------------------------------------------------------------- */
.premier-champ {
  position: absolute;
  inset: 0;
  max-height: 820px;
  margin-block: auto;
  /* Un dessin ne prend jamais le clic : tout ce qui est cliquable est ailleurs. */
  pointer-events: none;
  /* Ils s'effacent en partant, sans disparaître tout à fait : il en reste une
     trace quand la bande d'accès direct arrive. */
  opacity: calc(1 - var(--p) * 0.7);
  --echelle: 0.54;
}

/* L'ÉCHELLE SUIT LA HAUTEUR AUTANT QUE LA LARGEUR.

   Elle ne suivait que la largeur, et c'est de là que venait la moitié des
   recouvrements : sur une fenêtre large mais basse — un portable de 1 280 × 800,
   le cas le plus courant — les dessins gardaient leur taille de grand écran
   pendant que la bande où les poser perdait un tiers de sa hauteur. Deux
   réglages qui ne parlent pas de la même chose. Chaque palier de hauteur rend
   donc un cran.

   Le cran de pleine taille attend 1 200 px, la largeur à partir de laquelle
   l'enseigne cesse d'être bornée : à 1 100 px les dessins passaient à la taille
   du grand écran pendant que la marge, elle, ne bougeait pas encore, et le
   service venait sur le nom de la maison. Les deux seuils sont le même. */
@media (min-width: 700px) {
  .premier-champ {
    --echelle: 0.72;
  }
}

@media (min-width: 1200px) {
  .premier-champ {
    --echelle: 1;
  }
}

@media (min-width: 1600px) {
  .premier-champ {
    --echelle: 1.06;
  }
}

@media (min-width: 700px) and (max-height: 900px) {
  .premier-champ {
    --echelle: 0.78;
  }
}

@media (min-width: 700px) and (max-height: 780px) {
  .premier-champ {
    --echelle: 0.66;
  }
}

/*
  Chaque dessin porte six réglages, et rien d'autre :

    --x --y ... où il est, en pourcentage du champ
    --l ....... sa largeur de référence, ramenée par --echelle
    --ratio ... les proportions de son image, pour connaître sa hauteur avant
                qu'elle ne soit chargée
    --r ....... de combien il penche au repos
    --chute ... de combien il descend — ou monte — quand on quitte l'écran
    --vrille .. de combien il tourne en chemin

  LES POSITIONS NE SONT PAS DE L'INSPIRATION. Elles ont été cherchées par la
  mesure : un programme place les dessins à cent soixante-neuf tailles d'écran,
  relève chaque recouvrement — entre deux dessins, et avec l'enseigne — et l'on
  ne garde que ce qui n'en produit aucun. Après toute retouche, relancer
  `node scripts/essai-premier-ecran.mjs`.

  LA CHUTE SUIT LA HAUTEUR : les dessins du haut MONTENT en partant, ceux du bas
  DESCENDENT. Le champ s'ouvre autour de l'enseigne au lieu de se refermer, et
  deux dessins ne peuvent donc pas se rejoindre en chemin. Une chute au hasard
  faisait passer le service sur le couvert à mi-parcours.

  La vrille, elle, reste libre : elle change de valeur et de signe d'un dessin à
  l'autre, et c'est elle qui porte le désordre. Tourner ne rapproche personne.
*/
.premier-piece {
  position: absolute;
  /* LA POSITION DEMANDÉE, PUIS LA POSITION TENUE.

     `--x` et `--y` disent où le dessin devrait être. Mais un dessin posé à 92 %
     de la hauteur avec 300 px de haut sort du cadre, et la section le coupe :
     « il ne doit jamais y avoir de choses coupées ». Chaque dessin calcule donc
     sa propre demi-boîte — celle de son image PENCHÉE, plus large et plus haute
     que l'image droite — et sa position est bornée entre cette demi-boîte et le
     bord opposé. Un dessin qui tiendrait déjà ne bouge pas d'un pixel ; celui
     qui sortait est ramené juste au bord. Les deux premières lignes sont la
     position demandée : un navigateur qui ne connaît pas `sin()` s'y arrête. */
  left: var(--x);
  top: var(--y);
  --largeur: calc(var(--l) * var(--echelle));
  --hauteur: calc(var(--largeur) / var(--ratio));
  /* Le sinus d'un angle négatif est négatif, et une boîte n'a pas de largeur
     négative : `max` en prend la valeur absolue. */
  --sinus: max(sin(var(--r)), -1 * sin(var(--r)));
  --demi-x: calc((var(--largeur) * cos(var(--r)) + var(--hauteur) * var(--sinus)) / 2 + 4px);
  --demi-y: calc((var(--hauteur) * cos(var(--r)) + var(--largeur) * var(--sinus)) / 2 + 4px);
  left: clamp(var(--demi-x), var(--x), calc(100% - var(--demi-x)));
  top: clamp(var(--demi-y), var(--y), calc(100% - var(--demi-y)));
  width: var(--largeur);
  height: auto;
  transform: translate(-50%, -50%)
    translateY(calc(var(--p) * var(--chute) + var(--arrivee)))
    rotate(calc(var(--r) + var(--p) * var(--vrille)));
}

/* Le navigateur n'est prévenu que sur les écrans qui ont de quoi tenir neuf
   couches composées. Au doigt, la promesse coûterait plus qu'elle ne rapporte. */
@media (min-width: 1100px) {
  .premier-piece {
    will-change: transform;
  }
}

/* LES HUIT DESSINS, ET LEURS DEUX ENCRES.

   Six sont en VERT SAPIN — le service, la raquette, le joueur, le filet, les
   chaussures, les balles : c'est le club. Deux sont en TERRE BATTUE — les
   verres et le couvert : c'est la table.

   La règle d'alternance du site modèle — jamais deux dessins de la même encre
   côte à côte — ne tient plus : avec six dessins d'une couleur et deux de
   l'autre, elle est arithmétiquement impossible. Elle est remplacée par une
   règle qui, elle, tient : LES DEUX DESSINS DE TABLE SONT DIAGONALEMENT
   OPPOSÉS — le couvert en haut à droite, les verres en bas à gauche. L'ocre
   traverse l'écran, et l'œil fait le trajet.

   Les largeurs ne sont pas égales : elles sont choisies pour que chaque dessin
   pèse OPTIQUEMENT autant que ses voisins. Les verres et le couvert sont des
   dessins couchés — trois fois plus larges que hauts pour les premiers — et il
   leur faut deux fois la largeur d'un dessin debout pour ne pas disparaître. */
.premier-piece--service {
  --ratio: calc(389 / 776);
  --x: 8%;
  --y: 23%;
  --l: 150px;
  --r: -13deg;
  --chute: -150px;
  --vrille: 15deg;
}

.premier-piece--baskets {
  --ratio: calc(400 / 222);
  --x: 33%;
  --y: 9%;
  --l: 190px;
  --r: 18deg;
  --chute: -175px;
  --vrille: -20deg;
}

.premier-piece--couvert {
  --ratio: calc(520 / 300);
  --x: 86%;
  --y: 11%;
  --l: 230px;
  --r: 8deg;
  --chute: -165px;
  --vrille: -14deg;
}

.premier-piece--raquette {
  --ratio: calc(240 / 373);
  --x: 7.5%;
  --y: 64%;
  --l: 130px;
  --r: 9deg;
  --chute: 150px;
  --vrille: 16deg;
}

/* Le tube de balles est le plus étroit de tous : à largeur égale il serait
   deux fois plus haut que le joueur. */
.premier-piece--balles {
  --ratio: calc(172 / 470);
  --x: 93%;
  --y: 47%;
  --l: 95px;
  --r: -8deg;
  --chute: 60px;
  --vrille: 21deg;
}

.premier-piece--verres {
  --ratio: calc(620 / 187);
  --x: 21%;
  --y: 88%;
  --l: 310px;
  --r: -6deg;
  --chute: 240px;
  --vrille: -11deg;
}

.premier-piece--filet {
  --ratio: calc(440 / 314);
  --x: 50%;
  --y: 92%;
  --l: 180px;
  --r: -15deg;
  --chute: 255px;
  --vrille: -19deg;
}

/* Le joueur est arrivé en septembre au trait continu, en remplacement du fusain
   précédent. Il est passé de debout à couché — 470 × 358 au lieu de 460 × 566 —
   et il lui faut donc plus de largeur pour peser autant. */
.premier-piece--joueur {
  --ratio: calc(470 / 358);
  --x: 81%;
  --y: 89%;
  --l: 200px;
  --r: -6deg;
  --chute: 250px;
  --vrille: -17deg;
}

/* ---------------------------------------------------------------------------
   TROIS DISPOSITIONS, ET C'EST LA PLACE LIBRE AUTOUR DE L'ENSEIGNE QUI LES
   DÉCIDE.

   L'enseigne est un bloc de texte : sa largeur suit celle de l'écran, sa
   hauteur non. Ce qu'il reste autour d'elle change donc de FORME selon la
   fenêtre, et une seule composition ne peut pas convenir aux trois cas.

     — Sur un grand écran, elle occupe six dixièmes de la largeur et six de la
       hauteur : il reste une COURONNE, et les huit dessins en font le tour.
     — Sur un écran étroit — un téléphone, une tablette debout, une fenêtre à
       moitié refermée — elle barre l'écran d'un bord à l'autre. Il ne reste que
       DEUX BANDES, l'une au-dessus, l'autre en dessous, et cinq dessins s'y
       rangent.
     — Sur un écran plus large que haut mais bas — un téléphone couché, un
       portable de 620 px — c'est l'inverse : plus de bandes, mais deux MARGES
       LATÉRALES, et quatre dessins s'y tiennent.

   Les seuils ne sont pas ronds par hasard : 900 px de large est la largeur à
   partir de laquelle les marges tiennent un dessin à côté de l'enseigne, et
   700 px de haut celle à partir de laquelle les bandes en tiennent un. En
   dessous, la composition CHANGE plutôt que de se serrer.
   --------------------------------------------------------------------------- */

/* DEUX BANDES : tout ce qui est plus étroit que 900 px.

   Cinq dessins, pas huit. Le relevé est sans appel : la bande au-dessus de
   l'enseigne fait 219 px sur un iPhone, et deux dessins l'un sous l'autre en
   demandent 280. On en a essayé sept, puis six ; à chaque fois deux d'entre eux
   se touchaient à l'une ou l'autre des tailles. Trois en haut, deux en bas. */
@media (max-width: 899px) {
  .premier-champ {
    --echelle: 0.54;
  }

  .premier-piece--service,
  .premier-piece--couvert,
  .premier-piece--joueur {
    display: none;
  }

  .premier-piece--balles {
    --x: 14%;
    --y: 14%;
    --chute: -190px;
  }

  .premier-piece--raquette {
    --x: 48%;
    --y: 12%;
    --chute: -180px;
  }

  .premier-piece--baskets {
    --x: 84%;
    --y: 15%;
    --chute: -170px;
  }

  .premier-piece--verres {
    --x: 25%;
    --y: 85%;
    --chute: 230px;
  }

  .premier-piece--filet {
    --x: 76%;
    --y: 86%;
    --chute: 250px;
  }
}

/* SUR UN TÉLÉPHONE, LES DESSINS SE RAPPROCHENT DE L'ENSEIGNE ET GROSSISSENT.

   Collés aux quatre coins et réglés à l'échelle d'une tablette, ils laissaient
   cent cinquante pixels de crème vide entre eux et le nom de la maison — un
   tiers de l'écran pour rien. Sous 600 px de large, l'enseigne est étroite et
   les bandes sont hautes : c'est la seule taille où l'on peut à la fois les
   grossir d'un dixième et les descendre de neuf points vers le centre. Ces deux
   chiffres sont le maximum relevé : un cran de plus, et le tube de balles
   touchait l'enseigne sur une tablette de 540 px. */
@media (max-width: 599px) {
  .premier-champ {
    --echelle: 0.6;
  }

  .premier-piece--balles {
    --y: 23%;
  }

  .premier-piece--raquette {
    --y: 21%;
  }

  .premier-piece--baskets {
    --y: 24%;
  }

  .premier-piece--verres {
    --y: 76%;
  }

  .premier-piece--filet {
    --y: 77%;
  }
}

/* Une fenêtre étroite ET basse : les deux bandes se resserrent, et les dessins
   reculent vers les bords plutôt que de venir sur l'enseigne. */
@media (max-width: 899px) and (max-height: 720px) {
  .premier-champ {
    --echelle: 0.54;
  }

  .premier-piece--balles {
    --y: 17%;
  }

  .premier-piece--raquette {
    --y: 15%;
  }

  .premier-piece--baskets {
    --y: 18%;
  }

  .premier-piece--verres {
    --y: 83%;
  }

  .premier-piece--filet {
    --y: 84%;
  }
}

/* Le plus petit téléphone passe en dernier : il est étroit avant d'être bas, et
   c'est sa largeur qui commande la taille des dessins. */
@media (max-width: 379px) {
  .premier-champ {
    --echelle: 0.48;
  }
}

/* DEUX MARGES : plus large que haut. L'enseigne y prend toute la hauteur, et
   c'est sur les côtés qu'il reste de la place. La règle se lit sur la FORME de
   la fenêtre et non sur sa largeur : un téléphone couché fait 844 px de large,
   un portable bas 1 280, et les deux demandent la même chose. */
@media (max-height: 699px) and (min-aspect-ratio: 13 / 10) {
  .premier-champ {
    --echelle: 0.62;
  }

  .premier-piece--service,
  .premier-piece--couvert,
  .premier-piece--joueur,
  .premier-piece--baskets {
    display: none;
  }

  .premier-piece--raquette {
    --x: 9%;
    --y: 26%;
    --chute: -150px;
  }

  .premier-piece--balles {
    --x: 92%;
    --y: 28%;
    --chute: -140px;
  }

  .premier-piece--verres {
    --x: 9%;
    --y: 78%;
    --chute: 220px;
  }

  .premier-piece--filet {
    --x: 92%;
    --y: 78%;
    --chute: 230px;
  }
}

/* Un petit téléphone couché tombe dans les deux cas à la fois : il est étroit
   ET bas. C'est la disposition en marges qui l'emporte, et l'enseigne y perd un
   cran de corps pour laisser passer les dessins. */
@media (max-width: 899px) and (max-height: 620px) {
  .premier-champ {
    --echelle: 0.42;
  }
}


/* ---------------------------------------------------------------------------
   L'ARRIVÉE

   Elle n'existe que si le script a marqué la page. Sans JavaScript, sans
   IntersectionObserver, ou si le système demande un mouvement réduit, les
   dessins sont simplement là — c'est la même règle que partout ailleurs sur
   le site.

   `backwards` et non `both` : passé la dernière image, la règle rend la main,
   et `--p` redevient celui que le défilement écrit. Sans quoi le dessin
   resterait figé à sa position d'arrivée.
   --------------------------------------------------------------------------- */
[data-js="on"] .premier-piece {
  animation: premier-arrivee var(--dur-apparition) var(--sortie-expo) backwards;
  animation-delay: var(--retard, 0s);
}

.premier-piece--baskets {
  --retard: 60ms;
}
.premier-piece--balles {
  --retard: 120ms;
}
.premier-piece--raquette {
  --retard: 180ms;
}
.premier-piece--couvert {
  --retard: 250ms;
}
.premier-piece--verres {
  --retard: 320ms;
}
.premier-piece--filet {
  --retard: 390ms;
}
.premier-piece--service {
  --retard: 460ms;
}
.premier-piece--joueur {
  --retard: 530ms;
}

/* Un dixième de chute, pas trois : le dessin part d'à peine plus haut que sa
   place. À trois dixièmes, ceux du haut arrivaient de 80 px au-dessus du cadre
   et entraient coupés ; à un dixième, la réserve de `--demi-y` absorbe la part
   du trajet qui se voit, et le geste reste lisible. */
/* L'ARRIVÉE NE SE MESURE PLUS EN CHUTE, MAIS EN PIXELS.

   Elle empruntait `--chute`, et cela ne pouvait plus tenir : la chute a
   maintenant un SIGNE — les dessins du haut montent en partant, ceux du bas
   descendent — et ceux du haut seraient donc entrés par le bas.

   Chacun monte désormais de la même chose, quatorze pixels. Deux dessins
   gardent ainsi exactement leur écart pendant toute l'arrivée ; et comme
   l'enseigne, elle, ne bouge pas — la règle ne pose `--arrivee` que sur les
   pièces — il suffit que cette montée reste sous la marge la plus étroite du
   champ pour que rien ne se touche jamais.

   C'est le fondu qui porte l'arrivée ; la montée ne fait que la poser. */
@keyframes premier-arrivee {
  from {
    opacity: 0;
    --arrivee: -14px;
  }
}

/* ============================================================================
   3. LA BANDE D'ACCÈS DIRECT

   Les quatre seules questions qu'on se pose devant un site de restaurant. Le
   site de référence n'en a pas — mais il vend des chambres, pas un service à
   midi. Ici, savoir tout de suite si c'est ouvert et où c'est vaut mieux qu'un
   écran de vide de plus.
   ============================================================================ */
/* La bande est réglée par ses deux filets : elle ne peut pas porter de vide en
   dessous sans que le filet du bas descende avec. C'est donc la section qui la
   suit qui porte le joint entier — un cas, une règle, écrite ici. */
.accesdirect-bande {
  border-block: 1px solid var(--trait);
}

.accesdirect-bande + .section {
  padding-top: var(--joint);
}

.accesdirect-grille {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  max-width: var(--colonne);
  margin-inline: auto;
}

@media (min-width: 900px) {
  .accesdirect-grille {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

.accesdirect-cellule {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--sp-3);
  min-height: 118px;
  padding: var(--sp-5) var(--marge);
  border-left: 1px solid var(--trait);
  border-top: 1px solid var(--trait);
  transition: background-color var(--dur-vif) var(--sortie);
}

/* Les filets ne doublent jamais celui de la bande, ni celui du bord. */
.accesdirect-cellule:nth-child(-n + 2) {
  border-top: 0;
}

.accesdirect-cellule:nth-child(odd) {
  border-left: 0;
}

@media (min-width: 900px) {
  .accesdirect-cellule {
    border-top: 0;
    border-left: 1px solid var(--trait);
  }

  .accesdirect-cellule:first-child {
    border-left: 0;
  }
}

@media (hover: hover) and (pointer: fine) {
  .accesdirect-cellule:hover {
    background-color: var(--sable);
  }
}

.accesdirect-cellule:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

.accesdirect-libelle {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--sp-3);
  /* L'accent, pas l'encre pâle : ces quatre libellés sont les seuls repères de
     la bande, et ils reculaient au lieu d'appeler. Sur la crème ils passent en
     terre battue, sur l'olive ils héritent de la balle — sans qu'on ait à
     l'écrire deux fois. */
  color: var(--accent);
  font-family: var(--mono);
  font-size: var(--fs-exergue);
  letter-spacing: 0.18em;
  line-height: 1.2;
  text-transform: uppercase;
  transition: color var(--dur-vif) var(--sortie);
}

.accesdirect-cellule:hover .accesdirect-libelle,
.accesdirect-cellule:focus-visible .accesdirect-libelle {
  color: var(--accent);
}

.accesdirect-libelle svg {
  flex: none;
}

.accesdirect-valeur {
  color: var(--encre);
  font-family: var(--serif);
  font-size: var(--fs-plat);
  font-weight: 500;
  line-height: 1.3;
  font-variant-numeric: tabular-nums;
}

/* ============================================================================
   4. LE MOTIF TEXTE / PHOTOGRAPHIE

   Toutes les proportions de ce bloc viennent d'une MESURE prise au navigateur
   sur le site de référence, à 1600 px de large — pas d'une estimation à l'œil :

     bande de contenu ...... 1216 px  (marges de 192 px de chaque côté)
     photographie ..........  614 px  soit 38 % de l'écran, en paysage 4/3
     colonne de texte ......  602 px
     texte, largeur réelle ..  456 px
     vide entre le texte et la photographie ..............  90 px
     vide entre le texte et le bord extérieur de la bande .  56 px

   Le premier relevé donnait 736 px pour la photographie : il mesurait l'image
   AGRANDIE par la dérive (× 1,2), pas son cadre. Toutes les valeurs ci-dessus
   sont prises sur les cadres, transformations neutralisées.
   ============================================================================ */
.duo-duo {
  display: grid;
  gap: var(--sp-8);
  align-items: start;
}

@media (min-width: 900px) {
  /*
    Par défaut la photographie est à gauche : c'est elle qui prend les 55 %.
    Quand elle passe à droite, les deux proportions s'échangent avec elle —
    sinon la photographie se retrouve dans la colonne étroite et rétrécit d'un
    bloc à l'autre, ce qui casse tout le rythme.
  */
  .duo-duo {
    grid-template-columns: minmax(0, 614fr) minmax(0, 602fr);
    gap: 0;
  }

  .duo-droite {
    grid-template-columns: minmax(0, 602fr) minmax(0, 614fr);
  }

  .duo-droite .duo-visuel {
    order: 2;
  }

  /*
    Le texte se serre CONTRE la photographie et laisse tout le vide du côté
    extérieur. C'est ce déséquilibre voulu qui fait respirer la page — un texte
    centré dans sa colonne donnerait deux blocs qui se font face, donc un
    tableau.
  */
  /*
    Le texte est plaqué contre le bord EXTÉRIEUR de sa colonne, à 56 px, et
    laisse donc 90 px entre lui et la photographie. C'est ce déséquilibre qui
    fait respirer la page : un texte centré dans sa colonne donnerait deux
    blocs qui se font face, donc un tableau.
  */
  .duo-texte {
    margin-left: auto;
    padding-right: 56px;
  }

  .duo-droite .duo-texte {
    margin-left: 0;
    padding-right: 0;
    padding-left: 56px;
  }
}

.duo-visuel {
  position: relative;
}

.duo-texte {
  /* 512 px de boîte pour 456 px de texte : les 56 px de marge extérieure sont
     compris dedans. */
  max-width: 32rem;
}

/* `--fs-h1` est réservé au h1 unique de chaque page. Les `.duo-titre` de
   l'accueil sont des h2 : les composer au même corps que le titre de la page
   annulait la différence entre le nom de la maison et un intertitre. */
.duo-titre {
  font-size: var(--fs-h2);
  letter-spacing: -0.025em;
  line-height: 1.06;
}

/* Sauf sur les pages intérieures, où `.duo-titre` EST le h1 de la page. */
h1.duo-titre {
  font-size: var(--fs-h1);
}

.duo-texte > * + * {
  margin-top: var(--sp-5);
}

/* Le détail recule derrière l'accroche, et non l'inverse. */
.duo-texte > p + p {
  color: var(--encre-2);
}

.duo-texte > [data-actions] {
  margin-top: var(--sp-7);
}

/* Quarante-huit pixels séparent le bouton de ce qui le précède, et c'est la
   bonne mesure quand un paragraphe s'intercale entre le titre et lui. Les deux
   cartes n'en ont plus depuis que leurs chapôs sont supprimés : le bouton suit
   le titre directement, et l'écart tombe à vingt-quatre. « Il y a trop d'espace
   entre le début et la fin. » */
.duo-titre + [data-actions] {
  margin-top: var(--sp-5);
}

@media (max-width: 899px) {
  .duo-texte {
    order: 1;
  }

  .duo-visuel {
    order: 2;
  }

}

/* Un duo sans image : le titre prend la colonne de lecture et rien d'autre. */
.duo-seul {
  display: block;
}

.duo-seul .duo-texte {
  max-width: var(--colonne-lecture);
  margin-inline: 0;
}

/* ============================================================================
   5 bis. LA RESPIRATION

   UN ÉCRAN DE CRÈME, UN DESSIN AU MILIEU, ET RIEN D'AUTRE.

   Ce bloc remplace les deux paires d'images du site modèle — deux
   photographies côte à côte, avec un dessin au trait posé sous la plus courte.
   Il les remplace pour une raison arithmétique, pas pour une raison de goût :
   une paire demande deux photographies qui ne servent nulle part ailleurs sur
   la page, et cette page en a exactement autant qu'elle a d'emplacements. La
   dernière paire aurait dû répéter une image déjà vue plus haut, ce que la
   maison s'interdit.

   Mais le principe qu'elle portait, lui, reste : « le vide est un élément ».
   Il s'agit d'un silence entre deux propos, et c'est le seul endroit du site,
   hors du premier écran, où un dessin passe en grand. On garde donc le silence
   et on retire les photographies — ce qui, à bien y regarder, est le silence
   plus franc des deux.

   Il se sépare de ses voisines par le SOUFFLE, et symétriquement : on entre
   dans le silence et on en sort de la même façon. Les deux voisines apportant
   chacune leur demi-joint, celui-ci pose les deux qui manquent.
   ============================================================================ */
/* ⚠ CE BLOC A ÉTÉ RAMENÉ À SA PLACE EN SEPTEMBRE. Il mesurait 596 px de haut
   pour un seul dessin, et c'était le plus gros vide de la page. Un silence
   qu'on entend est une respiration ; un silence qu'on traverse est un trou.
   Il ne pose donc plus que le joint courant, et le dessin a grandi pour occuper
   ce qui reste. */
.respiration-section {
  padding-block: calc(var(--joint) - var(--demi-joint));
}

/* Le dessin n'est PAS centré sur la page : il est posé au tiers, du côté
   opposé à la photographie du bloc précédent. Le milieu exact est la seule
   position qui ne veuille rien dire. */
.respiration-dessin {
  display: block;
  width: clamp(150px, 22vw, 320px);
  height: auto;
  margin-inline: auto;
  transform: rotate(-5deg);
}

@media (min-width: 700px) {
  .respiration-dessin {
    margin-left: 62%;
    transform: translateX(-50%) rotate(-5deg);
  }
}

/* ============================================================================
   6. LE TABLEAU D'AFFICHAGE

   C'EST LE BLOC QUI REMPLACE LA GRILLE DE QUATRE ASSIETTES du site modèle, et
   ce n'est pas un caprice de mise en page : c'est une conséquence de la carte.

   La carte imprimée du restaurant marque d'une étoile les plats qui signent la
   maison. Ils sont onze, pas quatre. Une grille de photographies en aurait
   montré quatre — donc en aurait caché sept — et il aurait fallu quatre
   photographies de studio qu'on n'a pas.

   Alors on ne montre pas : ON AFFICHE. Onze lignes, le nom de code à gauche, le
   plat au milieu, le prix à droite, dans la machine à écrire, sur des filets
   d'un pixel. C'est le tableau d'un club de tennis — celui où l'on lit les
   tirages et les résultats — et c'est la seule chose de cette page qui ne
   pourrait se trouver sur aucun autre site de restaurant.

   Deux colonnes sur grand écran, une seule au doigt. Les lignes se lisent de
   haut en bas dans chaque colonne : le tableau est rempli par le HTML dans
   l'ordre, jamais par `grid-auto-flow: column`, qui obligerait à connaître le
   nombre de lignes d'avance.
   ============================================================================ */
.tableau-entete {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: flex-end;
  gap: var(--sp-5) var(--sp-7);
  margin-bottom: var(--sp-7);
}

.tableau-grille {
  border-top: 1px solid var(--trait);
}

@media (min-width: 900px) {
  .tableau-grille {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: clamp(2.5rem, 5vw, 5rem);
    /* Sur deux colonnes, le filet du haut ne peut plus être celui de la grille :
       il doit s'interrompre dans la gouttière. Il repasse donc aux deux
       premières lignes, qui sont les seules à ouvrir une colonne. */
    border-top: 0;
  }

  .tableau-ligne:nth-child(-n + 2) {
    border-top: 1px solid var(--trait);
  }
}

.tableau-ligne {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  gap: var(--sp-2) var(--sp-4);
  padding-block: var(--sp-4);
  border-bottom: 1px solid var(--trait);
}

.tableau-code {
  grid-column: 1 / -1;
  color: var(--accent);
  font-family: var(--mono);
  font-size: var(--fs-exergue);
  letter-spacing: 0.18em;
  line-height: 1.2;
  text-transform: uppercase;
}

.tableau-nom {
  font-family: var(--serif);
  font-size: var(--fs-plat);
  font-weight: 500;
  line-height: 1.25;
}

/* Chaque ligne mène à sa famille dans la carte. Le lien ne se souligne pas au
   repos — dix soulignements de suite feraient un peigne — mais il se souligne au
   survol, et l'ancre saute sous la barre du haut grâce à `--ancre-haut`.

   LA CIBLE EST PLUS HAUTE QUE LE TEXTE. Le nom du plat ne fait que dix-sept
   pixels de haut, et il y en a dix à la file : au doigt, on tape entre deux
   lignes. Le remplissage l'agrandit, la marge négative le remet exactement où
   il était — la cible grandit, le dessin ne bouge pas. Onze pixels de chaque
   côté, contre seize de vide entre deux lignes : deux cibles voisines ne se
   recouvrent jamais. */
.tableau-nom a {
  display: block;
  padding-block: 0.35rem;
  margin-block: -0.35rem;
  color: var(--encre);
  text-decoration: none;
  transition: color var(--dur-vif) var(--sortie);
}

.tableau-nom a:hover,
.tableau-nom a:focus-visible {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 0.18em;
}

.tableau-prix {
  color: var(--encre);
  font-family: var(--mono);
  font-size: var(--fs-corps-sm);
  letter-spacing: 0.04em;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* ============================================================================
   5. LE BANDEAU

   UNE PHOTOGRAPHIE, EN GRAND, ET PAS UN MOT.

   Il a d'abord porté une légende dans sa marge — une étiquette en machine à
   écrire et quatre lignes de texte. Le client a répondu : « les images avec un
   petit texte comme ça je ne veux pas ». Il a raison, et pour une raison qui se
   dit : sur une bande verte, un paragraphe posé à côté d'une photographie ne se
   lit pas comme une légende mais comme un reste de mise en page — on ne sait
   pas s'il faut lire ou regarder, et on ne fait ni l'un ni l'autre.

   La photographie prend donc toute la bande. C'EST LE SEUL ENDROIT DU SITE OÙ
   UNE IMAGE TOUCHE LES DEUX BORDS DE L'ÉCRAN, et c'est ce qui en fait un
   événement plutôt qu'une illustration. La règle « aucune photographie ne
   touche le bord » vaut toujours pour les huit autres : une exception qui
   n'arrive qu'une fois n'affaiblit pas la règle, elle la souligne.
   ============================================================================ */
.bandeau-section {
  padding-block: var(--joint-souffle);
}

/* Ni `.wrap` ni marge : la bande verte lui sert de cadre. */
.bandeau-bandeau {
  display: block;
}

.bandeau-cadre {
  --ratio: 16 / 9;
}

/* Sur un très grand écran, un 16/9 pleine largeur deviendrait une frise : on
   l'aplatit à mesure qu'on s'élargit, pour que la salle reste une salle. */
@media (min-width: 1400px) {
  .bandeau-cadre {
    --ratio: 21 / 8;
  }
}

/* ============================================================================
   7. À TABLE — LE RUBAN DE PHOTOGRAPHIES

   IL AVANCE TOUT SEUL, SANS FIN, ET ON PEUT LE POUSSER À LA MAIN À TOUT MOMENT.
   Les trois à la fois, et c'est ce qui a demandé de tout reprendre.

   La première version était une animation CSS : une piste en `translateX`,
   arrêtée au survol. Elle bougeait bien, mais elle avait deux défauts que le
   client a vus tout de suite — elle s'arrêtait dès qu'on passait la souris
   dessus, et on ne pouvait pas la pousser, une piste en `transform` n'étant
   pas une zone défilante.

   LA VERSION ACTUELLE NE DÉPLACE PLUS LA PISTE : ELLE FAIT DÉFILER LA BOÎTE.
   Le ruban est une zone `overflow-x: auto` ordinaire — donc glissable au doigt,
   au trackpad et à la souris, comme n'importe quelle bande — et un petit script
   ajoute quelques dixièmes de pixel à son `scrollLeft` à chaque image de rendu.
   Les deux gestes s'additionnent au lieu de se disputer : on pousse le ruban où
   l'on veut, et il repart de là où on l'a laissé, sans qu'on ait eu à
   l'arrêter ni à le relancer.

   LA BOUCLE EST SANS FIN, et l'arithmétique doit être exacte. La piste porte
   DEUX FOIS la même série, la seconde marquée `aria-hidden`. Quand le
   défilement atteint la moitié de la piste, le script lui retranche cette
   moitié : on se retrouve au même pixel de la première série, et rien ne se
   voit.

   Pour que cette moitié tombe juste, il faut que la piste mesure un nombre
   ENTIER de motifs. Dix vignettes et neuf joints font 10i + 9j, dont la moitié
   vaut 5i + 4,5j — un demi-joint de trop à chaque tour, qui se verrait au bout
   de quelques boucles. D'où la marge intérieure de fin, et elle seule : elle
   ajoute le dixième joint, la piste vaut 10i + 10j, et la moitié vaut
   exactement une série.
   ============================================================================ */
.ruban-section {
  padding-block: 0;
}

.ruban-piste {
  position: relative;
  /* Le joint est déclaré ici parce que DEUX règles en ont besoin : l'écart
     entre les vignettes, et la marge de fin qui ferme la boucle. Écrit deux
     fois, il finirait par diverger. */
  --ruban-joint: clamp(14px, 2.75vw, 44px);
}

.ruban-defile {
  display: flex;
  gap: var(--ruban-joint);
  /* Le dixième joint. Voir le commentaire du haut : sans lui, la boucle
     dérive d'un demi-joint par tour. */
  padding-inline-end: var(--ruban-joint);
  overflow-x: auto;
  /* `auto` et non `smooth` : le script écrit `scrollLeft` soixante fois par
     seconde, et un défilement animé transformerait chaque écriture en une
     petite course qui n'aurait pas fini quand la suivante arrive. */
  scroll-behavior: auto;
  /* Le geste horizontal reste au ruban, le vertical à la page. */
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  /* La souris peut l'attraper : c'est le seul endroit du site où le curseur
     annonce qu'on peut saisir quelque chose. */
  cursor: grab;
}

.ruban-defile[data-saisi="true"] {
  cursor: grabbing;
  /* Pendant qu'on tire, le pointeur ne doit pas sélectionner les images ni
     déclencher le défilement natif : c'est le script qui mène. */
  user-select: none;
}

.ruban-defile::-webkit-scrollbar {
  display: none;
}

/* MOUVEMENT RÉDUIT : le script ne démarre pas — il lit lui-même la préférence —
   et la série en double s'efface, n'ayant de raison d'être que pour la boucle.
   Le ruban reste ce qu'il est : une bande qu'on pousse. */
@media (prefers-reduced-motion: reduce) {
  .ruban-vignette[aria-hidden="true"] {
    display: none;
  }

  .ruban-defile {
    scroll-snap-type: x mandatory;
    scroll-padding-inline-start: var(--marge);
    padding-inline-start: var(--marge);
  }

  .ruban-vignette {
    scroll-snap-align: start;
  }
}

.ruban-vignette {
  flex: 0 0 76vw; /* la mesure au doigt */
}

/* À la souris, la vignette valait 63 % de l'écran : sur un grand moniteur, un
   gros plan d'assiette y occupait presque toute la largeur et se lisait comme
   une texture, plus comme un plat. Elle prend maintenant un peu plus du tiers,
   sans jamais dépasser 520 px. Trois photographies tiennent ainsi de front et
   une quatrième déborde : un ruban qui avance doit montrer qu'il est un ruban,
   ce que deux vignettes ne font pas. */
@media (min-width: 900px) {
  .ruban-vignette {
    flex-basis: min(34vw, 520px);
  }
}

/* Ici, et seulement ici, toutes les vignettes ont le même format. Le décalage
   est la règle de la page — mais dans un ruban qui avance, des hauteurs
   inégales se lisent comme un défaut d'alignement, pas comme un parti pris. */
.ruban-vignette .photo-cadre {
  --ratio: 1264 / 800;
}

/* Une image qu'on tire ne doit pas partir toute seule : le navigateur propose
   de la déplacer dès qu'on la saisit, ce qui annule le geste. */
.ruban-vignette img {
  -webkit-user-drag: none;
  user-select: none;
}

/* ============================================================================
   8. VENIR CHEZ NOUS
   ============================================================================ */
/* LE TITRE ET SES DEUX COLONNES SONT UN SEUL BLOC.

   Ils n'en faisaient pas un. « Venir chez nous » se posait seul, puis venaient
   soixante-quatre pixels de crème, puis un filet, puis « ADRESSE » : au doigt,
   le titre flottait au-dessus d'un trait qui n'était pas le sien, et les deux
   moitiés se lisaient comme deux choses sans rapport. Le client : « le venir
   chez nous, c'est trop moche, c'est séparé, ça n'a aucun sens ».

   Le filet devient celui du TITRE — c'est la composition de toute la maison,
   celle des titres de famille de la carte : un titre tenu par un trait, et son
   contenu dessous. L'écart tombe de soixante-quatre à vingt-quatre pixels, et
   la première colonne rend le trait qu'elle portait, qui ferait doublon. */
.infospratiques-titre {
  padding-bottom: var(--sp-5);
  border-bottom: 1px solid var(--trait);
}

.infospratiques-colonnes {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-6);
  margin-top: var(--sp-5);
}

@media (min-width: 768px) {
  .infospratiques-colonnes {
    grid-template-columns: repeat(2, 1fr);
    gap: 0;
  }
}

.infospratiques-colonne {
  padding-block: var(--sp-5);
  border-top: 1px solid var(--trait);
}

/* Le trait du titre tient déjà lieu de séparateur : la première colonne
   n'ajoute pas le sien, et se colle à celui du dessus. */
.infospratiques-colonne:first-child {
  padding-top: 0;
  border-top: 0;
}

@media (min-width: 768px) {
  .infospratiques-colonne {
    padding: var(--sp-6) var(--sp-7);
    border-top: 0;
    border-left: 1px solid var(--trait);
  }

  .infospratiques-colonne:first-child {
    padding-top: var(--sp-6);
    padding-left: 0;
    border-left: 0;
  }

  .infospratiques-colonne:last-child {
    padding-right: 0;
  }

  /* Les deux colonnes n'ont pas le même nombre de lignes — l'adresse en fait
     trois, la réservation cinq — et leurs boutons se posaient chacun à sa
     hauteur, à 50 px l'un de l'autre. La colonne devient une pile, pour que
     son bouton puisse descendre au pied : voir `.infospratiques-action`. */
  .infospratiques-colonne {
    display: flex;
    flex-direction: column;
  }
}

.infospratiques-colonne h3 {
  margin-bottom: var(--sp-5);
  color: var(--encre-3);
  font-family: var(--mono);
  font-size: var(--fs-exergue);
  font-weight: 400;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

.infospratiques-colonne p,
.infospratiques-colonne address {
  color: var(--encre);
  font-size: var(--fs-corps-sm);
  line-height: 1.7;
}

.infospratiques-colonne p a,
.infospratiques-colonne address a {
  padding-block: var(--sp-2);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}

/* Le numéro est l'action principale de la page : sa cible fait quarante-quatre
   pixels, la mesure du doigt, et non celle de ses lettres. */
.infospratiques-telephone {
  display: inline-block;
  padding-block: 0.3rem;
  display: inline-block;
  margin-bottom: var(--sp-2);
  font-family: var(--serif);
  font-size: var(--fs-h3);
  font-weight: 500;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
}

.infospratiques-telephone:hover {
  text-decoration: underline;
  text-underline-offset: 5px;
}

.infospratiques-action {
  margin-top: var(--sp-6);
}

/* Côte à côte, le bouton descend au pied de sa colonne : « Itinéraire » et
   « Appeler » se posent sur la même ligne, quel que soit le nombre de lignes
   au-dessus. Le remplissage garde l'écart d'avec le texte. */
@media (min-width: 768px) {
  .infospratiques-action {
    margin-top: auto;
    padding-top: var(--sp-6);
  }
}

/* Le plan reste dans la colonne, comme tout le reste : il ne va pas d'un bord
   de l'écran à l'autre. Au doigt, il suit le bouton « Appeler » de plus près :
   96 px de marge, ajoutés au vide de la colonne, faisaient 120 px de crème. */
.plan-bloc {
  margin-top: var(--sp-7);
}

@media (min-width: 768px) {
  .plan-bloc {
    margin-top: var(--sp-9);
  }
}

/* ============================================================================
   9. LE GRAND APPEL

   La réservation se fait au téléphone, uniquement. Ce n'est pas une limite à
   cacher : c'est le dernier mot de la page, écrit en grand.
   ============================================================================ */
.bandeauappel-bande {
  padding-block: var(--demi-joint);
  text-align: center;
}

.bandeauappel-titre {
  font-size: var(--fs-h2);
  letter-spacing: -0.02em;
  line-height: 1.06;
}

.bandeauappel-numero {
  display: inline-block;
  margin-top: var(--sp-6);
  color: var(--encre);
  font-family: var(--serif);
  font-size: clamp(2.25rem, 1.2rem + 4.6vw, 4rem);
  font-weight: 500;
  letter-spacing: -0.03em;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 12px;
  transition: text-decoration-thickness var(--dur-vif) var(--sortie);
}

.bandeauappel-numero:hover {
  text-decoration-thickness: 2px;
}

.bandeauappel-detail {
  margin-top: var(--sp-7);
  color: var(--encre-2);
  font-family: var(--mono);
  font-size: var(--fs-legende);
  line-height: 1.8;
}
