/* ==========================================================================
   Sapora — one-page restaurant template
   --------------------------------------------------------------------------
   One stylesheet, no build step, no framework. Read it top to bottom:

     1. Design tokens        colours, type scale, spacing — change them here
     2. Reset and base       the handful of rules the browser gets wrong
     3. Header and menu      including the burger below 991px
     4. Hero                 the oversized title and the five floating dishes
     5. Shared pieces        buttons, section headings, star ratings
     6. About us
     7. Menu                 the course filter is CSS only (`:has()`)
     8. How we make food
     9. Fresh flavours
    10. Reviews              the two sliding rows
    11. Locations
    12. Follow               the endless photo strip
    13. Footer
    14. Scroll reveals       hidden only when `js` is on <html>
    15. Breakpoints          991 / 767 / 479 — the type scale is redeclared
                             here and nowhere else

   The type scale lives in `--titre-1` and `--titre-2`. A heading never carries
   its own media query: change the token and every screen follows.
   ========================================================================== */

/* ==========================================================================
   1. Design tokens
   ========================================================================== */

:root {
  /* --- Colours ----------------------------------------------------------
     Near-black rather than pure black: it carries a trace of red, which
     keeps it from looking blue next to the photos.

     The accent exists in three shades and they move together. `--accent`
     passes AA on white (5.2:1) and on the grey fill (4.6:1); `--accent-fonce`
     is the hover; `--accent-clair` is the only one legible on a dark photo
     (8.6:1 on --noir). Changing one means recomputing the other two.        */
  --noir: #0c0407;
  --blanc: #fff;
  --fond: #f0f0f0;
  --fond-doux: #f7f7f7;
  --trait: #eaeaea;
  --trait-fort: #dfdfdf;
  --trait-leger: #f1f1f1;
  --gris: #666;
  --gris-clair: #808080;

  --accent: #c0421f;
  --accent-fonce: #93300f;
  --accent-clair: #f0906b;
  --etoile: #ffa600;

  /* --- Type -------------------------------------------------------------
     Boldonse ships a single weight and it is already very heavy. Never ask
     for 700: the browser would fake it and smear the shapes.               */
  --police-titre: "Boldonse", "Arial Black", sans-serif;
  --police-texte: "Inter", system-ui, sans-serif;

  --titre-1: 72px;
  --titre-1-hauteur: 1.32;
  --titre-2: 48px;
  --titre-2-hauteur: 1.45;
  --titre-3: 20px;
  --texte: 14px;
  --texte-petit: 12px;

  /* --- Spacing ----------------------------------------------------------- */
  --marge-section: 80px;
  --gouttiere: 24px;
  --cadre-max: 1200px;
  --cadre-marge: 20px;

  /* --- Motion ------------------------------------------------------------
     `--reveal-duree` and `--reveal-pas` drive every scroll reveal; the
     original ran 400ms with 100ms between siblings, and so does this.       */
  --reveal-duree: 400ms;
  --reveal-retard: 200ms;
  --reveal-pas: 100ms;
  --defile-duree: 30s;
  --sortie: cubic-bezier(0.22, 1, 0.36, 1);
}

/* ==========================================================================
   2. Reset and base
   ========================================================================== */

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

/* Chromium leaves <summary> in content-box whatever `*` says, and the two
   extra pixels of border show up as a misaligned button. */
summary {
  box-sizing: border-box;
}

html {
  /* `clip`, not `hidden`: `hidden` would turn the root into a scroll
     container and break every `position: sticky` further down. */
  overflow-x: clip;
  scroll-behavior: smooth;
  /* The sticky header would otherwise cover the top of an anchored section. */
  scroll-padding-top: 90px;
}

