/* ==========================================================================
   Components
   ========================================================================== */

/* --- Buttons ------------------------------------------------------------ */
.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-weight: 600;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  padding: 0.85rem 1.75rem;
  border-radius: var(--radius-md);
  border: var(--hairline) solid transparent;
  transition: background var(--transition-base), color var(--transition-base),
    border-color var(--transition-base);
}
.btn--primary {
  background: var(--color-burgundy);
  color: var(--text-on-dark);
}
.btn--primary:hover {
  background: var(--color-burgundy-deep);
  color: var(--text-on-dark);
}
.btn--ghost {
  border-color: currentColor;
  color: var(--text-accent);
}
.btn--ghost:hover {
  background: var(--color-burgundy);
  color: var(--text-on-dark);
  border-color: var(--color-burgundy);
}
.section--dark .btn--ghost {
  color: var(--color-gold-soft);
}
.section--dark .btn--ghost:hover {
  background: var(--color-gold);
  color: var(--color-ink);
  border-color: var(--color-gold);
}

/* Text link with arrow ("Lire la suite →") */
.link-more {
  display: inline-flex;
  align-items: center;
  min-height: 24px;   /* WCAG 2.2 target size (2.5.8): it rendered at 23px */
  gap: 0.4rem;
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-weight: 600;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-accent);
}
.link-more__arrow { transition: transform var(--transition-base); }
.link-more:hover .link-more__arrow { transform: translateX(4px); }

/* ==========================================================================
   Hero — full-bleed photo, name overlaid
   ========================================================================== */
.hero {
  position: relative;
  min-height: min(88vh, 760px);
  display: flex;
  align-items: flex-end;
  color: var(--text-on-dark);
  overflow: hidden;
  background: var(--surface-dark);
}
.hero__media {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 25%;
}
/* Legibility scrim (bottom) + cinematic vignette (edges) for depth */
.hero__scrim {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(
      to top,
      rgba(6, 6, 8, 0.85) 0%,
      rgba(6, 6, 8, 0.45) 38%,
      rgba(6, 6, 8, 0.12) 70%,
      rgba(6, 6, 8, 0.28) 100%
    ),
    radial-gradient(120% 120% at 50% 32%, transparent 55%, rgba(0, 0, 0, 0.55) 100%);
}
.hero__inner {
  position: relative;
  width: 100%;
  padding-block: var(--space-6);
}
/* Burgundy plate rather than coloured text. The hero photo is `object-fit:
   cover`, so a different part of the image sits behind this word at every
   viewport width — light hair and skin at 390px, dark foliage at 1440px.
   Measured against the real composited pixels, NO flat colour passes WCAG at
   all three widths: the old gold scored 1.02:1 at 390px (invisible) and
   burgundy text would score 1.59:1 at 1440px (dark on dark). Carrying its own
   background is the only fix that holds at every size — cream on burgundy is
   8.8:1 regardless of the photo. */
.hero__eyebrow {
  display: inline-block;
  background: var(--color-burgundy);
  color: var(--color-cream);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  /* Extra right padding balances the tracking, which adds a trailing gap
     after the final letter and would otherwise look off-centre. */
  padding: 0.3em 0.75em 0.3em 0.9em;
  border-radius: var(--radius-sm);
}
.hero__name {
  font-family: var(--font-name);
  font-size: var(--text-hero);
  /* 600, not 700 — see the note on .brand: Cormorant's charm is its hairlines. */
  font-weight: 600;
  line-height: 1.02;   /* Cormorant's taller ascenders need a touch more room than Playfair's 0.98 */
  letter-spacing: var(--tracking-normal);
  color: var(--text-on-dark);
  margin-top: var(--space-2);
}
.hero__tagline {
  max-width: 44ch;
  margin-top: var(--space-3);
  font-size: var(--text-lg);
  font-style: italic;
  color: color-mix(in srgb, var(--text-on-dark) 88%, transparent);
}
.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-4);
}

/* Upcoming-date chip inside the hero */
/* Critic punchline — the first line a visitor reads on opening the page */
.hero__quote {
  margin: var(--space-4) 0 0;
  /* Wider + a step down the scale than a 3-word pull-quote would need:
     the Crescendo citation runs ~140 characters and must not out-shout the name. */
  max-width: 48ch;
  padding-left: var(--space-3);
  border-left: 2px solid color-mix(in srgb, var(--color-gold) 70%, transparent);
}
.hero__quote-text {
  font-family: var(--font-display);
  font-style: italic;
  font-size: clamp(var(--text-base), 2.2vw, var(--text-xl));
  line-height: 1.35;
  color: var(--text-on-dark);
}
.hero__quote-cite {
  display: block;
  margin-top: var(--space-2);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-gold-soft);
}

/* ==========================================================================
   Pull-quote (press social proof)
   ========================================================================== */
.pullquote {
  text-align: center;
}
.pullquote__mark {
  font-family: var(--font-display);
  font-size: 4rem;
  line-height: 0;
  color: var(--color-gold);
  display: block;
  height: 2.2rem;
}
.pullquote__text {
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  font-style: italic;
  font-weight: 500;
  line-height: var(--leading-snug);
  color: var(--text-primary);
  max-width: 24ch;
  margin-inline: auto;
}
.section--dark .pullquote__text { color: var(--text-on-dark); }
.pullquote__cite {
  display: block;
  margin-top: var(--space-3);
  font-family: var(--font-body);
  font-style: normal;
  font-size: var(--text-sm);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--color-gold);
}

/* ==========================================================================
   Section heading block
   ========================================================================== */
.section-head { margin-bottom: var(--space-5); }
.section-head--center { text-align: center; }
.section-head--center .rule { margin-inline: auto; }
.section-head__title { font-size: var(--text-2xl); }

/* ==========================================================================
   About teaser — text + optional portrait, two columns
   ========================================================================== */
.about {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-6);
  align-items: center;
}
.about__portrait {
  aspect-ratio: 4 / 5;
  object-fit: cover;
  width: 100%;
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-md);
}
.about__lead {
  font-size: var(--text-lg);
  color: var(--text-primary);
}
.about__body {
  margin-top: var(--space-3);
}
.about__cta { margin-top: var(--space-4); }

/* ==========================================================================
   Repertoire teaser — featured roles
   ========================================================================== */
.roles {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-3);
}
.role-card {
  padding: var(--space-4) var(--space-3);
  background: var(--surface-card);
  border: var(--hairline) solid var(--border-hairline);
  border-top: 3px solid var(--color-gold);
  border-radius: var(--radius-sm);
  transition: transform var(--transition-base), box-shadow var(--transition-base);
}
.role-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-md);
}
.role-card__year {
  font-family: var(--font-body);
  font-size: var(--text-sm);
  letter-spacing: var(--tracking-wide);
  color: var(--color-gold);
}
.role-card__role {
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: 700;
  margin-top: var(--space-1);
}
.role-card__work {
  font-style: italic;
  color: var(--text-secondary);
  margin-top: 0.25rem;
}
.role-card__venue {
  margin-top: var(--space-2);
  font-size: var(--text-sm);
  color: var(--text-secondary);
}

