/* ==========================================================================
   donovan adp — feuille de style unique
   --------------------------------------------------------------------------
   Sommaire
     01. Jetons de design (couleurs, typo, rythme)
     02. Reset et base
     03. Grain argentique
     04. En-tete
     05. Hero video plein ecran
     06. Carton-titre « free your mind »
     07. En-tetes de section
     08. Grille motion (videos)
     09. Grille stills (photos)
     10. Compteur de vue (HUD)
     11. Pied de page
     12. Page contact
     13. Apparitions au scroll
     14. Accessibilite et mouvement reduit
   ========================================================================== */


/* 01. Jetons de design ===================================================== */

:root {
  /* Couleurs. Le site est volontairement monochrome : la seule couleur de la
     page vient des photos et des videos. C'est le parti pris — une chambre
     noire, et le travail comme unique source de couleur. */
  --ink:        #080807;   /* fond, noir legerement chaud */
  --ink-raised: #0f0f0e;   /* aplats surleves (cartes, champs) */
  --paper:      #ede9e3;   /* blanc papier argentique, jamais #fff */
  --paper-dim:  #8b8b85;   /* texte secondaire, labels */
  --paper-mute: #55554f;   /* texte tertiaire */
  --rule:       rgba(237, 233, 227, 0.13);
  --rule-soft:  rgba(237, 233, 227, 0.07);

  /* Typographie. Hanken Grotesk pour le nom et les titres (geometrique,
     grande hauteur d'x, proche de la typo de reference du client).
     DM Mono pour tout ce qui est donnee : labels, compteurs, legendes. */
  --f-display: 'Hanken Grotesk', 'Avenir Next', Avenir, 'Segoe UI', system-ui, sans-serif;
  --f-mono: 'DM Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  --t-wordmark: clamp(1.0625rem, 1.5vw, 1.3125rem);
  --t-title:    clamp(1.75rem, 6.2vw, 4.25rem);
  --t-section:  clamp(1.5rem, 3.4vw, 2.5rem);
  --t-body:     clamp(0.9375rem, 1.05vw, 1.0625rem);
  --t-meta:     0.6875rem;

  /* Rythme.
     La colonne est volontairement etroite : les videos et les photos sont
     toutes verticales, et en pleine largeur d'ecran une carte 9/16 depasse
     la hauteur du viewport — on ne verrait jamais une image entiere. A cette
     mesure, une carte tient dans l'ecran sur un portable comme sur un fixe. */
  --shell:  1060px;
  --gutter: clamp(1.25rem, 4.4vw, 3.5rem);
  /* Hauteur de l'en-tete fixe. Sert a caler ce qui doit commencer juste
     dessous (le portrait collant de la page contact). */
  --head-h: clamp(3.4rem, 6vw, 4.9rem);
  --gap:    clamp(0.5rem, 1.5vw, 1.375rem);
  --band:   clamp(4.5rem, 11vw, 10rem);

  /* Hero. Voir README « Cadrage de la video d'intro » pour le reglage.
     Le format est declare en deux nombres, car il sert a la fois au
     rapport d'image et au calcul de recadrage. */
  --hero-ar-w: 9;
  --hero-ar-h: 16;
  --hero-zoom: 1.02;   /* leger sur-cadrage : evite un lisere sur les bords */
  --hero-shift: 0%;    /* remonter/descendre le recadrage sur petit ecran */

  /* Masquage de l'habillage YouTube (titre en haut, barre en bas).
     ------------------------------------------------------------------
     On donne a l'iframe la largeur exacte du cadre, mais 2 x --chrome-pad
     de hauteur en plus. YouTube conserve le format de la video : il la
     centre et complete avec deux bandes noires, une en haut et une en bas,
     de --chrome-pad chacune. Or le titre et la barre de lecture se placent
     sur les bords du LECTEUR, donc sur ces bandes noires — pas sur la
     video. Il suffit alors de recentrer l'iframe pour que les deux bandes
     tombent hors du cadre : l'habillage part avec elles.

     C'est ce qui distingue cette methode d'un simple sur-cadrage : la video
     n'est pas rognee, et le haut comme le bas sont couverts de la meme
     quantite. Augmenter --chrome-pad si un element reapparait. */
  --chrome-pad: 64px;

  /* Petite marge d'erreur : si le format reel d'une video s'ecarte un peu de
     celui de son cadre, ce leger agrandissement evite un lisere noir. */
  --safety-zoom: 1.03;

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}


/* 02. Reset et base ======================================================== */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--f-display);
  font-size: var(--t-body);
  font-weight: 400;
  line-height: 1.55;
  letter-spacing: -0.005em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

img, picture, iframe, svg { display: block; max-width: 100%; }
img { height: auto; }

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

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

::selection { background: var(--paper); color: var(--ink); }

/* Conteneur horizontal commun. */
.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Rythme vertical commun. Une seule classe pour toutes les sections, afin
   d'eviter les regles de marge qui s'annulent entre elles. */
