/* ==========================================================================
   Components — Navbar, Cards, Buttons, Form, Timeline, Tags
   ==========================================================================

   CONVENTION BEM (Block__Element--Modifier)
   ------------------------------------------
   Ce fichier utilise la convention de nommage BEM, tres repandue en CSS.
   BEM decoupe chaque composant en trois niveaux :

   1. BLOCK (Bloc) : le composant autonome.
      Exemple : .navbar, .btn, .project-card, .contact

   2. ELEMENT (Element) : une partie interne du bloc, notee avec "__".
      Exemple : .navbar__logo   → le logo A L'INTERIEUR de la navbar
                .navbar__menu   → le menu A L'INTERIEUR de la navbar
                .project-card__image → l'image A L'INTERIEUR de la carte

   3. MODIFIER (Modificateur) : une variante du bloc ou de l'element, notee avec "--".
      Exemple : .btn--primary   → variante "primary" du bouton
                .btn--secondary → variante "secondary" du bouton
                .navbar__icon--sun  → variante "sun" de l'icone navbar
                .contact__field--full → variante "full" (pleine largeur) du champ

   Pourquoi BEM ?
   - On sait immediatement a quel composant appartient chaque classe.
   - On evite les conflits de noms entre composants.
   - Le CSS reste plat (pas de selecteurs imbriques profonds).
   ========================================================================== */


/* ---------- Navbar ---------- */

/*
 * POSITION: FIXED
 * ----------------
 * "position: fixed" retire l'element du flux normal de la page
 * et le fixe par rapport a la FENETRE du navigateur (viewport).
 * Meme quand l'utilisateur scrolle, la navbar reste visible en haut.
 *
 * On combine avec top: 0, left: 0, width: 100% pour qu'elle couvre
 * toute la largeur et colle au sommet de l'ecran.
 *
 * Z-INDEX ET CONTEXTE D'EMPILEMENT (STACKING CONTEXT)
 * -----------------------------------------------------
 * z-index controle l'ordre d'empilement des elements positionnes.
 * Plus le z-index est eleve, plus l'element est "au-dessus".
 *   - La navbar a z-index: 1000 → elle passe au-dessus du contenu.
 *   - Le modal (plus bas) a z-index: 2000 → il passe au-dessus de la navbar.
 *   - Le preloader a z-index: 9999 → il passe au-dessus de tout.
 * Attention : z-index ne fonctionne QUE sur les elements positionnes
 * (position: fixed, absolute, relative ou sticky).
 *
 * TRANSITION (RACCOURCI)
 * -----------------------
 * "transition" est un raccourci pour animer le passage d'une valeur CSS
 * a une autre. La syntaxe est :
 *   transition: [propriete] [duree] [fonction-timing] [delai];
 * Ici on anime "background-color" et "box-shadow" avec la duree
 * definie dans la variable --transition-base (par ex. 300ms ease).
 * On peut enchainer plusieurs transitions separees par des virgules.
 *
 * padding-block est un raccourci logique pour padding-top + padding-bottom.
 */
.navbar {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  z-index: 1000;
  padding-block: 1rem;
  background-color: transparent;
  transition: background-color var(--transition-base), box-shadow var(--transition-base);
}

/*
 * CLASSE D'ETAT ".is-scrolled" (State Class Pattern)
 * ----------------------------------------------------
 * Les classes prefixees par "is-" ou "has-" representent un ETAT temporaire
 * de l'element, generalement ajoute/retire par JavaScript.
 *
 * Ici, quand l'utilisateur scrolle, le JS ajoute ".is-scrolled" a la navbar.
 * Cela declenche :
 *   - un fond opaque (--bg-elevated) au lieu de transparent
 *   - une ombre portee (box-shadow) pour creer une separation visuelle
 *
 * Ce pattern est prefere a la manipulation directe du style en JS car :
 *   - Le CSS reste maintenable et centralisé dans les feuilles de style.
 *   - Le JS ne fait qu'ajouter/retirer une classe, ce qui est plus propre.
 */
.navbar.is-scrolled {
  background-color: var(--bg-elevated);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
}

/*
 * FLEXBOX POUR LA MISE EN PAGE DE LA NAVBAR
 * -------------------------------------------
 * "display: flex" active le modele Flexbox sur le conteneur.
 * Les enfants directs deviennent des "flex items" alignes sur un axe.
 *
 * justify-content: space-between → repartit l'espace ENTRE les enfants
 *   (le logo a gauche, le menu au centre, les actions a droite).
 *
 * align-items: center → centre les enfants VERTICALEMENT sur l'axe
 *   secondaire (cross axis), utile quand les elements ont des hauteurs
 *   differentes (logo, liens, icones).
 */
.navbar__container {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.navbar__logo {
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--accent);
}

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

/*
 * Le menu utilise aussi Flexbox.
 * "gap: 2rem" ajoute un espace de 2rem ENTRE chaque lien (pas autour).
 * C'est plus propre que d'utiliser margin-right sur chaque enfant.
 */
.navbar__menu {
  display: flex;
  gap: 2rem;
}

/*
 * "position: relative" sur le lien permet de positionner le pseudo-element
 * ::after en absolute PAR RAPPORT a ce lien (voir .navbar__link.is-active::after).
 */
.navbar__link {
  font-size: var(--font-size-sm);
  font-weight: 500;
  color: var(--text-secondary);
  transition: color var(--transition-fast);
  position: relative;
}

