/* Portfolio — Mathéo Pérodeau
   Parti pris : un index, pas une brochure. Densité d'information, typographie
   système, une seule couleur d'accent. Pas de dégradé, pas d'ombre portée,
   pas d'animation d'apparition. */

:root {
  --bg: #fdfdfc;
  --surface: #ffffff;
  --text: #1b1e21;
  --muted: #666c73;
  --faint: #8b9198;
  --rule: #e2e2de;
  --rule-strong: #c9c9c4;
  --accent: #0d7581;
  --accent-bg: #e7f2f3;
  --prod: #1f7a4d;
  --warn: #a06a12;

  --mono: ui-monospace, "SFMono-Regular", "Cascadia Mono", "Segoe UI Mono", Menlo, Consolas, monospace;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

  /* Largeur de lecture pour les fiches et les articles. */
  --measure: 46rem;
  /* La page d'accueil tient sur deux colonnes et respire plus large. */
  --measure-large: 74rem;
  /* Marge laissee entre le contenu et le bord de l'ecran. Une seule valeur
     pour tout le site : masthead, panneaux et pied de page s'alignent. */
  --gutter: 1.5rem;
}

:root[data-theme="dark"] {
  --bg: #14171a;
  --surface: #191d21;
  --text: #e3e6e8;
  --muted: #9aa0a7;
  --faint: #767c83;
  --rule: #272c32;
  --rule-strong: #3a4149;
  --accent: #45c0cf;
  --accent-bg: #16272a;
  --prod: #5cc48d;
  --warn: #d4a44f;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #14171a;
    --surface: #191d21;
    --text: #e3e6e8;
    --muted: #9aa0a7;
    --faint: #767c83;
    --rule: #272c32;
    --rule-strong: #3a4149;
    --accent: #45c0cf;
    --accent-bg: #16272a;
    --prod: #5cc48d;
    --warn: #d4a44f;
  }
}

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

/* Toute règle `display` de ce fichier l'emporterait sinon sur le `display:none`
   que le navigateur applique à [hidden] : le filtrage marquerait les éléments
   sans jamais les masquer. */
[hidden] { display: none !important; }

/* Tout le site est dimensionne en rem : agrandir la base agrandit la page
   entiere — texte, vignettes, gouttieres — sans rien deregler. Le palier bas
   laisse le telephone et les ecrans etroits exactement comme ils etaient ; au
   dela de 1200 px l'echelle monte doucement jusqu'a 18,5 px, la ou il y a de
   la place a revendre.

   Note : dans une media query, `rem` se mesure toujours sur 16 px, jamais sur
   cette valeur. Les points de rupture ne bougent donc pas. */
html {
  -webkit-text-size-adjust: 100%;
  font-size: clamp(16px, 13px + 0.25vw, 18.5px);
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: 1rem;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; text-decoration-color: var(--rule-strong); text-underline-offset: 3px; }
a:hover { text-decoration-color: var(--accent); color: var(--accent); }

h1, h2, h3 { line-height: 1.25; font-weight: 600; margin: 0; letter-spacing: -0.012em; }

.wrap {
  max-width: var(--measure);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

.skip { position: absolute; left: -9999px; }
.skip:focus {
  left: 1rem; top: 1rem;
  z-index: 10;
  background: var(--surface);
  border: 1px solid var(--accent);
  padding: 0.5rem 0.75rem;
  border-radius: 2px;
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 1px;
}

/* --- En-tête ------------------------------------------------------------ */

/* Même conflit que le pied de page : `padding` en raccourci remettrait le
   retrait horizontal de `.wrap` à zéro. */
.masthead { padding-top: 3.5rem; }

.identity {
  display: flex;
  align-items: center;
  gap: 1.1rem;
}

.avatar {
  width: 4.5rem;
  height: 4.5rem;
  border-radius: 50%;
  flex: none;
  /* Un simple anneau : le portrait doit se détacher du fond sans effet de carte. */
  box-shadow: 0 0 0 1px var(--rule-strong);
  background: var(--surface);
}

.masthead h1 {
  font-size: clamp(1.6rem, 4.5vw, 2.1rem);
  letter-spacing: -0.02em;
}

.masthead .role {
  margin: 0.35rem 0 0;
  color: var(--muted);
  font-size: 1rem;
}

.masthead .meta {
  margin: 0.15rem 0 0;
  font-family: var(--mono);
  font-size: 0.78rem;
  color: var(--faint);
  letter-spacing: 0.01em;
}

/* L'accroche porte le propos : elle est plus grande que le corps de texte,
   mais reste une phrase, pas un slogan centré. */
.headline {
  margin: 1.75rem 0 0;
  max-width: 36rem;
  font-size: 1.12rem;
  line-height: 1.5;
  color: var(--text);
}

.subline {
  margin: 0.5rem 0 0;
  max-width: 36rem;
  color: var(--muted);
  font-size: 0.97rem;
}

/* Chiffres calculés à partir des projets : ils donnent à la présentation la
   même densité factuelle que l'index, sans prendre le ton d'un tableau de bord. */
.figures {
  display: flex;
  flex-wrap: wrap;
  gap: 0 2.25rem;
  margin: 1.75rem 0 0;
  padding: 0.9rem 0;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
}

.figures div { display: flex; flex-direction: column; gap: 0.1rem; }

.figures b {
  font-family: var(--mono);
  font-size: 1.32rem;
  font-weight: 600;
  line-height: 1.1;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}

.figures span {
  font-family: var(--mono);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--faint);
}