.band { padding-block: var(--band); }

/* Sur mobile, les videos et les photos sortent des marges pour occuper toute
   la largeur de l'ecran : c'est la lecture attendue sur un book, et le format
   9/16 des sources correspond alors exactement a celui du telephone. Les
   textes, eux, gardent leurs marges. */
@media (max-width: 719px) {
  .bleed { margin-inline: calc(var(--gutter) * -1); }
}

/* Etiquette monospace : labels de section, compteurs, mentions. */
.tag {
  font-family: var(--f-mono);
  font-size: var(--t-meta);
  font-weight: 300;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--paper-dim);
  line-height: 1;
}

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

.skip-link {
  position: fixed;
  top: 0; left: 50%;
  transform: translate(-50%, -120%);
  z-index: 200;
  padding: 0.75rem 1.25rem;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--f-mono);
  font-size: var(--t-meta);
  letter-spacing: 0.18em;
  text-transform: uppercase;
}
.skip-link:focus-visible { transform: translate(-50%, 0); }


/* 03. Grain argentique ====================================================
   Toutes les photos du book sont des prises argentiques granuleuses. Ce voile
   de grain tres leger relie l'interface aux images pour qu'elles n'aient pas
   l'air posees sur un fond etranger. Volontairement sans mix-blend-mode :
   une couche fixe en fusion couterait cher au scroll sur mobile. */

body::after {
  content: '';
  position: fixed;
  inset: -50%;
  z-index: 90;
  pointer-events: none;
  opacity: 0.05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
}


/* 04. En-tete ============================================================== */

.masthead {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  /* La page passe SOUS l'encoche (viewport-fit=cover dans le <head>) pour que
     la video d'introduction aille vraiment d'un bord a l'autre. La barre, elle,
     doit rester au-dessous de la zone systeme : d'ou le retrait ajoute en haut,
     nul sur les appareils qui n'en ont pas. */
  padding: calc(clamp(1rem, 2.2vw, 1.75rem) + env(safe-area-inset-top, 0px))
           var(--gutter)
           clamp(1rem, 2.2vw, 1.75rem);

  /* La barre garde son fond sur toute la page, video d'introduction comprise.
     Ce n'est donc pas un etat pose au defilement : c'est le meme repere en haut
     de l'ecran partout, et la navigation reste lisible quelle que soit l'image
     qui passe dessous. Aucun JavaScript n'y touche — la barre est correcte meme
     s'il ne se charge pas.

     La video d'introduction, elle, passe DESSOUS la barre : elle part du pixel
     zero et deborde de l'ecran des quatre cotes. La barre la recouvre sur sa
     hauteur, elle ne la repousse pas — sans quoi la video serait rapetissee et
     laisserait une bande vide en bas. Voir la section 05. */
  background: rgba(8, 8, 7, 0.82);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--rule);
}


.wordmark {
  font-size: var(--t-wordmark);
  font-weight: 900;
  letter-spacing: -0.045em;
  line-height: 1;
  text-transform: lowercase;
}

.masthead__nav {
  display: flex;
  align-items: center;
  gap: clamp(1rem, 2vw, 1.75rem);
}

.masthead__link {
  font-family: var(--f-mono);
  font-size: var(--t-meta);
  font-weight: 300;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--paper-dim);
  transition: color 0.3s var(--ease);
}
.masthead__link:hover,
.masthead__link[aria-current='page'] { color: var(--paper); }


/* 05. Hero video plein ecran ===============================================
   La source est verticale (9/16). Deux presentations, selon l'appareil :

   - Sur un appareil mobile tenu droit — telephone, tablette — elle remplit
     l'ecran et deborde des quatre cotes. La barre du menu la recouvre sur sa
     hauteur, elle ne la repousse pas : la video part bien de la coordonnee
     zero, sans quoi elle laisserait une bande vide en bas.

   - Sur un ordinateur, ou sur un appareil tactile couche, la faire remplir
     imposerait de n'en garder qu'une bande centrale. On la montre donc
     ENTIERE, posee dans le viewport
     avec une marge en haut et en bas, et l'espace qui reste autour recoit une
     copie d'elle-meme, floutee et tres assombrie : la lumiere du projecteur
     sur le mur de la piece. Un filet et une ombre portee cernent le cadre —
     c'est ce qui fait lire un objet encadre plutot qu'une video trop petite
     pour l'ecran.

   La video est servie en fichier et n'accepte aucun clic : elle ne peut pas
   etre mise en pause. Le son a son bouton, dans la ligne du bas. */

.hero {
  position: relative;
  min-height: 100vh;
  min-height: 100svh;
  overflow: hidden;
  background: #000;

  /* Laquelle des deux presentations est active. C'est la SEULE definition du
     seuil : main.js lit ce jeton plutot que de refaire le calcul de son cote,
     ce qui rendrait possible un desaccord entre la mise en page et le
     chargement du fond. 0 = plein ecran, 1 = video entiere. */
  --hero-fit: 0;
}