/* ==========================================================================
   Press strip — publication names / quotes
   ========================================================================== */
.press-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: var(--space-4);
}
.press-item {
  padding-inline-start: var(--space-3);
  border-inline-start: 2px solid var(--color-gold);
}
.press-item__quote {
  font-family: var(--font-display);
  font-style: italic;
  font-size: var(--text-lg);
  color: var(--text-primary);
  line-height: var(--leading-snug);
}
.section--dark .press-item__quote { color: var(--text-on-dark); }
.press-item__source {
  display: block;
  margin-top: var(--space-2);
  font-size: var(--text-sm);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--color-gold);
}

/* ==========================================================================
   Social icons
   ========================================================================== */
.socials {
  display: flex;
  gap: var(--space-2);
}
.socials--footer { margin-top: var(--space-1); }
.social-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  border: var(--hairline) solid color-mix(in srgb, var(--text-on-dark) 30%, transparent);
  border-radius: 50%;
  color: var(--text-on-dark);
  transition: background var(--transition-base), color var(--transition-base),
    border-color var(--transition-base);
}
.social-link svg { width: 18px; height: 18px; }
.social-link:hover {
  background: var(--color-gold);
  border-color: var(--color-gold);
  color: var(--color-ink);
}

/* ==========================================================================
   Interior page header (smaller than the home hero)
   ========================================================================== */
.page-head {
  position: relative;
  padding-block: var(--space-7) var(--space-6);
  text-align: center;
  background-color: var(--surface-dark);
  background-image: var(--vignette), var(--sheen-top-strong);
  background-repeat: no-repeat;
  border-bottom: var(--hairline) solid var(--border-hairline);
}
.page-head__eyebrow {
  font-family: var(--font-body);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-gold-soft);
}
.page-head__title {
  font-size: var(--text-3xl);
  margin-top: var(--space-2);
  color: var(--text-on-dark);
}
/* Only when the page title IS her name (Biographie). The other interior pages
   are titled "Actualités", "On en parle", "Écrivez-moi" — those stay Playfair,
   so the signature font never becomes just "the heading font". */
.page-head__title--name {
  font-family: var(--font-name);
  font-weight: 600;
  letter-spacing: var(--tracking-normal);
}
.page-head__lead {
  max-width: 52ch;
  margin: var(--space-3) auto 0;
  font-size: var(--text-lg);
  font-style: italic;
  color: color-mix(in srgb, var(--text-on-dark) 80%, transparent);
}

/* ==========================================================================
   In-page section sub-nav (jump links on the merged Biography page)
   Sticks just under the main header; scrolls horizontally on narrow screens.
   Links work with no JS; main.js adds active-section highlighting on scroll.
   ========================================================================== */
.subnav {
  position: sticky;
  top: var(--header-height);
  z-index: var(--z-subnav);
  background: color-mix(in srgb, var(--surface-page) 92%, transparent);
  backdrop-filter: blur(8px);
  border-bottom: var(--hairline) solid var(--border-hairline);
}
.subnav__list {
  display: flex;
  gap: var(--space-4);
  overflow-x: auto;               /* horizontal scroll if the pills overflow */
  scrollbar-width: none;          /* Firefox: hide the scrollbar */
  -webkit-overflow-scrolling: touch;
  padding-block: var(--space-2);
}
.subnav__list::-webkit-scrollbar { display: none; }  /* WebKit: hide the scrollbar */
.subnav__link {
  white-space: nowrap;
  font-family: var(--font-display);   /* Playfair Display — ties the sub-nav to the headings */
  font-size: var(--text-base);
  font-style: italic;
  letter-spacing: var(--tracking-wide);
  color: var(--text-secondary);
  padding-bottom: 3px;
  border-bottom: 2px solid transparent;
  transition: color var(--transition-base), border-color var(--transition-base);
}
.subnav__link:hover { color: var(--text-primary); }
.subnav__link[aria-current="true"] {
  color: var(--color-gold);
  border-bottom-color: var(--color-gold);
}

/* Offset anchor targets so a jump doesn't tuck the heading under the two sticky bars */
.section[id] { scroll-margin-top: calc(var(--header-height) + 4.5rem); }

/* ==========================================================================
   Long-form prose (biography)
   ========================================================================== */
.prose > p + p { margin-top: var(--space-3); }
.prose p { color: var(--text-secondary); }
.prose em { color: var(--text-primary); }
.prose__lead {
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-style: italic;
  line-height: var(--leading-snug);
  color: var(--text-primary) !important;
  margin-bottom: var(--space-4);
}
.prose__lead::first-letter { color: var(--color-gold); }

/* Two-column: main narrative + facts aside */
.bio-grid {
  display: grid;
  grid-template-columns: 1fr minmax(220px, 300px);
  gap: var(--space-6);
  align-items: start;
}
/* Right column: portrait stacked above the facts card. Stretched to the full row
   height so the sticky "En bref" card below still has room to travel as you scroll. */
.bio-side {
  align-self: stretch;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.bio-portrait {
  margin: 0;
  border: var(--hairline) solid var(--border-hairline);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.bio-portrait img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 5;      /* portrait crop, consistent on any source image */
  object-fit: cover;
}
.bio-aside {
  position: sticky;
  top: calc(var(--header-height) + 4.5rem);   /* clears the main header + the sticky sub-nav */
  padding: var(--space-4);
  background: var(--surface-card);
  border: var(--hairline) solid var(--border-hairline);
  border-top: 3px solid var(--color-gold);
  border-radius: var(--radius-sm);
}
.bio-aside__title {
  font-family: var(--font-body);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-gold);
  margin-bottom: var(--space-2);
}
.facts { display: grid; gap: var(--space-2); }
.facts__row { display: grid; gap: 0.15rem; }
.facts__key {
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-secondary);
}
.facts__val { color: var(--text-primary); }

/* Sub-section heading inside content pages */
.subhead {
  font-size: var(--text-xl);
  margin-bottom: var(--space-3);
  color: var(--text-primary);
}
.subhead--gold { color: var(--color-gold); }

/* Simple labelled list (training / education) */
.list-plain > li {
  padding-block: var(--space-2);
  border-bottom: var(--hairline) solid var(--border-hairline);
  color: var(--text-secondary);
}
.list-plain > li:last-child { border-bottom: 0; }

/* Discography entry */
.disc {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-2);
  align-items: baseline;
  padding: var(--space-3);
  background: var(--surface-card);
  border: var(--hairline) solid var(--border-hairline);
  border-radius: var(--radius-sm);
}
.disc__title { font-family: var(--font-display); font-size: var(--text-lg); font-style: italic; color: var(--text-primary); }
.disc__meta { color: var(--text-secondary); font-size: var(--text-sm); }
.disc__year { color: var(--color-gold); font-size: var(--text-sm); letter-spacing: var(--tracking-wide); }