/*
 * Deux selecteurs separes par une virgule = meme style pour les deux.
 * :hover → au survol de la souris
 * .is-active → classe d'etat ajoutee par le JS (scroll spy) pour indiquer
 *   la section actuellement visible.
 */
.navbar__link:hover,
.navbar__link.is-active {
  color: var(--accent);
}

/*
 * PSEUDO-ELEMENT ::after
 * -----------------------
 * ::after cree un element virtuel APRES le contenu du lien.
 * On l'utilise ici pour dessiner une petite barre coloree sous le lien actif.
 *
 * "content: ''" est OBLIGATOIRE pour que le pseudo-element apparaisse
 * (meme s'il est vide).
 *
 * "position: absolute" le positionne par rapport au parent le plus proche
 * qui a "position: relative" (ici .navbar__link).
 *
 * bottom: -4px → 4px sous le texte du lien.
 * width: 100%  → toute la largeur du lien.
 * height: 2px  → epaisseur de la barre.
 */
.navbar__link.is-active::after {
  content: '';
  position: absolute;
  bottom: -4px;
  left: 0;
  width: 100%;
  height: 2px;
  background-color: var(--accent);
  border-radius: 1px;
}

/*
 * Lien "Espace Professeur" dans la navbar.
 * Style distinct pour le différencier des liens d'ancre internes.
 * Bordure + padding + border-radius en font un bouton pill.
 */
.navbar__link--prof {
  border: 1px solid var(--accent);
  padding: 0.35rem 0.85rem;
  border-radius: 9999px;
  font-size: var(--font-size-xs);
  color: var(--accent);
  transition: background var(--transition-fast), color var(--transition-fast);
}

.navbar__link--prof:hover {
  background: var(--accent);
  color: var(--text-on-accent);
}

.navbar__actions {
  display: flex;
  align-items: center;
  gap: 1rem;
}

.navbar__social {
  color: var(--text-secondary);
  transition: color var(--transition-fast);
  display: flex;
  align-items: center;
}

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

.navbar__icon {
  display: block;
}

.navbar__theme-toggle {
  color: var(--text-secondary);
  transition: color var(--transition-fast);
  display: flex;
  align-items: center;
  padding: 0.25rem;
}

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

/*
 * BASCULEMENT ICONE SOLEIL / LUNE AVEC data-theme
 * --------------------------------------------------
 * Le selecteur [data-theme="dark"] cible un element HTML qui possede
 * l'attribut data-theme avec la valeur "dark" (generalement <html> ou <body>).
 *
 * Le JS change cet attribut quand l'utilisateur clique sur le bouton theme.
 * Selon la valeur, on affiche (display: block) ou cache (display: none)
 * l'icone correspondante :
 *
 *   - Theme sombre → on affiche le soleil (pour passer au clair)
 *   - Theme clair  → on affiche la lune  (pour passer au sombre)
 *
 * C'est un pattern tres courant pour le "dark mode toggle".
 * L'espace entre [data-theme="dark"] et .navbar__icon--sun signifie
 * "un .navbar__icon--sun qui est DESCENDANT d'un element [data-theme='dark']".
 *
 * Ici, "--sun" et "--moon" sont des MODIFICATEURS BEM de l'element
 * .navbar__icon (Block: navbar, Element: icon, Modifier: sun / moon).
 */
[data-theme="dark"] .navbar__icon--sun {
  display: block;
}

[data-theme="dark"] .navbar__icon--moon {
  display: none;
}

[data-theme="light"] .navbar__icon--sun {
  display: none;
}

[data-theme="light"] .navbar__icon--moon {
  display: block;
}

/*
 * MENU HAMBURGER (ICONE A 3 BARRES)
 * ------------------------------------
 * Sur desktop, le hamburger est cache avec "display: none".
 * En responsive (dans le fichier responsive.css), on le passe en
 * "display: flex" pour les petits ecrans, et on cache le menu classique.
 *
 * "flex-direction: column" empile les 3 barres verticalement.
 * "gap: 5px" espace les barres de 5px.
 *
 * Les barres (.navbar__hamburger-line) sont de simples <span> stylises
 * en rectangles de 24x2px. La transition sur transform et opacity
 * permet d'animer la transformation en "X" quand le menu est ouvert.
 */
.navbar__hamburger {
  display: none;
  flex-direction: column;
  gap: 5px;
  padding: 0.25rem;
}

.navbar__hamburger-line {
  display: block;
  width: 24px;
  height: 2px;
  background-color: var(--text-primary);
  transition: transform var(--transition-base), opacity var(--transition-base);
}

/* ---------- Buttons ---------- */

/*
 * COMPOSANT BOUTON AVEC BEM : .btn, .btn--primary, .btn--secondary
 * -------------------------------------------------------------------
 * .btn est le BLOC de base : il contient les styles communs a TOUS les
 * boutons (padding, taille de police, border-radius, transitions...).
 *
 * .btn--primary et .btn--secondary sont des MODIFICATEURS BEM qui
 * ajoutent les couleurs specifiques a chaque variante.
 *
 * En HTML, on combine les deux classes :
 *   <a class="btn btn--primary">Contactez-moi</a>
 *   <a class="btn btn--secondary">Voir le projet</a>
 *
 * "display: inline-flex" est comme inline-block + flexbox : l'element
 * reste en ligne (a cote du texte) mais son contenu interne est un
 * conteneur flex (utile pour centrer une icone a cote du texte).
 *
 * La transition sur "transform" permet l'effet de survol ci-dessous
 * (le bouton "monte" legerement).
 */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.75rem 1.5rem;
  font-size: var(--font-size-sm);
  font-weight: 600;
  border-radius: var(--radius-sm);
  transition: background-color var(--transition-fast), color var(--transition-fast), transform var(--transition-fast);
}