.hero__stage {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
}

/* Cadre calcule pour couvrir le viewport sans deformer la video :
   largeur = la plus grande des deux contraintes, hauteur deduite du ratio.

   Il est place a la main, et non laisse au centrage de la grille parente.
   La raison est un detail du navigateur qui coute cher ici : quand un element
   deborde de son conteneur et que celui-ci masque le debordement — c'est le
   cas du hero — le centrage bascule en mode « sur », qui cale l'element en
   haut plutot que de le laisser deborder des deux cotes. Le rognage se faisait
   alors entierement par le bas : sur une tablette tenue droite, un quart de
   l'image partait, et avec lui les paroles incrustees en bas du cadre.

   « top: 50% » plus une translation de la moitie de sa propre hauteur, elles,
   centrent quoi qu'il arrive : ce qui depasse est retire a parts egales en
   haut et en bas. */
.hero__frame {
  position: absolute;
  top: 50%;
  left: 50%;
  width: max(100vw, calc(100svh * var(--hero-ar-w) / var(--hero-ar-h)));
  aspect-ratio: var(--hero-ar-w) / var(--hero-ar-h);
  transform: translate(-50%, calc(-50% + var(--hero-shift))) scale(var(--hero-zoom));
}

.hero__frame video,
.hero__poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Aucun clic n'atteint la video : il n'y a pas de pause sur l'introduction. */
.hero__frame video {
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.9s var(--ease);
}
.hero.is-live .hero__frame video { opacity: 1; }

.hero__poster {
  transform: scale(1.02);
  transition: opacity 0.9s var(--ease);
}
.hero.is-live .hero__poster { opacity: 0; }

/* Lumiere de projection ----------------------------------------------------
   Absente par defaut : elle ne sert que sur un ecran plus large que la video,
   et main.js ne charge son fichier que la. */
.hero__glow {
  display: none;
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}

.hero__glow video {
  position: absolute;
  /* Le flou mange les bords : on deborde largement pour qu'aucun bord net
     ne reapparaisse dans le cadre. */
  top: -12%;
  left: -12%;
  width: 124%;
  height: 124%;
  object-fit: cover;
  /* Tres assombrie et desaturee. Le parti pris de la page est monochrome :
     ce qui reste ici doit etre une lueur, pas une seconde image. La seule
     vraie couleur de l'ecran reste celle du cadre. */
  filter: blur(48px) saturate(0.85) brightness(0.62);
  opacity: 0;
  transition: opacity 1.6s var(--ease);
}
.hero.is-live .hero__glow video { opacity: 1; }

/* La lueur s'eteint vers les bords : la piece, elle, reste noire. */
.hero__glow::after {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(125% 95% at 50% 46%,
              rgba(8, 8, 7, 0) 38%, rgba(8, 8, 7, 0.42) 78%, rgba(8, 8, 7, 0.86) 100%);
}

/* Voile : assombrit le haut et le bas pour que le nom et la legende restent
   lisibles quelle que soit l'image de la video. */
.hero__veil {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(to bottom, rgba(8,8,7,.72) 0%, rgba(8,8,7,0) 26%),
    linear-gradient(to top,    rgba(8,8,7,.82) 0%, rgba(8,8,7,0) 34%);
}

.hero__foot {
  position: absolute;
  inset: auto 0 0 0;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1.5rem;
  padding: 0 var(--gutter) clamp(1.75rem, 4vw, 3rem);
}

.hero__tag { margin: 0; }

/* Le bouton de son et l'invite a defiler, groupes a droite. */
.hero__aside {
  display: flex;
  align-items: center;
  gap: clamp(0.85rem, 2vw, 1.5rem);
}

/* Sur l'introduction, le bouton de son reste visible en permanence : c'est la
   seule commande de la page, et rien d'autre n'indique qu'il y a du son a
   entendre. Sur les cartes, il se revele au survol — la, une carte survolee
   designe sans ambiguite la video concernee. */
.hero__foot .ctrl { opacity: 1; pointer-events: auto; }

/* Sur un ecran etroit, la ligne du bas n'a pas la place de porter en plus le
   bouton : il vole 54 px a la legende, qui passe alors sur deux lignes (mesure
   sur tous les telephones jusqu'a 412 px). Il se pose donc juste au-dessus,
   dans le coin — exactement ou les cartes de la grille placent le leur.
   « bottom: 100% » l'accroche au bord haut de la ligne : aucune valeur en dur
   a maintenir si la legende change de taille. */
.hero__foot .ctrl--sound {
  position: absolute;
  right: var(--gutter);
  bottom: 100%;
  margin-bottom: 0.85rem;
}