/* ==========================================================================
   Timeline (repertoire)
   ========================================================================== */
.timeline { border-top: var(--hairline) solid var(--border-hairline); }
.timeline__item {
  display: grid;
  grid-template-columns: 7rem 1fr;
  gap: var(--space-3);
  padding-block: var(--space-3);
  border-bottom: var(--hairline) solid var(--border-hairline);
}
.timeline__year {
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: 600;
  color: var(--color-gold);
  white-space: nowrap;
}
.timeline__desc { color: var(--text-secondary); }
.timeline__desc .work {
  font-style: italic;
  color: var(--text-primary);
}

/* Filmography — compact cards */
.filmography {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-3);
}
.film-card {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
  background: var(--surface-card);
  border: var(--hairline) solid var(--border-hairline);
  border-left: 3px solid var(--color-gold);
  border-radius: var(--radius-sm);
}
/* Movie poster thumbnail (links to the same IMDb page as the title) */
.film-card__poster {
  flex: 0 0 auto;
  width: clamp(130px, 34%, 168px);
  display: block;
  border-radius: var(--radius-sm);
  overflow: hidden;
  border: var(--hairline) solid var(--border-hairline);
}
.film-card__poster img {
  display: block;
  width: 100%;
  aspect-ratio: 2 / 3;
  object-fit: cover;
  transition: transform var(--transition-base);
}
.film-card__poster:hover img,
.film-card__poster:focus-visible img { transform: scale(1.04); }
.film-card__info { min-width: 0; }
.film-card__year { color: var(--color-gold); font-size: var(--text-sm); letter-spacing: var(--tracking-wide); }
.film-card__title { font-family: var(--font-display); font-size: var(--text-lg); font-style: italic; margin-top: 0.2rem; }
.film-card__title a { color: inherit; text-decoration: none; transition: color var(--transition-base); }
.film-card__title a:hover,
.film-card__title a:focus-visible { color: var(--color-gold); text-decoration: underline; text-underline-offset: 3px; }
.film-card__role { color: var(--text-secondary); font-size: var(--text-sm); margin-top: 0.2rem; }

/* ==========================================================================
   Press page — critic cards, gallery, video
   ========================================================================== */
.press-cards { display: grid; gap: var(--space-4); }

/* Horizontal snap-scroller variant — keeps the critiques from running long.
   Cards are equal width; on narrow screens one card fills the view with a peek
   of the next, signalling that the row scrolls. */
.press-cards--scroll {
  display: flex;
  gap: var(--space-4);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding-bottom: var(--space-2);          /* room for the scrollbar */
  scrollbar-color: var(--color-gold) transparent;
}
.press-cards--scroll::-webkit-scrollbar { height: 8px; }
.press-cards--scroll::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, var(--color-gold) 60%, transparent);
  border-radius: 99px;
}
.press-cards--scroll:focus-visible {
  outline: 2px solid var(--color-gold);
  outline-offset: 4px;
}
.press-cards--scroll .press-card {
  flex: 0 0 auto;
  width: min(440px, 86vw);
  padding: var(--space-5) var(--space-4);
  scroll-snap-align: start;
  /* Ivory "press cutting" — dark text on warm off-white, gold rule kept */
  background: var(--surface-card-light);
  border-color: color-mix(in srgb, var(--color-ink) 14%, transparent);
  border-left-color: var(--color-gold);
}
.press-cards--scroll .press-card__quote { color: var(--color-ink); }
.press-cards--scroll .press-card__meta { color: var(--text-ink-muted); }
.press-cards--scroll .press-card__pub { color: var(--color-burgundy); }
.press-cards--scroll .press-card .link-more { color: var(--color-burgundy); }
.press-cards--scroll .press-card .link-more:hover { color: var(--color-burgundy-deep); }
.press-card {
  padding: var(--space-4);
  background: var(--surface-card);
  border: var(--hairline) solid var(--border-hairline);
  border-left: 3px solid var(--color-gold);
  border-radius: var(--radius-sm);
}
.press-card__quote {
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-style: italic;
  line-height: var(--leading-snug);
  color: var(--text-primary);
}
.press-card__meta {
  margin-top: var(--space-3);
  font-size: var(--text-sm);
  color: var(--text-secondary);
}
.press-card__pub {
  font-weight: 600;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--color-gold);
}
.press-card__link { margin-top: var(--space-2); }

/* Plain press mentions (no quote) */
.mentions > li {
  padding-block: var(--space-2);
  border-bottom: var(--hairline) solid var(--border-hairline);
}
.mentions > li:last-child { border-bottom: 0; }
.mentions a { color: var(--text-primary); }
.mentions a:hover { color: var(--color-gold); }

/* ==========================================================================
   Médias — photos + videos grouped into one "folder" per production
   ========================================================================== */
.media-group + .media-group { margin-top: var(--space-6); }
.media-group__title {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-1) var(--space-2);
  font-family: var(--font-display);
  font-size: var(--text-xl);
  color: var(--text-primary);
  padding-bottom: var(--space-2);
  margin-bottom: var(--space-4);
  border-bottom: var(--hairline) solid var(--border-hairline);
}
.media-group__sub {
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-style: italic;
  letter-spacing: var(--tracking-wide);
  color: var(--color-gold);
}
/* A featured video inside a folder shouldn't dominate — cap its width */
.media-group .featured-video { max-width: 720px; }

.media-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: var(--space-3);
}
.media-tile { margin: 0; }
.media-tile__link { display: block; color: inherit; }
.media-tile__frame {
  position: relative;
  display: block;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: var(--surface-dark);
  border: var(--hairline) solid var(--border-hairline);
  border-radius: var(--radius-sm);
  transition: border-color var(--transition-base), box-shadow var(--transition-base);
}
.media-tile__frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--transition-base);
}
.media-tile__link:hover .media-tile__frame,
.media-tile__link:focus-visible .media-tile__frame {
  border-color: color-mix(in srgb, var(--color-gold) 55%, var(--border-hairline));
  box-shadow: var(--shadow-md);
}
.media-tile__link:hover .media-tile__frame img,
.media-tile__link:focus-visible .media-tile__frame img { transform: scale(1.04); }
.media-tile__cap {
  margin-top: var(--space-1);
  font-size: var(--text-sm);
  color: var(--text-secondary);
}
.media-tile__ext {
  display: block;
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--color-gold);
}
/* Short intro line under a subhead */
.section-intro {
  max-width: 60ch;
  margin-bottom: var(--space-4);
  color: var(--text-secondary);
}

/* --- Video tiles: play badge + platform label -----------------------------
   Positioned over a .media-tile__frame that carries the .media-tile--video
   modifier. Reused by any video tile inside a Médias folder. */