body {
  margin: 0;
  background: var(--blanc);
  color: var(--noir);
  font-family: var(--police-texte);
  font-size: var(--texte);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

img {
  display: block;
  max-width: 100%;
  height: auto;
}

h1,
h2,
h3,
p,
figure,
blockquote {
  margin: 0;
}

ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

a {
  color: inherit;
  text-decoration: none;
}

button {
  font: inherit;
  color: inherit;
}

fieldset {
  margin: 0;
  padding: 0;
  border: 0;
}

/* One focus ring for the whole page, drawn outside the element so it never
   changes the layout. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

.visuellement-cachee {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.lien-evitement {
  position: absolute;
  top: 8px;
  left: 8px;
  z-index: 20;
  padding: 10px 16px;
  background: var(--noir);
  color: var(--blanc);
  font-weight: 600;
  transform: translateY(-200%);
}

.lien-evitement:focus {
  transform: none;
}

/* The <svg> that only carries <symbol>: out of the flow, never painted. */
.reserve-icones {
  position: absolute;
  width: 0;
  height: 0;
}

.cadre {
  width: 100%;
  max-width: calc(var(--cadre-max) + 2 * var(--cadre-marge));
  margin-inline: auto;
  padding-inline: var(--cadre-marge);
}

/* ==========================================================================
   3. Header and menu
   ========================================================================== */

.bandeau {
  position: sticky;
  top: 0;
  z-index: 10;
  padding-block: 12px;
  background: color-mix(in srgb, var(--blanc) 88%, transparent);
  backdrop-filter: blur(12px);
  transition: box-shadow 200ms ease;
}

/* main.js adds this once the page has scrolled past the first screen. */
.bandeau.est-detache {
  box-shadow: 0 1px 0 var(--trait);
}

.bandeau__interieur {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}

.marque {
  font-family: var(--police-titre);
  font-size: 20px;
  line-height: 1;
  letter-spacing: -0.02em;
  text-transform: uppercase;
}

.pied .marque {
  color: var(--blanc);
}

.nav__liste {
  display: flex;
  align-items: center;
  gap: 24px;
}

.nav__lien {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding-block: 7px;
  font-weight: 500;
  letter-spacing: -0.01em;
  background: none;
  border: 0;
  cursor: pointer;
  transition: color 160ms ease;
}

.nav__lien:hover {
  color: var(--accent);
}

.nav__chevron {
  width: 14px;
  height: 14px;
  transition: transform 300ms ease;
}

.nav__item--deroulant {
  position: relative;
}

/* The panel is closed by default and both states are described here, so the
   script only ever flips `aria-expanded`. */
.nav__panneau {
  position: absolute;
  top: calc(100% + 10px);
  left: -12px;
  min-width: 190px;
  padding: 8px;
  background: var(--blanc);
  border: 1px solid var(--trait);
  opacity: 0;
  transform: translateY(-8px);
  visibility: hidden;
  transition:
    opacity 300ms ease,
    transform 300ms ease,
    visibility 0s linear 300ms;
}

.nav__item--deroulant:has([aria-expanded="true"]) .nav__panneau {
  opacity: 1;
  transform: none;
  visibility: visible;
  transition-delay: 0s;
}

.nav__item--deroulant:has([aria-expanded="true"]) .nav__chevron {
  transform: rotate(-180deg);
}

.nav__sous-lien {
  display: block;
  padding: 8px 12px;
  font-weight: 500;
  transition:
    background 160ms ease,
    color 160ms ease;
}

.nav__sous-lien:hover {
  background: var(--fond);
  color: var(--accent);
}

/* The gap between the trigger and the panel: without this bridge the pointer
   leaves the item on the way down and the panel closes under the cursor. */
.nav__item--deroulant::after {
  content: "";
  position: absolute;
  inset: 100% -12px auto;
  height: 12px;
}

.bandeau__actions {
  display: flex;
  align-items: center;
  gap: 20px;
}

.bandeau__reserver {
  font-weight: 500;
  letter-spacing: -0.01em;
  transition: color 160ms ease;
}

.bandeau__reserver:hover {
  color: var(--accent);
}

.hamburger {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 42px;
  height: 42px;
  padding: 0;
  background: var(--fond);
  border: 1px solid var(--trait-fort);
  cursor: pointer;
}

.hamburger__barre {
  width: 18px;
  height: 2px;
  margin-inline: auto;
  background: currentColor;
  transition: transform 240ms ease;
}

.hamburger[aria-expanded="true"] .hamburger__barre:first-child {
  transform: translateY(3.5px) rotate(45deg);
}

.hamburger[aria-expanded="true"] .hamburger__barre:last-child {
  transform: translateY(-3.5px) rotate(-45deg);
}

/* ==========================================================================
   4. Hero
   ========================================================================== */

.hero {
  position: relative;
  padding-block: var(--marge-section);
  /* Two of the dishes hang 36px past the container on purpose. `clip` rather
     than `hidden`, so the hero never becomes a scroll container. */
  overflow-x: clip;
}

.hero__interieur {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

.hero__pastille {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 12px 4px 4px;
  background: var(--blanc);
  border: 1px solid var(--trait);
  border-radius: 50px;
  font-weight: 500;
}

.hero__convives {
  width: 60px;
  height: auto;
  margin-right: 4px;
}

.hero__pastille-chiffre {
  color: var(--gris);
}

/* 790px and not a count in `ch`: Boldonse has very wide figures, so `ch`
   would put the line break somewhere else at every font size. This width is
   the one that breaks the sentence after "bite", like the original. */
.hero__titre {
  max-width: 790px;
  margin-top: 24px;
  font-family: var(--police-titre);
  font-size: var(--titre-1);
  font-weight: 400;
  line-height: var(--titre-1-hauteur);
  text-transform: uppercase;
}

.hero__texte {
  max-width: 65ch;
  margin-top: 12px;
  letter-spacing: -0.01em;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--gouttiere);
  margin-top: 32px;
}

/* --- The five dishes ------------------------------------------------------
   The big one stays in the flow: it is what gives the hero its height, and
   the four others hang off it in absolute position, exactly where the
   original put them. Below 767px all five come back into a swipeable row.

   The offsets are written in pixels rather than percentages because that is
   what the source does, and because a percentage of the container would move
   the composition apart as the screen grows. */

.hero__galerie {
  position: static;
  width: 100%;
  margin-top: 80px;
}

.hero__piece--1 {
  position: relative;
  width: 340px;
  max-width: 100%;
  margin-inline: auto;
}

.hero__piece--2,
.hero__piece--3,
.hero__piece--4,
.hero__piece--5 {
  position: absolute;
}

.hero__plat {
  width: 100%;
  height: auto;
  border-radius: 50%;
  aspect-ratio: 1;
  object-fit: cover;
}

.hero__piece--2 {
  left: 120px;
  top: 447px;
  width: 205px;
}

.hero__piece--3 {
  left: -36px;
  top: 271px;
  width: 145px;
}

.hero__piece--4 {
  right: 120px;
  top: 447px;
  width: 205px;
}

.hero__piece--5 {
  right: -36px;
  top: 271px;
  width: 145px;
}

/* The chat-bubble corner: square where the tail would be. Offsets are read
   from the dish itself, so a bubble follows its photo at every width. */
.hero__bulle {
  position: absolute;
  left: 58%;
  top: 8%;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 12px 16px;
  background: var(--blanc);
  border: 1px solid var(--trait-leger);
  border-radius: 12px 12px 12px 0;
  font-size: 16px;
  font-weight: 500;
  letter-spacing: -0.03em;
  white-space: nowrap;
}

.hero__bulle img {
  width: 18px;
  height: 18px;
}

.hero__piece--1 .hero__bulle {
  top: 16%;
}

.hero__piece--3 .hero__bulle {
  left: 65%;
  top: -12%;
}

/* The last one sits on the other side, so its tail moves to the other
   corner: a bubble whose tail points away from its photo reads as a mistake. */
.hero__piece--5 .hero__bulle {
  left: auto;
  right: 55%;
  top: -4%;
  border-radius: 12px 12px 0 12px;
}

/* ==========================================================================
   5. Shared pieces — buttons, section headings, ratings
   ========================================================================== */

.section {
  padding-block: var(--marge-section);
}

.section__titre {
  font-family: var(--police-titre);
  font-size: var(--titre-2);
  font-weight: 400;
  line-height: var(--titre-2-hauteur);
  text-transform: uppercase;
}

.section__texte {
  max-width: 62ch;
  letter-spacing: -0.01em;
}

.section__entete {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-bottom: 40px;
}

.section__entete--centre {
  align-items: center;
  text-align: center;
}

/* --- Buttons -------------------------------------------------------------
   Square corners everywhere, exactly like the original. The arrow slides out
   to the right on hover and a second one slides in from the left, which is
   the same two-step move the source ran at 500ms.                          */

.bouton {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 11.5px 20px;
  border: 1px solid transparent;
  font-size: var(--texte);
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.12;
  cursor: pointer;
  transition:
    background 200ms ease,
    color 200ms ease,
    border-color 200ms ease;
}

.bouton--plein {
  background: var(--noir);
  color: var(--blanc);
}

.bouton--plein:hover {
  background: var(--accent);
}

.bouton--doux {
  background: var(--fond);
  border-color: var(--trait-fort);
}

.bouton--doux:hover {
  background: var(--blanc);
  border-color: var(--noir);
}

.bouton--petit {
  padding: 8px 14px;
  font-size: var(--texte-petit);
}

.bouton__fleche {
  width: 16px;
  height: 16px;
  flex: none;
  transition: transform 500ms var(--sortie);
}

.bouton:hover .bouton__fleche {
  transform: translateX(4px);
}

/* Phone and plus keep still: only an arrow reads as "it goes somewhere". */
.bouton__fleche--fixe {
  transition: none;
}

.bouton:hover .bouton__fleche--fixe {
  transform: none;
}

/* --- Star ratings --------------------------------------------------------- */

.note {
  display: inline-flex;
  align-items: center;
  gap: 2px;
}

.note--ligne {
  gap: 3px;
}

.note__etoile {
  width: 14px;
  height: 14px;
  flex: none;
  fill: var(--etoile);
}

.note__etoile--vide {
  fill: var(--trait-fort);
}

.note__chiffre {
  margin-left: 5px;
  color: var(--gris);
}

/* ==========================================================================
   6. About us
   ========================================================================== */

.histoire__grille {
  display: grid;
  /* minmax(0, …) and never 1fr: a grid column defaults to `auto` as its
     minimum, so one long word would push the column past its share. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--gouttiere);
  align-items: start;
}

.histoire__gauche {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* The gap before the two small photos is deliberate: it lines their top edge
   up with the claim in the right-hand column. */
.histoire__paire {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 20px;
  margin-top: 140px;
  max-width: 460px;
}

.histoire__petite {
  width: 100%;
  aspect-ratio: 220 / 270;
  object-fit: cover;
}

.histoire__droite {
  display: flex;
  flex-direction: column;
  align-items: start;
  gap: 16px;
}

.histoire__grande {
  width: 100%;
  aspect-ratio: 589 / 340;
  object-fit: cover;
}

.histoire__accroche {
  font-size: var(--titre-3);
  font-weight: 600;
  letter-spacing: -0.04em;
}

.histoire__droite .bouton {
  margin-top: 8px;
}

/* ==========================================================================
   7. Menu — the course filter is pure CSS
   ==========================================================================
   Each dish lists its courses in `data-cours`. Ticking a radio flips one
   `:has()` rule on the section, and every dish that does not carry the course
   is taken out. No script, and it still works with JavaScript off. */

.filtres {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
  margin-bottom: 16px;
}

.filtre__radio {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
}

.filtre {
  padding: 11.5px 20px;
  background: var(--fond);
  border: 1px solid var(--trait-fort);
  font-weight: 600;
  letter-spacing: -0.01em;
  cursor: pointer;
  transition:
    background 200ms ease,
    color 200ms ease,
    border-color 200ms ease;
}

.filtre:hover {
  border-color: var(--noir);
}

/* The original gave the selected tab no mark at all — you could not tell
   which course you were looking at. Here it inverts. */
.filtre__radio:checked + .filtre {
  background: var(--noir);
  border-color: var(--noir);
  color: var(--blanc);
}

.filtre__radio:focus-visible + .filtre {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* Filled by main.js only when the filter changes: `role="status"` is there to
   announce a change, not to state the obvious on arrival. Empty, it must not
   leave a gap. */
.filtres__compte {
  margin-bottom: 24px;
  color: var(--gris);
  text-align: center;
}

.filtres__compte:empty {
  margin-bottom: 0;
}

.carte-section:has(#cours-signature:checked) .plat:not([data-cours~="signature"]),
.carte-section:has(#cours-matin:checked) .plat:not([data-cours~="matin"]),
.carte-section:has(#cours-midi:checked) .plat:not([data-cours~="midi"]),
.carte-section:has(#cours-soir:checked) .plat:not([data-cours~="soir"]) {
  display: none;
}

.grille-plats {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--gouttiere);
  /* Each card grows on its own when its details panel opens, instead of
     stretching the whole row. */
  align-items: start;
}

.plat {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.plat__photo {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
}

.plat__ligne {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-top: 4px;
}

.plat__nom {
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.03em;
  line-height: 1.2;
}

.plat__prix {
  flex: none;
  font-weight: 500;
  letter-spacing: -0.01em;
}

.plat__texte {
  font-size: var(--texte-petit);
  line-height: 1.5;
}

.plat__actions {
  display: flex;
  align-items: start;
  gap: 12px;
  margin-top: 4px;
}

.plat__fiche > summary {
  display: inline-flex;
  list-style: none;
}

.plat__fiche > summary::-webkit-details-marker {
  display: none;
}

.plat__fiche[open] > summary .bouton__fleche,
.plat__fiche[open] > summary {
  color: var(--accent);
}

.plat__fiche[open] > summary .bouton__fleche {
  transform: rotate(90deg);
}

.plat__detail {
  margin-top: 10px;
  padding: 12px;
  background: var(--fond-doux);
  border: 1px solid var(--trait-leger);
  font-size: var(--texte-petit);
  line-height: 1.6;
}

/* ==========================================================================
   8. How we make food
   ========================================================================== */

.savoir-faire__grille {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--gouttiere);
}

.etape {
  display: flex;
  flex-direction: column;
  padding: 24px;
  background: var(--fond-doux);
  border: 1px solid rgb(0 0 0 / 0.06);
}

.etape__icone {
  width: 72px;
  height: 72px;
  /* The gap between icon and title is what gives the card its height in the
     original: 155px of nothing, on purpose. */
  margin-bottom: 155px;
}

.etape__icone--vert {
  color: #1f9d55;
}

.etape__icone--bleu {
  color: #1d6fd0;
}

.etape__icone--orange {
  color: #e06a12;
}

.etape__titre {
  margin-bottom: 8px;
  font-size: var(--titre-3);
  font-weight: 600;
  letter-spacing: -0.04em;
  line-height: 1.2;
}

.etape__texte {
  max-width: 42ch;
  letter-spacing: -0.01em;
}

/* ==========================================================================
   9. Fresh flavours
   ========================================================================== */

.saveurs__grille {
  display: grid;
  grid-template-columns: minmax(0, 694fr) minmax(0, 486fr);
  gap: var(--gouttiere);
  align-items: start;
}

.saveurs__texte-zone {
  display: flex;
  flex-direction: column;
  gap: 32px;
}

.saveurs__titre {
  max-width: 12ch;
}

.saveurs__colonnes {
  display: grid;
  grid-template-columns: minmax(0, 234fr) minmax(0, 436fr);
  gap: var(--gouttiere);
}

.saveurs__surtitre {
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.03em;
}

.saveurs__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 24px;
}

.saveurs__paire {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 20px;
  max-width: 460px;
  margin-left: auto;
}

.saveurs__petite {
  width: 100%;
  aspect-ratio: 220 / 262;
  object-fit: cover;
}

.saveurs__grande {
  width: 100%;
  aspect-ratio: 486 / 677;
  object-fit: cover;
}

/* ==========================================================================
   10. Reviews — two rows sliding in opposite directions
   ==========================================================================
   Without JavaScript the row is an ordinary horizontal scroller: you swipe
   it, you tab into it, nothing is hidden. main.js clones the cards and the
   `js` class hands the row over to the animation. */

.defile {
  overflow-x: auto;
  scrollbar-width: none;
  scroll-snap-type: x proximity;
}

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

.js .defile {
  overflow-x: hidden;
}

.defile + .defile {
  margin-top: var(--gouttiere);
}

.defile__piste {
  display: flex;
  gap: var(--gouttiere);
  width: max-content;
  padding-inline: var(--cadre-marge);
}

.defile__piste > * {
  scroll-snap-align: start;
}

.js .defile__piste--gauche {
  animation: defile-gauche var(--defile-duree) linear infinite;
}

.js .defile__piste--droite {
  animation: defile-droite var(--defile-duree) linear infinite;
}

.js .defile__piste--lente {
  --defile-duree: 45s;
}

/* Reading a review while it slides away is impossible, and so is clicking a
   photo. Both stop the row. */
.js .defile:hover .defile__piste,
.js .defile:focus-within .defile__piste {
  animation-play-state: paused;
}

/* The clone is exactly half the track, so -50% lands on the same frame. */
@keyframes defile-gauche {
  to {
    transform: translateX(calc(-50% - var(--gouttiere) / 2));
  }
}

@keyframes defile-droite {
  from {
    transform: translateX(calc(-50% - var(--gouttiere) / 2));
  }
}

.avis__carte {
  display: flex;
  gap: var(--gouttiere);
  width: 545px;
  flex: none;
  padding: 12px;
  background: var(--fond-doux);
  border: 1px solid rgb(0 0 0 / 0.06);
}

.avis__portrait {
  width: 245px;
  height: 245px;
  flex: none;
  object-fit: cover;
}

.avis__contenu {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding-block: 8px;
}

/* The quotation marks are written inside the quote, not added with `::before`:
   a pseudo-element sits before the indentation of the HTML, which renders as
   a space, and the opening mark ends up floating away from its first word. */
.avis__texte {
  font-weight: 500;
  letter-spacing: -0.01em;
}

.avis__signature {
  margin-top: auto;
  letter-spacing: -0.01em;
}

.avis__signature b {
  font-weight: 600;
}

.avis__signature span {
  color: var(--gris);
}

.avis__signature span::before {
  content: " — ";
}

/* ==========================================================================
   11. Locations
   ========================================================================== */

.adresses__grille {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--gouttiere);
}

.lieu {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.lieu__photo {
  width: 100%;
  aspect-ratio: 588 / 381;
  object-fit: cover;
  margin-bottom: 8px;
}

.lieu__nom {
  font-size: var(--titre-3);
  font-weight: 600;
  letter-spacing: -0.04em;
}

.lieu__ligne {
  letter-spacing: -0.01em;
}

.lieu__ligne a {
  text-decoration: underline;
  text-underline-offset: 3px;
  transition: color 160ms ease;
}

.lieu__ligne a:hover {
  color: var(--accent);
}

/* ==========================================================================
   12. Follow — the endless photo strip
   ========================================================================== */

.social__case {
  position: relative;
  width: 276px;
  flex: none;
  overflow: hidden;
}

.social__case img {
  width: 100%;
  height: 334px;
  object-fit: cover;
  transition: transform 500ms var(--sortie);
}

/* Alternating heights: without them the strip reads as a plain filmstrip. */
.social__case--basse img {
  height: 261px;
}

.social__marque {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background: rgb(0 0 0 / 0.2);
  opacity: 0;
  transition: opacity 500ms ease;
}

.social__marque svg {
  width: 24px;
  height: 24px;
  color: var(--blanc);
  transform: scale(0.5);
  transition: transform 500ms ease;
}

.social__case:hover img {
  transform: scale(1.1);
}

.social__case:hover .social__marque {
  opacity: 1;
}

.social__case:hover .social__marque svg {
  transform: none;
}

/* ==========================================================================
   13. Footer
   ========================================================================== */

.pied {
  padding-top: var(--marge-section);
  padding-bottom: 24px;
  background: var(--noir);
  color: var(--blanc);
}

.pied__grille {
  display: grid;
  grid-template-columns: minmax(0, 1.9fr) repeat(3, minmax(0, 1fr));
  gap: var(--gouttiere);
  padding-bottom: 60px;
}

.pied__marque {
  display: flex;
  flex-direction: column;
  align-items: start;
  gap: 16px;
  max-width: 34ch;
}

.pied__texte {
  color: rgb(255 255 255 / 0.72);
  letter-spacing: -0.01em;
}

.pied__reseaux {
  display: flex;
  gap: 20px;
  margin-top: 8px;
}

.pied__reseaux svg {
  width: 24px;
  height: 24px;
  fill: currentColor;
  color: var(--gris-clair);
  transition: color 200ms ease;
}

.pied__reseaux a:hover svg {
  color: var(--blanc);
}

.pied__titre {
  margin-bottom: 20px;
  color: rgb(255 255 255 / 0.7);
  font-size: var(--texte);
  font-weight: 400;
  letter-spacing: -0.01em;
}

.pied__horaires {
  display: flex;
  flex-direction: column;
  gap: 12px;
  letter-spacing: -0.01em;
}

.pied__liens {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* The label is written twice and the pair slides up on hover — the same
   trick the original used, minus the animation engine. */
.pied__lien {
  display: block;
  height: 24px;
  overflow: hidden;
  font-weight: 500;
  letter-spacing: -0.01em;
}

.pied__lien span {
  display: block;
  height: 24px;
  transition: transform 300ms var(--sortie);
}

.pied__lien:hover span {
  transform: translateY(-24px);
}

.pied__bas {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 12px;
  padding-top: 24px;
  border-top: 1px solid rgb(255 255 255 / 0.14);
  color: rgb(255 255 255 / 0.72);
}

/* ==========================================================================
   14. Scroll reveals
   ==========================================================================
   The hidden state is written under `.js`, and enhance.js is what puts that
   class on <html>. Strip the scripts and every block is visible, in place,
   with no animation — which is also what a printer and a crawler see.

   The original moved 50px up, faded in and lifted a 5px blur over 400ms,
   with 100ms between siblings. Same numbers here. */

.js [data-reveal] {
  opacity: 0;
  transform: translateY(50px);
  filter: blur(5px);
}

.js [data-reveal].est-visible {
  opacity: 1;
  transform: none;
  filter: none;
  transition:
    opacity var(--reveal-duree) ease,
    transform var(--reveal-duree) ease,
    filter var(--reveal-duree) ease;
  transition-delay: calc(var(--reveal-retard) + var(--reveal-rang, 0) * var(--reveal-pas));
}

/* The rank is written in the HTML as `data-reveal-rang` and read here. Doing
   it with attribute selectors rather than an inline style is what keeps the
   page compatible with a strict CSP: `style-src 'self'` blocks style
   attributes, whoever writes them. Need a seventh step? Add a line. */
[data-reveal-rang="1"] {
  --reveal-rang: 1;
}
[data-reveal-rang="2"] {
  --reveal-rang: 2;
}
[data-reveal-rang="3"] {
  --reveal-rang: 3;
}
[data-reveal-rang="4"] {
  --reveal-rang: 4;
}
[data-reveal-rang="5"] {
  --reveal-rang: 5;
}
[data-reveal-rang="6"] {
  --reveal-rang: 6;
}

/* ==========================================================================
   15. Breakpoints
   ==========================================================================
   The four widths the original was drawn for. Only the tokens change here,
   plus the handful of grids that have to fold. */

@media (max-width: 991px) {
  :root {
    --titre-1: 50px;
    --titre-1-hauteur: 1.32;
    --titre-2: 40px;
    --marge-section: 64px;
  }

  .hamburger {
    display: flex;
    order: 3;
  }

  .bandeau__interieur {
    flex-wrap: wrap;
  }

  /* Closed by default on this width; the button toggles `aria-expanded` and
     both states are described from that single source of truth.

     `minmax(0, 0fr)` and never plain `0fr`: an `fr` track keeps an automatic
     minimum, so the row would refuse to shrink past the list's own padding
     and the closed menu would leave a 24px band under the header. Same trap
     as `minmax(0, 1fr)` on columns, one axis over. */
  .js .nav {
    display: grid;
    grid-template-rows: minmax(0, 0fr);
    order: 4;
    width: 100%;
    transition: grid-template-rows 300ms ease;
  }

  .js .nav > .nav__liste {
    min-height: 0;
    overflow: hidden;
  }

  .js .bandeau:has([data-nav-bouton][aria-expanded="true"]) .nav {
    grid-template-rows: minmax(0, 1fr);
  }

  .nav__liste {
    flex-direction: column;
    align-items: start;
    gap: 4px;
    padding-block: 12px;
  }

  .nav__lien {
    font-size: 16px;
  }

  .nav__panneau {
    position: static;
    opacity: 1;
    transform: none;
    visibility: visible;
    padding: 0 0 0 12px;
    border: 0;
    transition: none;
  }

  .bandeau__actions {
    order: 2;
    margin-left: auto;
  }

  /* The menu keeps three columns here and only drops to two at 767: the
     original does the same, and it is what keeps the eight dishes from
     turning into a very long column on a tablet. */
  .grille-plats {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 12px;
  }

  .histoire__grille,
  .saveurs__grille,
  .savoir-faire__grille {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .savoir-faire__grille,
  .histoire__paire,
  .saveurs__paire {
    gap: 20px;
  }

  /* The 140px breathing space only makes sense while the two columns sit side
     by side; once they are narrow it is just a hole. */
  .histoire__paire {
    margin-top: 40px;
  }

  .histoire__paire,
  .saveurs__paire {
    max-width: none;
  }

  .saveurs__grande {
    grid-column: 1 / -1;
    aspect-ratio: 951 / 640;
  }

  .etape__icone {
    margin-bottom: 32px;
  }

  /* The strip shrinks with the screen instead of keeping its desktop size:
     six photos 276px wide on a tablet is a very tall section for very little. */
  .social__case {
    width: 220px;
  }

  .social__case img {
    height: 268px;
  }

  .social__case--basse img {
    height: 209px;
  }

  .hero__galerie {
    margin-top: 60px;
  }

  .hero__piece--1 {
    width: 250px;
  }

  .hero__piece--2 {
    left: 57px;
    top: 444px;
    width: 160px;
  }

  .hero__piece--3 {
    left: 0;
    top: 304px;
    width: 120px;
  }

  .hero__piece--4 {
    right: 57px;
    top: 444px;
    width: 160px;
  }

  .hero__piece--5 {
    right: 0;
    top: 304px;
    width: 120px;
  }
}

@media (max-width: 767px) {
  :root {
    --titre-1: 40px;
    --titre-2: 32px;
    --marge-section: 56px;
  }

  .bandeau__reserver {
    display: none;
  }

  .grille-plats {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 20px;
  }

  /* About us stacks: two columns of 350px would leave the text too narrow to
     read comfortably, and the original stacks here too. */
  .histoire__grille,
  .saveurs__grille,
  .saveurs__colonnes {
    grid-template-columns: minmax(0, 1fr);
  }

  .saveurs__colonnes {
    gap: 12px;
  }

  .saveurs__paire,
  .histoire__paire {
    margin-left: 0;
  }

  /* The two rooms stay side by side down to 479: they are photographs, and a
     photograph shown 350px wide is still a photograph. */
  .adresses__grille {
    gap: 20px;
  }

  .etape__icone {
    margin-bottom: 24px;
  }

  .social__case {
    width: 180px;
  }

  .social__case img {
    height: 218px;
  }

  .social__case--basse img {
    height: 170px;
  }

  /* The floating dishes come back into the flow: five circles in a row you
     can swipe, rather than a composition that no longer has room.
     `width: 100%` matters — the hero is a centred flex column, so without it
     the row would take its content width and push the page sideways. */
  .hero__galerie {
    display: flex;
    width: 100%;
    gap: 16px;
    margin-top: 48px;
    padding-bottom: 8px;
    overflow-x: auto;
    scrollbar-width: none;
  }

  .hero__galerie::-webkit-scrollbar {
    display: none;
  }

  .hero__piece,
  .hero__piece--1,
  .hero__piece--2,
  .hero__piece--3,
  .hero__piece--4,
  .hero__piece--5 {
    position: relative;
    left: auto;
    right: auto;
    top: auto;
    width: 150px;
    margin-inline: 0;
    flex: none;
  }

  .hero__bulle {
    position: static;
    display: flex;
    justify-content: center;
    margin-top: 8px;
    padding: 8px 12px;
    font-size: 14px;
  }

  .hero__piece--2 .hero__bulle,
  .hero__piece--3 .hero__bulle {
    border-radius: 12px 12px 12px 0;
  }

  /* The review card turns into a column: side by side, the photo would leave
     the quote about twenty characters per line. */
  .avis__carte {
    width: 292px;
    flex-direction: column;
    gap: 12px;
  }

  .avis__portrait {
    width: 100%;
    height: 180px;
  }

  .pied__grille {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    row-gap: 40px;
  }

  .pied__marque {
    grid-column: 1 / -1;
  }
}

@media (max-width: 479px) {
  :root {
    --titre-1: 30px;
    --titre-2: 28px;
  }

  .grille-plats,
  .adresses__grille,
  .savoir-faire__grille {
    grid-template-columns: minmax(0, 1fr);
  }

  .histoire__paire,
  .saveurs__paire {
    margin-top: 24px;
    gap: 14px;
  }

  .hero__titre {
    max-width: none;
  }

  .plat__actions {
    flex-wrap: wrap;
  }

  .pied__grille {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ==========================================================================
   Reduced motion
   --------------------------------------------------------------------------
   Delays count as motion too: a reveal that waits 700ms before appearing is
   still movement spread over time. Everything lands on its final state at
   once, and the sliding rows become plain scrollers again.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    transition-delay: 0s !important;
  }

  .js [data-reveal] {
    opacity: 1;
    transform: none;
    filter: none;
  }

  .js .defile {
    overflow-x: auto;
  }

  .js .defile__piste {
    animation: none;
  }
}