.hero__cue {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
}
.hero__cue::after {
  content: '';
  width: 1px;
  height: 2.5rem;
  background: linear-gradient(to bottom, var(--paper-dim), transparent);
  animation: cue 2.4s var(--ease) infinite;
  transform-origin: top;
}

@keyframes cue {
  0%, 100% { transform: scaleY(0.35); opacity: 0.35; }
  45%      { transform: scaleY(1);    opacity: 1; }
}

/* Ecrans tres etroits : l'invite a defiler s'efface -------------------------
   La ligne du bas doit loger cote a cote la legende (216 px) et « defiler »
   (74 px), avec 24 px d'ecart : 314 px en tout. La place disponible vaut la
   largeur de l'ecran moins deux marges de 20 px. Le compte y est donc a partir
   de 354 px — mais de justesse : il ne reste que 6 px a 360 px, et la police
   de repli, si Google Fonts ne repond pas, est plus large que DM Mono. Sous
   cette largeur la legende passerait sur deux lignes.

   C'est l'invite qui part, pas la legende : elle est decorative (aria-hidden),
   et sur un ecran aussi etroit la video qui deborde sous le pli annonce deja
   qu'il y a quelque chose en dessous. La legende, elle, dit qui est Donovan.

   Si vous changez le texte de la legende, ce seuil est a recalculer. */
@media (max-width: 374px) {
  .hero__cue { display: none; }
}


/* La presentation « tirage encadre » ---------------------------------------
   Elle est reservee a l'ordinateur. Partout ailleurs la video remplit l'ecran,
   comme au premier jour.

   La frontiere ne se lit ni en pixels ni en format d'ecran, mais sur l'appareil
   lui-meme : « hover: hover » veut dire qu'un pointeur peut survoler, donc
   qu'il y a une souris ou un pave tactile — un ordinateur. Un ecran tactile
   repond « hover: none », quelle que soit sa taille.

   C'est le seul critere qui tienne. Un seuil en format d'ecran laissait passer
   les tablettes tenues droites (0,75) et les fenetres partagees d'Android, qui
   se retrouvaient avec la mise en page d'ordinateur alors que ce sont bien des
   appareils mobiles. Un seuil en largeur trahissait pareil, dans l'autre sens.

   Seconde clause : un appareil tactile COUCHE revient lui aussi au cadre. Le
   remplir demanderait de recadrer une video verticale sur un ecran horizontal
   — on n'en verrait qu'un quart. En portrait, remplir ne coute qu'un peu de
   hauteur : au pire un quart sur une tablette, et les paroles incrustees dans
   l'image restent dans le champ (elles courent entre 79 % et 83 % de la
   hauteur ; le recadrage n'entame que 13 % en haut et 13 % en bas). */
@media (hover: hover), (orientation: landscape) {

  .hero {
    --hero-fit: 1;

    /* La scene se cale sur la video : jamais plus haute que l'ecran, jamais
       plus haute non plus que la video etalee sur toute la largeur. Sur un
       16/9 les deux valeurs se rejoignent et l'introduction occupe l'ecran
       exactement ; sur un 16/10 la scene s'arrete au bas de l'image, et la
       section suivante affleure — ce qui invite a defiler plutot que de
       laisser une bande morte sous la video. */
    --hero-h: min(100svh, calc(100vw * var(--hero-ar-h) / var(--hero-ar-w)));
    min-height: var(--hero-h);
    height: var(--hero-h);
  }

  /* La video occupe TOUTE la largeur de l'ecran. Rien n'est reserve en haut
     ni en bas : la barre du menu passe par-dessus, comme sur telephone, et la
     ligne de legende repose sur l'image — le voile (.hero__veil) est la pour
     que les deux restent lisibles.

     Une version precedente posait la video dans un cadre plus petit, avec un
     filet et une ombre portee, entre la barre du menu et la legende : c'etait
     la seule facon de montrer une video VERTICALE en entier sur un ecran
     large. Depuis qu'une version horizontale est servie ici, la contrainte est
     tombee — une 16/9 en pleine largeur ne perd rien et occupe l'ecran. */
  .hero__stage {
    padding: 0;
  }

  .hero__frame {
    /* Meme centrage a la main que la presentation plein ecran (voir plus
       haut) : la scene ayant deja la hauteur de la video, couvrir revient a
       la poser sur toute la largeur sans rien rogner. Le rognage ne reprend
       que sur un ecran plus large que 16/9 — un 21/9 par exemple — ou la
       hauteur est alors bornee par l'ecran.

       Le leger sur-cadrage n'est pas decoratif : largeur et hauteur tombent
       ici sur des valeurs calculees, et un arrondi au demi-pixel laissait un
       trait clair sur un bord. */
    width: max(100vw, calc(var(--hero-h) * var(--hero-ar-w) / var(--hero-ar-h)));
    transform: translate(-50%, -50%) scale(1.01);
  }

  /* Pas de son sur l'introduction dans cette presentation : le bouton
     disparait entierement — display, et non une simple transparence, pour
     qu'il sorte aussi du parcours au clavier.

     main.js remet la video en muet des que cette presentation prend la main
     (section 04). Sans cela, un visiteur qui aurait allume le son sur son
     telephone puis couche l'appareil se retrouverait avec du son et plus
     aucun moyen de le couper. */
  .hero__foot .ctrl--sound { display: none; }
}