.media-tile--video .media-tile__frame::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(6, 6, 8, 0.45), rgba(6, 6, 8, 0) 55%);
}
.video-card__play {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 3.25rem;
  height: 3.25rem;
  border-radius: 50%;
  color: var(--color-ink);
  background: color-mix(in srgb, var(--color-gold) 92%, transparent);
  box-shadow: 0 2px 14px rgba(0, 0, 0, 0.45);
  z-index: 1;
  transition: transform var(--transition-base), background var(--transition-base);
}
.video-card__play svg { width: 1.4rem; height: 1.4rem; margin-left: 2px; }
.media-tile__link:hover .video-card__play,
.media-tile__link:focus-visible .video-card__play {
  transform: translate(-50%, -50%) scale(1.08);
  background: var(--color-gold-bright);
}
.video-card__platform {
  position: absolute;
  top: var(--space-2);
  left: var(--space-2);
  z-index: 1;
  font-family: var(--font-body);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-on-dark);
  padding: 0.25rem 0.55rem;
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--color-black) 62%, transparent);
  backdrop-filter: blur(4px);
}
/* --- Featured self-hosted video ------------------------------------------- */
.featured-video {
  margin: 0 0 var(--space-5);
}
.featured-video__player {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  background: var(--surface-dark);
  border: var(--hairline) solid var(--border-hairline);
  border-radius: var(--radius-sm);
}
.featured-video__caption { margin-top: var(--space-3); }
.featured-video__title {
  font-family: var(--font-display);
  font-size: var(--text-lg);
  color: var(--text-primary);
}
.featured-video__credits {
  margin-top: var(--space-1);
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: var(--leading-normal);
}

/* --- Press clippings — horizontally scrolling strip of scanned articles --- */
.clippings {
  display: flex;
  gap: var(--space-4);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding-bottom: var(--space-2);          /* breathing room for the scrollbar */
  scrollbar-color: var(--color-gold) transparent;
}
.clippings::-webkit-scrollbar { height: 8px; }
.clippings::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, var(--color-gold) 60%, transparent);
  border-radius: 99px;
}
.clipping {
  flex: 0 0 auto;
  width: min(300px, 78vw);
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
}
.clipping__link {
  display: flex;
  flex-direction: column;
  color: inherit;
  background: var(--surface-card);
  border: var(--hairline) solid var(--border-hairline);
  border-radius: var(--radius-sm);
  overflow: hidden;
  transition: transform var(--transition-base), box-shadow var(--transition-base),
    border-color var(--transition-base);
}
.clipping__link:hover,
.clipping__link:focus-visible {
  transform: translateY(-4px);
  box-shadow: var(--shadow-md);
  border-color: color-mix(in srgb, var(--color-gold) 55%, var(--border-hairline));
}
.clipping__frame {
  display: block;
  aspect-ratio: 3 / 4;
  overflow: hidden;
  background: var(--surface-dark);
}
.clipping__frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top;                    /* keep headline + photo in view */
}
.clipping__body {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  padding: var(--space-3);
}
.clipping__title {
  font-family: var(--font-display);
  font-size: var(--text-md);
  font-style: italic;
  line-height: var(--leading-snug);
  color: var(--text-primary);
}
.clipping__meta { font-size: var(--text-sm); color: var(--text-secondary); }
.clipping__cta {
  margin-top: 0.2rem;
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--color-gold);
}
/* Collapsible transcription (accessibility + SEO for image-only articles) */
.clipping__text {
  margin-top: var(--space-2);
  font-size: var(--text-sm);
}
.clipping__text > summary {
  cursor: pointer;
  color: var(--color-gold);
  letter-spacing: var(--tracking-wide);
  padding-block: 2px;   /* 23px → 27px: clears the 24px tap-target minimum */
}
.clipping__text > p { margin-top: var(--space-2); color: var(--text-secondary); }
.clipping__text em { color: var(--text-primary); font-style: italic; }
.clipping__byline { font-style: italic; }

/* ==========================================================================
   Lightbox — enlarge a media tile in place, with prev/next.
   Built and controlled by js/main.js. Progressive enhancement: with no JS the
   tiles stay plain links to the file, so the gallery still works.
   ========================================================================== */
.lightbox[hidden] { display: none; }
.lightbox {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  padding: clamp(1rem, 4vw, 3rem);
  background: color-mix(in srgb, var(--color-black) 92%, transparent);
  backdrop-filter: blur(6px);
}
.lightbox__stage {
  display: flex;
  align-items: center;
  justify-content: center;
  max-width: min(1100px, 92vw);
  width: 100%;
}
.lightbox__media {
  max-width: 92vw;
  max-height: 78vh;
  width: auto;
  height: auto;
  object-fit: contain;
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-md);
  background: var(--surface-dark);
}
.lightbox__caption {
  min-height: 1.4em;
  max-width: 60ch;
  text-align: center;
  color: var(--text-secondary);
  font-size: var(--text-sm);
}
.lightbox__caption em { color: var(--text-primary); font-style: italic; }
.lightbox__counter { color: var(--color-gold); letter-spacing: var(--tracking-wide); }

.lightbox__btn {
  position: absolute;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 3rem;
  height: 3rem;
  border-radius: 50%;
  color: var(--text-on-dark);
  background: color-mix(in srgb, var(--color-black) 55%, transparent);
  border: var(--hairline) solid color-mix(in srgb, var(--color-cream) 25%, transparent);
  transition: background var(--transition-base), color var(--transition-base),
    border-color var(--transition-base);
}
.lightbox__btn:hover,
.lightbox__btn:focus-visible {
  background: var(--color-gold);
  color: var(--color-ink);
  border-color: var(--color-gold);
}
.lightbox__btn[hidden] { display: none; }
.lightbox__btn svg { width: 1.4rem; height: 1.4rem; }
.lightbox__close { top: clamp(0.75rem, 2.5vw, 1.5rem); right: clamp(0.75rem, 2.5vw, 1.5rem); }
.lightbox__prev { left: clamp(0.4rem, 2vw, 1.5rem); top: 50%; transform: translateY(-50%); }
.lightbox__next { right: clamp(0.4rem, 2vw, 1.5rem); top: 50%; transform: translateY(-50%); }

/* ==========================================================================
   Cursor sparkle (decorative) — gold star particles that trail the mouse.
   Created/removed by js/main.js; size, colour and position are set inline.
   ========================================================================== */
.sparkle {
  position: fixed;
  z-index: 9999;
  pointer-events: none;
  clip-path: polygon(
    50% 0%, 59% 41%, 100% 50%, 59% 59%,
    50% 100%, 41% 59%, 0% 50%, 41% 41%
  );
  filter: drop-shadow(0 0 4px rgba(224, 200, 143, 0.7));
  will-change: transform, opacity;
}

/* Belt and braces: never show sparkles for reduced-motion users */
@media (prefers-reduced-motion: reduce) {
  .sparkle { display: none; }
}

/* ==========================================================================
   Contact form
   ========================================================================== */
