/* ═══════════════════════════════════════════════════════════════════════════
   components.css — navigation, sélecteur de langue, boutons, liens, badges,
   cartes projet, panneau plein écran, pellicule de motion, plaques de
   livraison, tuiles média, lecteur vidéo.

   Bootstrap fournit la grille, l'offcanvas et quelques utilitaires. Tout ce
   qui se voit est repris ici : aucune classe .btn-primary, aucune .card
   laissée à son style d'origine.

   Une règle de mouvement, tenue partout : la **révélation au défilement**
   occupe la propriété `translate`, le **survol** occupe `transform`. Ce sont
   deux propriétés distinctes, elles se composent, et c'est ce qui permet à une
   carte de se soulever sans annuler son apparition. Voir animations.css.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ──────────────────────────────── NAVIGATION ─────────────────────────────── */

.nav {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: 100;
  background: color-mix(in srgb, var(--bg) 78%, transparent);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border-block-end: 1px solid transparent;
  transition: border-color var(--dur) var(--ease);
  /* Repli pour les navigateurs sans `scrollbar-gutter` : nav.js mesure la
     barre de défilement avant de verrouiller le corps. */
  padding-inline-end: var(--sbw);
}
.nav.is-scrolled { border-block-end-color: var(--line); }

.nav__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-5);
  block-size: var(--nav-h);
}

.nav__brand {
  display: inline-flex;
  align-items: center;
  gap: var(--s-3);
  font-family: var(--font-display);
  font-size: var(--step-0);
  font-weight: 500;
  white-space: nowrap;
}
.nav__mark {
  inline-size: 10px; block-size: 10px;
  background: var(--accent);
  flex: none;
  transition: transform var(--dur) var(--ease);
}
.nav__brand:hover .nav__mark { transform: rotate(45deg); }

/* Huit entrées, trois langues, un sélecteur, une bascule : mesuré, cela fait
   1 026 px de contenu incompressible. Sous 1 160 px la barre déborde — elle
   débordait déjà à 1 440 px avant qu'on raccourcisse les libellés de langue.
   C'est le seul point de rupture ajouté par la refonte. */
.nav__links { display: none; gap: var(--s-4); }
@media (min-width: 1160px) { .nav__links { display: flex; } }

/* Les numéros de chapitre coûtent 28 px par entrée, soit 224 px : ils sont un
   agrément, pas une information. Ils ne s'affichent qu'une fois la place
   acquise, et restent toujours visibles dans le menu. */
@media (max-width: 1279px) { .nav__links .nav__n { display: none; } }

.nav__links a {
  position: relative;
  display: inline-flex;
  align-items: baseline;
  gap: 0.45em;
  font-size: var(--step--1);
  color: var(--ink-2);
  padding-block: var(--s-2);
  /* « Sites livrés » et « Savoir-faire » se coupaient en deux lignes et
     déformaient la barre. Huit entrées tiennent sur une ligne au-dessus de
     1080 px ; en dessous, c'est le menu qui prend le relais. */
  white-space: nowrap;
  transition: color var(--dur-fast) var(--ease);
}
.nav__links a:hover { color: var(--ink); }
.nav__n { color: var(--ink-3); font-size: var(--step--2); }

/* La section active se signale par un filet qui se dessine sous le lien —
   en scaleX, comme tout le reste du site. `transform-origin` bascule en RTL
   (voir rtl.css). */
.nav__links a::after {
  content: "";
  position: absolute;
  inset-block-end: 0;
  inset-inline: 0;
  block-size: 1px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur-fast) var(--ease);
}
.nav__links a.is-active { color: var(--ink); }
.nav__links a.is-active::after { transform: scaleX(1); }
.nav__links a.is-active .nav__n { color: var(--accent); }

.nav__end { display: flex; align-items: center; gap: var(--s-4); }

/* Barre de progression de lecture — scaleX, jamais width. */
.nav__progress {
  block-size: 2px;
  background: transparent;
  overflow: hidden;
}
.nav__progress i {
  display: block;
  block-size: 100%;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
}

/* ── Sélecteur de langue ────────────────────────────────────────────────────
   Choix exclusif : aria-current sur l'actif, jamais aria-pressed. */