/* 06. Carton-titre ========================================================
   Le seul moment orchestre de la page : la phrase se pose en s'installant,
   l'interlettrage se resserre pendant l'apparition. */

.titlecard {
  display: grid;
  place-items: center;
  /* Pas de .band ici : le carton gere sa propre respiration, en padding
     plutot qu'en hauteur minimale — une hauteur minimale calee sur le
     viewport creait un grand vide sur les ecrans hauts. Moins de marge en
     bas qu'en haut : la section suivante apporte deja la sienne. */
  padding-block: clamp(4rem, 10vh, 7rem) clamp(2.5rem, 6vh, 4.5rem);
  text-align: center;
}

/* La section qui suit le carton n'a pas besoin de sa marge haute complete :
   le carton vient de laisser du blanc. */
.titlecard + .band { padding-top: clamp(2rem, 5vw, 4.5rem); }

.titlecard__line {
  margin: 0;
  font-size: var(--t-title);
  font-weight: 800;
  line-height: 1.05;
  letter-spacing: 0.05em;
  opacity: 0;
  transform: translateY(1rem);
  transition:
    opacity 1.1s var(--ease),
    transform 1.1s var(--ease),
    letter-spacing 1.4s var(--ease);
}
.titlecard__line.is-in {
  opacity: 1;
  transform: none;
  letter-spacing: -0.03em;
}

/* Les tirets sont dans la copie voulue par le client ; on les met en retrait
   pour que les trois mots portent seuls. */
.titlecard__dash {
  color: var(--paper-mute);
  font-weight: 300;
  padding-inline: 0.35em;
}


/* 07. En-tetes de section ================================================== */

.lede {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1.5rem;
  padding-bottom: clamp(1.25rem, 2.5vw, 2rem);
  margin-bottom: clamp(1.5rem, 3vw, 2.75rem);
  border-bottom: 1px solid var(--rule);
}

.lede__title {
  margin: 0;
  font-size: var(--t-section);
  font-weight: 800;
  letter-spacing: -0.035em;
  line-height: 1;
  text-transform: lowercase;
}

.lede__count { white-space: nowrap; }


/* 08. Grille motion (videos) =============================================== */

.reel {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--gap);
}

@media (min-width: 720px) {
  .reel { grid-template-columns: repeat(2, 1fr); }
  /* La video large occupe toute la ligne. */
  .reel__item--wide { grid-column: 1 / -1; }
}

.reel__item {
  position: relative;
  overflow: hidden;
  background: #000;
  aspect-ratio: 9 / 16;
}

.reel__item--wide { aspect-ratio: 16 / 9; }

/* La video est legerement agrandie et l'iframe deborde du cadre : tout residu
   d'interface YouTube sur les bords tombe hors du conteneur. */
.reel__stage {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.reel__stage iframe,
.reel__stage > div {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 100%;
  height: calc(100% + 2 * var(--chrome-pad));
  transform: translate(-50%, -50%) scale(var(--safety-zoom));
  border: 0;
  /* Les clics atteignent le lecteur : un clic sur la video la met en pause,
     avec l'icone de YouTube. Inutile d'ajouter notre propre bouton, qui
     ferait doublon. Ce qui rend la chose acceptable, c'est --chrome-pad :
     le titre et la barre de lecture, qui se revelent aussi au survol,
     restent hors du cadre. */
  pointer-events: auto;
}

/* Le lecteur reste invisible tant qu'il n'a pas commence a jouer : sinon on
   voit son fond noir puis sa roue de chargement pendant la mise en memoire
   tampon. On ne montre que la miniature, puis on fond de l'une a l'autre. */
.reel__stage iframe { opacity: 0; transition: opacity 0.6s var(--ease); }
.reel__item.is-live .reel__stage iframe { opacity: 1; }

.reel__poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.04);
  filter: brightness(0.92);
  transition: opacity 0.7s var(--ease);
}
.reel__item.is-live .reel__poster { opacity: 0; }

/* Commandes de la carte : pause et son. ------------------------------------
   Ce sont nos propres boutons. Ceux de YouTube ne sont jamais atteignables
   (pointer-events: none sur l'iframe), donc ils ne doivent jamais s'afficher
   non plus — d'ou le retour a la miniature des qu'une video est en pause. */

.controls {
  position: absolute;
  right: 0.75rem;
  bottom: 0.75rem;
  z-index: 3;
  display: flex;
  gap: 0.5rem;
  /* Le conteneur ne doit rien intercepter : seuls les boutons visibles
     reprennent les clics. Sinon il ferait un trou mort dans la zone
     cliquable de la video. */
  pointer-events: none;
}