/*
 * translateY(-1px) deplace le bouton de 1px vers le haut au survol,
 * ce qui cree un subtil effet de "soulèvement" (micro-interaction).
 */
.btn:hover {
  transform: translateY(-1px);
}

/*
 * MODIFICATEUR BEM --primary : bouton avec fond de couleur accent.
 * --text-on-accent est une couleur garantie lisible sur le fond accent
 * (souvent blanc ou noir, selon le theme).
 */
.btn--primary {
  background-color: var(--accent);
  color: var(--text-on-accent);
}

.btn--primary:hover {
  background-color: var(--accent-hover);
  color: var(--text-on-accent);
}

/*
 * MODIFICATEUR BEM --secondary : bouton "outline" (bordure visible, fond transparent).
 * Au survol, on ajoute un leger fond avec --accent-glow (souvent une
 * version tres transparente de la couleur accent).
 */
.btn--secondary {
  background-color: transparent;
  color: var(--accent);
  border: 1px solid var(--accent);
}

.btn--secondary:hover {
  background-color: var(--accent-glow);
  color: var(--accent-hover);
}

/* ---------- Hero ---------- */

/*
 * "flex: 1" est un raccourci pour "flex-grow: 1; flex-shrink: 1; flex-basis: 0%".
 * Cela signifie que .hero__content prendra tout l'espace disponible restant
 * dans le conteneur flex parent (a cote de l'image hero).
 */
.hero__content {
  display: flex;
  flex-direction: column;
  flex: 1;
}

.hero__title {
  margin-bottom: 0.5rem;
}

/* Modificateur BEM --accent pour colorer une partie du titre */
.hero__title--accent {
  color: var(--accent);
}

.hero__subtitle {
  font-size: var(--font-size-h3);
  font-weight: 500;
  color: var(--text-secondary);
  margin-bottom: 0.25rem;
}

.hero__tagline {
  font-size: var(--font-size-body);
  font-weight: 300;
  color: var(--text-muted);
  margin-bottom: 1.5rem;
}

.hero__about {
  font-size: var(--font-size-body);
  color: var(--text-secondary);
  line-height: 1.8;
}

/*
 * FLEX-SHRINK: 0
 * ---------------
 * Par defaut, les flex items peuvent retrecir (flex-shrink: 1) quand le
 * conteneur manque de place. Avec flex-shrink: 0, on INTERDIT a l'image
 * hero de retrecir : elle gardera toujours sa taille naturelle.
 *
 * C'est essentiel ici pour que la photo de profil ne soit pas ecrasee
 * quand le texte a cote est long. Le texte (.hero__content avec flex: 1)
 * s'adaptera a la place, pas l'image.
 */
.hero__image {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-shrink: 0;
}

/*
 * "overflow: hidden" coupe tout ce qui depasse du conteneur.
 * Combine avec border-radius, cela arrondit les coins de l'image
 * a l'interieur sans que les pixels de l'image ne depassent.
 */
.hero__photo-wrapper {
  width: 300px;
  height: 300px;
  overflow: hidden;
  border: 3px solid var(--accent);
  border-radius: var(--radius-md);
}

/*
 * OBJECT-FIT: COVER vs CONTAIN
 * ------------------------------
 * Quand une image a width: 100% et height: 100%, elle peut se deformer
 * si ses proportions ne correspondent pas au conteneur.
 *
 * object-fit controle comment l'image remplit son conteneur :
 *
 *   - "cover" : l'image couvre TOUT le conteneur, quitte a etre rognee.
 *     → Utilise ici pour la photo de profil : on veut remplir le cadre
 *       sans bandes vides, meme si les bords sont coupes.
 *
 *   - "contain" : l'image est entierement VISIBLE, quitte a laisser
 *     des bandes vides autour.
 *     → Utilise plus bas pour la galerie de projet, ou on veut voir
 *       l'image complete (captures d'ecran par ex.).
 *
 * C'est l'equivalent de background-size: cover / contain, mais pour
 * les balises <img>.
 */