.langbar {
  display: flex;
  align-items: center;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  overflow: hidden;
  background: var(--surface);
}
.langbar__btn {
  padding: var(--s-1) var(--s-3);
  font-size: var(--step--1);
  color: var(--ink-3);
  white-space: nowrap;
  transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.langbar__btn + .langbar__btn { border-inline-start: 1px solid var(--line); }
.langbar__btn:hover { color: var(--ink); background: var(--surface-alt); }
.langbar__btn[aria-current="true"] { color: var(--accent-ink); background: var(--accent-soft); }

/* Le nom entier de chaque langue — « Français », « العربية » — coûte 230 px à
   la barre, qui n'en a pas. Le code à deux lettres en coûte 140, et se lit
   aussi bien. **Le nom accessible ne change pas** : il vit dans `aria-label`,
   et c'est lui que la synthèse vocale annonce. */
.langbar__long { display: none; }
.langbar__short { display: inline; font-family: var(--font-mono); }

/* ── Bascule clair / sombre ────────────────────────────────────────────────
   Une seule icône visible à la fois. Les deux sont dans le document : les
   permuter par `display` évite de recharger un SVG à chaque bascule. */
.theme {
  display: grid;
  place-items: center;
  inline-size: 32px;
  block-size: 32px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--ink-2);
  transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.theme:hover { color: var(--ink); border-color: var(--ink-3); }
.theme svg { inline-size: 17px; block-size: 17px; }

.theme__moon { display: none; }
[data-theme="dark"] .theme__sun { display: none; }
[data-theme="dark"] .theme__moon { display: block; }

/* ── Bouton hamburger ──────────────────────────────────────────────────────── */
.nav__burger {
  display: grid;
  gap: 5px;
  padding: var(--s-2);
}
@media (min-width: 1160px) { .nav__burger { display: none; } }
.nav__burger span {
  display: block;
  inline-size: 20px; block-size: 1.5px;
  background: var(--ink);
}

/* ── Menu mobile (offcanvas Bootstrap, entièrement resurfacé) ──────────────── */
.menu { background: var(--bg); inline-size: min(340px, 86vw); border-inline-start: 1px solid var(--line); }
.menu .offcanvas-header { padding: var(--s-5); border-block-end: 1px solid var(--line); }
.menu__title { font-family: var(--font-display); font-size: var(--step-1); }
.menu__close { font-size: 28px; line-height: 1; color: var(--ink-2); }
.menu .offcanvas-body { padding: var(--s-5); display: grid; gap: var(--s-1); align-content: start; }
.menu .offcanvas-body a {
  display: flex;
  align-items: baseline;
  gap: var(--s-3);
  font-family: var(--font-display);
  font-size: var(--step-1);
  padding-block: var(--s-3);
  border-block-end: 1px solid var(--line);
  transition: color var(--dur-fast) var(--ease);
}
.menu .offcanvas-body a:hover { color: var(--accent); }
.menu .nav__n { font-family: var(--font-mono); }

/* ────────────────────────────────── BOUTONS ──────────────────────────────── */

.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-3) var(--s-5);
  border-radius: var(--r-sm);
  border: 1px solid var(--ink);
  font-size: var(--step--1);
  font-weight: 500;
  transition: transform var(--dur-fast) var(--ease-out-ui),
              background var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease);
}
.btn:hover { transform: translateY(-2px); }

/* Le vermillon ne sert jamais de fond de bouton : il est réservé à l'aplat du
   hero et à la pastille de lecture. Un bouton plein est de l'encre. */
.btn--solid { background: var(--ink); color: var(--bg); }
.btn--solid:hover { background: color-mix(in srgb, var(--ink) 88%, var(--accent)); border-color: transparent; }

.btn--ghost { background: transparent; color: var(--ink); border-color: var(--line); }
.btn--ghost:hover { border-color: var(--ink); background: var(--surface); }

/* Le seul appel à l'action qui porte l'encre graphique : celui qui mène à la
   section Graphisme. */
.btn--design { border-color: var(--warm); color: var(--warm-ink); }
.btn--design:hover { border-color: var(--warm-ink); background: var(--warm-soft); }

/* ─────────────────────────────────── LIENS ───────────────────────────────── */
/* Le soulignement se dessine depuis l'origine du texte : background-size,
   jamais text-decoration. */
.link {
  color: var(--accent-ink);
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 0% 1px;
  transition: background-size var(--dur) var(--ease);
  padding-block-end: 1px;
}
.link:hover, .link:focus-visible { background-size: 100% 1px; }

/* ─────────────────────────────────── BADGES ──────────────────────────────── */
/* Un seul composant pour les états de projet, le format d'une vidéo et la
   mention d'une livraison. La couleur vient d'un modificateur, la forme non. */