.ctrl {
  display: grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  color: var(--paper);
  background: rgba(8, 8, 7, 0.5);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  opacity: 0;
  /* Invisible donc non cliquable : sans cela, un clic pres du coin bas-droit
     d'une video toucherait un bouton qu'on ne voit pas, au lieu de la mettre
     en pause. Le focus clavier, lui, n'est pas concerne. */
  pointer-events: none;
  transition: opacity 0.3s var(--ease), background-color 0.3s var(--ease);
}
.ctrl svg { width: 1rem; height: 1rem; }
.ctrl:hover { background: rgba(8, 8, 7, 0.78); }

/* Chaque bouton porte ses deux icones ; « is-alt » bascule de l'une a
   l'autre (pause/lecture, son coupe/actif). */
.ctrl__b { display: none; }
.ctrl.is-alt .ctrl__a { display: none; }
.ctrl.is-alt .ctrl__b { display: block; }

/* Discrets au repos, reveles au survol. Un bouton actif — video en pause ou
   son allume — reste visible : c'est l'indicateur de cet etat. */
.reel__item:hover .ctrl,
.reel__item:focus-within .ctrl,
.ctrl.is-alt { opacity: 1; pointer-events: auto; }

/* Au tactile il n'y a pas de survol : les commandes restent accessibles. */
@media (hover: none) {
  .ctrl { opacity: 0.85; pointer-events: auto; }
}


/* Video refusee par YouTube ------------------------------------------------
   Certaines videos ne peuvent pas etre lues hors de youtube.com : case
   « Autoriser l'integration » decochee, ou revendication de l'ayant droit de
   la musique. Le lecteur ne demarrera jamais, quoi qu'on fasse depuis le site.

   La carte garde sa miniature — elle est belle — et le meme bouton de son que
   les autres. Rien ne signale le refus : ni mention, ni lien sortant, ni
   bouton de lecture qui ne lancerait rien. Le correctif est cote chaine
   (YouTube Studio, « Autoriser l'integration »), pas cote site. La classe
   is-blocked reste posee sur la carte : elle ne sert a rien ici, mais elle
   se lit dans l'inspecteur quand on cherche pourquoi une carte est figee. */


/* Mouvement reduit : rien ne demarre seul, la carte devient un bouton. */
.reel__start {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: grid;
  place-items: center;
}
.reel__start-glyph {
  width: 3.5rem;
  height: 3.5rem;
  border: 1px solid rgba(237, 233, 227, 0.55);
  border-radius: 50%;
  background: rgba(8, 8, 7, 0.35);
  transition: background-color 0.3s var(--ease);
  position: relative;
}
.reel__start-glyph::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 54%;
  transform: translate(-50%, -50%);
  border-left: 0.75rem solid var(--paper);
  border-block: 0.5rem solid transparent;
}
.reel__start:hover .reel__start-glyph { background: rgba(8, 8, 7, 0.7); }

/* Repli sans JavaScript : la liste des videos reste accessible. */
.noscript-list {
  display: grid;
  gap: 0.5rem;
  padding: 1.5rem;
  border: 1px solid var(--rule);
}
.noscript-list a { text-decoration: underline; text-underline-offset: 0.25em; }


/* 09. Grille stills (photos) ==============================================
   Toutes les photos sont verticales mais de rapports differents (du 9/16 au
   4/5). On aligne les rangees par le haut et on decale la colonne de droite :
   la page respire comme une double page de book, sans rangees qui s'egalisent
   artificiellement. */

.gallery {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--gap);
}

@media (min-width: 720px) {
  .gallery {
    grid-template-columns: repeat(2, 1fr);
    align-items: start;
  }
  .gallery > *:nth-child(even) { margin-top: clamp(2rem, 7vw, 6.5rem); }
}

.frame {
  position: relative;
  margin: 0;
  overflow: hidden;
  /* Couleur moyenne de la photo, posee avant le chargement : pas de flash
     blanc ni de saut de mise en page. */
  background: var(--tone, var(--ink-raised));
}

.frame img {
  width: 100%;
  height: auto;
  transition: transform 1.2s var(--ease), opacity 0.8s var(--ease);
}

@media (hover: hover) {
  .frame:hover img { transform: scale(1.025); }
}


/* 10. Compteur de vue (HUD) ===============================================
   La signature du site : un compteur de defilement fixe, comme le compteur de
   vues d'un appareil argentique ou le timecode d'un derushage. Il dit toujours
   ou l'on se trouve dans le book — quelle bande, quelle image sur combien. */