.facts.about {
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  margin-top: 1.25rem;
}

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 1.5rem 0 0;
}

/* Ancres de section : utiles seulement quand tout s'empile sur une colonne.
   Sur grand ecran les deux panneaux sont visibles d'un coup, elles n'ont plus
   rien a raccourcir. */
.jump { display: none; }

@media (max-width: 59.99rem) {
  .jump {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem 1.1rem;
    margin: 1.4rem 0 0;
    padding-top: 1.1rem;
    border-top: 1px solid var(--rule);
    font-family: var(--mono);
    font-size: 0.76rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
  }
  .jump a {
    color: var(--muted);
    text-decoration: none;
    padding: 0.3rem 0;
  }
  .jump a:hover, .jump a:focus-visible { color: var(--accent); }
}

/* Les ancres ne doivent pas deposer le titre vise sous le bord de l'ecran. */
:target { scroll-margin-top: 1.5rem; }

.btn {
  font-family: var(--mono);
  font-size: 0.78rem;
  padding: 0.4rem 0.7rem;
  border: 1px solid var(--rule-strong);
  border-radius: 2px;
  background: var(--surface);
  color: var(--text);
  text-decoration: none;
  cursor: pointer;
  line-height: 1.4;
}
.btn:hover { border-color: var(--accent); color: var(--accent); }
.btn[aria-pressed="true"] { border-color: var(--accent); background: var(--accent-bg); color: var(--accent); }

/* Boutons de filtre : la pastille reprend la couleur de la techno, le compteur
   annonce le nombre de résultats avant même de cliquer. */
.filters .btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}

.filters .btn .dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex: none;
  box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.18);
}

.filters .btn b {
  font-weight: 600;
  font-size: 0.92em;
  color: var(--faint);
  font-variant-numeric: tabular-nums;
}
.filters .btn[aria-pressed="true"] b { color: inherit; }

/* Sépare les filtres de statut de ceux de technologie : ils ne répondent pas
   à la même question. */
.filters-sep {
  width: 1px;
  align-self: stretch;
  background: var(--rule);
  margin: 0 0.25rem;
}

/* --- Mise en page de l'accueil ------------------------------------------- */

/* Téléphone : pas de grille. `display: contents` dissout le panneau de gauche
   pour que ses sections puissent être réordonnées une à une — l'identité en
   tête, les projets juste après, le parcours à la fin. */
@media (max-width: 59.99rem) {
  .layout { display: flex; flex-direction: column; }
  .pane-side { display: contents; }
  .masthead      { order: 1; }
  .pane-main     { order: 2; }
  #competences   { order: 3; }
  #experience    { order: 4; }
  #formation     { order: 5; }
  footer.wrap    { order: 6; }
  /* `display: contents` dissout aussi la boite du panneau : sa gouttiere
     disparait avec elle. Elle est donc reportee sur chaque section, qui devient
     un enfant direct de la grille. */
  .pane,
  .pane-side > * {
    max-width: var(--measure);
    margin-inline: auto;
    padding-inline: var(--gutter);
    width: 100%;
  }
}