.contact-grid {
  display: grid;
  grid-template-columns: 1.4fr 1fr;
  gap: var(--space-6);
  align-items: start;
}

.form { display: grid; gap: var(--space-3); }
.field { display: grid; gap: 0.4rem; }
.field__label {
  font-size: var(--text-sm);
  letter-spacing: var(--tracking-wide);
  color: var(--text-primary);
}
.field__req { color: var(--color-gold); }
.field input,
.field textarea {
  width: 100%;
  padding: 0.75rem 0.9rem;
  background: var(--surface-card);
  color: var(--text-primary);
  border: var(--hairline) solid var(--border-hairline);
  border-radius: var(--radius-sm);
  font-size: var(--text-base);
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
.field input::placeholder,
.field textarea::placeholder { color: color-mix(in srgb, var(--text-secondary) 70%, transparent); }
.field input:focus,
.field textarea:focus {
  outline: none;
  border-color: var(--color-gold);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-gold) 25%, transparent);
}
.field textarea { resize: vertical; min-height: 9rem; }
/* invalid state only after the browser has flagged it */
.field input:user-invalid,
.field textarea:user-invalid { border-color: var(--color-burgundy); }

/* Honeypot — visually hidden, off-screen (anti-spam) */
.hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.form__status {
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  border: var(--hairline) solid transparent;
}
.form__status[hidden] { display: none; }
.form__status--ok {
  color: var(--color-gold-bright);
  background: color-mix(in srgb, var(--color-gold) 12%, transparent);
  border-color: color-mix(in srgb, var(--color-gold) 40%, transparent);
}
.form__status--error {
  color: #e7a6a6;
  background: color-mix(in srgb, var(--color-burgundy) 22%, transparent);
  border-color: color-mix(in srgb, var(--color-burgundy) 55%, transparent);
}

/* Contact aside */
.contact-aside {
  padding: var(--space-4);
  background: var(--surface-card);
  border: var(--hairline) solid var(--border-hairline);
  border-top: 3px solid var(--color-gold);
  border-radius: var(--radius-sm);
}
.contact-aside__title {
  font-family: var(--font-body);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-gold);
  margin-bottom: var(--space-2);
}
/* The aside gained a second labelled block on 2026-08-03 (the direct email
   address, under the socials). Only a *subsequent* title needs a top margin —
   the first one opens the aside and would otherwise push it out of alignment
   with the form beside it. */
.contact-aside__title:not(:first-child) { margin-top: var(--space-4); }
.contact-aside__text { color: var(--text-secondary); font-size: var(--text-sm); }
.contact-aside .socials { margin-top: var(--space-3); }
.contact-aside .social-link {
  color: var(--text-primary);
  border-color: var(--border-hairline);
}

/* ==========================================================================
   Responsive
   ========================================================================== */
@media (max-width: 820px) {
  .about {
    grid-template-columns: 1fr;
    gap: var(--space-4);
  }
  .about__portrait { max-width: 380px; }
  .roles { grid-template-columns: 1fr; }
  .press-strip { grid-template-columns: 1fr; gap: var(--space-3); }

  .bio-grid { grid-template-columns: 1fr; gap: var(--space-4); }
  .bio-aside { position: static; }
  .filmography { grid-template-columns: 1fr; }
  .contact-grid { grid-template-columns: 1fr; gap: var(--space-4); }
}

@media (max-width: 500px) {
  .hero { min-height: 78vh; }
  .pullquote__text { font-size: var(--text-xl); }

  .timeline__item { grid-template-columns: 1fr; gap: var(--space-1); }
  .timeline__year { font-size: var(--text-base); }
  .press-card__quote { font-size: var(--text-lg); }
}

/* --- Contact — consent checkbox (RGPD) ----------------------------------- */
.field--consent .field__consent {
  display: flex;
  align-items: flex-start;
  gap: var(--space-1);
  font-size: 0.9rem;
  line-height: 1.5;
  color: var(--color-cream-soft);
  cursor: pointer;
}
.field--consent input[type="checkbox"] {
  flex: 0 0 auto;
  margin-top: 0.2em;
  width: 1.05em;
  height: 1.05em;
  accent-color: var(--color-gold);
}
.field--consent a {
  color: var(--color-gold);
}

/* ==========================================================================
   Actualités — news feed + duo project panels
   --------------------------------------------------------------------------
   This page is the one Élodie updates most often, so the markup is optimised
   for hand-editing: one flat <article> per entry, no wrapper soup, no class
   that has to be kept in sync with anything else. See actualites.html for the
   copy-paste template at the top of the list.
   ========================================================================== */

/* Cards side by side rather than a stacked list: two items fit on one screen,
   so the visitor sees the news without scrolling. auto-fit means adding a
   third or fourth entry reflows on its own — no CSS change when news grows. */
/* `min(300px, 100%)` rather than a bare 300px: a minmax() floor is a hard
   floor, so on a 320px phone the 300px column plus the container padding was
   wider than the screen and the cards bled off the right edge. min() lets the
   single column shrink to the container when there isn't room for 300px. */
.news-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
  gap: var(--space-4);
}
.news {
  display: flex;
  flex-direction: column;
  background: var(--surface-card);
  border: var(--hairline) solid var(--border-hairline);
  border-radius: var(--radius-sm);
  overflow: hidden;
  transition: border-color var(--transition-base), box-shadow var(--transition-base);
}
.news:hover,
.news:focus-within {
  border-color: color-mix(in srgb, var(--color-gold) 55%, var(--border-hairline));
  box-shadow: var(--shadow-md);
}

/* Fixed 4:3 frame + object-fit: cover, so a portrait poster and a landscape
   stage photo sit side by side without one card growing taller than the other. */
.news__frame {
  display: block;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: var(--surface-dark);
  border-bottom: var(--hairline) solid var(--border-hairline);
}
.news__frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--transition-base);
}
.news:hover .news__frame img { transform: scale(1.04); }

.news__body {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: var(--space-3);
}
.news__date {
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-gold);
}
.news__title {
  margin-top: var(--space-1);
  font-family: var(--font-display);
  font-size: var(--text-xl);
  line-height: var(--leading-tight);
  color: var(--text-primary);
}
.news__meta {
  margin-top: var(--space-1);
  font-size: var(--text-sm);
  font-style: italic;
  color: var(--text-secondary);
}
.news__text {
  margin-top: var(--space-2);
  font-size: var(--text-sm);
  color: var(--text-primary);
}
/* margin-top:auto pins the link to the card's bottom edge, so the links line
   up across cards even when the descriptions differ in length. */
.news__links {
  margin-top: auto;
  padding-top: var(--space-3);
}

/* "À venir" flag — a dated entry still in the future. Not colour alone:
   it carries the word, so it survives greyscale and screen readers. */