.hud {
  position: fixed;
  left: clamp(1rem, 3vw, 1.75rem);
  bottom: clamp(1rem, 3vw, 1.75rem);
  z-index: 95;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  pointer-events: none;
  opacity: 0;
  transform: translateY(0.5rem);
  transition: opacity 0.5s var(--ease), transform 0.5s var(--ease);
  text-shadow: 0 1px 12px rgba(0, 0, 0, 0.85);
}
.hud.is-on { opacity: 1; transform: none; }

.hud__reel { color: var(--paper); }
.hud__rule { width: clamp(1.5rem, 4vw, 3rem); height: 1px; background: var(--rule); }
.hud__num { color: var(--paper); font-variant-numeric: tabular-nums; }
.hud__total { color: var(--paper-mute); }


/* 11. Pied de page ========================================================= */

.colophon {
  border-top: 1px solid var(--rule);
  padding-block: clamp(3rem, 7vw, 5.5rem) clamp(2rem, 4vw, 3rem);
}

.colophon__top {
  display: grid;
  gap: clamp(2.5rem, 6vw, 4rem);
  margin-bottom: clamp(3rem, 7vw, 5rem);
}

@media (min-width: 860px) {
  .colophon__top { grid-template-columns: 1fr auto; align-items: start; }
}

.colophon__mark {
  font-size: clamp(2rem, 8vw, 5rem);
  font-weight: 900;
  letter-spacing: -0.05em;
  line-height: 0.9;
  margin: 0 0 1rem;
  text-transform: lowercase;
}

.colophon__mail {
  font-family: var(--f-mono);
  font-size: var(--t-body);
  font-weight: 300;
  color: var(--paper-dim);
  border-bottom: 1px solid var(--rule);
  padding-bottom: 0.15em;
  transition: color 0.3s var(--ease), border-color 0.3s var(--ease);
}
.colophon__mail:hover { color: var(--paper); border-bottom-color: var(--paper); }

.socials {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  list-style: none;
  margin: 0 -0.55rem;
  padding: 0;
}

/* Pas de cadre autour des marques : le trait du logo se suffit. La zone
   cliquable garde ses 44 px reglementaires, elle est simplement invisible. */
.socials__link {
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  color: rgba(237, 233, 227, 0.72);
  transition: color 0.3s var(--ease), transform 0.3s var(--ease);
}
.socials__link svg { width: 1.75rem; height: 1.75rem; }
.socials__link:hover {
  color: var(--paper);
  transform: translateY(-2px);
}

.colophon__base {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding-top: clamp(1.5rem, 3vw, 2rem);
  border-top: 1px solid var(--rule-soft);
}


/* 12. Page contact ========================================================
   La page prolonge le book au lieu d'ouvrir sur un formulaire : un portrait
   plein cadre occupe la gauche et reste a l'ecran pendant tout le defilement,
   la colonne de droite deroule l'adresse, la fiche et le formulaire. Le
   visage reste present pendant qu'on lit les conditions de travail. */

.booking {
  display: grid;
  grid-template-columns: 1fr;
  align-items: start;
  /* Demarrer sous l'en-tete fixe, sinon il recouvre le haut du portrait. */
  padding-top: var(--head-h);
}

.booking__portrait {
  position: relative;
  margin: 0;
  overflow: hidden;
  background: var(--tone, var(--ink-raised));
  /* Bandeau plein ecran en tete sur mobile. */
  height: 62svh;
}

.booking__portrait img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Le visage est dans le tiers superieur du cadrage d'origine. */
  object-position: center 32%;
}

.booking__body {
  padding: clamp(3rem, 9vw, 5.5rem) var(--gutter) clamp(4rem, 9vw, 7rem);
}

@media (min-width: 900px) {
  .booking {
    grid-template-columns: minmax(0, 40%) minmax(0, 1fr);
    /* Le portrait touche le bord gauche de l'ecran : c'est ce qui distingue
       la page d'une simple mise en colonnes. */
    padding-left: 0;
  }

  .booking__portrait {
    position: sticky;
    top: var(--head-h);
    height: calc(100svh - var(--head-h));
  }

  .booking__body {
    padding: clamp(4rem, 8vw, 7rem) var(--gutter) clamp(5rem, 10vw, 8rem) clamp(2.5rem, 5vw, 4.5rem);
    max-width: 46rem;
  }
}

.booking__title {
  margin: 0.875rem 0 clamp(2rem, 5vw, 3.25rem);
  font-size: clamp(2.25rem, 6vw, 3.75rem);
  font-weight: 800;
  letter-spacing: -0.045em;
  line-height: 0.98;
  text-transform: lowercase;
}

/* L'adresse mail est le vrai bouton de la page : elle est traitee comme un
   titre, pas comme une ligne de texte. `break-word` evite qu'elle deborde
   sur les petits ecrans. */
.booking__mail {
  display: block;
  font-size: clamp(1.5rem, 4.4vw, 2.75rem);
  font-weight: 800;
  letter-spacing: -0.045em;
  line-height: 1.08;
  overflow-wrap: break-word;
  padding-bottom: 0.35em;
  border-bottom: 1px solid var(--rule);
  transition: border-color 0.35s var(--ease), color 0.35s var(--ease);
}
.booking__mail:hover { border-bottom-color: var(--paper); }