.hero__photo {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ---------- Preloader ---------- */

/*
 * PRELOADER AVEC TRANSITION SUR .is-hidden
 * -------------------------------------------
 * Le preloader est un ecran de chargement affiche en plein ecran
 * pendant que la page se charge.
 *
 * "position: fixed; inset: 0" le fixe sur toute la fenetre.
 * (inset: 0 est un raccourci pour top: 0; right: 0; bottom: 0; left: 0)
 *
 * z-index: 9999 le place AU-DESSUS de tout le reste (navbar a 1000,
 * modal a 2000... le preloader doit etre au sommet).
 *
 * Quand le chargement est termine, le JS ajoute la classe ".is-hidden" :
 *   - opacity: 0 → le preloader devient transparent (avec une transition
 *     de 0.5s pour un fondu doux).
 *   - visibility: hidden → apres le fondu, l'element est completement
 *     retire de l'interaction (plus cliquable, plus accessible).
 *
 * POURQUOI visibility ET OPACITY ?
 * opacity: 0 rend l'element invisible mais il reste cliquable et present
 * dans le flux. visibility: hidden le retire de l'interaction.
 * On anime les deux pour avoir le fondu (opacity) ET la disparition
 * complete (visibility).
 */
.preloader {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: var(--bg-primary);
  transition: opacity 0.5s ease, visibility 0.5s ease;
}

.preloader.is-hidden {
  opacity: 0;
  visibility: hidden;
}

.preloader__logo {
  font-size: 3rem;
  font-weight: 700;
  color: var(--accent);
}

/* ---------- Profil ---------- */

/*
 * margin-inline: auto centre un element en bloc horizontalement.
 * C'est le raccourci logique pour margin-left: auto; margin-right: auto.
 */
.profil__content {
  max-width: 800px;
  margin-inline: auto;
}

.profil__text {
  font-size: var(--font-size-body);
  color: var(--text-secondary);
  line-height: 1.8;
  margin-bottom: 2.5rem;
  text-align: center;
}

/*
 * CSS GRID pour la mise en page des details du profil.
 * grid-template-columns: 1fr 1fr → deux colonnes de largeur egale.
 * "1fr" signifie "1 fraction de l'espace disponible".
 */
.profil__details {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 2rem;
}

.profil__subtitle {
  font-size: var(--font-size-h3);
  color: var(--text-primary);
  margin-bottom: 0.75rem;
}

.profil__section p {
  color: var(--text-secondary);
  line-height: 1.7;
}

/* ---------- Competences ---------- */

/*
 * CSS GRID avec repeat(3, 1fr) → 3 colonnes de largeur egale.
 * "repeat(n, taille)" evite d'ecrire "1fr 1fr 1fr".
 */
.competences__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.5rem;
}

/*
 * Transition sur plusieurs proprietes pour un effet de survol riche :
 * la bordure change de couleur, une ombre apparait, et la carte monte
 * legerement (translateY).
 */
.competences__card {
  background-color: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 2rem 1.5rem;
  text-align: center;
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast), transform var(--transition-fast);
}

.competences__card:hover {
  border-color: var(--accent);
  box-shadow: 0 4px 20px var(--accent-glow);
  transform: translateY(-3px);
}

/*
 * L'icone est un cercle : border-radius: 50% sur un element carre
 * (width = height = 56px) donne un cercle parfait.
 * margin: 0 auto centre l'element horizontalement dans un contexte block.
 */
.competences__icon {
  width: 56px;
  height: 56px;
  margin: 0 auto 1.25rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px solid var(--accent);
  border-radius: 50%;
  color: var(--accent);
  font-size: 1.5rem;
}

/*
 * text-transform: uppercase → met le texte en majuscules.
 * letter-spacing: 0.08em → ecarte les lettres pour un style "etiquette".
 * L'unite "em" est relative a la taille de police de l'element.
 */
.competences__cat-title {
  font-size: var(--font-size-sm);
  font-weight: 700;
  color: var(--text-primary);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin-bottom: 1rem;
}

.competences__text {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  line-height: 1.7;
}

.competences__empty {
  font-size: var(--font-size-sm);
  color: var(--text-muted);
  font-style: italic;
}

/* ---------- Project Cards ---------- */

/*
 * CSS GRID avec repeat(auto-fill, minmax(300px, 1fr)) :
 * C'est une technique tres puissante pour creer une grille responsive
 * SANS media queries.
 *
 *   - auto-fill : cree autant de colonnes que possible dans l'espace disponible.
 *   - minmax(300px, 1fr) : chaque colonne fait au minimum 300px et peut
 *     grandir jusqu'a 1fr (repartition egale de l'espace restant).
 *
 * Resultat : sur un ecran large → 3-4 cartes par ligne,
 *            sur tablette → 2 cartes, sur mobile → 1 carte.
 */
.projets__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 2rem;
}

.projets__empty {
  font-size: var(--font-size-sm);
  color: var(--text-muted);
  font-style: italic;
}

.projets__show-all {
  display: block;
  margin: 2rem auto 0;
}

/*
 * "position: relative" est necessaire pour positionner l'overlay (.project-card__overlay)
 * en absolute a l'interieur de la carte (voir plus bas).
 *
 * "overflow: hidden" empeche le contenu (comme l'image zoomee au survol)
 * de depasser les bords arrondis de la carte.
 */
.project-card {
  background-color: var(--bg-card);
  border-radius: var(--radius-md);
  overflow: hidden;
  position: relative;
  transition: transform 400ms ease, opacity 400ms ease;
}

.project-card:hover {
  transform: translateY(-4px);
}

/*
 * Classe d'etat pour les cartes filtrees (cachees).
 * scale(0.8) reduit la carte a 80% de sa taille avec un fondu (opacity: 0).
 */
.project-card.is-hidden {
  opacity: 0;
  transform: scale(0.8);
}