.news__badge {
  display: inline-block;
  /* .news__body is a column flex container, which blockifies this and stretches it to the
     full column width — the gold plate then runs edge to edge instead of hugging its label.
     `align-self` is what actually keeps it pill-sized; `display: inline-block` alone does not. */
  align-self: flex-start;
  margin-bottom: var(--space-1);
  padding: 0.15em 0.6em;
  font-family: var(--font-body);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--color-ink);
  background: var(--color-gold);
  border-radius: var(--radius-sm);
}

/* A claim we can't yet source — shown to Élodie, not to the public.
   Delete the <span> once she confirms; the page reads correctly without it. */
.to-confirm {
  font-style: normal;
  color: var(--color-gold-soft);
  border-bottom: var(--hairline) dashed currentColor;
  cursor: help;
}

/* ==========================================================================
   Placeholder block — copy Élodie has not written yet
   --------------------------------------------------------------------------
   Replaces the earlier approach of printing the open questions as body text.
   Questions-as-copy read like real prose to anyone skimming, which is exactly
   how placeholder text ends up shipping on a real person's site. A skeleton
   block cannot be mistaken for content: it is visibly an empty slot waiting to
   be filled, and it stays legible about *what* is missing via its label.

   The questions themselves are not lost — they now live in an HTML comment
   directly above each block, where they serve the person filling it in rather
   than the visitor reading it.
   ========================================================================== */
.placeholder {
  margin-top: var(--space-3);
  padding: var(--space-3);
  border: var(--hairline) dashed color-mix(in srgb, var(--color-gold) 45%, transparent);
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--color-gold) 4%, transparent);
}
.placeholder__label {
  display: block;
  font-family: var(--font-body);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-gold);
  margin-bottom: var(--space-2);
}
/* The greyed bars that stand in for lines of text. Widths vary so the block
   reads as a paragraph rather than a table; the last line is short, as a real
   final line is. */
.placeholder__lines {
  display: grid;
  gap: 0.55rem;
}
.placeholder__line {
  display: block;
  height: 0.7rem;
  border-radius: 99px;
  background: color-mix(in srgb, var(--text-secondary) 22%, transparent);
}
.placeholder__line:nth-child(2) { width: 96%; }
.placeholder__line:nth-child(3) { width: 99%; }
.placeholder__line:nth-child(4) { width: 62%; }

/* Compact variant for the bullet lists under each panel */
.placeholder--points {
  margin-top: var(--space-3);
  padding: var(--space-2) var(--space-3);
}
.placeholder--points .placeholder__lines { gap: 0.7rem; }
.placeholder--points .placeholder__line { height: 0.6rem; }
.placeholder--points .placeholder__line:nth-child(1) { width: 58%; }
.placeholder--points .placeholder__line:nth-child(2) { width: 72%; }
.placeholder--points .placeholder__line:nth-child(3) { width: 45%; }

/* --- Duo project panel ---------------------------------------------------
   A self-contained block: portrait/poster on the left, description + media on
   the right. Used for the two duo projects; the audio players live INSIDE the
   panel so a listener can never be in doubt about which duo they're hearing. */
.duo {
  display: grid;
  grid-template-columns: minmax(0, 300px) 1fr;
  gap: var(--space-5);
  align-items: start;
}
.duo + .duo {
  margin-top: var(--space-7);
  padding-top: var(--space-7);
  border-top: var(--hairline) solid var(--border-hairline);
}
/* Wrapper when a duo has more than one image — keeps them as ONE grid item so
   the two-column layout (media | text) still holds. */
.duo__media { display: grid; gap: var(--space-3); }
.duo__figure { margin: 0; }
.duo__img {
  width: 100%;
  height: auto;
  display: block;
  border: var(--hairline) solid var(--border-hairline);
  border-radius: var(--radius-sm);
}
.duo__caption {
  margin-top: var(--space-1);
  font-size: var(--text-xs);
  color: var(--text-secondary);
}
.duo__title {
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  line-height: var(--leading-tight);
  color: var(--text-primary);
}
.duo__players {
  margin-top: var(--space-1);
  font-size: var(--text-md);
  font-style: italic;
  color: var(--color-gold);
}
.duo__text {
  margin-top: var(--space-3);
  max-width: 60ch;
}

/* --- Playlist ------------------------------------------------------------
   Native <audio controls>: no JS, keyboard-accessible and screen-reader
   labelled for free. `color-scheme: dark` is what makes the browser's own
   control bar render dark instead of light-grey on our near-black page —
   a standards-based fix rather than a filter hack. */
.playlist {
  list-style: none;
  margin: var(--space-2) 0 0;
  padding: 0;
  border-top: var(--hairline) solid var(--border-hairline);
}
/* The top margin lives on the label, not the list: the label is what the
   preceding paragraph needs separating from. */
.playlist__label {
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-gold);
  margin: var(--space-5) 0 var(--space-1);
}
/* Cycle-level credit. When every track shares a composer and poet — as with
   the four Steinkogler/Verlaine extracts — the attribution belongs here once,
   the way a concert programme heads a song cycle, rather than repeated
   verbatim on all four lines. Per-track `.track__source` stays for playlists
   whose tracks genuinely differ. */
.playlist__credit {
  margin: 0 0 var(--space-2);
  font-size: var(--text-sm);
  color: var(--text-secondary);
}
.playlist__credit em { color: var(--text-primary); font-style: italic; }
.track {
  padding-block: var(--space-2);
  border-bottom: var(--hairline) solid var(--border-hairline);
  transition: background var(--transition-base);
}
/* The row currently sounding. Reinforcement only — the native player's own
   play/pause button is what actually states the state, so this never has to
   carry the meaning by colour alone. */
.track.is-playing {
  background: color-mix(in srgb, var(--color-gold) 7%, transparent);
}
.track.is-playing .track__title { color: var(--color-gold-bright); }
.track.is-playing .track__num { color: var(--color-gold-bright); }
.track__head {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
}
.track__num {
  font-family: var(--font-display);
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
  color: var(--color-gold);
}
.track__title {
  font-family: var(--font-display);
  font-size: var(--text-md);
  color: var(--text-primary);
}
.track__source {
  margin-left: auto;
  font-size: var(--text-xs);
  font-style: italic;
  color: var(--text-secondary);
}
.track__player {
  color-scheme: dark;
  display: block;
  width: 100%;
  margin-top: var(--space-1);
}

/* --- The poem behind the track -------------------------------------------
   These are mélodies: the point is a poem set to music, so the text belongs
   next to the recording. Verlaine died in 1896 — public domain, nothing to
   clear. Texts transcribed from the validated Wikisource scans of the Vanier
   Œuvres complètes (1902); see the comment in actualites.html.

   Built on <details> deliberately: it is keyboard-operable and openable with
   no JS at all, so the poem is never content that a script has to rescue.
   js/main.js only opens it *early* — on play — which is the enhancement. */