@media (min-width: 60rem) {
  .layout {
    display: grid;
    grid-template-columns: 25rem minmax(0, 1fr);
    column-gap: 3.5rem;
    align-items: start;
    max-width: var(--measure-large);
    margin: 0 auto;
    padding: 0 2rem;
  }

  .pane-main > section:first-child { margin-top: 3.5rem; }
  .pane-side > section:first-child { margin-top: 3rem; }

  /* Colonne étroite : les grilles à deux colonnes n'y tiennent pas. */
  .figures { display: grid; grid-template-columns: 1fr 1fr; gap: 0.85rem 1rem; }
  .facts.about { grid-template-columns: 1fr; gap: 0.7rem; }
  .pane-side .entry,
  .pane-side .skill-row { grid-template-columns: 1fr; gap: 0.1rem; }
  .pane-side .entry .when,
  .pane-side .skill-row dt { padding-top: 0; margin-bottom: 0.15rem; }
  .pane-side .entry .note { grid-column: 1; }
  .pane-side footer.wrap { padding-left: 0; padding-right: 0; max-width: none; }

  /* Dans une colonne de 23rem, le nom se coupait en deux lignes : la taille
     fluide n'a pas conscience de la largeur réelle du panneau. */
  .pane-side .masthead h1 { font-size: 1.85rem; }
  .pane-side .identity { gap: 0.9rem; }
  .pane-side .avatar { width: 4.25rem; height: 4.25rem; }
}

/* Écran large et assez haut : chaque panneau défile pour lui-même, ce qui
   permet de parcourir les projets sans perdre le profil de vue. En dessous de
   ces seuils on garde un défilement de page classique, plus sûr. */
@media (min-width: 75rem) and (min-height: 40rem) {
  html, body { height: 100%; overflow: hidden; }

  .layout {
    height: 100dvh;
    max-width: none;
    padding: 0;
    column-gap: 0;
    /* La colonne de profil suit la fenetre au lieu d'etre figee : large ecran,
       colonne large. Les bornes l'empechent d'etouffer les projets en dessous
       de 1400 px et de s'etaler betement au dela de 2000 px. */
    grid-template-columns: clamp(26rem, 25vw, 31rem) minmax(0, 1fr);
  }

  .pane {
    height: 100%;
    overflow-y: auto;
    /* Le défilement s'arrête au bord du panneau au lieu d'entraîner la page. */
    overscroll-behavior: contain;
    padding: 0 2.5rem;
    scrollbar-width: thin;
    scrollbar-color: var(--rule-strong) transparent;
  }

  .pane-side { border-right: 1px solid var(--rule); }
  .pane-main { padding-right: 3rem; }

  /* Le panneau devient son propre référent de taille : la liste réagit à la
     largeur réellement disponible, pas à celle de la fenêtre — qui inclut la
     colonne de profil et fausserait le calcul. */
  .pane-main { container-type: inline-size; }

  .pane::-webkit-scrollbar { width: 10px; }
  .pane::-webkit-scrollbar-thumb {
    background: var(--rule-strong);
    border-radius: 5px;
    border: 3px solid var(--bg);
  }
  .pane::-webkit-scrollbar-track { background: transparent; }
}

/* --- Sections ----------------------------------------------------------- */

section { margin-top: 3.25rem; }

.section-head {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  border-bottom: 1px solid var(--rule-strong);
  padding-bottom: 0.4rem;
  margin-bottom: 1.25rem;
}

.section-head h2 {
  font-family: var(--mono);
  font-size: 0.76rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--text);
}

.section-head .count {
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--faint);
  margin-left: auto;
}

/* --- Index des projets -------------------------------------------------- */

/* Contrôles optionnels : masqués tant que le script n'a pas pris la main,
   pour ne jamais afficher un bouton inerte. */
.filters, #theme-toggle { display: none; }
.js .filters {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-bottom: 1.5rem;
}
.js #theme-toggle { display: inline-block; }

.projects { list-style: none; margin: 0; padding: 0; }

.project {
  padding: 1.15rem 0;
  border-bottom: 1px solid var(--rule);
  display: flex;
  align-items: flex-start;
  gap: 0.95rem;
  position: relative;
}
.project:last-child { border-bottom: 0; }

/* Survol : un aplat, pas une animation d'apparition. Il est porte par un
   pseudo-element pour deborder dans la gouttiere sans emporter le filet de
   separation, qui doit rester aligne sur les titres de section. */
.project::before {
  content: "";
  position: absolute;
  inset: 0.25rem -0.8rem;
  border-radius: 8px;
  background: var(--surface);
  box-shadow: inset 0 0 0 1px var(--rule);
  opacity: 0;
  transition: opacity 130ms ease;
  pointer-events: none;
}
.project > * { position: relative; }