/*
 * ASPECT-RATIO
 * -------------
 * La propriete "aspect-ratio" force un ratio largeur/hauteur sur l'element.
 * Ici "16 / 9" signifie que pour 16 unites de largeur, il y aura 9 unites
 * de hauteur (format widescreen, comme un ecran de cinema).
 *
 * C'est tres utile pour les conteneurs d'images : on n'a pas besoin de
 * definir une hauteur fixe, le navigateur la calcule automatiquement
 * en fonction de la largeur.
 *
 * Avant aspect-ratio, on utilisait le "padding-top hack" (padding-top: 56.25%
 * pour du 16:9), ce qui etait moins lisible.
 */
.project-card__image {
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background-color: var(--bg-secondary);
}

/*
 * OBJECT-FIT: COVER sur les images de carte projet.
 * L'image remplit tout le conteneur 16:9 sans deformation, quitte a
 * rogner les bords. C'est le choix ideal pour des visuels de type
 * "vignette" ou "miniature".
 *
 * La transition sur transform permet le zoom leger au survol (scale 1.05
 * ci-dessous).
 */
.project-card__image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--transition-base);
}

/*
 * Selecteur contextuel : quand on survole la CARTE entiere, l'image
 * a l'interieur est zoomee a 105%. Le overflow: hidden sur le parent
 * empeche l'image de deborder.
 */
.project-card:hover .project-card__image img {
  transform: scale(1.05);
}

.project-card__content {
  padding: 1.25rem;
}

.project-card__title {
  font-size: var(--font-size-body);
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: 0.25rem;
}

.project-card__year {
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  margin-bottom: 0.75rem;
}

/*
 * flex-wrap: wrap permet aux tags de passer a la ligne suivante
 * quand il n'y a plus de place horizontalement.
 */
.project-card__tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem;
  margin-bottom: 0.75rem;
}

/*
 * -WEBKIT-LINE-CLAMP : TRONCATURE DE TEXTE MULTI-LIGNES
 * -------------------------------------------------------
 * Ce groupe de proprietes tronque le texte apres un nombre de lignes donne
 * et ajoute "..." a la fin.
 *
 *   display: -webkit-box       → active l'ancien modele de boite flexible WebKit
 *   -webkit-line-clamp: 2      → limite a 2 lignes
 *   -webkit-box-orient: vertical → orientation verticale (necessaire)
 *   overflow: hidden            → cache le texte qui depasse
 *
 * C'est une solution imparfaite (prefixee -webkit-) mais supportee par tous
 * les navigateurs modernes. Il n'existe pas encore d'alternative standard
 * aussi simple pour la troncature multi-lignes.
 *
 * Pour tronquer sur UNE seule ligne, on utiliserait plutot :
 *   white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
 */