.poem {
  margin-top: var(--space-2);
}
.poem__toggle {
  cursor: pointer;
  padding-block: 2px;   /* clears the 24px tap-target minimum */
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--color-gold);
  transition: color var(--transition-fast);
}
.poem__toggle:hover { color: var(--color-gold-bright); }
.poem__body {
  margin-top: var(--space-2);
  padding-left: var(--space-3);
  border-left: 2px solid color-mix(in srgb, var(--color-gold) 45%, transparent);
}
.poem__source {
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-wide);
  color: var(--text-secondary);
  margin-bottom: var(--space-2);
}
.poem__source em { color: var(--text-primary); font-style: italic; }
/* Verlaine's epigraph to "Il pleure dans mon cœur" — set apart and to the
   right, as it is printed. */
.poem__epigraph {
  margin-bottom: var(--space-2);
  font-style: italic;
  font-size: var(--text-sm);
  text-align: right;
  color: var(--text-secondary);
}
.poem__epigraph-cite {
  font-style: normal;
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-wide);
  color: var(--color-gold-soft);
}
.poem__stanza {
  color: var(--text-primary);
  line-height: var(--leading-relaxed);
}
.poem__stanza + .poem__stanza { margin-top: var(--space-2); }
/* Colombine sets its 3rd and 6th lines short and indented; the shape of the
   stanza is part of the poem, so it is preserved rather than flattened. */
.poem__in { display: inline-block; margin-left: var(--space-4); }

/* Opening — whether by click or because the track started playing. */
.poem[open] .poem__body { animation: poem-in 500ms ease both; }
@keyframes poem-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .poem[open] .poem__body { animation: none; }
}

@media (max-width: 820px) {
  .duo { grid-template-columns: 1fr; gap: var(--space-4); }
  .duo__figure { max-width: 340px; }
}
@media (max-width: 560px) {
  .track__head { flex-wrap: wrap; }
  .track__source { margin-left: 0; flex-basis: 100%; }
}

/* ==========================================================================
   Reveal on scroll
   --------------------------------------------------------------------------
   The hidden state is scoped to html.js — and js/main.js is what adds that
   class. No JS ⇒ no .js ⇒ nothing is ever hidden. An animation must never be
   able to hide content from someone whose script didn't run.
   ========================================================================== */
.js .reveal {
  opacity: 0;
  transform: translateY(18px);
  /* Long, soft ease-out: the motion decelerates into place rather than
     arriving abruptly. Opacity leads slightly so it reads as "emerging". */
  transition:
    opacity 700ms cubic-bezier(0.16, 1, 0.3, 1),
    transform 700ms cubic-bezier(0.16, 1, 0.3, 1);
  transition-delay: var(--reveal-delay, 0ms);
  will-change: opacity, transform;
}
.js .reveal.is-visible {
  opacity: 1;
  transform: none;
  will-change: auto;
}

/* Stagger children of a group: each step is small enough to read as one
   gesture rather than a queue. Set --reveal-delay inline for one-offs. */
.js .reveal-group > *:nth-child(1) { --reveal-delay: 0ms; }
.js .reveal-group > *:nth-child(2) { --reveal-delay: 90ms; }
.js .reveal-group > *:nth-child(3) { --reveal-delay: 180ms; }
.js .reveal-group > *:nth-child(4) { --reveal-delay: 270ms; }

/* Images drift up from slightly further and scale back to true size — a
   restrained "settle" that suits photography without looking like a slideshow. */
.js .reveal--media {
  transform: translateY(28px) scale(1.03);
  transition-duration: 900ms;
}
.js .reveal--media.is-visible { transform: none; }

@media (prefers-reduced-motion: reduce) {
  .js .reveal,
  .js .reveal--media {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* ==========================================================================
   Transmissions — alternating teaching panels
   ========================================================================== */
.teach {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(var(--space-4), 5vw, var(--space-7));
  align-items: center;
}
/* Every other panel flips, so the eye zig-zags down the page instead of
   marching down a single column. `order` keeps the DOM in reading order —
   the text always comes first for a screen reader, whatever the visual side. */
.teach--flip .teach__media { order: 2; }

.teach__media { margin: 0; }
.teach__img {
  width: 100%;
  height: auto;
  display: block;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border: var(--hairline) solid var(--border-hairline);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-md);
}
.teach__caption {
  margin-top: var(--space-1);
  font-size: var(--text-xs);
  color: var(--text-secondary);
}

.teach__num {
  font-family: var(--font-display);
  font-size: var(--text-sm);
  letter-spacing: var(--tracking-label);
  color: var(--color-gold);
}
.teach__title {
  margin-top: var(--space-1);
  font-family: var(--font-display);
  font-size: clamp(var(--text-xl), 3.2vw, var(--text-3xl));
  line-height: var(--leading-tight);
  color: var(--text-primary);
}
.teach__sub {
  margin-top: var(--space-1);
  font-size: var(--text-md);
  font-style: italic;
  color: var(--color-gold);
}
.teach__text {
  margin-top: var(--space-3);
  max-width: 56ch;
  line-height: var(--leading-relaxed);
}
.teach__points {
  margin: var(--space-3) 0 0;
  padding: 0;
  list-style: none;
}
.teach__points li {
  position: relative;
  padding-left: var(--space-3);
  margin-top: var(--space-1);
  color: var(--text-secondary);
}
.teach__points li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.65em;
  width: 10px;
  height: var(--hairline);
  background: var(--color-gold);
}
.teach__cta { margin-top: var(--space-4); }

/* Volet 02 carries three studio photos rather than one. The first spans the
   full width and the other two sit side by side beneath it, so the panel stays
   near the height of a single image and the zig-zag rhythm down the page holds.
   A plain 3-across strip was tried and rejected: at 320px each frame would be
   about 100px wide, which is too small to read as a photograph. */
.teach__gallery {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-1);
}
.teach__gallery > :first-child { grid-column: 1 / -1; }

/* A heading *inside* a panel ("Cours particuliers", "Objectifs pédagogiques").
   The panel's own h2 is .teach__title, so this is an h3 and must read smaller
   than it — otherwise the hierarchy the markup declares isn't the one you see. */
.teach__h3 {
  margin-top: var(--space-4);
  font-size: var(--text-lg);
  color: var(--color-gold-bright);
}

/* Élodie's own words, set apart from the description they sit in. */
.teach__quote {
  margin: var(--space-4) 0 0;
  padding-left: var(--space-3);
  border-left: var(--hairline) solid var(--color-gold);
  max-width: 56ch;
  font-style: italic;
  font-size: var(--text-md);
  line-height: var(--leading-relaxed);
  color: var(--text-primary);
}
.teach__sign {
  margin-top: var(--space-2);
  font-size: var(--text-sm);
  font-style: italic;
  color: var(--color-gold);
}

/* A partner's logo and booking details, under the volet run with them.
   The pale plate behind the mark is not decoration: this logo is drawn in
   black ink on transparency, so straight onto the page's near-black surface
   it would be invisible. Recolouring somebody else's brand mark isn't ours to
   do — giving it back the light ground it was designed for is. Any future
   partner logo gets the same treatment for the same reason. */