@media (hover: hover) {
  .project:hover::before,
  .project:focus-within::before { opacity: 1; }
}

.project-main { flex: 1; min-width: 0; }

/* Vignette : le logo du projet, ou son monogramme quand il n'en a pas.
   La teinte du monogramme est dérivée du nom, donc stable et sans collision
   visuelle entre deux voisins. */
.thumb {
  flex: none;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 6px;
  display: grid;
  place-items: center;
  overflow: hidden;
  background: var(--surface);
  box-shadow: inset 0 0 0 1px var(--rule);
}
.thumb img { width: 100%; height: 100%; object-fit: contain; display: block; }

.thumb-mono {
  font-family: var(--mono);
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: hsl(var(--hue) 55% 30%);
  background: hsl(var(--hue) 48% 93%);
  box-shadow: inset 0 0 0 1px hsl(var(--hue) 40% 84%);
}
:root[data-theme="dark"] .thumb-mono {
  color: hsl(var(--hue) 62% 76%);
  background: hsl(var(--hue) 28% 18%);
  box-shadow: inset 0 0 0 1px hsl(var(--hue) 25% 30%);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .thumb-mono {
    color: hsl(var(--hue) 62% 76%);
    background: hsl(var(--hue) 28% 18%);
    box-shadow: inset 0 0 0 1px hsl(var(--hue) 25% 30%);
  }
}

.project-title {
  font-size: 1.02rem;
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}
.project-title a { text-decoration: none; }
.project-title a:hover { text-decoration: underline; }

.project-version {
  flex: none;
  font-family: var(--mono);
  font-size: 0.74rem;
  color: var(--faint);
  white-space: nowrap;
  padding-top: 0.25rem;
}

.project-summary {
  margin: 0.3rem 0 0;
  color: var(--muted);
  font-size: 0.94rem;
  max-width: 38rem;
}

.project-foot {
  margin-top: 0.55rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem 0.8rem;
  font-family: var(--mono);
  font-size: 0.74rem;
  color: var(--faint);
}

.stack { display: flex; flex-wrap: wrap; gap: 0.55rem; }

/* Pastille de langage : la couleur est celle que GitHub associe à la techno,
   donc un repère déjà connu du lecteur plutôt qu'une décoration. */
.tech { display: inline-flex; align-items: center; gap: 0.3rem; }
.tech i {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex: none;
  /* Un contour très léger garde les teintes claires visibles sur fond blanc. */
  box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.18);
}
.tech b { font-weight: 600; color: var(--muted); }

/* Le sprite ne sert qu'à définir les symboles : il ne doit rien occuper. */
.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

/* Pictogramme de lien : calé sur la taille du texte, dans sa couleur, et
   volontairement discret — il repère l'action, il ne la remplace pas. */
.ico {
  width: 1em;
  height: 1em;
  flex: none;
  vertical-align: -0.13em;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0.75;
}
a:hover > .ico { opacity: 1; }

.project-links { display: flex; gap: 0.85rem; margin-left: auto; }
.project-links a { display: inline-flex; align-items: center; gap: 0.32rem; }
.btn .ico { margin-right: 0.4rem; }
.crumb a { display: inline-flex; align-items: center; gap: 0.35rem; }
.project-links a { color: var(--accent); text-decoration: none; }
.project-links a:hover { text-decoration: underline; }

/* Statut : une pastille discrète, colorée uniquement quand c'est signifiant. */
.status {
  font-family: var(--mono);
  font-size: 0.68rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--faint);
  display: inline-flex;
  align-items: center;
  gap: 0.32rem;
  white-space: nowrap;
}
.status::before {
  content: "";
  width: 6px; height: 6px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}
.status[data-status="prod"] { color: var(--prod); }
.status[data-status="wip"] { color: var(--warn); }
.status[data-status="archived"]::before { background: none; box-shadow: inset 0 0 0 1px currentColor; }

.empty { padding: 2rem 0; color: var(--muted); font-size: 0.94rem; }

/* Au-delà de cette largeur, une liste sur une seule colonne laisse un vide à
   droite et éloigne les liens du texte. Deux colonnes remplissent l'espace et
   montrent plus de projets d'un coup, sans changer la nature de la liste. */