.tag {
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  padding: 0.18em 0.6em;
  border-radius: var(--r-pill);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--ink-2);
  background: var(--surface-alt);
  border: 1px solid color-mix(in srgb, currentColor 30%, transparent);
}
.tag--live,
.tag--wip     { color: var(--accent-ink); background: var(--accent-soft); }
.tag--motion  { color: var(--warm-ink);   background: var(--warm-soft); }
.tag::before {
  content: "";
  inline-size: 0.45em;
  block-size: 0.45em;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}
.tag--wip::before { animation: pulse-soft 2400ms ease-in-out infinite; }

/* ──────────────────────── MARQUES DES TECHNOLOGIES ───────────────────────── */
/* Dimensionnées en `em` : une icône suit toujours la taille du texte qu'elle
   accompagne, dans une vignette de carte comme dans un panneau plein écran. */
.ico {
  inline-size: 1.15em;
  block-size: 1.15em;
  flex: none;
  display: block;
}

/* Les listes de badges deviennent des rangées icône + nom. */
.tags li,
.card__stack li {
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
}

/* En arabe, l'icône reste du côté du texte : `gap` et `inline-flex` suffisent,
   l'ordre suit le sens de lecture sans une règle de plus. */

/* ─────────────────────────────── CARTE PROJET ────────────────────────────── */

.card {
  position: relative;
  display: grid;
  gap: 0;
  align-content: start;
  text-align: start;
  inline-size: 100%;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  overflow: hidden;
  transition: border-color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease-out-ui),
              transform 260ms var(--ease-out-ui),
              opacity var(--dur) var(--ease);
}
.card:hover {
  border-color: var(--accent);
  box-shadow: var(--shadow-lift);
  transform: translateY(-6px);
}

/* Le filet d'accent en tête de carte : il se dessine au survol, depuis le
   sens de lecture. C'est le seul vermillon de la grille. */
.card__edge {
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  block-size: 3px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur) var(--ease);
  z-index: 2;
}
.card:hover .card__edge,
.card:focus-within .card__edge { transform: scaleX(1); }

/* La barre d'en-tête : numéro d'ordre, année, état. Elle donne à la grille
   l'allure d'un sommaire plutôt que d'une liste de vignettes. */
.card__bar {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  background: var(--surface-alt);
  border-block-end: 1px solid var(--line);
  color: var(--ink-3);
  font-size: var(--step--2);
}
.card__bar .tag { margin-inline-start: auto; }
.card__no { letter-spacing: 0.16em; }

.card__thumb {
  overflow: hidden;
  background: var(--surface-alt);
  border-block-end: 1px solid var(--line);
  filter: saturate(.62);
  transition: filter var(--dur) var(--ease), transform 520ms var(--ease-out-ui);
}
.card:hover .card__thumb { filter: saturate(1); transform: scale(1.03); }

.card__info { display: grid; align-content: start; padding: var(--s-5); }

@media (min-width: 768px) {
  .card--featured { grid-template-columns: 1.05fr 1fr; align-items: stretch; }
  .card--featured .card__bar { grid-column: 1 / -1; }
  .card--featured .card__thumb { order: 2; border-block-end: 0; border-inline-start: 1px solid var(--line); }
  .card--featured .card__info { padding: var(--s-7); align-content: center; }
  /* La maquette de Jahiz est un téléphone : à 54 % de large elle mesure deux
     fois sa largeur en hauteur, et creuse un vide de la même hauteur dans la
     colonne de texte d'à côté. */
  .card--featured .card__thumb .mk-phone { inline-size: 44%; }
}

.card__meta {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  color: var(--ink-3);
  margin-block-end: var(--s-3);
}
.card__meta span + span::before { content: "·"; margin-inline-end: var(--s-3); }

/* Le titre est un `<button>` dans un `<h3>`, et non une carte-bouton :
   un `<button>` n'accepte que du contenu de phrasé, il ne peut pas contenir le
   titre, le paragraphe et la liste de la carte. La zone cliquable est rendue à
   toute la carte par un `::after` étiré — le nom du projet reste le nom
   accessible du bouton, et la hiérarchie des titres reste valide. */
.card__h { margin: 0; font: inherit; }

.card__title {
  color: var(--ink-title);
  font-family: var(--font-display);
  font-size: var(--step-2);
  line-height: var(--lh-tight);
  letter-spacing: -0.022em;
  font-variation-settings: "opsz" 72, "SOFT" 20, "WONK" 0;
  display: inline;
  text-align: start;
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 0% 1px;
  transition: background-size var(--dur-slow) var(--ease);
}
.card__title::after { content: ""; position: absolute; inset: 0; }
.card:hover .card__title { background-size: 100% 1px; }
.card--featured .card__title { font-size: var(--step-3); }