.teach__partner {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin-top: var(--space-4);
}
.teach__partner-logo {
  flex: none;
  width: 5.5rem; /* optical size for this mark — small enough to read as a credit, not a banner */
  height: auto;
  padding: var(--space-1);
  background: var(--color-cream);
  border-radius: var(--radius-sm);
}
.teach__partner-info {
  /* Grow into whatever is left beside the logo, and be allowed to shrink below
     the intrinsic width of the email address — without `min-width: 0` a flex
     item refuses to go narrower than its longest unbreakable word, which pushed
     the whole block onto a second line and left the logo sitting on its own. */
  flex: 1 1 14rem;
  min-width: 0;
  font-size: var(--text-sm);
  line-height: var(--leading-relaxed);
}

/* The contact links on this page are the ones a visitor actually taps on a
   phone — a booking address and a phone number — so they are lifted to the
   24px floor (WCAG 2.5.8) rather than claimed under the inline-link exemption
   that covers links buried in a sentence. Vertical padding on an inline
   element grows the hit area without touching the line box, so the paragraph's
   rhythm is unchanged. Same fix as the .link-more / footer links, 2026-08-02. */
.teach__partner-info a,
.teach__text a {
  padding-block: 0.25rem;
}

@media (max-width: 820px) {
  .teach { grid-template-columns: 1fr; gap: var(--space-4); }
  .teach--flip .teach__media { order: 0; }
}

/* ==========================================================================
   Legal pages — mentions légales & politique de confidentialité
   --------------------------------------------------------------------------
   Two pages of dense administrative prose, which the rest of the site never
   has. They need one thing the editorial components don't provide: a hard
   reading measure. `.container` is 1120px wide, and legal text set that wide
   is genuinely hard to read — the eye loses the line on the return sweep.
   `--measure-legal` caps it at ~68 characters, the usual comfortable range.

   Deliberately NOT styled to look attractive. These pages are consulted, not
   read for pleasure: numbered articles, obvious headings, no reveal animation
   (a visitor looking for the host's address should never wait for a fade).
   ========================================================================== */
.legal {
  --measure-legal: 68ch;
  max-width: var(--measure-legal);
}

/* Numbered article, e.g. "1. Éditeur du site". The rule above each one gives
   the page a scannable rhythm so a visitor can find one clause quickly. */
.legal__article + .legal__article {
  margin-top: var(--space-5);
  padding-top: var(--space-5);
  border-top: var(--hairline) solid var(--color-ink-line);
}
.legal__article h2 {
  font-size: var(--text-lg);
  color: var(--color-gold);
  margin-bottom: var(--space-2);
}
.legal__article h3 {
  font-family: var(--font-body);
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--text-primary);
  margin: var(--space-3) 0 var(--space-1);
}
.legal__article p,
.legal__article li {
  color: var(--text-secondary);
  line-height: var(--leading-relaxed);
}
.legal__article > p + p { margin-top: var(--space-2); }

/* Key/value rows — publisher, host, contact. A <dl> rather than a table: this
   is a set of labelled facts, not tabular data, and it collapses to one column
   on a phone without any of a table's horizontal-scroll problems. */
.legal-dl {
  display: grid;
  grid-template-columns: minmax(9rem, 12rem) 1fr;
  gap: var(--space-1) var(--space-3);
  margin-top: var(--space-2);
}
.legal-dl dt {
  font-size: var(--text-sm);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-cream-soft);
}
.legal-dl dd {
  margin: 0;
  color: var(--text-primary);
}
/* A paragraph following a definition list needs its own breathing room: the
   `> p + p` rule only spaces consecutive paragraphs, so prose set directly
   after a <dl> would otherwise sit flush against the last value. */
.legal-dl + p { margin-top: var(--space-3); }
@media (max-width: 620px) {
  .legal-dl { grid-template-columns: 1fr; gap: 0.15rem; }
  .legal-dl dd { margin-bottom: var(--space-2); }
}

/* Bulleted clause list (the RGPD rights, the data collected). */
.legal-list {
  margin: var(--space-2) 0 0;
  padding: 0;
  list-style: none;
}
.legal-list li {
  position: relative;
  padding-left: var(--space-3);
  margin-top: var(--space-1);
}
.legal-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.75em;
  width: 10px;
  height: var(--hairline);
  background: var(--color-gold);
}

/* "Dernière mise à jour" line under the page title. Sits where
   `.page-head__lead` would, and takes the same --space-3 offset so it doesn't
   crowd the H1's descenders — but stays at --text-sm, because a revision date
   is metadata, not a lead paragraph. */
.legal__updated {
  margin-top: var(--space-3);
  font-size: var(--text-sm);
  font-style: italic;
  color: var(--color-cream-soft);
}

/* ==========================================================================
   Bilingual FR/EN — visibility switching (Phase 3)
   --------------------------------------------------------------------------
   Approach (a) from PROJECT-PLAN §4, confirmed 2026-08-03: both languages sit
   in the HTML, each wrapped in an element carrying a `lang` attribute, and CSS
   shows one while hiding the other. No fetch, no build step, both languages in
   the DOM for search engines, and inline <em> markup survives — which a
   textContent swap would have destroyed.

   ⚠ THE `body` IN THESE SELECTORS IS LOAD-BEARING. Without it, `[lang="fr"]`
   also matches <html lang="fr"> itself, so switching to English would hide the
   entire document. Scoping under body means only in-page blocks are candidates.

   ⚠ PROGRESSIVE ENHANCEMENT: `data-lang="fr"` is written into the HTML by hand,
   not by JS. So with JavaScript disabled the French is what shows and the
   English stays hidden — never the reverse, and never a blank page. This is the
   same contract as `.reveal`: nothing is hidden by default that needs JS to
   become visible.

   `display: none` (not visibility/clip) is deliberate: it removes the inactive
   language from the accessibility tree, so a screen reader reads the page once
   rather than twice.
   ========================================================================== */
html[data-lang="fr"] body [lang="en"]:not([data-keep]) { display: none; }
html[data-lang="en"] body [lang="fr"]:not([data-keep]) { display: none; }

/* `data-keep` = "this is in that language, but show it in both".
   Needed for source material that must NOT be translated but must still be
   readable to an English visitor, and must still be correctly tagged for
   screen readers and search engines:
     · the Verlaine poems on Actualités — the sung text; translating them would
       be both artistically wrong and a new derivative work
     · the Rogliano press transcription — a verbatim French newspaper article
   Without the exemption these would vanish in English, which is worse than
   showing them untranslated. */

/* No styling for the active FR/EN button is needed here — `.lang-toggle__btn
   [aria-pressed="true"]` already exists in layout.css and has been styling the
   inert buttons since Phase 1. Wiring `aria-pressed` in i18n.js is therefore
   all it takes to make the highlight follow the real language. */
