/* ------------------------------------------------------------------ *
 * Portfolio — feuille de style unique.
 * Parti pris : le dessin porte la couleur, l'interface n'en a pas.
 * ------------------------------------------------------------------ */

:root {
  --encre:       #1a1a1a;
  --encre-pale:  #8a8a8a;
  --papier:      #fbfbfa;
  --trait:       #e6e4e0;
  --largeur-menu: 230px;
  --marge:       clamp(1.5rem, 4vw, 3.5rem);
  --ecart:       0;        /* espacement de la grille, lu aussi par app.js
                              (app.js relit column-gap : une seule valeur
                              suffit pour l'horizontale et la verticale) */
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--papier);
  color: var(--encre);
  font: 400 15px/1.65 "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-feature-settings: "kern" 1;
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; }

/* ---------- Grain du papier -------------------------------------------- *
 * Un bruit fractal très léger, en fond seulement : le papier a une matière,
 * les dessins posés dessus restent nets. Le motif est un SVG de 180px qui se
 * raccorde à la répétition — aucun fichier à charger, aucune couture visible.
 * ----------------------------------------------------------------------- */

body {
  background-color: var(--papier);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23g)' opacity='0.15'/%3E%3C/svg%3E");
  background-repeat: repeat;
}

/* Sur un écran à haute densité, le motif serait deux fois trop fin pour se
   voir : on le grossit pour conserver le même grain à l'œil. */
@media (min-resolution: 2dppx) {
  body { background-size: 90px 90px; }
}

/* ---------- Structure ---------------------------------------------- */

.mise-en-page {
  display: grid;
  grid-template-columns: var(--largeur-menu) 1fr;
  min-height: 100vh;
}

/* ---------- Menu de gauche ------------------------------------------ */

/* Tout est centré dans la colonne : logo, rubriques et mention. Le retrait
   est donc symétrique — sans quoi l'axe du menu ne tomberait pas au milieu
   de la colonne. */
.menu {
  position: sticky;
  top: 0;
  align-self: start;
  height: 100vh;
  padding: var(--marge);
  padding-top: calc(var(--marge) + env(safe-area-inset-top, 0px));
  border-right: 1px solid var(--trait);
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

/* `align-items: center` sur le menu fait que les enfants ne s'étirent plus.
   Sans largeur définie ici, un logo SVG — qui n'a pas de dimensions propres,
   seulement un rapport — s'effondrerait à 0 x 0. */
.signature {
  width: 100%;
  margin: 0 0 3rem;
  font-size: 1.05rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: lowercase;
}
.signature a { text-decoration: none; }

/* Visible seulement sur petit écran (voir plus bas). */
.logo-mobile { display: none; }

/* Le logo remplace le texte : on retire l'interlettrage du nom et on borne
   la hauteur pour qu'un fichier trop grand ne pousse pas le menu. */
.signature.avec-logo {
  letter-spacing: normal;
  text-transform: none;
}
.signature img {
  display: block;
  width: auto;
  max-width: 100%;
  max-height: 90px;
  margin-inline: auto;
}

.menu nav {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  align-items: center;
}

/* Zone cliquable généreuse : confortable au doigt comme à la souris. */
.menu nav a { padding: 0.4rem 0; }

.menu nav a {
  text-decoration: none;
  color: var(--encre-pale);
  padding-bottom: 2px;
  border-bottom: 1px solid transparent;
  transition: color 0.18s ease, border-color 0.18s ease;
}
.menu nav a:hover { color: var(--encre); }
.menu nav a.actif {
  color: var(--encre);
  border-bottom-color: var(--encre);
}

.pied {
  margin-top: auto;
  margin-bottom: 0;
  font-size: 0.75rem;
  color: var(--encre-pale);
}

/* ---------- Contenu de droite --------------------------------------- */

/* La galerie va jusqu'aux bords de la fenêtre : aucune marge blanche autour
   des dessins. Seules les rubriques de texte gardent du retrait, sinon les
   lignes colleraient au bord de l'écran. */
.contenu {
  padding: 0;
  padding-top: env(safe-area-inset-top, 0px);
  min-width: 0;
}

.texte {
  padding: var(--marge);
  padding-top: calc(var(--marge) + env(safe-area-inset-top, 0px));
}

.rubrique { display: none; }
.rubrique.active { display: block; animation: apparait 0.3s ease; }

@keyframes apparait {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: none; }
}

.texte { max-width: calc(36rem + 2 * var(--marge)); }

/* ---------- À propos : illustration et texte ---------------------------- *
 * L'illustration occupe un cadre au format A5 (148 x 210 mm), à gauche du
 * texte. Le rapport est fixé, donc l'image le remplit et déborde de ses
 * côtés les plus longs plutôt que de laisser des bandes vides.
 * ----------------------------------------------------------------------- */