/* L'anneau de focus se pose sur la carte entière, pas sur le mot. */
.card:has(.card__title:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
.card__title:focus-visible { outline: none; }

.card__tagline {
  color: var(--ink-2);
  margin-block: var(--s-4) var(--s-5);
  max-width: 46ch;
}

.card__stack { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.card__stack li {
  padding: var(--s-1) var(--s-3);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  color: var(--ink-3);
  font-family: var(--font-mono);
  font-size: var(--step--2);
}

/* La rangée de pastilles média, en pied de carte. Elle doit passer **devant**
   le `::after` étiré du titre, sans quoi elle serait recouverte par la zone
   cliquable et paraîtrait inerte. */
.card__media {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  margin-block-start: var(--s-4);
  position: relative;
  z-index: 1;
  pointer-events: none;
}

/* Une note remplace la pile technique quand il n'y en a pas encore. */
.card__note {
  margin-block-start: var(--s-4);
  color: var(--ink-3);
  font-size: var(--step--1);
  font-style: italic;
}

/* La grille recule quand un panneau s'ouvre. */
.grid.is-dimmed .card { opacity: 0; transform: scale(0.97); }
.card.is-source { visibility: hidden; }

/* ════════════════════════════ 03 · LA PELLICULE ═══════════════════════════ */

.reel { min-inline-size: 0; }

.reel__open {
  display: block;
  inline-size: 100%;
  text-align: start;
  transition: transform 280ms var(--ease-out-ui);
}
.reel:hover .reel__open { transform: translateY(-10px); }
.reel__open:focus-visible { outline: none; }
.reel:has(.reel__open:focus-visible) .reel__frame {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.reel__frame {
  position: relative;
  display: block;
  /* Posé avant le moindre octet : le cadre réserve sa place, et rien ne saute
     quand l'affiche arrive. */
  aspect-ratio: 9 / 16;
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--surface-alt);
  border: 1px solid var(--line);
  transition: border-color var(--dur) var(--ease), box-shadow 280ms var(--ease-out-ui);
}
.reel:hover .reel__frame { border-color: var(--warm); box-shadow: var(--shadow-lift); }

.reel__poster,
.reel__preview {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}
.reel__poster { transition: transform 640ms var(--ease-out-ui); }
.reel:hover .reel__poster { transform: scale(1.05); }

/* L'aperçu est injecté au survol, et détruit en sortant. Il arrive en fondu
   pour ne pas faire clignoter l'affiche. */
.reel__preview { opacity: 0; transition: opacity 260ms var(--ease); z-index: 1; }
.reel__preview.is-on { opacity: 1; }

/* Le voile : il n'est pas décoratif. Sans lui, un texte blanc posé sur une
   affiche claire tomberait sous le seuil de lisibilité. */
.reel__frame::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(transparent 46%, rgba(0, 0, 0, .78));
  z-index: 2;
  pointer-events: none;
}

.reel__badge {
  position: absolute;
  inset-block-start: var(--s-3);
  inset-inline-start: var(--s-3);
  z-index: 3;
}

.reel__no {
  position: absolute;
  inset-block-end: var(--s-3);
  inset-inline-start: var(--s-3);
  z-index: 3;
  font-family: var(--font-display);
  font-size: var(--step-2);
  line-height: 1;
  color: #FFFFFF;
  opacity: .92;
}

.reel__time {
  position: absolute;
  inset-block-end: var(--s-3);
  inset-inline-end: var(--s-3);
  z-index: 3;
  padding: 0.14em 0.55em;
  border-radius: var(--r-pill);
  background: rgba(0, 0, 0, .55);
  color: #FFFFFF;
  font-size: var(--step--2);
}

.reel__play {
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 50%;
  translate: -50% -50%;
  z-index: 3;
  inline-size: 46px;
  block-size: 46px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--accent);
  color: var(--bg);
  opacity: 0;
  transform: scale(.86);
  transition: opacity 200ms var(--ease), transform 200ms var(--ease-out-ui);
}
[dir="rtl"] .reel__play { translate: 50% -50%; }
.reel:hover .reel__play,
.reel:has(.reel__open:focus-visible) .reel__play { opacity: 1; transform: scale(1); }
.reel__play svg { inline-size: 20px; block-size: 20px; }