/* Seuil en pixels, pas en rem : la base typographique suit desormais la
   largeur de la fenetre, un seuil en rem se deplacerait donc avec elle et la
   deuxieme colonne disparaitrait au moment ou l'ecran s'agrandit. */
@container (min-width: 1015px) {
  /* Colonnes typographiques, pas une grille : dans une grille chaque rangee
     prend la hauteur de sa fiche la plus haute, et la plus courte laisse un
     trou sous elle. Ici les fiches s'ecoulent a la suite et remplissent les
     deux colonnes sans vide intercalaire. */
  .projects, .notes {
    column-count: 2;
    column-gap: 3rem;
  }

  /* Une fiche coupee en deux au bas d'une colonne serait illisible. */
  .project, .note-row {
    break-inside: avoid;
    -webkit-column-break-inside: avoid;
  }

  /* Le filet du dernier element est retabli : en colonnes, le « dernier » est
     au bas de la colonne de droite, et son voisin de gauche se retrouverait
     seul a en porter un. */
  .projects .project:last-child,
  .notes .note-row:last-child { border-bottom: 1px solid var(--rule); }

  /* Les colonnes ci-dessus se lisent de haut en bas, colonne par colonne. Avec
     JavaScript on repasse a une lecture de gauche a droite — la plus recente en
     haut a gauche — sans rouvrir de trou : chaque fiche occupe un nombre entier
     de rangees d'1 px, et le placement automatique la glisse dans le premier
     creux libre. La classe est posee par le script juste avant de calculer ces
     hauteurs ; sans lui, les colonnes ci-dessus restent en place. */
  .projects.mosaique, .notes.mosaique {
    column-count: auto;
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: 3rem;
    grid-auto-rows: 1px;
    align-items: start;
  }

  /* Sur deux colonnes, la version en bout de ligne n'a plus la place : elle
     rejoint les métadonnées sous le titre. */
  .project { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 0 0.95rem; }
  .project .thumb { grid-row: 1 / span 2; }
  .project-version {
    grid-column: 2;
    order: 2;
    padding-top: 0;
    margin-top: 0.15rem;
  }
  .project-links { margin-left: 0; }
  .note-row { grid-template-columns: 1fr; }
  .note-row .when { padding-top: 0; margin-bottom: 0.15rem; }
}


/* --- Notes --------------------------------------------------------------- */

.notes { list-style: none; margin: 0; padding: 0; }

.note-row {
  display: grid;
  grid-template-columns: 10.5rem 1fr;
  gap: 0.2rem 1.25rem;
  padding: 1rem 0;
  border-bottom: 1px solid var(--rule);
}
.note-row:last-child { border-bottom: 0; }

.note-row .when {
  font-family: var(--mono);
  font-size: 0.76rem;
  color: var(--faint);
  padding-top: 0.22rem;
  white-space: nowrap;
}

.note-title {
  font-size: 1.02rem;
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}
.note-title a { text-decoration: none; }
.note-title a:hover { text-decoration: underline; }

.note-summary {
  margin: 0.3rem 0 0;
  color: var(--muted);
  font-size: 0.94rem;
  max-width: 38rem;
}

.note-foot {
  margin: 0.45rem 0 0;
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--faint);
}

/* --- Parcours ----------------------------------------------------------- */

.timeline { list-style: none; margin: 0; padding: 0; }

/* La colonne de dates doit contenir la plus longue d'entre elles
   (« janv. 2025 – juill. 2025 ») sans empieter sur le texte : 10.5rem couvre
   le format le plus large que produise `formatDate`. */
.entry {
  display: grid;
  grid-template-columns: 10.5rem 1fr;
  gap: 0.2rem 1.25rem;
  padding: 0.85rem 0;
  border-bottom: 1px solid var(--rule);
}
.entry:last-child { border-bottom: 0; }

.entry .when {
  font-family: var(--mono);
  font-size: 0.76rem;
  color: var(--faint);
  padding-top: 0.18rem;
  white-space: nowrap;
}
.entry .what { font-weight: 600; font-size: 0.97rem; }
.entry .where { color: var(--muted); font-size: 0.88rem; }
.entry .note { grid-column: 2; color: var(--muted); font-size: 0.9rem; margin-top: 0.25rem; }

/* Là où subgrid existe, la colonne se cale sur la date la plus longue de la
   liste : plus de largeur à deviner, et toutes les lignes restent alignées. */