.project-card__description {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  line-height: 1.5;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/*
 * PATTERN OVERLAY (CALQUE SEMI-TRANSPARENT AU SURVOL)
 * -----------------------------------------------------
 * L'overlay est un element qui recouvre ENTIEREMENT la carte au survol,
 * avec un fond sombre semi-transparent et des boutons d'action.
 *
 * "position: absolute" le positionne par rapport au parent le plus proche
 * qui a "position: relative" (ici .project-card).
 *
 * "inset: 0" est un raccourci pour top: 0; right: 0; bottom: 0; left: 0,
 * ce qui etire l'overlay sur toute la surface de la carte.
 *
 * "opacity: 0" le rend invisible par defaut. Au survol de la carte,
 * on passe a opacity: 1 (voir la regle suivante). La transition
 * rend le changement progressif (fondu).
 *
 * Flexbox est utilise pour centrer les boutons d'action dans l'overlay.
 */
.project-card__overlay {
  position: absolute;
  inset: 0;
  background-color: rgba(10, 14, 23, 0.85);
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 0.75rem;
  opacity: 0;
  transition: opacity var(--transition-base);
}

/*
 * Au survol de la carte, l'overlay apparait en fondu grace a opacity: 1.
 */
.project-card:hover .project-card__overlay {
  opacity: 1;
}

/* ---------- Project Detail Modal ---------- */

/*
 * PATTERN MODAL (FENETRE MODALE EN PLEIN ECRAN)
 * ------------------------------------------------
 * Un modal est une fenetre qui s'affiche par-dessus tout le contenu,
 * bloquant l'interaction avec le reste de la page.
 *
 * "position: fixed; inset: 0" le fixe sur toute la fenetre du navigateur.
 * Le fond semi-transparent (rgba noir a 85% d'opacite) assombrit la page.
 *
 * "z-index: 2000" le place au-dessus de la navbar (1000) mais en dessous
 * du preloader (9999). C'est la hierarchie d'empilement du projet :
 *   Contenu normal → Navbar (1000) → Modal (2000) → Preloader (9999)
 *
 * Flexbox centre le contenu du modal (la boite blanche) au milieu de l'ecran.
 * "overflow-y: auto" permet de scroller SI le contenu depasse la fenetre.
 */
.project-detail {
  position: fixed;
  inset: 0;
  background-color: rgba(0, 0, 0, 0.85);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2000;
  padding: 1rem;
  overflow-y: auto;
}

/*
 * Le contenu du modal : une boite avec un fond, une bordure, et des coins
 * arrondis. max-width: 95vw et max-height: 95vh garantissent qu'elle ne
 * depasse jamais 95% de la fenetre (vw = viewport width, vh = viewport height).
 *
 * "position: relative" permet de positionner le bouton de fermeture
 * (position: absolute) dans le coin superieur droit.
 */
.project-detail__content {
  background-color: var(--bg-primary);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 2rem;
  max-width: 95vw;
  width: 100%;
  max-height: 95vh;
  overflow-y: auto;
  position: relative;
}

/*
 * Le bouton de fermeture (X) est positionne en absolute dans le coin
 * superieur droit du modal. z-index: 2 le place au-dessus du contenu
 * du modal pour qu'il reste toujours cliquable.
 */
.project-detail__close {
  position: absolute;
  top: 1rem;
  right: 1rem;
  font-size: 1.75rem;
  color: var(--text-secondary);
  transition: color var(--transition-fast);
  line-height: 1;
  z-index: 2;
}

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

.project-detail__github {
  position: absolute;
  top: 1rem;
  right: 3.5rem;
  color: var(--text-secondary);
  transition: color var(--transition-fast);
  z-index: 2;
  display: flex;
  align-items: center;
}

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

/*
 * Le layout du modal utilise Flexbox pour placer la galerie d'images
 * a gauche et le texte descriptif a droite.
 */
.project-detail__layout {
  display: flex;
  gap: 2rem;
}

/*
 * "flex: 0 0 45%" est un raccourci pour :
 *   flex-grow: 0   → ne grandit pas
 *   flex-shrink: 0 → ne retrecit pas
 *   flex-basis: 45% → occupe exactement 45% de la largeur
 * max-width: 45% renforce cette contrainte.
 */
.project-detail__gallery-col {
  flex: 0 0 45%;
  max-width: 45%;
  display: flex;
  flex-direction: column;
  align-items: center;
}

/*
 * aspect-ratio: 16 / 10 pour l'affichage de la galerie (un peu moins
 * allonge que le 16/9 des miniatures, pour mieux montrer les captures).
 */
.project-detail__gallery-display {
  width: 100%;
  aspect-ratio: 16 / 10;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: var(--bg-card);
  border-radius: var(--radius-sm);
  overflow: hidden;
  cursor: pointer;
}

/*
 * OBJECT-FIT: CONTAIN pour les images de la galerie.
 * Contrairement a "cover" utilise pour les miniatures, "contain" affiche
 * l'image EN ENTIER sans rognage. Des bandes vides peuvent apparaitre
 * si les proportions ne correspondent pas, mais on voit tout le contenu.
 * C'est le bon choix pour des captures d'ecran ou des schemas techniques.
 */
.project-detail__gallery-img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.project-detail__gallery-video {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

.project-detail__gallery-nav {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-top: 0.75rem;
}

/*
 * Fleches de navigation de la galerie : des cercles de 36x36px
 * (border-radius: 50% sur un carre = cercle).
 */
.project-detail__gallery-arrow {
  width: 36px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.5rem;
  color: var(--text-secondary);
  background-color: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 50%;
  transition: color var(--transition-fast), border-color var(--transition-fast);
}

.project-detail__gallery-arrow:hover {
  color: var(--accent);
  border-color: var(--accent);
}

.project-detail__gallery-counter {
  font-size: var(--font-size-sm);
  color: var(--text-muted);
}

/*
 * "flex: 1" prend tout l'espace restant (a cote de la galerie a 45%).
 * "min-width: 0" est une astuce importante en Flexbox : par defaut,
 * un flex item ne peut pas etre plus petit que son contenu.
 * min-width: 0 leve cette restriction et evite que du texte long
 * ne fasse deborder le conteneur.
 */
.project-detail__text-col {
  flex: 1;
  min-width: 0;
}

/*
 * padding-right: 4rem laisse de la place pour le bouton de fermeture (X)
 * positionne en absolute en haut a droite, evitant que le titre
 * ne passe en dessous.
 */
.project-detail__title {
  font-size: var(--font-size-h2);
  color: var(--text-primary);
  margin-bottom: 0.25rem;
  padding-right: 4rem;
}

.project-detail__year {
  font-size: var(--font-size-sm);
  color: var(--text-muted);
  margin-bottom: 1rem;
}

.project-detail__tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 1.5rem;
}

.project-detail__description {
  font-size: var(--font-size-body);
  color: var(--text-secondary);
  line-height: 1.7;
  margin-bottom: 1.5rem;
}

.project-detail__links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

.project-detail__video-play {
  font-size: 2.5rem;
  color: #ffffff;
  width: 56px;
  height: 56px;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: rgba(59, 130, 246, 0.85);
  border-radius: 50%;
  line-height: 1;
}

.project-detail__video-label {
  font-size: var(--font-size-xs);
  color: var(--text-muted);
  margin-top: 0.5rem;
}

/* ---------- Project Filters ---------- */

.projets__filters {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  justify-content: center;
  margin-bottom: 2rem;
}

/*
 * "transition: all" anime TOUTES les proprietes CSS qui changent.
 * C'est pratique mais moins performant que de lister les proprietes
 * individuellement. A utiliser avec parcimonie.
 */
.projets__filter-btn {
  padding: 0.5rem 1.25rem;
  font-size: var(--font-size-sm);
  font-weight: 500;
  color: var(--text-secondary);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  transition: all var(--transition-fast);
}

.projets__filter-btn:hover {
  color: var(--accent);
  border-color: var(--accent);
}

/*
 * Classe d'etat .is-active sur le bouton de filtre selectionne :
 * fond plein accent avec texte contraste (meme pattern que la navbar).
 */
.projets__filter-btn.is-active {
  color: var(--text-on-accent);
  background-color: var(--accent);
  border-color: var(--accent);
}

/* ---------- Contact Form ---------- */

/*
 * CSS GRID POUR LES FORMULAIRES
 * --------------------------------
 * Le formulaire utilise CSS Grid avec 2 colonnes (1fr 1fr).
 * Les champs "nom" et "email" se placent cote a cote.
 *
 * Pour les champs qui doivent occuper TOUTE la largeur (sujet, message,
 * bouton d'envoi), on utilise le modificateur .contact__field--full avec :
 *   grid-column: 1 / -1
 *
 * GRID-COLUMN: 1 / -1 EXPLIQUE :
 *   - "1" = debut a la premiere ligne de grille (bord gauche)
 *   - "-1" = fin a la DERNIERE ligne de grille (bord droit)
 *   - L'element s'etend donc sur TOUTES les colonnes, quelle que soit
 *     leur nombre. C'est plus flexible que d'ecrire "1 / 3" qui
 *     cesserait de fonctionner si on changeait le nombre de colonnes.
 */
.contact__form {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.5rem;
}

.contact__field--full {
  grid-column: 1 / -1;
}

/*
 * TECHNIQUE HONEYPOT ANTI-SPAM
 * ------------------------------
 * Le honeypot ("pot de miel") est une technique de protection anti-spam
 * qui ne gene PAS l'utilisateur (contrairement aux CAPTCHA).
 *
 * Le principe : on ajoute un champ de formulaire CACHE, invisible pour
 * les humains mais visible pour les robots (bots) qui remplissent
 * automatiquement tous les champs.
 *
 * Si ce champ est rempli a la soumission → c'est un bot → on rejette.
 * Si ce champ est vide → c'est probablement un humain → on accepte.
 *
 * Pour le cacher, on utilise PLUSIEURS techniques combinees :
 *   - position: absolute + left: -9999px → deplace le champ hors ecran
 *   - opacity: 0 → le rend transparent
 *   - height: 0; overflow: hidden → lui donne une taille nulle
 *
 * ATTENTION : ne PAS utiliser "display: none" ou "visibility: hidden"
 * car certains bots avances detectent ces proprietes et evitent de
 * remplir ces champs. Le positionnement hors ecran est plus discret.
 */
.contact__honeypot {
  position: absolute;
  left: -9999px;
  opacity: 0;
  height: 0;
  overflow: hidden;
}

.contact__label {
  display: block;
  font-size: var(--font-size-sm);
  font-weight: 500;
  color: var(--text-secondary);
  margin-bottom: 0.5rem;
}

/* L'asterisque rouge a cote des champs obligatoires */
.contact__required {
  color: var(--error);
}

.contact__input {
  width: 100%;
  padding: 0.75rem 1rem;
  font-family: var(--font-main);
  font-size: var(--font-size-body);
  color: var(--text-primary);
  background-color: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

/*
 * :focus est un pseudo-classe qui s'active quand l'utilisateur clique
 * dans le champ ou y navigue au clavier (Tab).
 *
 * "outline: none" supprime le contour par defaut du navigateur.
 * On le remplace par un box-shadow colore qui sert de "focus ring"
 * (anneau de focus), plus esthetique mais toujours accessible.
 *
 * "box-shadow: 0 0 0 3px" cree un anneau de 3px autour du champ
 * (pas de decalage x/y, pas de flou, juste un spread de 3px).
 */
.contact__input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-glow);
}