.reel__name { display: block; margin-block-start: var(--s-4); }
.reel__title {
  display: block;
  font-family: var(--font-display);
  font-size: var(--step-1);
  line-height: 1.2;
  color: var(--ink-title);
}
.reel__client {
  display: block;
  margin-block-start: var(--s-1);
  color: var(--warm-ink);
  font-size: var(--step--2);
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.reel__note {
  margin-block-start: var(--s-2);
  color: var(--ink-3);
  font-size: var(--step--1);
  max-width: none;
}

/* ═══════════════════════ 04 · LES PLAQUES DE LIVRAISON ════════════════════
   Ni capture, ni `<iframe>`. Une capture périme le jour où le client modifie
   sa page ; une `<iframe>` vers un domaine tiers est lente, bloquable, non
   stylable, et tombe en panne à l'intérieur de ce portfolio. Une plaque
   typographique ne périme jamais et pèse zéro octet. */

.deliv {
  display: grid;
  align-content: start;
  gap: var(--s-4);
  padding: var(--s-6);
  block-size: 100%;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  transition: border-color var(--dur) var(--ease),
              background var(--dur) var(--ease),
              transform 260ms var(--ease-out-ui);
}
.deliv:hover { border-color: var(--accent); background: var(--surface); transform: translateY(-6px); }
.deliv:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.deliv__top { display: flex; align-items: center; gap: var(--s-3); }
.deliv__arrow {
  margin-inline-start: auto;
  inline-size: 1.15rem;
  block-size: 1.15rem;
  color: var(--ink-3);
  transition: transform var(--dur) var(--ease-out-ui), color var(--dur) var(--ease);
}
.deliv:hover .deliv__arrow {
  color: var(--accent);
  transform: translate(calc(3px * var(--dir)), -3px);
}

/* La signature : cinq pastilles pour le nuancier d'Akrem, trois lignes de
   gouttière pour l'éditeur de Dhia. Dessinée en CSS, elle évoque le concept
   du site sans prétendre le montrer. */
.deliv__sign { display: flex; gap: 5px; align-items: center; }
.deliv__sign i {
  display: block;
  inline-size: 12px;
  block-size: 12px;
  border-radius: 2px;
  background: var(--line);
  transition: background var(--dur) var(--ease), transform var(--dur) var(--ease-out-ui);
}
.deliv:hover .deliv__sign i { transform: translateY(-2px); }
.deliv__sign i:nth-child(1) { transition-delay: 0ms; }
.deliv__sign i:nth-child(2) { transition-delay: 40ms; }
.deliv__sign i:nth-child(3) { transition-delay: 80ms; }
.deliv__sign i:nth-child(4) { transition-delay: 120ms; }
.deliv__sign i:nth-child(5) { transition-delay: 160ms; }
.deliv--swatch .deliv__sign i:nth-child(1) { background: #C0341B; }
.deliv--swatch .deliv__sign i:nth-child(2) { background: #C08A2E; }
.deliv--swatch .deliv__sign i:nth-child(3) { background: #2E4FA8; }
.deliv--swatch .deliv__sign i:nth-child(4) { background: #5E7F6E; }
.deliv--swatch .deliv__sign i:nth-child(5) { background: #A62A46; }
.deliv--code .deliv__sign i { inline-size: 26px; block-size: 3px; border-radius: 999px; }
.deliv--code .deliv__sign i:nth-child(2) { inline-size: 16px; }
.deliv--code .deliv__sign i:nth-child(3) { inline-size: 21px; }

.deliv__name {
  color: var(--ink-title);
  font-family: var(--font-display);
  font-size: var(--step-4);
  font-weight: 300;
  font-variation-settings: "opsz" 144, "SOFT" 30, "WONK" var(--wonk);
  line-height: 0.96;
  letter-spacing: -0.035em;
}
.deliv__kind {
  display: block;
  font-family: var(--font-display);
  font-size: var(--step-1);
  color: var(--ink-2);
  margin-block-start: var(--s-2);
}

/* L'URL est écrite en toutes lettres. Une adresse tronquée par des points de
   suspension, dans une section qui promet la précision, est un mensonge. */
.deliv__url {
  display: inline-block;
  color: var(--accent-ink);
  font-size: var(--step--1);
  overflow-wrap: anywhere;
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 0% 1px;
  transition: background-size 260ms var(--ease);
}
.deliv:hover .deliv__url { background-size: 100% 1px; }

.deliv__facts {
  display: grid;
  gap: var(--s-3);
  margin: 0;
  padding-block-start: var(--s-4);
  border-block-start: 1px solid var(--line);
}
.deliv__facts > div { display: grid; grid-template-columns: 6.2rem 1fr; gap: var(--s-3); }
.deliv__facts dt {
  color: var(--ink-3);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.deliv__facts dd { color: var(--ink-2); font-size: var(--step--1); min-inline-size: 0; }

@media (max-width: 479px) {
  .deliv__facts > div { grid-template-columns: 1fr; gap: var(--s-1); }
}

/* ═══════════════════════════════ TUILES MÉDIA ═════════════════════════════
   Le bouton « Voir » d'une fiche projet, et le lien « Lire le rapport ».
   Les deux portent le poids du fichier en clair : le taire sur un forfait
   mobile est une faute, pas une simplification. */

.media {
  display: grid;
  gap: var(--s-4);
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  max-width: var(--measure);
}

.media-tile {
  position: relative;
  display: grid;
  gap: var(--s-2);
  align-content: start;
  text-align: start;
  padding: var(--s-4);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  transition: border-color var(--dur) var(--ease),
              transform 260ms var(--ease-out-ui),
              box-shadow var(--dur) var(--ease);
}
.media-tile:hover { border-color: var(--accent); transform: translateY(-3px); }
.media-tile:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.media-tile__poster {
  inline-size: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: var(--r-sm);
  background: var(--surface-alt);
  margin-block-end: var(--s-2);
}

.media-tile__play {
  position: absolute;
  inset-block-start: calc(var(--s-4) + 12%);
  inset-inline-start: 50%;
  translate: -50% 0;
  inline-size: 44px;
  block-size: 44px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--accent);
  color: var(--bg);
  transition: transform 220ms var(--ease-out-ui);
}
[dir="rtl"] .media-tile__play { translate: 50% 0; }
.media-tile:hover .media-tile__play { transform: scale(1.12); }
.media-tile__play svg { inline-size: 18px; block-size: 18px; }

.media-tile__ico {
  inline-size: 44px;
  block-size: 44px;
  display: grid;
  place-items: center;
  border-radius: var(--r-sm);
  background: var(--accent-soft);
  color: var(--accent-ink);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  letter-spacing: 0.06em;
  margin-block-end: var(--s-2);
}

.media-tile__label { font-weight: 500; color: var(--ink); }
.media-tile__meta { color: var(--ink-3); font-size: var(--step--2); opacity: .8; }

/* ────────────────────────────── PANNEAU PROJET ───────────────────────────── */

.panel { position: fixed; inset: 0; z-index: 150; }
.panel[hidden] { display: none; }

.panel__scrim {
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--ink) 40%, transparent);
  opacity: 0;
  transition: opacity var(--dur) var(--ease);
}
.panel.is-open .panel__scrim { opacity: 1; }

.panel__card {
  position: absolute;
  inset: 0;
  margin: auto;
  inline-size: min(1120px, 100%);
  block-size: min(100%, 100svh);
  background: var(--bg);
  border: 1px solid var(--line);
  overflow-y: auto;
  overscroll-behavior: contain;
  transform-origin: top left;
}
@media (min-width: 992px) {
  .panel__card { block-size: min(92svh, 100%); border-radius: var(--r-lg); }
}

/* La bande d'accent en tête du panneau : elle se dessine une fois le
   mouvement terminé, comme une signature posée en dernier. */
.panel__edge {
  position: sticky;
  inset-block-start: 0;
  z-index: 3;
  display: block;
  block-size: 6px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 640ms var(--ease);
}
.panel.is-ready .panel__edge { transform: scaleX(1); }

.panel__close {
  position: sticky;
  inset-block-start: var(--s-4);
  inset-inline-start: 100%;
  z-index: 4;
  margin-inline-end: var(--s-4);
  inline-size: 40px; block-size: 40px;
  display: grid; place-items: center;
  font-size: 26px; line-height: 1;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 50%;
  color: var(--ink-2);
  transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.panel__close:hover { color: var(--ink); border-color: var(--ink); }

.panel__body { padding: 0 var(--s-5) var(--s-9); }
@media (min-width: 768px) { .panel__body { padding-inline: var(--s-8); } }

/* Le contenu n'apparaît qu'une fois la taille finale atteinte : c'est la
   classe .is-ready, posée par projects.js à la fin du mouvement. La cascade
   est **plafonnée** — avec neuf blocs, un décalage non borné ferait attendre
   le dernier paragraphe près d'une seconde. */
.panel__block {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.panel.is-ready .panel__block {
  opacity: 1;
  transform: none;
  transition-delay: calc(min(var(--i, 0), 7) * 70ms);
}

.panel__head { padding-block-start: var(--s-6); }
.panel__title { color: var(--ink-title); font-size: var(--step-3); }
.panel__meta { display: flex; align-items: center; gap: var(--s-3); color: var(--ink-3); margin-block-end: var(--s-4); }
.panel__meta span + span::before { content: "·"; margin-inline-end: var(--s-3); }

.panel__label {
  display: flex;
  align-items: baseline;
  gap: var(--s-3);
  color: var(--ink-3);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-family: var(--font-mono);
  font-size: var(--step--2);
  margin-block: var(--s-8) var(--s-4);
}
.panel__label i { font-style: normal; opacity: .6; }

.panel__problem {
  color: var(--ink-title);
  font-family: var(--font-display);
  font-size: var(--step-2);
  line-height: 1.3;
  /* 24 caractères par ligne conviennent à un slogan de six mots. Deux phrases
     y deviennent une colonne étroite de vingt lignes — un mur, pas une
     accroche. */
  max-width: 34ch;
  font-variation-settings: "opsz" 24, "SOFT" 0, "WONK" 0;
}
.panel__solution { color: var(--ink-2); max-width: var(--measure); }
.panel__role { color: var(--ink-2); }

/* La note qui remplace la pile technique quand il n'y en a pas encore : un
   encadré discret, et non un vide. */
.panel__note {
  padding: var(--s-4) var(--s-5);
  border-inline-start: 2px solid var(--accent);
  background: var(--surface);
  color: var(--ink-2);
  max-width: var(--measure);
}

.panel__stack { display: grid; gap: var(--s-5); grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
.panel__stack h4 {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  font-weight: 400;
  color: var(--ink-3);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  margin-block-end: var(--s-3);
  padding-block-end: var(--s-3);
  border-block-end: 1px solid var(--line);
}

.panel__features { display: grid; gap: var(--s-4); max-width: var(--measure); counter-reset: feat; }
.panel__features li {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--s-4);
  padding-block-end: var(--s-4);
  border-block-end: 1px solid var(--line);
  color: var(--ink-2);
}
.panel__features li::before {
  content: counter(feat, decimal-leading-zero);
  counter-increment: feat;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--ink-3);
}

.panel__demo { margin-block-start: var(--s-6); }

/* ═════════════════════════════ ASSISTANT ═════════════════════════════════
   Un lanceur posé en bas du côté de lecture, et un panneau qui monte depuis
   lui. Il ne couvre jamais la page entière : on doit pouvoir lire une fiche
   et l'interroger en même temps. */

.bot__open {
  position: fixed;
  inset-block-end: var(--s-5);
  inset-inline-end: var(--s-5);
  z-index: 120;
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-3) var(--s-5);
  border-radius: var(--r-pill);
  background: var(--ink);
  color: var(--bg);
  font-size: var(--step--1);
  font-weight: 500;
  box-shadow: var(--shadow-lift);
  transition: transform var(--dur-fast) var(--ease-out-ui),
              background var(--dur-fast) var(--ease);
}
.bot__open:hover { transform: translateY(-2px); background: color-mix(in srgb, var(--ink) 88%, var(--accent)); }
.bot__open svg { inline-size: 18px; block-size: 18px; flex: none; }

/* Sous 480 px, le libellé pousserait le bouton hors du pouce : il ne reste
   que la marque, et `aria-label` porte le nom accessible. */
@media (max-width: 479px) {
  .bot__open { padding: var(--s-3); }
  .bot__open span { display: none; }
}

.bot {
  position: fixed;
  inset-block-end: calc(var(--s-5) + 56px);
  inset-inline-end: var(--s-5);
  z-index: 121;
  inline-size: min(380px, calc(100vw - var(--s-5) * 2));
  max-block-size: min(72svh, 640px);
  display: grid;
  grid-template-rows: auto 1fr auto auto;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lift);
  overflow: hidden;
  opacity: 0;
  transform: translateY(12px) scale(.98);
  transform-origin: bottom right;
  transition: opacity 200ms var(--ease-in), transform 200ms var(--ease-in);
}
[dir="rtl"] .bot { transform-origin: bottom left; }
.bot[hidden] { display: none; }
.bot.is-open {
  opacity: 1;
  transform: none;
  transition: opacity 280ms var(--ease), transform 280ms var(--ease);
}

.bot__head {
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  padding: var(--s-5);
  border-block-end: 1px solid var(--line);
  background: var(--surface-alt);
}
.bot__title {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--step-1);
  color: var(--ink-title);
}
/* La ligne qui dit ce qu'il est. Elle n'est pas décorative : sans elle, on
   croit parler à un modèle de langue. */
.bot__sub { margin: var(--s-1) 0 0; font-size: var(--step--2); color: var(--ink-3); max-width: none; }
.bot__close {
  margin-inline-start: auto;
  inline-size: 28px; block-size: 28px;
  display: grid; place-items: center;
  font-size: 22px; line-height: 1;
  border-radius: 50%;
  color: var(--ink-3);
  flex: none;
}
.bot__close:hover { color: var(--ink); background: var(--surface); }

.bot__log {
  display: grid;
  gap: var(--s-3);
  padding: var(--s-5);
  overflow-y: auto;
  overscroll-behavior: contain;
  align-content: start;
}
.bot__msg {
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-md);
  font-size: var(--step--1);
  line-height: 1.5;
  max-inline-size: 88%;
}
.bot__msg--me {
  justify-self: end;
  background: var(--ink);
  color: var(--bg);
  border-end-end-radius: 2px;
}
.bot__msg--bot {
  justify-self: start;
  background: var(--surface);
  border: 1px solid var(--line);
  color: var(--ink-2);
  border-end-start-radius: 2px;
}

.bot__acts { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.bot__act {
  display: inline-flex;
  align-items: center;
  padding: var(--s-1) var(--s-3);
  border-radius: var(--r-pill);
  border: 1px solid var(--accent);
  color: var(--accent-ink);
  font-size: var(--step--2);
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.bot__act:hover { background: var(--accent); color: var(--bg); }

.bot__hints { display: flex; flex-wrap: wrap; gap: var(--s-2); padding: 0 var(--s-5) var(--s-4); }
.bot__hint {
  padding: var(--s-1) var(--s-3);
  border-radius: var(--r-pill);
  border: 1px dashed var(--line);
  color: var(--ink-3);
  font-size: var(--step--2);
  text-align: start;
  transition: border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.bot__hint:hover { border-color: var(--ink-3); color: var(--ink); border-style: solid; }

.bot__form {
  display: flex;
  gap: var(--s-2);
  padding: var(--s-4) var(--s-5) var(--s-5);
  border-block-start: 1px solid var(--line);
}
.bot__input {
  flex: 1;
  min-inline-size: 0;
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: var(--step--1);
}
.bot__input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.bot__send {
  inline-size: 40px; block-size: 40px;
  flex: none;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--ink);
  color: var(--bg);
  transition: background var(--dur-fast) var(--ease);
}
.bot__send:hover { background: var(--accent); }
.bot__send svg { inline-size: 18px; block-size: 18px; }
/* La flèche d'envoi suit le sens de lecture. */
[dir="rtl"] .bot__send svg { transform: scaleX(-1); }

/* ══════════════════════════════ LECTEUR VIDÉO ═════════════════════════════ */

.lightbox { position: fixed; inset: 0; z-index: 200; display: grid; place-items: center; }
.lightbox[hidden] { display: none; }

.lightbox__scrim {
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--ink) 80%, transparent);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  opacity: 0;
  transition: opacity 220ms var(--ease);
}
.lightbox.is-open .lightbox__scrim { opacity: 1; }

.lightbox__frame {
  position: relative;
  display: grid;
  justify-items: center;
  gap: var(--s-3);
  padding: var(--s-5);
  opacity: 0;
  transform: scale(.94) translateY(12px);
  /* Sortie plus rapide et accélérée : un objet qui part accélère, un objet
     qui arrive décélère. C'est la seule place où --ease-in est juste. */
  transition: opacity 200ms var(--ease-in), transform 200ms var(--ease-in);
}
.lightbox.is-open .lightbox__frame {
  opacity: 1;
  transform: none;
  transition: opacity 380ms var(--ease), transform 380ms var(--ease);
}

.lightbox__title {
  margin: 0;
  color: var(--bg);
  font-size: var(--step--1);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-align: center;
  max-width: 60ch;
}
[data-theme="dark"] .lightbox__title { color: var(--ink); }

.lightbox__stage {
  aspect-ratio: var(--ratio, 16 / 9);
  block-size: min(78svh, 1040px);
  inline-size: auto;
  max-inline-size: 92vw;
  border-radius: var(--r-md);
  overflow: hidden;
  box-shadow: var(--shadow-lift);
  background: #000;
}
.lightbox__video { inline-size: 100%; block-size: 100%; display: block; }

.lightbox__close {
  position: absolute;
  inset-block-start: 0;
  inset-inline-end: 0;
  inline-size: 44px; block-size: 44px;
  display: grid; place-items: center;
  font-size: 28px; line-height: 1;
  border-radius: 50%;
  background: var(--surface);
  border: 1px solid var(--line);
  color: var(--ink-2);
  transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.lightbox__close:hover { color: var(--ink); border-color: var(--ink); }