.booking__hint {
  display: block;
  margin-top: 0.9rem;
  color: var(--paper-mute);
}

/* Fiche : ce qu'un directeur de casting cherche avant d'ecrire. */
.facts {
  display: grid;
  gap: 0;
  margin: clamp(2.5rem, 6vw, 4rem) 0 0;
  padding: 0;
  list-style: none;
}

.facts > div {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1.5rem;
  padding-block: 1rem;
  border-bottom: 1px solid var(--rule-soft);
}

.facts dt {
  flex: 0 0 8rem;
  font-family: var(--f-mono);
  font-size: var(--t-meta);
  font-weight: 300;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--paper-mute);
  padding-top: 0.3em;
}

.facts dd { flex: 1 1 14rem; margin: 0; }

.facts a {
  border-bottom: 1px solid var(--rule);
  transition: border-color 0.3s var(--ease);
}
.facts a:hover { border-bottom-color: var(--paper); }

/* Formulaire =============================================================
   Champs soulignes plutot qu'encadres : sur fond noir, une boite grise par
   champ alourdit la page et la fait ressembler a n'importe quel formulaire. */

.form {
  display: grid;
  gap: clamp(1.75rem, 4vw, 2.5rem);
  margin-top: clamp(3rem, 7vw, 4.5rem);
  padding-top: clamp(2.5rem, 5vw, 3.5rem);
  border-top: 1px solid var(--rule);
}

.form__lede { margin: 0; }

.field { display: grid; gap: 0.4rem; }

.field__label {
  font-family: var(--f-mono);
  font-size: var(--t-meta);
  font-weight: 300;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--paper-mute);
}

.field__input {
  width: 100%;
  padding: 0.5rem 0 0.75rem;
  font-family: var(--f-display);
  font-size: 1.0625rem;
  color: var(--paper);
  background: none;
  border: 0;
  border-bottom: 1px solid var(--rule);
  border-radius: 0;
  transition: border-color 0.3s var(--ease);
}
.field__input::placeholder { color: var(--paper-mute); }
.field__input:hover { border-bottom-color: var(--paper-mute); }
.field__input:focus { border-bottom-color: var(--paper); }

textarea.field__input { resize: vertical; min-height: 7rem; line-height: 1.6; }

.form__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.25rem 2rem;
}

.form__submit {
  padding: 1rem 2.5rem;
  font-family: var(--f-mono);
  font-size: var(--t-meta);
  font-weight: 400;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink);
  background: var(--paper);
  transition: opacity 0.3s var(--ease);
}
.form__submit:hover { opacity: 0.82; }
.form__submit[disabled] { opacity: 0.45; cursor: wait; }

.form__status {
  margin: 0;
  flex: 1 1 16rem;
  font-family: var(--f-mono);
  font-size: var(--t-meta);
  font-weight: 300;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  line-height: 1.6;
  color: var(--paper-dim);
}
.form__status[data-state='error'] { color: var(--paper); }

/* Tout en minuscules, page contact seulement -------------------------------
   Choix editorial : cette page-la parle a voix basse. Le reglage vit ici
   plutot que dans le HTML, pour que les libelles gardent leur ecriture
   normale dans le code — utile aux lecteurs d'ecran et aux moteurs : seule
   leur presentation change. Les selecteurs sont repris un a un parce que
   chacun force les capitales ailleurs sur le site, et gagnerait donc sur le
   simple heritage depuis <body>. */
.page-contact,
.page-contact .tag,
.page-contact .skip-link,
.page-contact .masthead__link,
.page-contact .facts dt,
.page-contact .field__label,
.page-contact .form__submit,
.page-contact .form__status { text-transform: lowercase; }

/* Sauf ce que le visiteur tape : un nom propre saisi dans le formulaire doit
   s'afficher tel qu'il a ete ecrit. Les invites de saisie, elles, sont deja
   redigees en minuscules dans le HTML. */
.page-contact .field__input { text-transform: none; }


/* 13. Apparitions au scroll ================================================ */

.reveal {
  opacity: 0;
  transform: translateY(1.75rem);
  transition:
    opacity 0.85s var(--ease),
    transform 0.85s var(--ease);
  transition-delay: var(--delay, 0ms);
}
.reveal.is-in { opacity: 1; transform: none; }


/* 14. Accessibilite et mouvement reduit ==================================== */

:focus-visible {
  outline: 2px solid var(--paper);
  outline-offset: 3px;
}

@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;
  }

  /* Rien ne doit rester invisible si les transitions sont neutralisees. */
  .reveal,
  .titlecard__line {
    opacity: 1;
    transform: none;
    letter-spacing: -0.03em;
  }

  .frame:hover img { transform: none; }
}