@supports (grid-template-columns: subgrid) {
  .timeline, .notes {
    display: grid;
    grid-template-columns: max-content 1fr;
  }
  .entry, .note-row {
    grid-column: 1 / -1;
    grid-template-columns: subgrid;
  }
}

/* --- Compétences -------------------------------------------------------- */

/* Barre de répartition des technologies, calculée à partir des projets réels. */
.techbar { margin: 0 0 1.5rem; }

.techbar .bar {
  display: flex;
  height: 8px;
  border-radius: 4px;
  overflow: hidden;
  background: var(--rule);
}
.techbar .slice { height: 100%; }
.techbar .slice + .slice { box-shadow: inset 1px 0 0 var(--bg); }

.techbar .legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 0.9rem;
  margin-top: 0.6rem;
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--faint);
}

.skills { margin: 0; padding: 0; }
.skill-row {
  display: grid;
  grid-template-columns: 10.5rem 1fr;
  gap: 1.25rem;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--rule);
}
.skill-row:last-child { border-bottom: 0; }
.skill-row dt { font-family: var(--mono); font-size: 0.76rem; color: var(--faint); padding-top: 0.15rem; }
.skill-row dd { margin: 0; font-size: 0.94rem; }

/* --- Pied de page ------------------------------------------------------- */

/* Le pied de page porte aussi la classe `wrap`, dont la spécificité (une
   classe) l'emporte sur un sélecteur d'élément : sans `footer.wrap`, la marge
   et les retraits verticaux ci-dessous étaient purement et simplement ignorés.
   Les retraits sont déclarés séparément pour ne pas écraser le retrait
   horizontal que `.wrap` fournit. */
footer.wrap {
  margin-top: 4.5rem;
  padding-top: 1.5rem;
  padding-bottom: 3.5rem;
  border-top: 1px solid var(--rule-strong);
  font-family: var(--mono);
  font-size: 0.74rem;
  color: var(--faint);
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  align-items: center;
}
footer a { color: var(--faint); }
footer .spacer { margin-left: auto; }

/* --- Fiche projet ------------------------------------------------------- */

.crumb {
  font-family: var(--mono);
  font-size: 0.76rem;
  color: var(--faint);
  padding-top: 2.5rem;
}
.crumb a { color: var(--accent); text-decoration: none; }
.crumb a:hover { text-decoration: underline; }

.detail-head {
  padding: 1rem 0 1.25rem;
  border-bottom: 1px solid var(--rule-strong);
}
/* Bannière de projet : ces visuels sont des bandeaux de titre, ils sont donc
   affichés dans leur format d'origine plutôt que recadrés en icône. Le cadre
   garantit le contraste des bannières claires sur fond blanc. */
.banner {
  margin: 1.5rem 0 0;
  border: 1px solid var(--rule);
  border-radius: 6px;
  overflow: hidden;
  background: var(--surface);
  line-height: 0;
}
.banner img { width: 100%; height: auto; display: block; }

.aliases {
  margin: 0.9rem 0 0;
  font-size: 0.9rem;
  color: var(--muted);
}
.aliases a { color: var(--accent); }

.detail-head .identity { gap: 0.9rem; }
.detail-head .thumb { width: 3.5rem; height: 3.5rem; border-radius: 8px; }
.detail-head .thumb-mono { font-size: 1.05rem; }
.detail-head h1 { font-size: clamp(1.5rem, 4vw, 1.95rem); }
.detail-head .lede { margin: 0.6rem 0 0; color: var(--muted); }

.facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: 0.9rem 1.5rem;
  margin: 1.25rem 0 0;
}
.facts div { min-width: 0; }
.facts dt {
  font-family: var(--mono);
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--faint);
}
.facts dd { margin: 0.15rem 0 0; font-size: 0.9rem; overflow-wrap: break-word; }

/* --- Galerie ------------------------------------------------------------- */

.gallery-section { margin-top: 3rem; }

.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: 1rem;
}

.shot { margin: 0; }

.shot a {
  display: block;
  border: 1px solid var(--rule);
  border-radius: 4px;
  overflow: hidden;
  background: var(--surface);
  line-height: 0;
}
.shot a:hover { border-color: var(--accent); }

.shot img {
  width: 100%;
  height: 100%;
  display: block;
  /* Un cadre commun évite que la grille parte en escalier, mais l'image y est
     contenue et non rognée : une capture verticale d'application mobile doit
     rester lisible, quitte à laisser des marges latérales. */
  aspect-ratio: 16 / 9;
  object-fit: contain;
  background: var(--accent-bg);
}