/*
 * ETATS VISUELS DE VALIDATION DU FORMULAIRE
 * --------------------------------------------
 * .has-error et .has-success sont des classes d'etat ajoutees par le JS
 * apres validation du champ.
 *
 *   - .has-error → bordure rouge (--error), indique une saisie invalide.
 *   - .has-success → bordure verte (--success), indique une saisie valide.
 *
 * Ce feedback visuel immediat aide l'utilisateur a corriger ses erreurs
 * avant de soumettre le formulaire (UX "inline validation").
 */
.contact__input.has-error {
  border-color: var(--error);
}

.contact__input.has-success {
  border-color: var(--success);
}

/*
 * "resize: vertical" permet a l'utilisateur de redimensionner le textarea
 * uniquement en hauteur (pas en largeur, ce qui casserait la mise en page).
 */
.contact__textarea {
  resize: vertical;
  min-height: 120px;
}

/* Le bouton d'envoi occupe toute la largeur (grid-column: 1 / -1) */
.contact__submit {
  grid-column: 1 / -1;
}

/* Le message de feedback (succes/erreur) occupe aussi toute la largeur */
.contact__feedback {
  grid-column: 1 / -1;
  font-size: var(--font-size-sm);
  text-align: center;
  min-height: 1.5em;
}

/* Modificateur BEM --success pour le message de succes (vert) */
.contact__feedback--success {
  color: var(--success);
}

/* Modificateur BEM --error pour le message d'erreur (rouge) */
.contact__feedback--error {
  color: var(--error);
}

/* ---------- Contact Info ---------- */

.contact__info {
  display: flex;
  flex-direction: column;
  gap: 2rem;
}

.contact__info-title {
  margin-bottom: 0.75rem;
}