.a-propos {
  display: grid;
  grid-template-columns: minmax(180px, 440px) minmax(0, 36rem);
  gap: calc(var(--marge) * 1.5);
  align-items: center;      /* le texte est centré à la verticale de l'illustration */
  padding: var(--marge);
  padding-top: calc(var(--marge) + env(safe-area-inset-top, 0px));
}

/* Le texte a déjà son retrait via la grille. */
.a-propos .texte { padding: 0; max-width: none; }

.illustration {
  margin: 0;
  aspect-ratio: 148 / 210;   /* A5 portrait */
  background: #f0efec;
  overflow: hidden;
}

.illustration img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.texte h2 {
  margin: 0 0 1.2rem;
  font-size: 1rem;
  font-weight: 500;
  letter-spacing: 0.06em;
}
.texte p { margin: 0 0 1rem; color: #3a3a3a; }
.texte code {
  font-size: 0.85em;
  background: #f0efec;
  padding: 0.1em 0.35em;
  border-radius: 2px;
}

/* ---------- Galerie (masonry) ---------------------------------------- *
 * Grille en maçonnerie : chaque dessin garde son format d'origine, aucune
 * image n'est recadrée, et les hauteurs inégales s'imbriquent.
 * La grille ne montre que les dessins — aucun texte. Titre, technique et
 * format n'apparaissent qu'en vue agrandie.
 *
 * Réalisé en CSS Grid avec des rangées de 1px : chaque figure occupe autant
 * de rangées que sa hauteur réelle (calculé dans app.js). Contrairement à
 * `columns`, l'ordre de lecture reste de gauche à droite — indispensable
 * puisque l'ordre de la galerie se règle dans la page de gestion.
 * ------------------------------------------------------------------- */

.galerie {
  display: grid;
  /* Colonnes « demi-largeur » : un dessin normal en occupe 2, un petit 1, un
     grand 4. Leur nombre (--sous) est fixé par app.js d'après --col-min, la
     largeur minimale d'une colonne normale, qui change selon l'écran. */
  --col-min: 460px;
  grid-template-columns: repeat(var(--sous, 2), minmax(0, 1fr));
  grid-auto-rows: 1px;      /* trame fine : unité de calcul, pas d'espacement */
  grid-auto-flow: row dense;/* chaque dessin se tasse au plus haut de sa colonne */
  column-gap: var(--ecart);

  /* Indispensable. Par défaut une figure s'étire sur toute la hauteur des
     rangées qu'on vient de lui attribuer ; la mesure suivante lit cette
     hauteur gonflée, réattribue davantage de rangées, et la figure descend
     sans fin. `start` fige la figure sur la hauteur de son contenu. */
  align-items: start;
}

.galerie figure {
  margin: 0;
  overflow: hidden;         /* le zoom d'une vignette ne déborde pas sur ses voisines */
  cursor: zoom-in;
  grid-row-end: span 1;     /* remplacé par la hauteur mesurée */
}

.galerie img {
  display: block;
  width: 100%;
  height: auto;             /* format d'origine, sauf recadrage fixé par app.js */
  background: #f0efec;      /* teinte d'attente pendant le chargement */
  transition: opacity 0.25s ease;
}
.galerie figure:hover img { opacity: 0.82; }

/* Tant que les hauteurs ne sont pas mesurées, on évite un empilement brutal. */
.galerie:not(.mesuree) figure { grid-row-end: auto; }

.etat {
  color: var(--encre-pale);
  font-size: 0.85rem;
}

/* Texte réservé aux lecteurs d'écran. */
.annonce {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---------- Bandeau de clôture ----------------------------------------- *
 * Les colonnes d'un masonry ne finissent jamais à la même hauteur. Plutôt
 * que de masquer cet escalier, on lui donne un appui : un bandeau pleine
 * largeur contre lequel la mosaïque vient buter. La fin devient délibérée.
 * ----------------------------------------------------------------------- */

.pied-galerie {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem 2rem;
  justify-content: space-between;
  align-items: baseline;

  margin-top: var(--marge);
  padding: var(--marge);
  padding-bottom: calc(var(--marge) + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--trait);

  font-size: 0.8rem;
  color: var(--encre-pale);
}

.pied-galerie p { margin: 0; }

.pied-galerie a {
  color: var(--encre);
  text-decoration: none;
  border-bottom: 1px solid var(--trait);
  padding-bottom: 1px;
  transition: border-color 0.18s ease;
}
.pied-galerie a:hover { border-bottom-color: var(--encre); }

/* ---------- Visionneuse plein écran ---------------------------------- */

.visionneuse {
  position: fixed;
  inset: 0;
  z-index: 50;
  background: rgba(251, 251, 250, 0.985);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(2.5rem, 7vw, 5rem);
  animation: apparait 0.2s ease;
}

.visionneuse figure {
  margin: 0;
  max-width: 100%;
  max-height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.visionneuse img {
  max-width: 100%;
  max-height: calc(100vh - 11rem);
  max-height: calc(100dvh - 11rem);   /* dvh : tient compte de la barre
                                          d'adresse mobile qui se rétracte */
  object-fit: contain;

  /* Zoom : la transformation est pilotée par app.js. */
  cursor: zoom-in;
  transform-origin: center center;
  transition: transform 0.22s ease;
  touch-action: none;        /* on gère nous-mêmes pincement et glissement */
  user-select: none;
  -webkit-user-drag: none;
  will-change: transform;
}

/* Une fois agrandie, l'image se déplace à la souris ou au doigt. */
.visionneuse.zoomee img { cursor: grab; }
.visionneuse.zoomee.deplacement img {
  cursor: grabbing;
  transition: none;          /* le suivi du doigt doit être immédiat */
}

/* Le fond ne défile pas sous une image agrandie. */
.visionneuse.zoomee figcaption { opacity: 0; }


/* Ancrée au bas du cadre, hors du flux de la figure : le dessin agrandi
   peut déborder sans jamais passer dessous. */
.visionneuse figcaption {
  position: absolute;
  left: 50%;
  bottom: calc(1.9rem + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%);
  max-width: calc(100% - 7rem);
  font-size: 0.8rem;
  color: var(--encre-pale);
  text-align: center;
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  justify-content: center;
  transition: opacity 0.2s ease;
}
.visionneuse figcaption .titre { color: var(--encre); }

.visionneuse button {
  position: absolute;
  background: none;
  border: 0;
  color: var(--encre-pale);
  cursor: pointer;
  font-family: inherit;
  line-height: 1;
  padding: 0.5rem 0.9rem;
  transition: color 0.15s ease;
}
.visionneuse button:hover { color: var(--encre); }

.visionneuse .fermer {
  top: calc(1rem + env(safe-area-inset-top, 0px));
  right: 1rem;
  font-size: 1.9rem;
}
.visionneuse .precedent,
.visionneuse .suivant {
  top: 50%;
  transform: translateY(-50%);
  font-size: 2.6rem;
}
.visionneuse .precedent { left: 0.5rem; }
.visionneuse .suivant   { right: 0.5rem; }

[hidden] { display: none !important; }

/* ---------- Bascule du menu (mobile) --------------------------------- */

.bascule-menu {
  display: none;
  position: fixed;
  z-index: 40;
  top: calc(1rem + env(safe-area-inset-top, 0px));
  right: 1rem;
  width: 2.4rem;
  height: 2.4rem;
  background: var(--papier);
  border: 1px solid var(--trait);
  cursor: pointer;
  padding: 0;
}
.bascule-menu span {
  display: block;
  width: 14px;
  height: 1px;
  margin: 4px auto;
  background: var(--encre);
  transition: transform 0.2s ease, opacity 0.2s ease;
}
.bascule-menu[aria-expanded="true"] span:first-child {
  transform: translateY(2.5px) rotate(45deg);
}
.bascule-menu[aria-expanded="true"] span:last-child {
  transform: translateY(-2.5px) rotate(-45deg);
}

/* Voile sous le menu ouvert : assombrit la galerie et capte le clic
   de fermeture. Inerte tant que le menu est fermé. */
.voile {
  position: fixed;
  inset: 0;
  z-index: 34;
  background: rgba(26, 26, 26, 0.18);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease;
}
.voile.visible { opacity: 1; pointer-events: auto; }

/* ---------- Tablettes -------------------------------------------------- *
 * Le menu occupe encore sa colonne, mais la place restante (477px sur un
 * écran de 768px) ne suffit plus à deux colonnes de 260px. On abaisse le
 * seuil pour garder deux ou trois dessins de front plutôt qu'un seul,
 * immense.
 * ----------------------------------------------------------------------- */

@media (min-width: 721px) and (max-width: 1024px) {
  .galerie { --col-min: 220px; }
}

/* ---------- Écrans étroits ------------------------------------------- */

@media (max-width: 900px) {
  /* L'illustration passe au-dessus du texte : côte à côte, elle deviendrait
     trop étroite pour se lire. */
  .a-propos {
    grid-template-columns: minmax(0, 30rem);
  }
}

@media (max-width: 720px) {
  .bascule-menu { display: block; }

  /* Bandeau fixe : le logo à gauche, le bouton du menu à droite. */
  .logo-mobile {
    display: flex;
    align-items: center;
    position: fixed;
    z-index: 30;
    top: 0;
    left: 0;
    right: 0;
    height: calc(4rem + env(safe-area-inset-top, 0px));
    padding: env(safe-area-inset-top, 0px) 4.2rem 0 var(--marge);
    background: var(--papier);
    border-bottom: 1px solid var(--trait);
    color: var(--encre);
    text-decoration: none;
    font-size: 1.05rem;
    font-weight: 500;
    letter-spacing: 0.12em;
  }
  .logo-mobile.avec-logo { letter-spacing: normal; }
  .logo-mobile img {
    display: block;
    width: auto;
    max-width: 100%;
    max-height: 2.6rem;
  }

  .mise-en-page { grid-template-columns: 1fr; }

  .menu {
    position: fixed;
    inset: 0 auto 0 0;
    width: min(74vw, var(--largeur-menu));
    background: var(--papier);
    z-index: 35;
    transform: translateX(-100%);
    transition: transform 0.25s ease;
  }
  .menu.ouvert { transform: none; }

  .contenu { padding-top: calc(4.5rem + env(safe-area-inset-top, 0px)); }

  /* La galerie n'est pas forcée à une colonne : `auto-fill` en donne deux
     dès que la largeur le permet, une seule en dessous de ~290px utiles. */
}

/* ---------- Téléphones ------------------------------------------------ */

@media (max-width: 520px) {
  :root {
    --marge: 1.1rem;
  }

  /* Colonnes plus étroites : deux dessins de front restent lisibles. */
  .galerie { --col-min: 150px; }

  /* Flèches ramenées en bas, à portée du pouce, hors de l'image. */
  .visionneuse {
    padding: 3.5rem 1rem calc(4.5rem + env(safe-area-inset-bottom, 0px));
  }
  .visionneuse .precedent,
  .visionneuse .suivant {
    top: auto;
    bottom: env(safe-area-inset-bottom, 0px);
    transform: none;
    font-size: 2.1rem;
    min-width: 48px;
    min-height: 48px;
  }
  .visionneuse .precedent { left: 22%; }
  .visionneuse .suivant   { right: 22%; }
  .visionneuse .fermer { min-width: 44px; min-height: 44px; }

  /* Les flèches occupent le bas du cadre : la légende passe au-dessus. */
  .visionneuse figcaption {
    bottom: calc(4.2rem + env(safe-area-inset-bottom, 0px));
    max-width: calc(100% - 2rem);
  }

  .visionneuse img {
    max-height: calc(100vh - 12rem);
    max-height: calc(100dvh - 12rem);
  }
}

/* ---------- Très petits écrans ---------------------------------------- */

@media (max-width: 360px) {
  .galerie { --col-min: 100000px; }   /* une seule colonne normale */
}

/* ---------- Préférences d'accessibilité -------------------------------- */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* ---------- Réseaux (page Contact) --------------------------------------- *
 * Pictogrammes monochromes : ils prennent la couleur du texte.
 * ----------------------------------------------------------------------- */

.reseaux {
  list-style: none;
  margin: 1.6rem 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;          /* en rangée, à la ligne si l'écran est étroit */
  gap: 0.8rem 1.6rem;
}
.reseaux a {
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  color: var(--encre);
  text-decoration: none;
}
.reseaux a:hover { text-decoration: underline; text-underline-offset: 3px; }
.reseaux svg { width: 1.15rem; height: 1.15rem; fill: currentColor; flex: none; }

/* Dissuasion du téléchargement : pas de sélection, pas de menu d'appui long
   sur iOS, pas de glisser natif. */
.galerie img, .visionneuse img, .illustration img {
  -webkit-touch-callout: none;
  -webkit-user-drag: none;
  user-select: none;
}

/* ---------- Expos ------------------------------------------------------- */

.menu nav a[hidden] { display: none; }

.expos {
  padding: var(--marge);
  padding-top: calc(var(--marge) + env(safe-area-inset-top, 0px));
  max-width: calc(56rem + 2 * var(--marge));
}
.expos h2 { font-weight: 400; font-size: 1rem; letter-spacing: 0.08em; margin: 0 0 2rem; }

.expo { padding-bottom: 3rem; margin-bottom: 3rem; border-bottom: 1px solid var(--trait); }
.expo:last-child { border-bottom: 0; margin-bottom: 0; }
.expo h3 { font-weight: 400; font-size: 1.25rem; margin: 0 0 0.4rem; }
.expo p { max-width: 36rem; margin: 0 0 1rem; color: #3a3a3a; }
.expo .expo-infos { color: var(--encre-pale); font-size: 0.85rem; margin-bottom: 1.4rem; }

.expo-images {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr));
  gap: 0.75rem;
  margin-top: 1.6rem;
  align-items: start;
}
.expo-images img {
  display: block;
  width: 100%;
  height: auto;            /* images entières, jamais recadrées */
  background: #f0efec;
  -webkit-touch-callout: none;
  -webkit-user-drag: none;
  user-select: none;
}