.shot figcaption {
  margin-top: 0.4rem;
  font-size: 0.82rem;
  line-height: 1.45;
  color: var(--muted);
}

@media (max-width: 34rem) {
  .gallery { grid-template-columns: 1fr; gap: 1.25rem; }
}

/* Le texte suit la meme largeur que la ligne de faits au-dessus : une colonne
   plus etroite laissait un vide a droite du panneau. */
.prose { margin-top: 2rem; }
.prose h1 { font-size: 1.35rem; margin: 2rem 0 0.6rem; }
.prose h2 { font-size: 1.15rem; margin: 2rem 0 0.6rem; padding-bottom: 0.3rem; border-bottom: 1px solid var(--rule); }
.prose h3 { font-size: 1rem; margin: 1.5rem 0 0.4rem; }
.prose h4 { font-size: 0.92rem; margin: 1.25rem 0 0.35rem; color: var(--muted); }
.prose p, .prose ul, .prose ol { margin: 0 0 0.9rem; }
.prose ul, .prose ol { padding-left: 1.25rem; }
.prose li { margin-bottom: 0.3rem; }
.prose li::marker { color: var(--faint); }
.prose img { max-width: 100%; height: auto; border: 1px solid var(--rule); border-radius: 2px; }
.prose blockquote {
  margin: 0 0 0.9rem;
  padding-left: 1rem;
  border-left: 2px solid var(--rule-strong);
  color: var(--muted);
}
.prose code {
  font-family: var(--mono);
  font-size: 0.85em;
  background: var(--accent-bg);
  padding: 0.12em 0.35em;
  border-radius: 2px;
}
.prose pre {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 2px;
  padding: 0.85rem 1rem;
  overflow-x: auto;
  font-size: 0.82rem;
  line-height: 1.55;
}
.prose pre code { background: none; padding: 0; font-size: inherit; }
.prose hr { border: 0; border-top: 1px solid var(--rule); margin: 2rem 0; }

/* Le langage du bloc de code, en étiquette, comme sur GitHub. */
.prose pre[data-lang] { position: relative; padding-top: 1.9rem; }
.prose pre[data-lang]::before {
  content: attr(data-lang);
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  padding: 0.25rem 1rem;
  font-family: var(--mono);
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--faint);
  border-bottom: 1px solid var(--rule);
  background: var(--bg);
}

/* Ancre de titre : discrète, révélée au survol, comme sur GitHub. */
.prose h2, .prose h3, .prose h4 { position: relative; }
.prose .anchor {
  position: absolute;
  left: -1.1rem;
  width: 1.1rem;
  text-align: left;
  color: var(--faint);
  text-decoration: none;
  opacity: 0;
  font-weight: 400;
}
.prose h2:hover .anchor,
.prose h3:hover .anchor,
.prose h4:hover .anchor,
.prose .anchor:focus { opacity: 1; }
@media (max-width: 48rem) { .prose .anchor { display: none; } }