.contact__social-list {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.contact__social-link {
  color: var(--text-secondary);
  font-size: var(--font-size-sm);
  transition: color var(--transition-fast);
}

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

/* ---------- Parcours (CV timeline) ---------- */

/*
 * TIMELINE AVEC PSEUDO-ELEMENTS ::before
 * -----------------------------------------
 * Le parcours (experience + formation) est presente sous forme de
 * "timeline" : une ligne verticale avec des points a chaque etape.
 *
 * Cette ligne et ces points sont crees ENTIEREMENT en CSS avec des
 * pseudo-elements (::before), sans HTML supplementaire.
 */
.parcours__column-title {
  margin-bottom: 1.5rem;
  color: var(--accent);
  font-size: var(--font-size-h3);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  border-bottom: 2px solid var(--accent);
  padding-bottom: 0.5rem;
}

/*
 * "position: relative" cree un contexte de positionnement pour le
 * pseudo-element ::before (la ligne verticale).
 * padding-left: 1.5rem laisse de la place pour la ligne a gauche.
 */
.parcours__list {
  position: relative;
  padding-left: 1.5rem;
}

/*
 * LA LIGNE VERTICALE DE LA TIMELINE
 * -----------------------------------
 * ::before cree un element virtuel AVANT le contenu de .parcours__list.
 * "content: ''" est obligatoire (meme vide) pour qu'il apparaisse.
 *
 * position: absolute le positionne par rapport a .parcours__list.
 * left: 0 → colle au bord gauche.
 * top: 0.5rem et bottom: 0.5rem → la ligne ne va pas jusqu'aux bords
 *   extremes, laissant un petit espace en haut et en bas.
 * width: 2px → epaisseur de la ligne.
 *
 * Resultat : une fine ligne verticale grise a gauche de la liste.
 */
.parcours__list::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.5rem;
  bottom: 0.5rem;
  width: 2px;
  background-color: var(--border);
}

.parcours__item {
  position: relative;
  padding: 0 0 1.5rem 1rem;
}

/*
 * LES POINTS (BULLETS) DE LA TIMELINE
 * --------------------------------------
 * Chaque etape a un petit cercle colore sur la ligne.
 * C'est un pseudo-element ::before positionne en absolute.
 *
 * left: -1.5rem le place exactement sur la ligne verticale
 * (qui est a left: 0 du parent .parcours__list, soit 1.5rem a gauche
 * du contenu du .parcours__item).
 *
 * width et height de 10px + border-radius: 50% = cercle parfait.
 * transform: translateX(-4px) ajuste finement la position horizontale
 * pour centrer le point sur la ligne de 2px.
 */
.parcours__item::before {
  content: '';
  position: absolute;
  left: -1.5rem;
  top: 0.4rem;
  width: 10px;
  height: 10px;
  background-color: var(--accent);
  border-radius: 50%;
  transform: translateX(-4px);
}

/* Le dernier element n'a pas de padding en bas (pas d'espace apres) */
.parcours__item:last-child {
  padding-bottom: 0;
}

.parcours__item-period {
  display: inline-block;
  font-size: var(--font-size-xs);
  font-weight: 600;
  color: var(--accent);
  margin-bottom: 0.25rem;
}

.parcours__item-title {
  font-size: var(--font-size-body);
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: 0.125rem;
}

.parcours__item-place {
  font-size: var(--font-size-sm);
  color: var(--text-muted);
  margin-bottom: 0.375rem;
}

.parcours__item-description {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  line-height: 1.6;
}

.parcours__empty {
  font-size: var(--font-size-sm);
  color: var(--text-muted);
  font-style: italic;
}

/*
 * repeat(auto-fit, minmax(280px, 1fr)) est similaire a auto-fill
 * mais avec une nuance :
 *   - auto-fill : cree des colonnes vides si l'espace le permet
 *   - auto-fit  : etire les colonnes existantes pour remplir l'espace
 * En pratique, auto-fit est prefere quand on a peu d'elements.
 */
.parcours__certifications-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 1rem;
}

.parcours__cert-item {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding: 1rem 1.5rem;
  background-color: var(--bg-card);
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
}

.parcours__cert-title {
  font-size: var(--font-size-sm);
  font-weight: 600;
  color: var(--text-primary);
}

.parcours__cert-meta {
  font-size: var(--font-size-xs);
  color: var(--text-muted);
}

.parcours__cv {
  text-align: center;
  margin-top: 2rem;
}

/* ---------- Tags ---------- */

/*
 * TAGS AVEC FORME "PILULE" (PILL SHAPE)
 * ----------------------------------------
 * "border-radius: 9999px" est une astuce pour creer des coins
 * COMPLETEMENT arrondis, quelle que soit la taille de l'element.
 *
 * Pourquoi 9999px et pas 50% ?
 *   - 50% sur un rectangle donne une ELLIPSE (pas joli).
 *   - 9999px (une valeur absurdement grande) est automatiquement
 *     limitee par le navigateur au rayon maximum possible, ce qui
 *     donne des demi-cercles parfaits aux extremites.
 *
 * Resultat : une forme de "pilule" ou "capsule", tres utilisee pour
 * les tags, badges, et labels dans les interfaces modernes.
 *
 * display: inline-block permet au tag de s'adapter a la largeur de
 * son contenu tout en acceptant padding et margin.
 */
.tag {
  display: inline-block;
  padding: 0.25rem 0.75rem;
  font-size: var(--font-size-xs);
  font-weight: 500;
  color: var(--accent);
  background-color: var(--accent-glow);
  border-radius: 9999px;
}