/* Alertes GitHub : `> [!NOTE]`, `> [!WARNING]`… */
.prose .alert {
  border-left: 3px solid var(--alert, var(--rule-strong));
  padding: 0.75rem 1rem;
  margin: 0 0 1rem;
  color: var(--text);
  background: color-mix(in srgb, var(--alert, var(--rule)) 7%, transparent);
  border-radius: 0 3px 3px 0;
}
.prose .alert p { margin: 0 0 0.5rem; }
.prose .alert p:last-child { margin-bottom: 0; }
.prose .alert-title {
  font-family: var(--mono);
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--alert, var(--muted));
}
.prose .alert-note { --alert: #3b7ea1; }
.prose .alert-tip { --alert: var(--prod); }
.prose .alert-important { --alert: #7c5cbf; }
.prose .alert-warning { --alert: var(--warn); }
.prose .alert-caution { --alert: #b4453c; }

/* Listes de tâches : la puce est remplacée par la case à cocher. */
.prose li.task { list-style: none; margin-left: -1.25rem; }
.prose li.task input {
  margin: 0 0.5rem 0 0;
  vertical-align: middle;
  accent-color: var(--accent);
}

/* Balises que les README emploient sans qu'on les ait prévues. */
.prose kbd {
  font-family: var(--mono);
  font-size: 0.8em;
  padding: 0.12em 0.4em;
  border: 1px solid var(--rule-strong);
  border-bottom-width: 2px;
  border-radius: 3px;
  background: var(--surface);
}
.prose mark { background: var(--accent-bg); color: var(--text); padding: 0 0.2em; }
.prose details {
  border: 1px solid var(--rule);
  border-radius: 3px;
  padding: 0.6rem 0.9rem;
  margin: 0 0 0.9rem;
}
.prose summary { cursor: pointer; font-weight: 600; }
.prose details[open] summary { margin-bottom: 0.6rem; }
.prose ul ul, .prose ol ol, .prose ul ol, .prose ol ul { margin: 0.3rem 0 0; }
.prose p > img { margin: 0.3rem 0; }

.table-scroll { overflow-x: auto; margin: 0 0 0.9rem; }
.prose table { border-collapse: collapse; font-size: 0.88rem; min-width: 100%; }
.prose th, .prose td {
  text-align: left;
  padding: 0.45rem 0.75rem 0.45rem 0;
  border-bottom: 1px solid var(--rule);
}
.prose th {
  font-family: var(--mono);
  font-size: 0.74rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--faint);
  font-weight: 600;
}

/* --- Responsive --------------------------------------------------------- */

@media (max-width: 34rem) {
  body { font-size: 15px; }

  .masthead { padding-top: 2.25rem; }
  .identity { gap: 0.85rem; }
  .avatar { width: 3.75rem; height: 3.75rem; }
  .masthead h1 { font-size: 1.45rem; }
  .masthead .role { font-size: 0.93rem; }
  .headline { font-size: 1.05rem; }

  /* Quatre chiffres sur une seule rangée deviennent illisibles sous 375 px :
     deux colonnes leur redonnent de l'air. */
  .figures {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.85rem 1rem;
  }
  .figures b { font-size: 1.2rem; }

  /* Une colonne : à deux, les valeurs se coupent en plein milieu d'un mot. */
  .facts, .facts.about { grid-template-columns: 1fr; gap: 0.7rem; }

  .entry, .skill-row, .note-row { grid-template-columns: 1fr; gap: 0.1rem; }
  .entry .note { grid-column: 1; }
  .note-row .when { padding-top: 0; margin-bottom: 0.15rem; }

  .project { gap: 0.7rem; }
  .thumb { width: 2.15rem; height: 2.15rem; border-radius: 5px; }
  .thumb-mono { font-size: 0.72rem; }
  /* La version passe sous le titre plutot que de comprimer la colonne. */
  .project-version { display: none; }

  /* Les métadonnées descendent à 11 px sur mobile : on les remonte un peu,
     elles portent la stack et les dates. */
  .project-foot, .note-foot { font-size: 0.77rem; gap: 0.3rem 0.7rem; }

  /* Une description de six lignes par projet rend l'index interminable au
     pouce. On la borne ici ; la fiche du projet affiche le texte entier. */
  .project-summary {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    line-clamp: 3;
    overflow: hidden;
  }
  .section-head .count { font-size: 0.75rem; }

  /* Cibles tactiles : des liens de 11 px collés les uns aux autres sont
     impossibles à viser au pouce. */
  .project-links {
    margin-left: 0;
    width: 100%;
    gap: 1.25rem;
    margin-top: 0.1rem;
  }
  .project-links a { padding: 0.3rem 0; }
  .btn { padding: 0.5rem 0.8rem; }
  .filters { gap: 0.45rem; }
}

/* --- Impression --------------------------------------------------------- */

@media print {
  :root {
    --bg: #fff; --surface: #fff; --text: #000; --muted: #333;
    --faint: #555; --rule: #ccc; --rule-strong: #999; --accent: #000;
  }
  .actions, .filters, #theme-toggle, .skip { display: none !important; }

  /* Les panneaux à défilement propre n'ont pas de sens sur papier : sans cela
     l'impression s'arrêterait à la hauteur d'un écran. */
  html, body { height: auto !important; overflow: visible !important; }
  .layout { display: block !important; max-width: none !important; }
  .pane {
    height: auto !important;
    overflow: visible !important;
    max-width: none !important;
    padding: 0 !important;
    border: 0 !important;
  }
  .thumb, .avatar, .techbar .bar, .tech i {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
  body { font-size: 11pt; }
  .project, .entry { break-inside: avoid; }
  section { margin-top: 1.5rem; }
  a[href^="http"]::after {
    content: " (" attr(href) ")";
    font-size: 0.75em;
    color: #555;
    word-break: break-all;
  }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}
