

{"id":1868,"date":"2026-03-25T17:03:34","date_gmt":"2026-03-25T16:03:34","guid":{"rendered":"https:\/\/spes.anivise.fr\/?page_id=1868"},"modified":"2026-10-14T18:17:18","modified_gmt":"2026-10-14T16:17:18","slug":"fiches-spes-test-modifs","status":"publish","type":"page","link":"https:\/\/spes.anivise.fr\/index.php\/fiches-spes-test-modifs\/","title":{"rendered":"Fiches SPES &#8211; TEST MODIFS"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-post\" data-elementor-id=\"1868\" class=\"elementor elementor-1868\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-e978140 e-flex e-con-boxed parallax_section_no qode_elementor_container_no e-con e-parent\" data-id=\"e978140\" data-element_type=\"container\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t<div class=\"elementor-element elementor-element-383d0ed e-con-full e-flex parallax_section_no qode_elementor_container_no e-con e-child\" data-id=\"383d0ed\" data-element_type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-6a1a66a elementor-button-align-stretch elementor-widget elementor-widget-form\" data-id=\"6a1a66a\" data-element_type=\"widget\" id=\"search-espece\" data-settings=\"{&quot;step_next_label&quot;:&quot;Next&quot;,&quot;step_previous_label&quot;:&quot;Previous&quot;,&quot;button_width&quot;:&quot;100&quot;,&quot;step_type&quot;:&quot;number_text&quot;,&quot;step_icon_shape&quot;:&quot;circle&quot;}\" data-widget_type=\"form.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<form class=\"elementor-form\" method=\"post\" name=\"New Form\">\n\t\t\t<input type=\"hidden\" name=\"post_id\" value=\"1868\"\/>\n\t\t\t<input type=\"hidden\" name=\"form_id\" value=\"6a1a66a\"\/>\n\t\t\t<input type=\"hidden\" name=\"referer_title\" value=\"Fiches SPES &#8211; TEST MODIFS\" \/>\n\n\t\t\t\t\t\t\t<input type=\"hidden\" name=\"queried_id\" value=\"1868\"\/>\n\t\t\t\n\t\t\t<div class=\"elementor-form-fields-wrapper elementor-labels-above\">\n\t\t\t\t\t\t\t\t<div class=\"elementor-field-type-text elementor-field-group elementor-column elementor-field-group-searchespece elementor-col-100\">\n\t\t\t\t\t\t\t\t\t\t\t\t<label for=\"form-field-searchespece\" class=\"elementor-field-label\">\n\t\t\t\t\t\t\t\tESPECE RECHERCHEE\t\t\t\t\t\t\t<\/label>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<input size=\"1\" type=\"text\" name=\"form_fields[searchespece]\" id=\"form-field-searchespece\" class=\"elementor-field elementor-size-sm  elementor-field-textual\" placeholder=\"Ecrire ici\">\n\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t\t\t\t<div class=\"elementor-field-group elementor-column elementor-field-type-submit elementor-col-100 e-form__buttons\">\n\t\t\t\t\t<button class=\"elementor-button elementor-size-sm\" type=\"submit\">\n\t\t\t\t\t\t<span class=\"elementor-button-content-wrapper\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/span>\n\t\t\t\t\t<\/button>\n\t\t\t\t<\/div>\n\t\t\t<\/div>\n\t\t<\/form>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-ac9acb5 e-flex e-con-boxed parallax_section_no qode_elementor_container_no e-con e-parent\" data-id=\"ac9acb5\" data-element_type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-019c501 elementor-widget elementor-widget-html\" data-id=\"019c501\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<script>\r\ndocument.addEventListener('DOMContentLoaded', () => {\r\n  console.log('shortcode fiches_rest_nonce exists?', (typeof window.wp !== 'undefined'));\r\n});\r\n<\/script>\r\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-991917d elementor-widget elementor-widget-shortcode\" data-id=\"991917d\" data-element_type=\"widget\" data-widget_type=\"shortcode.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"elementor-shortcode\">\n\n<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-794bd2b elementor-widget elementor-widget-shortcode\" data-id=\"794bd2b\" data-element_type=\"widget\" data-widget_type=\"shortcode.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<div class=\"elementor-shortcode\"><script>window.FICHES_EXPORT = { canExportPdf: false };<\/script><\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-0122c1f elementor-widget elementor-widget-html\" data-id=\"0122c1f\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<!-- Fonts -->\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Inter:wght@400;600&display=swap\" rel=\"stylesheet\">\n\n<!-- Librairies -->\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/papaparse@5.4.1\/papaparse.min.js\"><\/script>\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/fuse.js@6.6.2\"><\/script>\n<script src=\"https:\/\/www.gstatic.com\/charts\/loader.js\"><\/script>\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/chart.js\"><\/script>\n\n<style>\n\/* Masquer le logo du th\u00e8me Bridge *\/\n.q_logo,\n.header_logo,\n.logo_wrapper {\n  display: none !important;\n}\n\n\/* Police et base *\/\nbody {\n  font-family: 'Inter', sans-serif;\n  background-color: #f8f9fa;\n  color: #222;\n  margin: 0;\n  padding: 0;\n  line-height: 1.6;\n}\n\n\n\/* Conteneur du champ de recherche *\/\n#search-espece {\n  position: relative;\n  z-index: 999; \/* assure la superposition *\/\n}\n\n\/* Champ de recherche *\/\n#search-espece input[type=\"text\"] {\n  width: 100%;\n  max-width: 400px;\n  padding: 12px 16px;\n  font-size: 1rem;\n  border: 1px solid #ccc;\n  border-radius: 8px;\n  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);\n  outline: none;\n  transition: border-color 0.2s ease;\n  margin: 20px auto;\n  display: block;\n}\n\n\/* Focus *\/\n#search-espece input[type=\"text\"]:focus {\n  border-color: #4ECDC4;\n}\n\n\/* \u2705 Input d\u00e9sactiv\u00e9 pendant chargement *\/\n#search-espece input[type=\"text\"][disabled] {\n  opacity: 0.6;\n  cursor: not-allowed;\n}\n\n\/* Liste des suggestions *\/\n#suggestions {\n  position: absolute;\n  top: 100%;\n  left: 0;\n  right: 0;\n  max-height: 150px;\n  overflow-y: auto;\n  overflow-x: hidden;\n  background-color: white;\n  border: 1px solid #ccc;\n  border-top: none;\n  z-index: 9999;\n  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);\n  overscroll-behavior: contain;\n  pointer-events: auto; \/* \ud83d\udc48 important *\/\n}\n\n\/* Scrollbar visible *\/\n#suggestions::-webkit-scrollbar {\n  width: 6px;\n}\n#suggestions::-webkit-scrollbar-thumb {\n  background-color: #ccc;\n  border-radius: 3px;\n}\n\n\/* \u00c9l\u00e9ment dans la liste *\/\n#suggestions div {\n  padding: 8px;\n  cursor: pointer;\n}\n\n\/* Hover + s\u00e9lection clavier *\/\n#suggestions div:hover,\n#suggestions div.highlight {\n  background-color: #e0e0e0;\n  color: #222;\n  font-weight: 500;\n}\n\n\n\/* Blocs d'infos *\/\n.bloc {\n  background: white;\n  border: 1px solid #ddd;\n  border-left: 4px solid #1A5641;\n  border-radius: 12px;\n  padding: 20px;\n  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.06);\n  margin: 20px auto;\n  display: none; \/* \u2705 \u00e0 ajouter ici *\/\n   max-width: 95vw; \/* \u2705 prend presque toute la largeur de l'\u00e9cran *\/\n  width: 100%;\n}\n\n#fiche-espece {\n  position: relative;\n  left: 50%;\n  transform: translateX(-50%);\n  width: 90vw;\n  max-width: min(90vw, 2000px); \/* \u2705 largeur fluide mais plafonn\u00e9e *\/\n  margin: 0;\n  padding: 0;\n  box-sizing: border-box;\n}\n\nh3 {\n  color: #1A5641;\n  border-bottom: 1px solid #eee;\n  padding-bottom: 5px;\n  margin-bottom: 15px;\n  text-transform: uppercase;\n}\n\n\n\/* =======================================================\n   \ud83d\udcca GRAPHIQUES + L\u00c9GENDES (VERSION PROPRE & RESPONSIVE)\n   ======================================================= *\/\n\n.charts-wrapper {\n  display: flex;\n  flex-wrap: wrap;\n  justify-content: center;\n  align-items: flex-start;\n  gap: 30px;\n  margin-top: 20px;\n}\n\n\/* Bloc complet : 1 camembert + sa l\u00e9gende *\/\n.chart-block {\n  flex: 1 1 420px;\n  max-width: 520px;\n  width: 100%;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n}\n\n\/* Conteneur du graphique *\/\n.chart-container {\n  width: 100%;\n  max-width: 500px;\n  min-width: 0;\n  height: auto !important;\n  overflow: visible !important;\n}\n\n\/* =====================\n   \ud83c\udfaf L\u00c9GENDE CENTR\u00c9E\n   ===================== *\/\n\n.custom-legend {\n  width: 100%;\n  max-width: 500px;\n  display: flex;\n  flex-direction: column;\n  align-items: center;              \/* centre les lignes *\/\n  gap: 8px;\n  margin: 14px auto 0 auto;         \/* centre le bloc *\/\n  font-size: 0.95rem;\n  line-height: 1.4;\n  text-align: center;\n}\n\n.legend-item {\n  display: flex;\n  align-items: center;\n  justify-content: center;          \/* centre pastille + texte *\/\n  gap: 8px;\n  line-height: 1.3;\n  width: fit-content;               \/* \u00e9vite les lignes trop larges *\/\n  max-width: 100%;\n}\n\n.legend-color {\n  width: 14px;\n  height: 14px;\n  border-radius: 50%;\n  flex-shrink: 0;\n  border: 1px solid #ccc;\n}\n\n\/* =====================\n   \ud83d\udcf1 MOBILE\n   ===================== *\/\n\n@media (max-width: 768px) {\n\n  .charts-wrapper {\n    flex-direction: column !important;\n    align-items: stretch !important;\n    gap: 24px !important;\n  }\n\n  .chart-block {\n    max-width: 100% !important;\n    width: 100% !important;\n  }\n\n  .chart-container,\n  .custom-legend {\n    max-width: 100% !important;\n    width: 100% !important;\n  }\n\n  .custom-legend {\n    margin: 10px auto 0 auto;\n    padding: 0 4px;\n    font-size: 0.9rem;\n  }\n\n  .legend-item {\n    justify-content: center;\n  }\n}\n\n\/* Bo\u00eetes UICN *\/\n.uicn-boxes {\n  display: flex;\n  flex-wrap: nowrap;\n  overflow-x: auto;\n  gap: 10px;\n  margin-top: 20px;\n  padding-bottom: 5px;\n  justify-content: center;\n}\n\n.uicn-box {\n  padding: 10px 18px;\n  border-radius: 999px;\n  background-color: #e0e0e0;\n  color: white;\n  font-weight: bold;\n  text-align: center;\n  min-width: 60px;\n  text-transform: uppercase;\n  transform: scale(0.85);\n  transition: transform 0.3s ease, background-color 0.3s ease;\n  flex-shrink: 0;\n}\n\n.uicn-box.active {\n  transform: scale(1.1);\n}\n\n.uicn-box.active.uicn-EX { background-color: #000; }\n.uicn-box.active.uicn-EW { background-color: purple; }\n.uicn-box.active.uicn-CR { background-color: #e74c3c; }\n.uicn-box.active.uicn-EN { background-color: #e67e22; }\n.uicn-box.active.uicn-VU { background-color: #f1c40f; color: #000; }\n.uicn-box.active.uicn-NT { background-color: #a3e4a1; color: #000; }\n.uicn-box.active.uicn-LC { background-color: #006400; }\n.uicn-box.active.uicn-DD { background-color: #ccc; color: #000; }\n\n\/* Rythme, Habitat, etc. *\/\n.rythme-wrapper {\n  display: flex;\n  justify-content: center;\n  gap: 20px;\n  flex-wrap: wrap;\n  margin-top: 20px;\n}\n\n.rythme-box {\n  flex: 1 1 250px;\n  background-color: #ffffff;\n  border: 1px solid #ddd;\n  border-left: 4px solid #1A5641;\n  border-radius: 12px;\n  padding: 20px;\n  text-align: center;\n  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.06);\n  min-width: 220px;\n}\n\n.rythme-box h4 {\n  margin-bottom: 10px;\n  color: #1A5641;\n  font-size: 1rem;\n  text-transform: uppercase;\n}\n\n.rythme-box .valeur {\n  font-size: 1.4rem;\n  font-weight: bold;\n  color: #222;\n}\n\n.rythme-liste {\n  display: flex;\n  flex-wrap: wrap;\n  justify-content: center;\n  gap: 20px;\n  text-align: center;\n  margin-top: 20px;\n}\n\n.rythme-liste > div {\n  flex: 1 1 200px;\n  min-width: 180px;\n}\n\n.rythme-titre {\n  font-size: 0.9rem;\n  color: #1A5641;\n  font-weight: 500;\n  text-transform: uppercase;\n  margin-bottom: 5px;\n}\n\n.rythme-valeur {\n  font-size: 1.1rem;\n  font-weight: bold;\n  color: #222;\n}\n\n.rythme-source {\n  font-size: 0.75rem;\n  color: #666;\n  margin-top: 2px;\n  font-weight: normal;\n}\n\n.rythme-valeur-wrapper {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n}\n\n#suggestions:hover {\n  overscroll-behavior: contain;\n  touch-action: pan-y;\n}\n\n.taxonomy-tree {\n  margin-top: 20px;\n  margin-left: 20px;\n  display: flex;\n  flex-direction: column;\n  font-size: 15px;\n}\n\n.niveau {\n  display: flex;\n  align-items: center;\n  position: relative;\n  height: 20px;\n  margin-bottom: 0;\n}\n\n.branche.horizontale {\n  height: 2px;\n  background-color: #ccc;\n  flex-shrink: 0;\n  margin-right: 10px;\n}\n\n\/* Largeur des traits horizontaux et indentation par niveau *\/\n.niveau-1 .branche.horizontale { width: 30px; margin-left: 0px; }\n.niveau-2 .branche.horizontale { width: 30px; margin-left: 20px; }\n.niveau-3 .branche.horizontale { width: 30px; margin-left: 40px; }\n.niveau-4 .branche.horizontale { width: 30px; margin-left: 60px; }\n.niveau-5 .branche.horizontale { width: 30px; margin-left: 80px; }\n.niveau-6 .branche.horizontale { width: 30px; margin-left: 100px; }\n\n\/* Lignes verticales exactement align\u00e9es avec les horizontales *\/\n.branche.verticale {\n  width: 2px;\n  height: 20px;\n  background-color: #ccc;\n  margin-top: -2px;\n}\n\n\/* D\u00e9calage des lignes verticales pour qu\u2019elles tombent au bon endroit *\/\n.branche.verticale.niveau-1 { margin-left: 30px; }\n.branche.verticale.niveau-2 { margin-left: 50px; }\n.branche.verticale.niveau-3 { margin-left: 70px; }\n.branche.verticale.niveau-4 { margin-left: 90px; }\n.branche.verticale.niveau-5 { margin-left: 110px; }\n\n.taxonomy-tree .label { font-size: 15px; }\n\n.flag { font-size: 18px; margin-right: 8px; }\n\n#image-wiki-test img {\n  width: 100%;\n  max-width: 400px;\n  height: auto;\n  object-fit: cover;\n  border-radius: 12px;\n  display: block;\n  margin: 0 auto;\n}\n\n.bloc img {\n  max-width: 95vw;\n  display: block;\n}\n\n\/* \ud83d\udd12 Emp\u00eache d'autres images parasites dans les blocs *\/\n.bloc:not(#image-wiki-test) > img {\n  display: none !important;\n}\n\n.nom-fr-latin { text-align: center; margin-bottom: 10px; }\n.nom-fr { font-size: 1.4rem; font-weight: 600; color: #222; }\n.nom-latin { font-size: 1.1rem; font-style: italic; color: #555; margin-top: -5px; }\n\n\/* Titre centr\u00e9 au-dessus des deux colonnes *\/\n.nom-principal-global { text-align: center; margin-bottom: 30px; }\n.nom-principal-global .nom-fr { font-size: 1.8rem; font-weight: 700; color: #222; margin-bottom: 5px; }\n.nom-principal-global .nom-latin { font-size: 1.2rem; font-style: italic; color: #666; }\n\n\/* Colonnes *\/\n.intro-cols { display: flex; flex-wrap: wrap; align-items: center; gap: 30px; }\n\n.col-gauche {\n  flex: 1 1 350px;\n  max-width: 400px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.col-droite { flex: 2 1 500px; max-width: 700px; }\n\n\/* Titres noms *\/\n#nom-principal { text-align: center; margin-bottom: 30px; }\n\n#nom-principal .nom-fr {\n  font-size: 1.8rem;\n  font-weight: 700;\n  margin-bottom: 5px;\n  color: #222;\n}\n\n#nom-principal .nom-latin {\n  font-size: 1.2rem;\n  font-style: italic;\n  color: #666;\n}\n\n.morpho-wrapper {\n  display: flex;\n  flex-wrap: wrap;\n  gap: 30px;\n  align-items: flex-start;\n  margin-top: 20px;\n}\n\n.morpho-texte { flex: 1 1 300px; min-width: 250px; }\n\n.morpho-image {\n  flex: 1 1 500px;\n  min-width: 300px;\n  max-width: 700px;\n}\n\n.morpho-image img {\n  width: 100%;\n  height: auto;\n  border-radius: 12px;\n  box-shadow: 0 4px 12px rgba(0,0,0,0.08);\n  object-fit: contain;\n}\n\n@media print {\n  body { background: white !important; color: black !important; font-size: 12px; line-height: 1.4; }\n\n  #fiche-espece { max-width: 100% !important; padding: 0 !important; box-shadow: none !important; }\n\n  .intro-cols, .morpho-wrapper, .rythme-liste, .charts-wrapper {\n    flex-direction: column !important;\n    align-items: flex-start !important;\n    gap: 0 !important;\n  }\n\n  .col-gauche, .col-droite, .morpho-image, .morpho-texte, .rythme-box {\n    max-width: 100% !important;\n    width: 100% !important;\n    flex: none !important;\n  }\n\n  .chart-container { width: 100% !important; height: auto !important; }\n\n  .uicn-boxes { flex-wrap: wrap !important; justify-content: flex-start !important; }\n\n  #btn-export-pdf,\n  .elementor-location-footer,\n  .elementor-location-header { display: none !important; }\n}\n\n.print-mode * {\n  box-shadow: none !important;\n  background: white !important;\n  color: black !important;\n  font-size: 12px !important;\n  line-height: 1.4 !important;\n}\n\n.print-mode {\n  width: 100% !important;\n  max-width: 800px !important;\n  margin: 0 auto !important;\n  padding: 0 !important;\n  background: white !important;\n}\n\n.print-mode .intro-cols,\n.print-mode .charts-wrapper,\n.print-mode .rythme-liste,\n.print-mode .morpho-wrapper { display: block !important; }\n\n.print-mode .col-gauche,\n.print-mode .col-droite,\n.print-mode .morpho-image,\n.print-mode .morpho-texte {\n  width: 100% !important;\n  max-width: 100% !important;\n  display: block !important;\n  flex: none !important;\n}\n\n.print-mode .uicn-boxes { flex-wrap: wrap !important; justify-content: flex-start !important; }\n\n.print-mode canvas {\n  display: block !important;\n  margin: 20px auto !important;\n  max-width: 100% !important;\n  height: auto !important;\n}\n\n.print-mode img {\n  max-width: 100% !important;\n  height: auto !important;\n  object-fit: contain !important;\n}\n\n.print-mode h3 { font-size: 1.1rem !important; color: #1A5641 !important; margin-top: 30px; }\n\nbody::before {\n  content: \"\";\n  position: fixed;\n  top: 0;\n  left: 0;\n  width: 100vw;\n  height: 100vh;\n  background-image: url('https:\/\/fiches-especes.simonpotier.fr\/wp-content\/uploads\/2026\/02\/alan_frijns-tropical-7459826-scaled.jpg');\n  background-size: cover;\n  background-position: center;\n  background-repeat: no-repeat;\n  z-index: -1;\n  opacity: 0.5;\n}\n\n@media (max-width: 768px) {\n  .intro-cols,\n  .morpho-wrapper,\n  .rythme-liste,\n  .charts-wrapper {\n    flex-direction: column !important;\n    align-items: stretch !important;\n  }\n\n  .col-gauche,\n  .col-droite,\n  .morpho-image,\n  .morpho-texte,\n  .rythme-box,\n  .chart-container {\n    max-width: 100% !important;\n    flex: none !important;\n    width: 100% !important;\n  }\n\n  .chart-container { width: 100% !important; height: auto !important; max-width: 100% !important; }\n\n  .rythme-liste { gap: 5px !important; margin-top: 10px !important; padding: 0 !important; }\n  .rythme-liste > div { padding: 4px 0 !important; margin: 0 !important; min-height: unset !important; height: auto !important; flex: none !important; }\n\n  .rythme-titre { font-size: 0.75rem !important; margin-bottom: 2px !important; line-height: 1.1 !important; }\n  .rythme-valeur { font-size: 1rem !important; line-height: 1.2 !important; margin: 0 !important; }\n  .rythme-source { font-size: 0.65rem !important; margin-top: 0 !important; line-height: 1 !important; }\n\n  #search-espece input[type=\"text\"] { max-width: 90%; }\n\n  .uicn-boxes { flex-wrap: wrap !important; justify-content: center; }\n\n  .bloc { margin: 10px 10px; padding: 15px; }\n\n  .taxonomy-tree { margin-left: 5px; font-size: 14px; }\n\n  #image-wiki-test img { max-width: 100%; height: auto; }\n\n  .morpho-image { width: 100% !important; max-width: 100% !important; }\n  .morpho-image img { width: 100% !important; height: auto !important; object-fit: contain !important; }\n}\n\nhtml, body { overflow-x: hidden !important; max-width: 100% !important; }\n\n.bloc,\n.chart-container,\n.morpho-wrapper,\n.morpho-image,\n.morpho-image img,\n#image-wiki-test img,\ncanvas,\nimg {\n  max-width: 100% !important;\n  width: 100% !important;\n  box-sizing: border-box !important;\n  height: auto !important;\n  object-fit: contain !important;\n  overflow-x: hidden !important;\n}\n\n.chart-container,\n.morpho-image,\n.morpho-image img,\n#image-wiki-test img,\ncanvas,\nimg {\n  display: block !important;\n  width: 100% !important;\n  max-width: 100% !important;\n  height: auto !important;\n  object-fit: contain !important;\n}\n\n\/* \u2705 S'assurer que les blocs prennent la hauteur de leur contenu *\/\n.chart-container,\n.morpho-image {\n  height: auto !important;\n  min-height: auto !important;\n  overflow: visible !important;\n}\n\nhtml, body, .bloc, .charts-wrapper {\n  overflow-x: hidden !important;\n  overflow-y: visible !important;\n}\n\n.col-gauche { flex: 0 0 20%; max-width: 20%; }\n.col-milieu { flex: 0 0 30%; max-width: 30%; }\n.col-droite { flex: 0 0 45%; max-width: 45%; }\n\n#carte-distribution img {\n  width: 100%;\n  max-width: 100%;\n  height: auto;\n  border-radius: 12px;\n  box-shadow: 0 4px 12px rgba(0,0,0,0.08);\n}\n\n@media (max-width: 768px) {\n  .intro-cols { flex-direction: column !important; }\n  .col-gauche, .col-milieu, .col-droite {\n    max-width: 100% !important;\n    width: 100% !important;\n    flex: none !important;\n  }\n}\n\n#bloc-regime { overflow: visible !important; max-height: none !important; }\n#bloc-regime .charts-wrapper { overflow: visible !important; max-height: none !important; }\n#bloc-regime .chart-container { overflow: visible !important; max-height: none !important; }\n\n.custom-legend {\n  display: flex;\n  flex-direction: column;\n  gap: 6px;\n  margin-top: 10px;\n  font-size: 0.95rem;\n}\n\n.legend-item {\n  display: flex;\n  align-items: center;\n  line-height: 1.2;\n}\n\n.legend-color {\n  width: 14px;\n  height: 14px;\n  border-radius: 50%;\n  margin-right: 8px;\n  flex-shrink: 0;\n  border: 1px solid #ccc;\n}\n\n.uicn-bar-container {\n  position: relative;\n  width: 100%;\n  max-width: 1000px;\n  margin: 20px auto;\n}\n\n.uicn-bar-image {\n  width: 100%;\n  height: auto;\n  display: block;\n}\n\n.uicn-arrow {\n  position: absolute;\n  top: -5px;\n  width: 20px;\n  height: 20px;\n  z-index: 10;\n}\n\n.uicn-arrow-head {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 0;\n  height: 0;\n  border-left: 10px solid transparent;\n  border-right: 10px solid transparent;\n  border-bottom: 15px solid black;\n  z-index: 2;\n}\n\n.uicn-arrow-bg {\n  position: absolute;\n  top: -5px;\n  left: -5px;\n  width: 20px;\n  height: 20px;\n  background: white;\n  border-radius: 4px;\n  z-index: 1;\n}\n\n.uicn-double-col {\n  display: flex;\n  flex-wrap: nowrap;\n  gap: 30px;\n  margin-top: 20px;\n  align-items: flex-start;\n  width: 100%;\n  box-sizing: border-box;\n}\n\n.uicn-col { box-sizing: border-box; }\n\n.uicn-gauche { flex: 0 0 75%; max-width: 75%; }\n.uicn-droite { flex: 0 0 25%; max-width: 25%; }\n\n.uicn-col h4 { font-size: 1.1rem; color: #1A5641; text-transform: uppercase; margin-bottom: 15px; }\n\n.uicn-droite p { margin-bottom: 15px; font-size: 1rem; }\n\n.annexes-ligne { display: flex; flex-direction: column; align-items: center; gap: 8px; }\n\n.annexes-labels,\n.annexes-valeurs {\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  gap: 60px;\n  width: 100%;\n  text-align: center;\n}\n\n.annexes-labels span { font-weight: bold; text-align: center; color: #333; }\n.annexes-valeurs span { text-align: center; }\n\n\/* On r\u00e9affirme la couleur blanche pour les badges *\/\n.badge-eu,\n.badge-cites { color: white !important; }\n\n.badge-eu {\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  width: 28px;\n  height: 28px;\n  border-radius: 50%;\n  font-size: 18px;\n  font-weight: 700;\n  color: white;\n  background-color: #ccc;\n  line-height: 1;\n  flex-shrink: 0;\n  flex-grow: 0;\n}\n\n\/* Couleurs par lettre *\/\n.badge-eu-a { background-color: #e74c3c; }\n.badge-eu-b { background-color: #f39c12; }\n.badge-eu-c { background-color: #4ECDC4; }\n.badge-eu-d { background-color: #8e44ad; }\n.badge-eu-na { background-color: #ccc; color: #666; }\n\n.annexes-valeurs span { flex: none; }\n\n.badge-cites {\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  width: 28px;\n  height: 28px;\n  border-radius: 50%;\n  font-size: 16px;\n  font-weight: 700;\n  color: #fff !important;\n  line-height: 1;\n  flex-shrink: 0;\n  flex-grow: 0;\n}\n\n\/* Couleurs par niveau *\/\n.badge-cites-i    { background-color: #c0392b; }\n.badge-cites-ii   { background-color: #d35400; }\n.badge-cites-iii  { background-color: #f1c40f; color: #000 !important; }\n.badge-cites-na   { background-color: #ccc; color: #666 !important; }\n\n@media (max-width: 768px) {\n  .uicn-double-col { flex-direction: column !important; align-items: stretch !important; gap: 20px !important; }\n  .uicn-col { max-width: 100% !important; width: 100% !important; flex: none !important; padding: 0 10px; box-sizing: border-box; }\n  .uicn-gauche, .uicn-centre, .uicn-droite { flex: none !important; max-width: 100% !important; width: 100% !important; text-align: center !important; }\n  .uicn-bar-container { max-width: 100% !important; margin: 0 auto !important; }\n  .annexes-labels, .annexes-valeurs { gap: 30px !important; }\n}\n\n.subfield { font-size: 0.75em; font-weight: 400; color: #4A403A; line-height: 1.4; }\n.subvalue { font-weight: 600; color: #000; }\n\n.morpho-texte p {\n  font-size: 1.1rem;\n  line-height: 1.3;\n  margin-bottom: 6px;\n  color: #333;\n  display: flex;\n  flex-wrap: wrap;\n}\n\n.morpho-texte p strong {\n  font-weight: normal;\n  color: inherit;\n  min-width: 240px;\n  display: inline-block;\n}\n\n#bloc-noms p {\n  font-size: 1.1rem;\n  line-height: 1.6;\n  margin-bottom: 10px;\n  color: #333;\n  display: flex;\n  align-items: center;\n}\n\n#bloc-noms .flag {\n  font-size: 1.4rem;\n  margin-right: 12px;\n  flex-shrink: 0;\n}\n\n#bloc-noms h3 {\n  font-size: 1.2rem;\n  color: #1A5641;\n  margin-bottom: 15px;\n  text-transform: uppercase;\n}\n\n\/* =======================================================\n   \u2705 OVERLAY CHARGEMENT (ajout)\n   ======================================================= *\/\n#loading-overlay {\n  position: fixed;\n  inset: 0;\n  z-index: 999999;\n  background: rgba(20, 18, 16, 0.88);\n  backdrop-filter: blur(4px);\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  padding: 20px;\n}\n#loading-card {\n  width: min(560px, 92vw);\n  background: rgba(255,255,255,0.95);\n  border-radius: 16px;\n  box-shadow: 0 10px 40px rgba(0,0,0,0.35);\n  padding: 22px 22px 18px 22px;\n  text-align: center;\n}\n#loading-title {\n  font-weight: 700;\n  font-size: 1.1rem;\n  margin-bottom: 10px;\n  color: #222;\n}\n#loading-progress-wrap {\n  width: 100%;\n  background: #eaeaea;\n  border-radius: 999px;\n  overflow: hidden;\n  height: 14px;\n  box-shadow: inset 0 1px 3px rgba(0,0,0,0.15);\n}\n#loading-progress-bar {\n  width: 0%;\n  height: 100%;\n  background: linear-gradient(90deg, #4ECDC4, #615241);\n  transition: width 0.25s ease;\n}\n#loading-percent { margin-top: 10px; font-weight: 700; color: #222; }\n#loading-funline { margin-top: 10px; font-size: 0.95rem; color: #444; min-height: 1.4em; }\n#loading-sub { margin-top: 10px; font-size: 0.85rem; color: #666; }\n.loading-hidden { display: none !important; }\n\n\n.infos-carte-distribution {\n  margin-top: 12px;\n  display: flex;\n  flex-wrap: wrap;\n  gap: 20px;\n  font-size: 1rem;\n  color: #444;\n  justify-content: center;\n}\n\n@media (max-width: 768px) {\n  .infos-carte-distribution {\n    flex-direction: column;\n    align-items: center;\n    text-align: center;\n    gap: 8px;\n  }\n\n  .infos-carte-distribution > div {\n    width: 100%;\n    text-align: center;\n  }\n}\n\n\n\/* ===== CARTE DISTRIBUTION ===== *\/\n#carte-distribution,\n.carte-distribution-wrap,\n.carte-image-wrap {\n  width: 100% !important;\n  max-width: 100% !important;\n  box-sizing: border-box !important;\n}\n\n.carte-image-wrap {\n  text-align: center;\n}\n\n.carte-image {\n  display: block !important;\n  width: 100% !important;\n  max-width: 100% !important;\n  height: auto !important;\n  margin: 0 auto !important;\n  border-radius: 12px;\n  box-shadow: 0 4px 12px rgba(0,0,0,0.08);\n}\n\n.carte-message {\n  color: #666;\n  font-style: italic;\n  text-align: center;\n  margin: 20px 0;\n}\n\n.infos-carte-distribution {\n  margin-top: 12px;\n  display: flex;\n  flex-wrap: wrap;\n  justify-content: center;\n  gap: 20px;\n  font-size: 1rem;\n  color: #444;\n  text-align: center;\n}\n\n.infos-carte-distribution > div {\n  text-align: center;\n}\n\n\/* ===== MOBILE ===== *\/\n@media (max-width: 768px) {\n  .intro-cols {\n    flex-direction: column !important;\n    gap: 20px !important;\n  }\n\n  .col-gauche,\n  .col-milieu,\n  .col-droite {\n    flex: 0 0 100% !important;\n    width: 100% !important;\n    max-width: 100% !important;\n  }\n\n  #carte-distribution {\n    width: 100% !important;\n    max-width: 100% !important;\n  }\n\n  .carte-distribution-wrap {\n    width: 100% !important;\n    max-width: 100% !important;\n  }\n\n  .carte-image-wrap {\n    width: 100% !important;\n    max-width: 100% !important;\n    margin: 0 !important;\n  }\n\n  .carte-image {\n    width: 100% !important;\n    max-width: 100% !important;\n  }\n\n  .infos-carte-distribution {\n    flex-direction: column !important;\n    align-items: center !important;\n    justify-content: center !important;\n    gap: 8px !important;\n    text-align: center !important;\n  }\n\n  .infos-carte-distribution > div {\n    width: 100% !important;\n    text-align: center !important;\n  }\n}\n\n\n.traits-climate-charts {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  gap: 24px;\n  margin-top: 35px;\n  width: 100%;\n}\n\n.trait-chart-card {\n  background: #ffffff;\n  border-radius: 14px;\n  padding: 18px;\n  box-shadow: 0 4px 18px rgba(0,0,0,0.08);\n  min-height: 340px;\n}\n\n.trait-chart-card h4 {\n  margin: 0 0 15px;\n  color: #1A5641;\n  font-size: 16px;\n  font-weight: 700;\n  text-transform: uppercase;\n}\n\n.trait-chart-card canvas {\n  width: 100% !important;\n  height: 280px !important;\n}\n\n@media (max-width: 768px) {\n  .traits-climate-charts {\n    grid-template-columns: 1fr;\n  }\n}\n\n\n\n\/* SPES : trois sections morphologiques *\/\n#morpho-mesures .spes-morpho-grid {\n  display: grid;\n  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 2.5fr);\n  gap: 22px;\n  align-items: stretch;\n  margin-top: 20px;\n}\n#morpho-mesures .spes-morpho-card {\n  min-width: 0;\n  padding: 18px;\n  border: 1px solid #e8ece9;\n  border-radius: 12px;\n  background: #fff;\n}\n#morpho-mesures .spes-morpho-card h4 {\n  color: #1A5641;\n  font-size: 1rem;\n  text-transform: uppercase;\n  text-align: center;\n  margin: 0 0 18px;\n}\n#morpho-mesures .spes-morpho-envergure,\n#morpho-mesures .spes-morpho-poids {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  text-align: center;\n}\n#morpho-mesures .spes-silhouette {\n  width: 100% !important;\n  max-width: 260px !important;\n  height: 150px !important;\n  object-fit: contain !important;\n  margin: 8px auto 16px;\n}\n#morpho-mesures .spes-morpho-value {\n  font-size: 1.4rem;\n  font-weight: 700;\n  color: #222;\n  line-height: 1.5;\n}\n#morpho-mesures .spes-morpho-note { color: #666; font-size: .85rem; }\n#morpho-mesures .spes-morpho-mesures-layout {\n  display: grid;\n  grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr);\n  align-items: center;\n  gap: 15px;\n}\n#morpho-mesures .spes-morpho-mesures-layout .morpho-texte { min-width: 0; }\n#morpho-mesures .spes-morpho-mesures-layout .morpho-texte p {\n  font-size: .88rem;\n  display: block;\n  margin: 0 0 7px;\n  line-height: 1.45;\n}\n#morpho-mesures .spes-morpho-mesures-layout .morpho-texte p strong {\n  min-width: 0;\n  display: inline;\n}\n#morpho-mesures .spes-morpho-mesures-layout .morpho-image {\n  min-width: 0;\n  max-width: 100%;\n  width: 100%;\n}\n#morpho-mesures .spes-morpho-mesures-layout .morpho-image img {\n  width: 100% !important;\n  height: auto !important;\n  object-fit: contain !important;\n}\n@media (max-width: 1100px) {\n  #morpho-mesures .spes-morpho-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }\n  #morpho-mesures .spes-morpho-mensurations { grid-column: 1 \/ -1; }\n}\n@media (max-width: 768px) {\n  #morpho-mesures .spes-morpho-grid { grid-template-columns: 1fr; }\n  #morpho-mesures .spes-morpho-mensurations { grid-column: auto; }\n  #morpho-mesures .spes-morpho-mesures-layout { grid-template-columns: 1fr; }\n}\n\n\n\/* SPES : plages de masse corporelle *\/\n#morpho-mesures .spes-poids-chart { width:100%; max-width:360px; margin:10px auto 0; text-align:left; }\n#morpho-mesures .spes-poids-row { margin:0 0 23px; }\n#morpho-mesures .spes-poids-heading { display:flex; justify-content:space-between; align-items:baseline; flex-wrap:wrap; gap:4px 10px; margin-bottom:8px; font-size:.9rem; }\n#morpho-mesures .spes-poids-heading strong { font-weight:600; color:#222; }\n#morpho-mesures .spes-poids-track { position:relative; height:18px; background:#edf0ed; border-radius:10px; }\n#morpho-mesures .spes-poids-range { position:absolute; top:2px; height:14px; border-radius:8px; min-width:0; }\n#morpho-mesures .spes-poids-range::before, #morpho-mesures .spes-poids-range::after { content:\"\"; position:absolute; top:3px; width:8px; height:8px; border-radius:50%; background:inherit; }\n#morpho-mesures .spes-poids-range::before { left:-3px; }\n#morpho-mesures .spes-poids-range::after { right:-3px; }\n#morpho-mesures .spes-poids-point { position:absolute; top:3px; width:12px; height:12px; border-radius:50%; transform:translateX(-50%); }\n#morpho-mesures .spes-poids-femelle { background:#b18a63; }\n#morpho-mesures .spes-poids-male { background:#1a5641; }\n#morpho-mesures .spes-poids-inconnu { background:#71818a; }\n#morpho-mesures .spes-poids-axis { display:flex; justify-content:space-between; color:#777; font-size:.75rem; margin-top:8px; }\n#morpho-mesures .spes-poids-empty { color:#777; text-align:center; }\n\n\n\/* \u00c9chelle dynamique de l'envergure : trait si valeur unique, segment si plage *\/\n#morpho-mesures .spes-wingspan-scale {\n  width: 100%;\n  max-width: 340px;\n  margin: 20px auto 16px;\n  min-height: 70px;\n}\n#morpho-mesures .spes-wingspan-scale svg {\n  display: block;\n  width: 100%;\n  height: auto;\n  overflow: visible;\n}\n\n\n\/* SPES \u2014 AVONICHE : strat\u00e9gies d'acquisition alimentaire (barres) *\/\n#bloc-regime #chart-avoniche.spes-acquisition-chart {\n  width: 100% !important;\n  max-width: 100% !important;\n  height: auto !important;\n  overflow: visible !important;\n  box-sizing: border-box;\n}\n#chart-avoniche .spes-acquisition-row { margin: 0 0 13px; }\n#chart-avoniche .spes-acquisition-head {\n  display: flex; justify-content: space-between; align-items: baseline;\n  gap: 8px; margin-bottom: 5px; line-height: 1.35;\n}\n#chart-avoniche .spes-acquisition-label { font-size: .84rem; color: #303a35; overflow-wrap: anywhere; }\n#chart-avoniche .spes-acquisition-value { font-size: .85rem; font-weight: 700; white-space: nowrap; color: #1A5641; }\n#chart-avoniche .spes-acquisition-track { width: 100%; height: 12px; background: #edf1ee; border-radius: 999px; overflow: hidden; }\n#chart-avoniche .spes-acquisition-fill { height: 100%; border-radius: 999px; }\n#chart-avoniche .spes-acquisition-note { font-size: .75rem; color: #68746d; margin-top: 12px; line-height: 1.4; }\n@media (max-width: 768px) {\n  #chart-avoniche .spes-acquisition-label { font-size: .8rem; }\n}\n\n\n\/* SPES \u2014 Sommaire de navigation de la fiche *\/\n\/* Navigation SPES : onglets fins int\u00e9gr\u00e9s \u00e0 la fiche, comme la maquette *\/\n#spes-nav-fiche {\n  display: none;\n  position: relative;\n  top: auto;\n  z-index: 990;\n  width: 100%;\n  margin: 8px 0 20px;\n  padding: 0 14px;\n  background: #fff;\n  border: 1px solid #e3e9e5;\n  border-radius: 10px;\n  box-shadow: 0 2px 8px rgba(0,0,0,.035);\n  box-sizing: border-box;\n}\n#spes-nav-fiche .spes-nav-links {\n  display: flex;\n  align-items: stretch;\n  justify-content: space-between;\n  gap: 0;\n  overflow-x: auto;\n  overflow-y: hidden;\n  scrollbar-width: thin;\n  -webkit-overflow-scrolling: touch;\n}\n#spes-nav-fiche a {\n  position: relative;\n  display: inline-flex;\n  flex: 0 0 auto;\n  align-items: center;\n  justify-content: center;\n  min-height: 60px;\n  padding: 13px 16px;\n  border: 0;\n  border-bottom: 3px solid transparent;\n  border-radius: 0;\n  background: transparent !important;\n  color: #365e50;\n  font-family: 'Inter', sans-serif;\n  font-size: 19px;\n  font-weight: 500;\n  line-height: 1.3;\n  text-decoration: none;\n  white-space: nowrap;\n  box-shadow: none !important;\n  transition: color .18s ease, border-color .18s ease;\n}\n#spes-nav-fiche a:hover,\n#spes-nav-fiche a:focus-visible {\n  color: #143c2d;\n  background: #f8faf8 !important;\n}\n#spes-nav-fiche a.is-active {\n  color: #143c2d;\n  font-weight: 700;\n  border-bottom-color: #1a5641;\n  background: transparent !important;\n}\n#fiche-espece #bloc-intro-complet,\n#fiche-espece .spes-nav-anchor,\n#fiche-espece #morpho-mesures,\n#fiche-espece #bloc-uicn,\n#fiche-espece #bloc-reproduction,\n#fiche-espece #bloc-rythme { scroll-margin-top: 160px; }\n@media (max-width: 900px) {\n  #spes-nav-fiche .spes-nav-links { justify-content: flex-start; }\n  #spes-nav-fiche a { padding: 14px 16px; font-size: 17px; }\n}\n@media (max-width: 767px) {\n  #spes-nav-fiche { top: auto; padding: 0 6px; }\n  #spes-nav-fiche a { min-height: 52px; padding: 13px 14px; font-size: 16px; }\n}\n\/* Positionnement fixe contr\u00f4l\u00e9 par JS : fonctionne m\u00eame si Elementor bloque sticky *\/\n#spes-nav-fiche.spes-nav-fixed {\n  position: fixed !important;\n  top: var(--spes-nav-top, 0px) !important;\n  left: var(--spes-nav-left, 0px) !important;\n  width: var(--spes-nav-width, 100%) !important;\n  margin: 0 !important;\n  z-index: 99990 !important;\n  box-shadow: 0 4px 16px rgba(0,0,0,.10);\n}\n@media print { #spes-nav-fiche { display:none !important; } }\n\n\n\/* SPES - langue de la fiche *\/\n#spes-language-switch {display:flex;justify-content:flex-end;gap:3px;max-width:min(90vw,2000px);margin:12px auto 0;font-family:Inter,sans-serif}\n#spes-language-switch button {cursor:pointer;border:1px solid #1a5641;background:#fff;color:#1a5641;font-size:16px;font-weight:650;padding:9px 17px;line-height:1.3;min-width:56px}\n#spes-language-switch button:first-child {border-radius:8px 0 0 8px}\n#spes-language-switch button:last-child {border-radius:0 8px 8px 0}\n#spes-language-switch button[aria-pressed=\"true\"] {background:#1a5641;color:#fff}\n@media print {#spes-language-switch {display:none!important}}\nhtml[data-spes-language=\"en\"] .spes-mature-label {font-size:0}\nhtml[data-spes-language=\"en\"] .spes-mature-label::after {content:\"mature individuals\";font-size:0.95rem}\n<\/style>\n\n\n\n\n\n\n\n\n\n\n<!-- \u2705 Overlay de chargement (ajout) -->\n<div id=\"loading-overlay\" class=\"loading-hidden\" aria-live=\"polite\" aria-busy=\"true\">\n  <div id=\"loading-card\">\n    <div id=\"loading-title\">Pr\u00e9paration des fiches\u2026<\/div>\n    <div id=\"loading-progress-wrap\" role=\"progressbar\" aria-valuemin=\"0\" aria-valuemax=\"100\" aria-valuenow=\"0\">\n      <div id=\"loading-progress-bar\"><\/div>\n    <\/div>\n    <div id=\"loading-percent\">0%<\/div>\n    <div id=\"loading-funline\">On apprivoise les donn\u00e9es\u2026<\/div>\n    <div id=\"loading-sub\">La recherche s\u2019active \u00e0 100%.<\/div>\n  <\/div>\n<\/div>\n\n<!-- S\u00e9lecteur de langue SPES (ne change pas les endpoints ni le CSV) -->\n<div id=\"spes-language-switch\" role=\"group\" aria-label=\"Langue de la fiche \/ Species sheet language\">\n  <button type=\"button\" data-spes-lang=\"fr\" aria-pressed=\"true\">FR<\/button>\n  <button type=\"button\" data-spes-lang=\"en\" aria-pressed=\"false\">EN<\/button>\n<\/div>\n\n<!-- \u2705 Conteneur principal de toute la fiche -->\n<div id=\"fiche-espece\">\n\n  <!-- Navigation interne : tous les blocs restent visibles -->\n  <nav id=\"spes-nav-fiche\" aria-label=\"Sommaire de la fiche\">\n    <div class=\"spes-nav-links\">\n      <a href=\"#bloc-intro-complet\">Description<\/a>\n      <a href=\"#morpho-mesures\">Morphologie<\/a>\n      <a href=\"#bloc-uicn\">Protection<\/a>\n      <a href=\"#spes-ancre-regime\">R\u00e9gime alimentaire<\/a>\n      <a href=\"#spes-ancre-strategies\">Strat\u00e9gies alimentaires<\/a>\n      <a href=\"#bloc-reproduction\">Reproduction<\/a>\n      <a href=\"#bloc-rythme\">Traits fonctionnels<\/a>\n    <\/div>\n  <\/nav>\n\n\n  <!-- Bloc contenant image + infos -->\n  <div class=\"bloc\" id=\"bloc-intro-complet\">\n    <!-- Nom centr\u00e9 au-dessus -->\n    <div id=\"nom-principal\" class=\"nom-principal-global\"><\/div>\n\n    <!-- Colonnes c\u00f4te \u00e0 c\u00f4te -->\n    <div class=\"intro-cols\">\n      <div class=\"col-gauche\">\n        <div id=\"image-wiki-test\" style=\"overflow: hidden; display: none;\"><\/div>\n      <\/div>\n      <div class=\"col-milieu\">\n        <div id=\"bloc-noms\"><\/div>\n        <div id=\"bloc-classification\"><\/div>\n      <\/div>\n      <div class=\"col-droite\">\n        <div id=\"carte-distribution\"><\/div>\n      <\/div>\n    <\/div>\n  <\/div> <!-- Fin bloc intro -->\n\n  <div class=\"bloc\" id=\"morpho-mesures\"><\/div>\n  <div class=\"bloc\" id=\"bloc-uicn\"><\/div>\n  <div class=\"bloc\" id=\"bloc-regime\"><\/div>\n  <div class=\"bloc\" id=\"bloc-rythme\"><\/div>\n\n<\/div> <!-- Fermeture de #fiche-espece -->\n\n<script>\n  \/\/ Injecte le viewport si absent\n  if (!document.querySelector('meta[name=\"viewport\"]')) {\n    const meta = document.createElement('meta');\n    meta.name = \"viewport\";\n    meta.content = \"width=device-width, initial-scale=1\";\n    document.head.appendChild(meta);\n  }\n<\/script>\n\n<script>\ndocument.addEventListener('DOMContentLoaded', function () {\n\n  \/\/ =======================\n  \/\/ \ud83d\udd01 Traductions UICN\n  \/\/ =======================\n  function traduireCategorieUICN(valeur) {\n    const map = {\n      'EX': '\u00c9teint',\n      'EW': '\u00c9teint dans la nature',\n      'CR': 'En danger critique',\n      'EN': 'En danger',\n      'VU': 'Vuln\u00e9rable',\n      'NT': 'Quasi menac\u00e9e',\n      'LC': 'Pr\u00e9occupation mineure',\n      'DD': 'Donn\u00e9es insuffisantes',\n      'NA': 'Inconnu'\n    };\n    return map[valeur?.toUpperCase()] || 'Inconnu';\n  }\n\nfunction traduireTendancePopulation(valeur) {\n  if (!valeur) return 'Inconnue';\n\n  const clean = valeur\n    .toString()\n    .normalize(\"NFD\")\n    .replace(\/[\\u0300-\\u036f]\/g, \"\")\n    .replace(\/[\\r\\n\\t]\/g, \"\")\n    .trim()\n    .toUpperCase();\n\n  console.log(\"Nettoy\u00e9 :\", clean); \/\/ debug\n\n  const map = {\n    'DECREASING': 'En baisse',\n    'INCREASING': 'En hausse',\n    'STABLE': 'Stable',\n    'UNKNOWN': 'Inconnue',\n    'NA': 'Inconnue'\n  };\n\n  return map[clean] || 'Inconnue';\n}\n\n\n\n  const input = document.querySelector('#search-espece input[type=\"text\"]');\n  const suggestionBox = document.createElement('div');\n  suggestionBox.id = 'suggestions';\n  document.getElementById('search-espece').appendChild(suggestionBox);\n\n  const loadingInfo = document.createElement('div');\n  loadingInfo.id = 'chargement-especes';\n  loadingInfo.style.textAlign = 'center';\n  loadingInfo.style.marginTop = '8px';\n  loadingInfo.style.fontSize = '14px';\n  loadingInfo.style.color = '#ffffff';\n  loadingInfo.style.textShadow = '0 1px 3px rgba(0,0,0,0.6)';\n  document.getElementById('search-espece').appendChild(loadingInfo);\n\n  function removeDiacritics(str) {\n    return str.normalize(\"NFD\").replace(\/[\\u0300-\\u036f]\/g, \"\").toLowerCase();\n  }\n\n  function decodeTexte(str) {\n    const textarea = document.createElement('textarea');\n    textarea.innerHTML = str;\n    return textarea.value;\n  }\n\n  \/\/ Emp\u00eache le scroll de la page quand on scroll dans la liste\n  suggestionBox.addEventListener('wheel', function (e) {\n    if (\n      (e.deltaY > 0 && suggestionBox.scrollTop + suggestionBox.clientHeight < suggestionBox.scrollHeight) ||\n      (e.deltaY < 0 && suggestionBox.scrollTop > 0)\n    ) {\n      e.stopPropagation();\n    }\n  }, { passive: true });\n\n  const blocs = {\n    noms: document.getElementById('bloc-noms'),\n    classification: document.getElementById('bloc-classification'),\n    uicn: document.getElementById('bloc-uicn'),\n    rythme: document.getElementById('bloc-rythme'),\n    regime: document.getElementById('bloc-regime'),\n    reproduction: null,\n    morpho: document.getElementById('morpho-mesures'),\n    image: document.getElementById('image-wiki-test')\n  };\n\n  let fuse;\n  let donneesCSV = [];\n\n  \/\/ =======================================================\n  \/\/ \u2705 Overlay + phrases lentes (ind\u00e9pendantes du %)\n  \/\/ =======================================================\n  const overlay = document.getElementById('loading-overlay');\n  const bar = document.getElementById('loading-progress-bar');\n  const pctEl = document.getElementById('loading-percent');\n  const funEl = document.getElementById('loading-funline');\n  const progressWrap = document.getElementById('loading-progress-wrap');\n\n  const funLinesFR = [\n    \"Chargement de la taille du bec\u2026 (\u00e7a picote un peu).\",\n    \"R\u00e9cup\u00e9ration de la couleur des plumes\u2026 nuancier valid\u00e9 par un paon.\",\n    \"Synchronisation des chants\u2026 volume \u00e0 11.\",\n    \"Indexation des esp\u00e8ces\u2026 elles se poussent dans la file.\",\n    \"Pr\u00e9paration des cartes de distribution\u2026 sans perdre le Nord.\",\n    \"On nourrit le moteur de recherche\u2026 avec des graines de donn\u00e9es.\",\n    \"V\u00e9rification des migrations\u2026 escales non incluses.\",\n    \"Derniers r\u00e9glages\u2026 on compte les plumes une par une.\"\n  ];\n  const funLinesEN = [\n    \"Measuring beak length\u2026 this might get a little prickly.\",\n    \"Checking feather colours\u2026 palette approved by a peacock.\",\n    \"Synchronising bird songs\u2026 volume turned up to 11.\",\n    \"Indexing species\u2026 they're lining up one by one.\",\n    \"Preparing distribution maps\u2026 keeping our bearings.\",\n    \"Feeding the search engine\u2026 with seeds of data.\",\n    \"Checking migrations\u2026 stopovers not included.\",\n    \"Final adjustments\u2026 counting feathers one by one.\"\n  ];\n  function loadingLanguage() {\n    const current = document.documentElement.getAttribute('data-spes-language');\n    if (current === 'fr' || current === 'en') return current;\n    try { return localStorage.getItem('spes-language') === 'en' ? 'en' : 'fr'; }\n    catch (_) { return 'fr'; }\n  }\n\n  let funTimer = null;\n  let lastFunPick = -1;\n\n  function pickFunLine() {\n    if (!funEl) return;\n    const lines = loadingLanguage() === 'en' ? funLinesEN : funLinesFR;\n    let idx = Math.floor(Math.random() * lines.length);\n    if (idx === lastFunPick) idx = (idx + 1) % lines.length;\n    lastFunPick = idx;\n    funEl.textContent = lines[idx];\n  }\n\n  window.spesUpdateLoadingLine = pickFunLine;\n\n  function startFunRotation() {\n    stopFunRotation();\n    pickFunLine();\n    funTimer = setInterval(() => pickFunLine(), 2500); \/\/ \u2705 cadence lisible\n  }\n\n  function stopFunRotation() {\n    if (funTimer) { clearInterval(funTimer); funTimer = null; }\n  }\n\n  function setSearchEnabled(enabled) {\n    if (!input) return;\n    input.disabled = !enabled;\n    if (!enabled) suggestionBox.innerHTML = '';\n  }\n\n  function showOverlay() {\n    overlay.classList.remove('loading-hidden');\n    overlay.setAttribute('aria-busy', 'true');\n    setSearchEnabled(false);\n    startFunRotation();\n  }\n\n  function hideOverlay() {\n    overlay.classList.add('loading-hidden');\n    overlay.setAttribute('aria-busy', 'false');\n    setSearchEnabled(true);\n    stopFunRotation();\n  }\n\n  function setProgress(pct) {\n    const p = Math.max(0, Math.min(100, Math.round(pct)));\n    if (bar) bar.style.width = p + '%';\n    if (pctEl) pctEl.textContent = p + '%';\n    if (progressWrap) progressWrap.setAttribute('aria-valuenow', String(p));\n    if (loadingInfo) loadingInfo.textContent = p < 100 ? `Chargement : ${p}%` : '';\n  }\n\n\/\/ =======================================================\n\/\/ SPES - Chargement d'une seule esp\u00e8ce \u00e0 la demande\n\/\/ =======================================================\n\nconst cacheEspeces = new Map();\nlet derniereRequeteEspece = 0;\n\nasync function chargerFicheEspece(especeIndex) {\n\n  const numeroRequete = ++derniereRequeteEspece;\n\n  const nomScientifique = (\n    especeIndex.scientific ||\n    especeIndex.latin ||\n    ''\n  ).trim();\n\n  if (!nomScientifique) {\n    console.error('Nom scientifique manquant.');\n    return;\n  }\n\n  try {\n\n    let espece = cacheEspeces.get(nomScientifique);\n\n    if (!espece) {\n\n      const url = window.FICHES_API.restUrl +\n        'fiches\/v1\/espece?scientific=' +\n        encodeURIComponent(nomScientifique);\n\n      const response = await fetch(url, {\n        credentials: 'same-origin',\n        headers: {\n          'Accept': 'application\/json',\n          'X-WP-Nonce': window.FICHES_API.nonce\n        }\n      });\n\n      if (!response.ok) {\n        throw new Error('Erreur API : ' + response.status);\n      }\n\n      const resultat = await response.json();\n\n      if (!resultat.ok || !resultat.item) {\n        throw new Error('Donn\u00e9es de l\u2019esp\u00e8ce introuvables.');\n      }\n\n      espece = resultat.item;\n\n      cacheEspeces.set(nomScientifique, espece);\n    }\n\n    if (numeroRequete !== derniereRequeteEspece) {\n      return;\n    }\n\n    afficherEspece(espece);\n\n  } catch (erreur) {\n\n    console.error('Erreur de chargement SPES :', erreur);\n\n    if (numeroRequete === derniereRequeteEspece) {\n      alert('Impossible de charger cette fiche esp\u00e8ce.');\n    }\n  }\n}\n\n  \/\/ =======================================================\n  \/\/ \u2705 Chargement API (ton code conserv\u00e9, avec overlay)\n  \/\/ =======================================================\n  async function loadDataFromApi() {\n    showOverlay();\n    setProgress(0);\n\n    \/\/ \u26a0\ufe0f requis : window.FICHES_API inject\u00e9 par le shortcode [fiches_rest_nonce]\n    if (!window.FICHES_API || !window.FICHES_API.restUrl || !window.FICHES_API.nonce) {\n      console.error(\"FICHES_API absent (restUrl\/nonce). Assure-toi que [fiches_rest_nonce] est pr\u00e9sent sur la page (widget Shortcode) et plac\u00e9 au-dessus du widget HTML.\");\n      if (funEl) funEl.textContent = \"API introuvable\u2026 impossible de charger les oiseaux.\";\n      return;\n    }\n\n    \/\/ ---- 1) Charger l'INDEX l\u00e9ger ----\n    const indexPerPage = 2000;\n    let indexPage = 1;\n    let indexTotal = null;\n    let indexLoaded = 0;\n    let indexAll = [];\n\n    while (true) {\n      const urlIndex = window.FICHES_API.restUrl + `fiches\/v1\/index?page=${indexPage}&per_page=${indexPerPage}`;\n\n      let resp;\n      try {\n        resp = await fetch(urlIndex, {\n          method: 'GET',\n          credentials: 'same-origin',\n          headers: {\n            'Accept': 'application\/json',\n            'X-WP-Nonce': window.FICHES_API.nonce\n          }\n        });\n      } catch (e) {\n        console.error(\"Erreur r\u00e9seau fetch INDEX:\", e);\n        if (funEl) funEl.textContent = \"Connexion capricieuse\u2026\";\n        return;\n      }\n\n      if (!resp.ok) {\n        const rawErr = await resp.text().catch(() => '');\n        console.error(\"API index error HTTP\", resp.status, rawErr);\n        if (funEl) funEl.textContent = \"Erreur serveur sur l\u2019index\u2026\";\n        return;\n      }\n\n      let json;\n      try {\n        json = await resp.json();\n      } catch (e) {\n        console.error(\"API index JSON parse error:\", e);\n        if (funEl) funEl.textContent = \"Index illisible\u2026\";\n        return;\n      }\n\n      const items = Array.isArray(json?.items) ? json.items : [];\n      if (typeof json?.total === 'number') indexTotal = json.total;\n\n      indexAll = indexAll.concat(items);\n      indexLoaded += items.length;\n\n      \/\/ 0 -> 30%\n      if (indexTotal && indexTotal > 0) {\n        const pctIndex = (indexLoaded \/ indexTotal) * 30;\n        setProgress(pctIndex);\n      } else {\n        setProgress(Math.min(30, indexPage * 5));\n      }\n\n      if (items.length < indexPerPage) break;\n      indexPage++;\n      if (indexPage > 2000) break;\n    }\n\n    \/\/ Index -> donn\u00e9es\n    donneesCSV = (indexAll || []).filter(ligne => (ligne.french || '').trim());\n\n    donneesCSV = donneesCSV.map(ligne => ({\n      ...ligne,\n      search_normalized: [\n        ligne.french,\n        ligne.scientific,\n        ligne.latin,\n        ligne.english2,\n        ligne.spanish,\n        ligne.german\n      ]\n        .filter(Boolean)\n        .map(removeDiacritics)\n        .join(' ')\n    }));\n\n    \/\/ \u2705 On ne rend PAS la recherche active ici : on attend 100%\n    function creerIndexRecherche() {\n      const en = document.documentElement.getAttribute('data-spes-language') === 'en';\n      fuse = new Fuse(donneesCSV, {\n        keys: en ? ['english2', 'latin', 'scientific'] : ['french', 'latin', 'scientific'],\n        threshold: 0.4,\n        ignoreLocation: true\n      });\n    }\n    creerIndexRecherche();\n    window.addEventListener('spes-language-changed', () => {\n      creerIndexRecherche();\n      suggestionBox.innerHTML = '';\n      currentSuggestions = [];\n      selectedIndex = -1;\n      const current = window.spesCurrentSpecies;\n      if (current && input) input.value = decodeTexte((document.documentElement.getAttribute('data-spes-language') === 'en' ? current.english2 : current.french) || current.latin || '');\n      if (input && input.value && !current) input.dispatchEvent(new Event('input', { bubbles: true }));\n    });\n\n    setProgress(30); \n\n    setProgress(100);\n\n    \/\/ \u2705 Activer recherche + masquer overlay\n    setTimeout(() => {\n      hideOverlay();\n      if (loadingInfo) loadingInfo.textContent = '';\n      chargerEspeceDepuisURL();\n    }, 350);\n  }\n\n  \/\/ Lance le chargement\n  loadDataFromApi();\n\n  \/\/ =======================================================\n  \/\/ \u2705 TON afficherEspece COMPLET (restaur\u00e9 int\u00e9gralement)\n  \/\/ =======================================================\n  function afficherEspece(espece) {\n    window.spesCurrentSpecies = espece;\n    const blocIntro = document.getElementById('bloc-intro-complet');\n    if (blocIntro) blocIntro.style.display = 'block';\n    const navFiche = document.getElementById('spes-nav-fiche');\n    if (navFiche) { navFiche.style.display = 'block'; window.dispatchEvent(new Event('resize')); }\n\n    \/\/ Afficher tous les blocs disponibles\n    Object.values(blocs).forEach(b => {\n      if (b) b.style.display = 'block';\n    });\n\n    \/\/ \ud83c\udff7\ufe0f Bloc #nom-principal (nom FR + latin)\n    const nomPrincipal = document.getElementById('nom-principal');\n    if (nomPrincipal) {\n      nomPrincipal.innerHTML = `\n        <div class=\"nom-fr\">${espece.french || '\u2013'}<\/div>\n        <div class=\"nom-latin\"><em>${espece.latin || '\u2013'}<\/em><\/div>\n      `;\n    }\n\n    \/\/ \ud83c\udf0d Bloc des noms (anglais, espagnol, allemand)\n    blocs.noms.innerHTML = `\n      <h3>Noms<\/h3>\n      <p><span class=\"flag\">\ud83c\uddec\ud83c\udde7<\/span> ${espece.english2 || '\u2013'}<\/p>\n      <p><span class=\"flag\">\ud83c\uddea\ud83c\uddf8<\/span> ${espece.spanish || '\u2013'}<\/p>\n      <p><span class=\"flag\">\ud83c\udde9\ud83c\uddea<\/span> ${espece.german || '\u2013'}<\/p>\n    `;\n\n    \/\/ \ud83d\udcf7 Image Wikipedia\n    const nomLatin = espece.latin?.trim().replace(\/_\/g, ' ');\n    const imageTest = document.getElementById('image-wiki-test');\n\n    if (nomLatin) {\n      const wikiApiUrl = `https:\/\/en.wikipedia.org\/w\/api.php?action=query&format=json&formatversion=2&origin=*&prop=pageimages&piprop=original|thumbnail&pithumbsize=800&redirects=1&titles=${encodeURIComponent(nomLatin)}`;\n\n      fetch(wikiApiUrl)\n        .then(res => res.json())\n        .then(data => {\n          const page = data?.query?.pages?.[0];\n\n          if (page?.original?.source || page?.thumbnail?.source) {\n            const imgUrl = page.original?.source || page.thumbnail?.source;\n\n            if (imageTest) {\n              imageTest.style.display = 'block';\n              imageTest.innerHTML = `\n                <img decoding=\"async\" \n                  src=\"${imgUrl}\" \n                  alt=\"Image Wikip\u00e9dia\"\n                  style=\"width: 100%; height: auto; object-fit: cover; display: block;\"\n                >\n              `;\n            }\n          } else {\n            console.warn(\"Aucune image trouv\u00e9e pour :\", nomLatin);\n            if (imageTest) {\n              imageTest.style.display = 'block';\n              imageTest.innerHTML = `<p style=\"color:#888\">Aucune image trouv\u00e9e pour <em>${nomLatin}<\/em><\/p>`;\n            }\n          }\n        })\n        .catch(err => {\n          console.error(\"Erreur lors du fetch Wikipedia :\", err);\n          if (imageTest) {\n            imageTest.style.display = 'block';\n            imageTest.innerHTML = `<p style=\"color:red\">Erreur lors du chargement de l\u2019image.<\/p>`;\n          }\n        });\n    } else {\n      console.warn(\"Nom latin vide, recherche d\u2019image impossible.\");\n      if (imageTest) {\n        imageTest.style.display = 'block';\n        imageTest.innerHTML = `<p style=\"color:#888\">Nom latin manquant \u2013 pas de recherche d\u2019image possible.<\/p>`;\n      }\n    }\n\n    const ordre = espece.iocorder || 'Ordre inconnu';\n    const famille = espece.blfamily || espece.blfamilylatin || 'Famille inconnue';\n\nconst carteDistributionHTML = (() => {\n  const nomCarte = espece.latin?.trim().replace(\/\\s+\/g, '_');\n  const urlCarte = `https:\/\/fiches-especes.simonpotier.fr\/cartes\/${nomCarte}.jpg`;\n\n  const latitudeIndice = espece.Latitude ? parseInt(espece.Latitude) : null;\n  const altMin = espece.Altitudemin ? parseInt(espece.Altitudemin) : null;\n  const altMax = espece.Altitudemax ? parseInt(espece.Altitudemax) : null;\n\n  const latitudeLabels = {\n    1: \"Tropicale\",\n    2: \"Tropicale \u00e0 temp\u00e9r\u00e9e\",\n    3: \"Temp\u00e9r\u00e9e\",\n    4: \"Temp\u00e9r\u00e9e \u00e0 polaire\",\n    5: \"Tropicale \u00e0 polaire\"\n  };\n\n  const latDisplay = latitudeLabels[latitudeIndice] || '\u2013';\n  const altDisplay = (!isNaN(altMin) && !isNaN(altMax)) ? `${altMin}\u2013${altMax} m` : '\u2013';\n\n  return `\n    <div class=\"carte-distribution-wrap\">\n      <h3>Carte de distribution<\/h3>\n\n      <div class=\"carte-image-wrap\">\n        <img decoding=\"async\" \n          src=\"${urlCarte}\" \n          alt=\"\"\n          class=\"carte-image\"\n          onerror=\"this.style.display='none'; this.insertAdjacentHTML('afterend', '<p class=&quot;carte-message&quot;>Carte prochainement disponible<\/p>');\"\n        >\n      <\/div>\n\n      <div class=\"infos-carte-distribution\">\n        <div><strong>R\u00e9gion :<\/strong> ${latDisplay}<\/div>\n        <div><strong>Altitude moyenne :<\/strong> ${altDisplay}<\/div>\n      <\/div>\n    <\/div>\n  `;\n})();\n\n    \/\/ --- bloc classification (colonne milieu) ---\n    blocs.classification.innerHTML = `\n      <h3 style=\"margin-top: 40px;\">Classification<\/h3>\n      <div class=\"taxonomy-tree\">\n        <div class=\"niveau niveau-1\">\n          <div class=\"branche horizontale\"><\/div>\n          <div class=\"label\"><strong>R\u00e8gne<\/strong> : Animalia<\/div>\n        <\/div>\n        <div class=\"branche verticale niveau-1\"><\/div>\n\n        <div class=\"niveau niveau-2\">\n          <div class=\"branche horizontale\"><\/div>\n          <div class=\"label\"><strong>Embranchement<\/strong> : Chordata<\/div>\n        <\/div>\n        <div class=\"branche verticale niveau-2\"><\/div>\n\n        <div class=\"niveau niveau-3\">\n          <div class=\"branche horizontale\"><\/div>\n          <div class=\"label\"><strong>Sous-embranchement<\/strong> : Vertebrata<\/div>\n        <\/div>\n        <div class=\"branche verticale niveau-3\"><\/div>\n\n        <div class=\"niveau niveau-4\">\n          <div class=\"branche horizontale\"><\/div>\n          <div class=\"label\"><strong>Classe<\/strong> : Aves<\/div>\n        <\/div>\n        <div class=\"branche verticale niveau-4\"><\/div>\n\n        <div class=\"niveau niveau-5\">\n          <div class=\"branche horizontale\"><\/div>\n          <div class=\"label\"><strong>Ordre<\/strong> : ${espece.iocorder || '\u2013'}<\/div>\n        <\/div>\n        <div class=\"branche verticale niveau-5\"><\/div>\n\n        <div class=\"niveau niveau-6\">\n          <div class=\"branche horizontale\"><\/div>\n          <div class=\"label\"><strong>Famille<\/strong> : ${espece.blfamily || espece.blfamilylatin || '\u2013'}<\/div>\n        <\/div>\n      <\/div>\n    `;\n\n    \/\/ Carte distribution\n    document.getElementById('carte-distribution').innerHTML = carteDistributionHTML;\n    document.getElementById('carte-distribution').style.display = 'block';\n\n    const categorieUICN = traduireCategorieUICN(espece.redlistcategory);\n\n    function getPoidsSexe(espece) {\n      const parse = v => {\n        if (v === undefined || v === null) return NaN;\n        const s = v.toString().trim();\n        if (!s || s === '#N\/A') return NaN;\n        return parseFloat(s.replace(',', '.'));\n      };\n\n      const femMin  = parse(espece.FemaleMinMass);\n      const femMax  = parse(espece.FemaleMaxMass);\n      const maleMin = parse(espece.MaleMinMass);\n      const maleMax = parse(espece.MaleMaxMass);\n      const unsMin  = parse(espece.UnsexedMinMass);\n      const unsMax  = parse(espece.UnsexedMaxMass);\n\n      const format = (min, max) => `${min}\u2013${max} g`;\n\n      const hasFemComplete  = !isNaN(femMin)  && !isNaN(femMax);\n      const hasMaleComplete = !isNaN(maleMin) && !isNaN(maleMax);\n\n      if (hasFemComplete && hasMaleComplete) {\n        return `\u2640 ${format(femMin, femMax)}<br>\u2642 ${format(maleMin, maleMax)}`;\n      }\n\n      const hasUnsexedComplete = !isNaN(unsMin) && !isNaN(unsMax);\n      if (hasUnsexedComplete) {\n        return format(unsMin, unsMax);\n      }\n\n      const poidsBrut = parse(espece.poids);\n      if (isNaN(poidsBrut)) return '\u2013';\n\n      if (poidsBrut < 1000) return Math.round(poidsBrut) + ' g';\n      return (Math.round(poidsBrut \/ 10) \/ 100).toFixed(2) + ' kg';\n    }\n\n    \/\/ Visualisation du poids sur une \u00e9chelle commune (grammes).\n    function getPoidsGraphique(espece) {\n      const nombre = v => {\n        if (v === null || v === undefined) return null;\n        const t = String(v).trim().replace(',', '.');\n        if (!t || !Number.isFinite(Number(t)) || Number(t) <= 0) return null;\n        return Number(t);\n      };\n      const series = [\n        { nom:'\u2640 Femelle', classe:'spes-poids-femelle', min:nombre(espece.FemaleMinMass), max:nombre(espece.FemaleMaxMass) },\n        { nom:'\u2642 M\u00e2le', classe:'spes-poids-male', min:nombre(espece.MaleMinMass), max:nombre(espece.MaleMaxMass) }\n      ].filter(x => x.min !== null || x.max !== null);\n      if (!series.length) {\n        const min = nombre(espece.UnsexedMinMass);\n        const max = nombre(espece.UnsexedMaxMass);\n        if (min !== null || max !== null) {\n          series.push({nom:'Poids non sex\u00e9',classe:'spes-poids-inconnu',min,max});\n        } else {\n          const v = nombre(espece.poids);\n          if (v !== null) series.push({nom:'Poids',classe:'spes-poids-inconnu',min:v,max:v});\n        }\n      }\n      if (!series.length) return '<div class=\"spes-poids-empty\">Donn\u00e9e indisponible<\/div>';\n      for (const x of series) {\n        if (x.min === null) x.min = x.max;\n        if (x.max === null) x.max = x.min;\n        if (x.min > x.max) [x.min,x.max] = [x.max,x.min];\n      }\n      const borne = Math.max(...series.map(x => x.max));\n      const fmt = n => new Intl.NumberFormat('fr-FR', {maximumFractionDigits:1}).format(n);\n      const rows = series.map(x => {\n        const debut = 100*x.min\/borne;\n        const fin = 100*x.max\/borne;\n        const single = Math.abs(x.max-x.min) < 0.000001;\n        const segment = single\n          ? `<span class=\"spes-poids-point ${x.classe}\" style=\"left:${debut}%\"><\/span>`\n          : `<span class=\"spes-poids-range ${x.classe}\" style=\"left:${debut}%;width:${fin-debut}%\"><\/span>`;\n        const valeur = single ? `${fmt(x.min)} g` : `${fmt(x.min)}\u2013${fmt(x.max)} g`;\n        return `<div class=\"spes-poids-row\"><div class=\"spes-poids-heading\"><span>${x.nom}<\/span><strong>${valeur}<\/strong><\/div><div class=\"spes-poids-track\" role=\"img\" aria-label=\"${x.nom} : ${valeur}\">${segment}<\/div><\/div>`;\n      }).join('');\n      return `<div class=\"spes-poids-chart\">${rows}<div class=\"spes-poids-axis\"><span>0 g<\/span><span>${fmt(borne)} g<\/span><\/div><\/div>`;\n    }\n\n    const habitatValue = v => {\n      const txt = String(v ?? '').trim();\n      return !txt || \/^(?:#N\\\/A|N\\\/A|NA)$\/i.test(txt) ? '\u2013' : txt;\n    };\n    blocs.rythme.innerHTML = `\n  <h3>Traits fonctionnels<\/h3>\n\n  <div class=\"rythme-liste\">\n    <div>\n      <div class=\"rythme-titre\">RYTHME CIRCADIEN<\/div>\n      <div class=\"rythme-valeur\">${habitatValue(espece.rythme_circadien)}<\/div>\n    <\/div>\n\n    <div>\n      <div class=\"rythme-titre\">HABITAT<\/div>\n      <div class=\"rythme-valeur-wrapper\">\n        <div class=\"rythme-valeur\">${habitatValue(espece.habitat)}<\/div>\n        ${\n          (() => {\n            const detail = espece.habitat_detaille?.trim();\n            return detail && detail !== '#N\/A'\n              ? `<div class=\"rythme-source\">${detail}<\/div>`\n              : '';\n          })()\n        }\n      <\/div>\n    <\/div>\n\n    <div>\n      <div class=\"rythme-titre\">MIGRATION<\/div>\n      <div class=\"rythme-valeur\">${habitatValue(espece.migration)}<\/div>\n    <\/div>\n\n    <div>\n      <div class=\"rythme-titre\">SOCIALIT\u00c9<\/div>\n      <div class=\"rythme-valeur\">\n        ${(() => {\n          const socialMap = {\n            Social_1: \"Esp\u00e8ce coloniale\",\n            Social_2: \"Esp\u00e8ce sociale\",\n            Social_3: \"Esp\u00e8ce vivant en couple ou groupes familiaux\",\n            Social_4: \"Esp\u00e8ce vivant seule ou en couple\",\n            Social_5: \"Esp\u00e8ce vivant seule\",\n            Social_6: \"Esp\u00e8ce avec syst\u00e8me de lek\"\n          };\n\n          const valeurs = Object.entries(socialMap)\n            .filter(([col]) => espece[col] === \"1\" || espece[col] === 1)\n            .map(([_, label]) => label);\n\n          return valeurs.length > 0 ? valeurs.join(\", \") : \"\u2013\";\n        })()}\n      <\/div>\n    <\/div>\n\n    <div>\n      <div class=\"rythme-titre\">LONG\u00c9VIT\u00c9<\/div>\n      <div class=\"rythme-valeur-wrapper\">\n        <div class=\"rythme-valeur\">\n          ${(() => {\n            const val = parseFloat(espece.longevity?.replace(',', '.'));\n            return isNaN(val) ? '\u2013' : Math.ceil(val) + ' ans';\n          })()}\n        <\/div>\n        ${(() => {\n          const source = (espece.source || '').toLowerCase();\n          if (source === 'wild') return '<div class=\"rythme-source\">\u00c9tat naturel<\/div>';\n          if (source === 'captivity') return '<div class=\"rythme-source\">En captivit\u00e9<\/div>';\n          if (source === 'unknown') return '<div class=\"rythme-source\">Inconnu<\/div>';\n          if (source === 'na' || source === '#n\/a') return '';\n          return source ? `<div class=\"rythme-source\">${source}<\/div>` : '';\n        })()}\n      <\/div>\n    <\/div>\n  <\/div>\n\n  <div class=\"traits-climate-charts\">\n    <div class=\"trait-chart-card\">\n      <h4>Niche thermique<\/h4>\n      <canvas id=\"temperatureChart\"><\/canvas>\n    <\/div>\n\n    <div class=\"trait-chart-card\">\n      <h4>Niche d\u2019aridit\u00e9<\/h4>\n      <canvas id=\"aridityChart\"><\/canvas>\n    <\/div>\n  <\/div>\n`;\n\ncreateClimateCharts(espece);\n\n    const statutUICN = categorieUICN;\n\n    const positionsUICN = {\n      'Donn\u00e9es insuffisantes': 10.55,\n      'Pr\u00e9occupation mineure': 23.69,\n      'Quasi menac\u00e9e': 36.84,\n      'Vuln\u00e9rable': 49.99,\n      'En danger': 63.14,\n      'En danger critique': 76.29,\n      '\u00c9teint dans la nature': 89.43\n    };\n\n    const largeurCategorie = 13.14;\n    const positionPercent = positionsUICN[statutUICN] || 0;\n\n    function getEULabel(value) {\n      if (!value || value === '#N\/A') return '\u2013';\n      return value.trim().charAt(0).toUpperCase();\n    }\n\n    function getEUClass(value) {\n      if (!value || value === '#N\/A') return 'badge-eu-na';\n      const first = value.trim().charAt(0).toUpperCase();\n      switch (first) {\n        case 'A': return 'badge-eu-a';\n        case 'B': return 'badge-eu-b';\n        case 'C': return 'badge-eu-c';\n        case 'D': return 'badge-eu-d';\n        default: return 'badge-eu-na';\n      }\n    }\n\n    function getCITESLabel(value) {\n      if (!value || value === '#N\/A') return '\u2013';\n      return value.trim().toUpperCase().split(\/[^I]\/)[0] || value.trim().charAt(0);\n    }\n\n    function getCITESClass(value) {\n      if (!value || value === '#N\/A') return 'badge-cites-na';\n      const upper = value.trim().toUpperCase();\n      if (upper.startsWith('I')) return 'badge-cites-i';\n      if (upper.startsWith('II')) return 'badge-cites-ii';\n      if (upper.startsWith('III')) return 'badge-cites-iii';\n      return 'badge-cites-na';\n    }\n\n    blocs.uicn.innerHTML = `\n      <h3>Statut de protection<\/h3>\n\n      <div class=\"uicn-double-col\" style=\"\n        display: flex;\n        flex-wrap: nowrap;\n        gap: 30px;\n        justify-content: space-between;\n        align-items: flex-start;\n      \">\n\n        <!-- \ud83d\udd34 COLONNE GAUCHE -->\n        <div class=\"uicn-col uicn-gauche\" style=\"\n          flex: 0 0 25%;\n          max-width: 25%;\n          display: flex;\n          flex-direction: column;\n          align-items: center;\n          text-align: center;\n        \">\n          <h4 style=\"margin-bottom: 10px;\">Tendance des populations<\/h4>\n\n          <div style=\"\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            gap: 20px;\n            height: 100px;\n          \">\n            ${(() => {\n  const tendance = (traduireTendancePopulation(espece.populationtrend) || '').toLowerCase();\n\nconst rawPop = espece.population_size;\nlet populationHTML = '';\n\nif (\n  rawPop &&\n  !['Unknown', '#N\/A', 'NA', 'na', 'Na', 'n\/a'].includes(rawPop.toString().trim())\n) {\n  const popClean = rawPop.toString().trim();\n  populationHTML = `\n    <div style=\"\n      margin-top: 8px;\n      font-size: 0.95rem;\n      color: #555;\n      font-weight: 500;\n    \">\n      ${popClean} <span class=\"spes-mature-label\">individus matures<\/span>\n    <\/div>\n  `;\n}\n\n  if (tendance === 'en baisse') {\n    return `\n      <svg width=\"50\" height=\"100\" viewBox=\"0 0 24 24\" fill=\"#d63031\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n        <path d=\"M11 3h2v10h4l-5 5-5-5h4V3z\"\/>\n      <\/svg>\n      <div>\n        <span style=\"font-size: 1.4rem; font-weight: 500; color: #d63031;\">En baisse<\/span>\n        ${populationHTML}\n      <\/div>\n    `;\n  } else if (tendance === 'en hausse') {\n    return `\n      <svg width=\"50\" height=\"100\" viewBox=\"0 0 24 24\" fill=\"#00b894\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n        <path d=\"M13 21h-2V11H7l5-5 5 5h-4v10z\"\/>\n      <\/svg>\n      <div>\n        <span style=\"font-size: 1.4rem; font-weight: 500; color: #00b894;\">En hausse<\/span>\n        ${populationHTML}\n      <\/div>\n    `;\n  } else if (tendance === 'stable') {\n    return `\n      <svg width=\"50\" height=\"100\" viewBox=\"0 0 24 24\" fill=\"#f1c40f\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n        <rect x=\"4\" y=\"11\" width=\"16\" height=\"2\" \/>\n      <\/svg>\n      <div>\n        <span style=\"font-size: 1.4rem; font-weight: 500; color: #f1c40f;\">Stable<\/span>\n        ${populationHTML}\n      <\/div>\n    `;\n  } else {\n  return `\n    <div style=\"\n      font-size: 2.5rem;\n      font-weight: 700;\n      font-family: 'Inter', sans-serif;\n      color: #7f8c8d;\n      line-height: 1;\n    \">?<\/div>\n    <div>\n      <span style=\"font-size: 1.4rem; font-weight: 500; color: #7f8c8d;\">Inconnue<\/span>\n      ${populationHTML}\n    <\/div>\n  `;\n}\n\n})()}\n\n          <\/div>\n        <\/div>\n\n        <!-- \ud83d\udfe1 COLONNE CENTRALE -->\n        <div class=\"uicn-col uicn-centre\" style=\"\n          flex: 1 1 50%;\n          max-width: 50%;\n          display: flex;\n          flex-direction: column;\n          align-items: center;\n          text-align: center;\n        \">\n          <h4 style=\"margin-bottom: 10px;\">Statut UICN<\/h4>\n\n          <div class=\"uicn-bar-container\" style=\"position: relative; width: 100%;\">\n            <img decoding=\"async\"\n              src=\"https:\/\/fiches-especes.simonpotier.fr\/wp-content\/uploads\/2025\/12\/UICN-Categories-scaled.png\"\n              alt=\"Barre des statuts UICN\"\n              class=\"uicn-bar-image\"\n              style=\"width: 100%; height: auto;\"\n            >\n\n            <div style=\"\n              position: absolute;\n              top: 0;\n              left: calc(${positionPercent}% - ${largeurCategorie \/ 2}%);\n              width: ${largeurCategorie}%;\n              height: 100%;\n              border: 3px solid black;\n              box-sizing: border-box;\n              pointer-events: none;\n            \"><\/div>\n\n            <div style=\"\n              position: absolute;\n              top: calc(90% - 1px);\n              left: calc(${positionPercent}% - 12px);\n              width: 0;\n              height: 0;\n              border-left: 12px solid transparent;\n              border-right: 12px solid transparent;\n              border-bottom: 18px solid black;\n              z-index: 2;\n            \"><\/div>\n          <\/div>\n        <\/div>\n\n        <!-- \ud83d\udfe2 COLONNE DROITE -->\n        <div class=\"uicn-col uicn-droite\" style=\"\n          flex: 0 0 25%;\n          max-width: 25%;\n          display: flex;\n          flex-direction: column;\n          align-items: center;\n          text-align: center;\n        \">\n          <h4>Annexes<\/h4>\n\n          <div class=\"annexes-ligne\">\n            <div class=\"annexes-labels\">\n              <span>EU<\/span>\n              <span>CITES<\/span>\n            <\/div>\n\n            <div class=\"annexes-valeurs\">\n              <span class=\"badge-eu ${getEUClass(espece.EU)}\">${getEULabel(espece.EU)}<\/span>\n              <span class=\"badge-cites ${getCITESClass(espece.CITES)}\">${getCITESLabel(espece.CITES)}<\/span>\n            <\/div>\n          <\/div>\n        <\/div>\n\n      <\/div>\n    `;\n\n    \/\/ \ud83d\udc23 Bloc Reproduction\n    const oldRepro = document.getElementById('bloc-reproduction');\n    if (oldRepro) oldRepro.remove();\n\n    blocs.reproduction = document.createElement('div');\n    blocs.reproduction.className = 'bloc';\n    blocs.reproduction.id = 'bloc-reproduction';\n\n    document.getElementById('bloc-rythme').before(blocs.reproduction);\n    blocs.reproduction.style.display = 'block';\n\n    function getSystemeAccouplement(espece) {\n      const mono = espece.Monogame === \"1\";\n      const poly = espece.Polygame === \"1\";\n      const coop = parseInt(espece.Coop);\n\n      let type = \"\u2013\";\n      if (mono && poly) type = \"Monogame et polygame\";\n      else if (mono) type = \"Monogame\";\n      else if (poly) type = \"Polygame\";\n\n      const coopMap = {\n        0: \"Elevage solitaire\",\n        1: \"Elevage coop\u00e9ratif\",\n        2: \"Aid\u00e9e par la famille\",\n        3: \"Aid\u00e9e occasionnellement\"\n      };\n      const mode = coopMap[coop] || \"\u2013\";\n\n      return `\n        <div class=\"subfield\">Syst\u00e8me d\u2019accouplement : <span class=\"subvalue\">${type}<\/span><\/div>\n        <div class=\"subfield\">M\u00e9thode d'\u00e9levage : <span class=\"subvalue\">${mode}<\/span><\/div>\n      `;\n    }\n\n    function getNid(espece) {\n      const typeMap = {\n        BU: \"Terrier\",\n        CP: \"Nid en coupe\",\n        CR: \"Fissure \/ anfractuosit\u00e9 rocheuse\",\n        CV: \"Cavit\u00e9 arboricole\",\n        DM: \"D\u00f4me\",\n        HC: \"Demi-coupe peu profonde\",\n        NO: \"Pas de nid\",\n        O:  \"Nid d\u2019une autre esp\u00e8ce\",\n        PL: \"Plateforme\",\n        PN: \"Nid suspendu (en sac)\",\n        SA: \"Nid en soucoupe\",\n        SC: \"Grattage \/ d\u00e9pression sommaire\",\n        SP: \"Nid sph\u00e9rique \/ globulaire\",\n        M:  \"Nid en monticule\"\n      };\n\n      const substratMap = {\n        A: \"Bambou\",\n        B: \"B\u00e2timent \/ construction humaine\",\n        C: \"Souche\",\n        G: \"Sol\",\n        K: \"Cactus\",\n        N: \"Nid pr\u00e9existant\",\n        P: \"Poteau \/ m\u00e2t\",\n        R: \"Rocher \/ paroi rocheuse\",\n        S: \"Arbuste, buisson ou liane\",\n        T: \"Arbre\",\n        W: \"Eau (flottant ou v\u00e9g\u00e9tation aquatique)\",\n        Z: \"Herbac\u00e9es \/ gramin\u00e9es\"\n      };\n\n      const decode = (raw, map) => {\n        if (!raw || raw === \"#N\/A\") return \"\u2013\";\n        return raw\n          .split(\",\")\n          .map(c => c.trim())\n          .map(c => map[c] || c)\n          .join(\", \");\n      };\n\n      const typeNid = decode(espece.Nest_type, typeMap);\n      const substrat = decode(espece.Nest_substrat, substratMap);\n\n      return `\n        <div class=\"subfield\">\n          Type de nid : <span class=\"subvalue\">${typeNid}<\/span>\n        <\/div>\n        <div class=\"subfield\">\n          Substrat : <span class=\"subvalue\">${substrat}<\/span>\n        <\/div>\n      `;\n    }\n\n    function formatRange(min, max, unit = '') {\n      const valMin = parseFloat(min?.toString().replace(',', '.'));\n      const valMax = parseFloat(max?.toString().replace(',', '.'));\n      if (isNaN(valMin) && isNaN(valMax)) return '\u2013';\n      if (!isNaN(valMin) && isNaN(valMax)) return `${valMin}${unit}`;\n      if (isNaN(valMin) && !isNaN(valMax)) return `${valMax}${unit}`;\n      return `${valMin}\u2013${valMax}${unit}`;\n    }\n\n    blocs.reproduction.innerHTML = `\n      <h3>Reproduction<\/h3>\n      <div class=\"rythme-liste\">\n        <div>\n          <div class=\"rythme-titre\">Strat\u00e9gie de reproduction<\/div>\n          <div class=\"rythme-valeur\">${getSystemeAccouplement(espece)}<\/div>\n        <\/div>\n        <div>\n          <div class=\"rythme-titre\">Nid<\/div>\n          <div class=\"rythme-valeur\">${getNid(espece)}<\/div>\n        <\/div>\n        <div>\n          <div class=\"rythme-titre\">Nombre d'\u0153ufs<\/div>\n          <div class=\"rythme-valeur\">${formatRange(espece.Clutch_min, espece.Clutch_max)}<\/div>\n        <\/div>\n        <div>\n          <div class=\"rythme-titre\">Incubation<\/div>\n          <div class=\"rythme-valeur\">${formatRange(espece.Incub_min, espece.Incub_max, ' j')}<\/div>\n        <\/div>\n        <div>\n          <div class=\"rythme-titre\">Envol des jeunes<\/div>\n          <div class=\"rythme-valeur\">${formatRange(espece.Fledg_min, espece.Fledg_max, ' j')}<\/div>\n        <\/div>\n      <\/div>\n    `;\n\n    \/\/ Morphologie : trois sections, dont l'image anatomique d'origine.\n    const numeroMorpho = (window.spesMorphoRequestId || 0) + 1;\n    window.spesMorphoRequestId = numeroMorpho;\n    blocs.morpho.innerHTML = `\n      <h3>Donn\u00e9es morphologiques<\/h3>\n      <div class=\"spes-morpho-grid\">\n        <section class=\"spes-morpho-card spes-morpho-envergure\">\n          <h4>Envergure<\/h4>\n          <div class=\"spes-morpho-value\" id=\"spes-wingspan-value\">Chargement\u2026<\/div>\n          <div class=\"spes-wingspan-scale\" id=\"spes-wingspan-scale\" role=\"img\" aria-label=\"\u00c9chelle d'envergure\"><\/div>\n          <div class=\"spes-morpho-note\" id=\"spes-wingspan-note\"><\/div>\n        <\/section>\n        <section class=\"spes-morpho-card spes-morpho-poids\">\n          <h4>Poids<\/h4>\n          ${getPoidsGraphique(espece)}\n        <\/section>\n        <section class=\"spes-morpho-card spes-morpho-mensurations\">\n          <h4>Mensurations<\/h4>\n          <div class=\"spes-morpho-mesures-layout\">\n            <div class=\"morpho-texte\">\n              ${[\n                { label: \"1 - Longueur du bec (culmen)\", val: espece[\"beak_length_culmen\"] },\n                { label: \"2 - Longueur du bec (narines)\", val: espece[\"beak_length_nares\"] },\n                { label: \"3 - Largeur du bec\", val: espece[\"beak_width\"] },\n                { label: \"4 - \u00c9paisseur du bec\", val: espece[\"beak_depth\"] },\n                { label: \"5 - Longueur du tarse\", val: espece[\"tarsus_length\"] },\n                { label: \"6 - Longueur de l'aile\", val: espece[\"wing_length\"] },\n                { label: \"7 - Distance de Kipps\", val: espece[\"kipps_distance\"] },\n                { label: \"8 - Hand-wing index\", val: espece[\"hand_wing_index\"] },\n                { label: \"9 - Longueur de la queue\", val: espece[\"tail_length\"] }\n              ].map(d => `<p><strong>${d.label} :<\/strong> ${d.val && d.val !== \"#N\/A\" ? d.val : \"\u2013\"}${d.label.startsWith('8 -') ? '' : ' mm'}<\/p>`).join('')}\n            <\/div>\n            <div class=\"morpho-image\">\n              <img decoding=\"async\" src=\"https:\/\/fiches-especes.simonpotier.fr\/wp-content\/uploads\/2025\/11\/Morphologie-scaled.jpg\" alt=\"Illustration anatomique num\u00e9rot\u00e9e des mensurations de l'oiseau\">\n            <\/div>\n          <\/div>\n        <\/section>\n      <\/div>\n    `;\n\n    \/\/ R\u00e9cup\u00e9ration s\u00e9curis\u00e9e des donn\u00e9es d'envergure de l'esp\u00e8ce.\n    (async () => {\n      const valeur = document.getElementById('spes-wingspan-value');\n      const note = document.getElementById('spes-wingspan-note');\n      const echelle = document.getElementById('spes-wingspan-scale');\n      if (!valeur || !note) return;\n      const scientific = String(espece.scientific || '').trim();\n      const latin = String(espece.latin || '').trim();\n      if (!scientific && !latin) {\n        valeur.textContent = '\u2013';\n        return;\n      }\n      try {\n        const params = new URLSearchParams({ scientific: scientific || latin, latin });\n        const url = window.FICHES_API.restUrl + 'fiches\/v1\/wingspan?' + params;\n        const response = await fetch(url, {\n          credentials: 'same-origin',\n          headers: { 'Accept': 'application\/json', 'X-WP-Nonce': window.FICHES_API.nonce }\n        });\n        if (!response.ok) throw new Error('API Wingspan : HTTP ' + response.status);\n        const data = await response.json();\n        if (window.spesMorphoRequestId !== numeroMorpho) return;\n        if (!data.ok || data.min == null || data.max == null) {\n          valeur.textContent = '\u2013';\n          note.textContent = 'Donn\u00e9e indisponible';\n          return;\n        }\n        const fmt = n => Number(n).toLocaleString('fr-FR', { maximumFractionDigits: 1 });\n        valeur.textContent = (Number(data.min) === Number(data.max)\n          ? fmt(data.min)\n          : fmt(data.min) + '\u2013' + fmt(data.max)) + ' cm';\n        note.textContent = data.source === 'reported' ? 'Envergure rapport\u00e9e' : 'Envergure estim\u00e9e';\n\n        \/\/ \u00c9chelle centr\u00e9e sur les donn\u00e9es : 5 cm de marge de chaque c\u00f4t\u00e9.\n        const min = Number(data.min);\n        const max = Number(data.max);\n        if (echelle && Number.isFinite(min) && Number.isFinite(max) && min > 0 && max >= min) {\n          const borneMin = Math.max(0, min - 5);\n          const borneMax = max + 5;\n          const largeur = 320, gauche = 12, y = 26;\n          const x = n => gauche + largeur * (n - borneMin) \/ (borneMax - borneMin);\n          \/\/ Cinq graduations r\u00e9parties uniform\u00e9ment, bornes incluses.\n          const graduations = Array.from({ length: 5 }, (_, i) =>\n            borneMin + (borneMax - borneMin) * i \/ 4\n          );\n          const trait = min === max\n            ? `<line x1=\"${x(min)}\" y1=\"10\" x2=\"${x(min)}\" y2=\"41\" stroke=\"#1A5641\" stroke-width=\"3\"\/><circle cx=\"${x(min)}\" cy=\"10\" r=\"4\" fill=\"#1A5641\"\/>`\n            : `<line x1=\"${x(min)}\" y1=\"${y}\" x2=\"${x(max)}\" y2=\"${y}\" stroke=\"#1A5641\" stroke-width=\"12\" stroke-linecap=\"round\"\/><circle cx=\"${x(min)}\" cy=\"${y}\" r=\"4\" fill=\"#fff\" stroke=\"#1A5641\" stroke-width=\"2\"\/><circle cx=\"${x(max)}\" cy=\"${y}\" r=\"4\" fill=\"#fff\" stroke=\"#1A5641\" stroke-width=\"2\"\/>`;\n          echelle.innerHTML = `<svg viewBox=\"0 0 344 70\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" aria-hidden=\"true\">\n            <line x1=\"${gauche}\" y1=\"${y}\" x2=\"${gauche + largeur}\" y2=\"${y}\" stroke=\"#e7ebe8\" stroke-width=\"9\" stroke-linecap=\"round\"\/>\n            ${graduations.map(n => `<line x1=\"${x(n)}\" y1=\"20\" x2=\"${x(n)}\" y2=\"37\" stroke=\"#aebbb4\" stroke-width=\"1\"\/><text x=\"${x(n)}\" y=\"58\" fill=\"#555\" font-size=\"11\" text-anchor=\"middle\">${fmt(n)}<\/text>`).join('')}\n            ${trait}\n            <text x=\"${gauche + largeur}\" y=\"69\" fill=\"#666\" font-size=\"9\" text-anchor=\"end\">cm<\/text>\n          <\/svg>`;\n          echelle.setAttribute('aria-label', min === max\n            ? `Envergure : ${fmt(min)} centim\u00e8tres`\n            : `Envergure : de ${fmt(min)} \u00e0 ${fmt(max)} centim\u00e8tres`);\n        }\n      } catch (error) {\n        if (window.spesMorphoRequestId !== numeroMorpho) return;\n        console.error('Erreur de chargement Wingspan :', error);\n        valeur.textContent = '\u2013';\n        note.textContent = 'Donn\u00e9e indisponible';\n      }\n    })();\n\n    google.charts.load('current', { packages: ['corechart'] });\n    google.charts.setOnLoadCallback(() => {\n\nblocs.regime.innerHTML = `\n  <style>\n    #bloc-regime .spes-graph-grid {\n      display: grid;\n      grid-template-columns: repeat(2, minmax(0, 1fr));\n      gap: 30px;\n      margin: 20px 0 30px;\n      align-items: start;\n    }\n\n    #bloc-regime .spes-graph-grid .chart-block {\n      width: 100%;\n      max-width: none;\n      min-width: 0;\n      display: flex;\n      flex-direction: column;\n      align-items: center;\n    }\n\n    #bloc-regime .spes-graph-grid h4 {\n      text-align: center;\n      color: #1A5641;\n      font-size: 1rem;\n      font-weight: 600;\n      margin: 0 0 15px;\n    }\n\n    #bloc-regime .spes-graph-grid .chart-container {\n  width: 100%;\n  max-width: 400px !important;\n  margin: 0 auto;\n}\n\n    #bloc-regime #chart-diet-monthly {\n  width: 100% !important;\n  max-width: 550px !important;\n  height: 350px !important;\n  margin: 0 auto;\n}\n\n    #bloc-regime #legend-diet-monthly {\n  width: 100% !important;\n  max-width: 550px !important;\n  margin: 20px auto 0 !important;\n  align-items: center !important;\n}\n\n#bloc-regime .spes-graph-grid .chart-container svg {\n  max-width: 100%;\n  height: auto;\n}\n\n#bloc-regime .spes-graph-grid .custom-legend {\n  margin-top: 12px;\n}\n\n    @media (max-width: 768px) {\n      #bloc-regime .spes-graph-grid {\n        grid-template-columns: 1fr;\n        gap: 30px;\n      }\n    }\n  <\/style>\n\n  <span id=\"spes-ancre-regime\" class=\"spes-nav-anchor\" aria-hidden=\"true\"><\/span>\n  <h3>R\u00e9gime alimentaire<\/h3>\n\n  <div class=\"spes-graph-grid\">\n\n    <!-- Graphique 1 : EltonTraits -->\n    <div class=\"chart-block\">\n      <h4>Composition du r\u00e9gime alimentaire<\/h4>\n\n      <div class=\"chart-container\" id=\"chart-diet\"><\/div>\n\n      <div id=\"legend-diet\" class=\"custom-legend\"><\/div>\n    <\/div>\n\n    <!-- Graphique 2 : SAviTraits -->\n    <div class=\"chart-block chart-block-mensuel\"\n         style=\"display:flex; flex-direction:column; align-items:center;\">\n\n      <h4>Variations saisonni\u00e8res du r\u00e9gime alimentaire<\/h4>\n\n      <div id=\"chart-diet-monthly\"\n           style=\"width:100%; height:350px;\">\n      <\/div>\n\n      <div id=\"legend-diet-monthly\"\n           class=\"custom-legend\"\n           style=\"width:100%; display:flex; flex-direction:column; align-items:center; margin-top:35px;\">\n      <\/div>\n\n    <\/div>\n\n  <\/div>\n\n  <span id=\"spes-ancre-strategies\" class=\"spes-nav-anchor\" aria-hidden=\"true\"><\/span>\n  <h3>Strat\u00e9gie de recherche alimentaire<\/h3>\n\n  <div class=\"spes-graph-grid\">\n\n    <!-- Graphique 3 : EltonTraits -->\n    <div class=\"chart-block\">\n      <h4>Lieux de prise alimentaire<\/h4>\n\n      <div class=\"chart-container\" id=\"chart-strat\"><\/div>\n\n      <div id=\"legend-strat\" class=\"custom-legend\"><\/div>\n    <\/div>\n\n    <!-- Graphique 4 : AVONICHE -->\n    <div class=\"chart-block\">\n      <h4>Strat\u00e9gies d'acquisition alimentaire<\/h4>\n\n      <div class=\"chart-container\" id=\"chart-avoniche\">\n        <p style=\"text-align:center; color:#777;\">\n          Graphique AVONICHE \u00e0 int\u00e9grer\n        <\/p>\n      <\/div>\n\n      <div id=\"legend-avoniche\" class=\"custom-legend\"><\/div>\n    <\/div>\n\n  <\/div>\n`;\n\n\n      function drawCharts() {\n        const dietColorMap = {\n          'Invert\u00e9br\u00e9s': '#C7F464',\n          'Vert\u00e9br\u00e9s endothermes': '#FF6B6B',\n          'Vert\u00e9br\u00e9s ectothermes': '#C3B1E1',\n          'Poissons': '#4ECDC4',\n          'Vert\u00e9br\u00e9s inconnus': '#FFB6B9',\n          'Charognes': '#6C5B7B',\n          'Fruits': '#F67280',\n          'Nectars': '#FF9F1C',\n          'Graines': '#FFE66D',\n          'V\u00e9g\u00e9taux': '#B5EAD7'\n        };\n\n        const stratColorMap = {\n          'Sous l\u2019eau': '#4ECDC4',\n          'Autour surface eau': '#6C5B7B',\n          'Au sol': '#FF6B6B',\n          'Sous-bois': '#C7F464',\n          'Milieu de canop\u00e9e': '#FFE66D',\n          'Canop\u00e9e': '#B5EAD7',\n          'Dans les airs': '#FF9F1C'\n        };\n\n        function prepareData(rows) {\n          const filtered = rows.filter(([_, value]) => value > 0);\n          let hasFake = false;\n\n          if (filtered.length === 1 && filtered[0][1] === 100) {\n            filtered.push(['', 0.00001]);\n            hasFake = true;\n          }\n\n          return { data: filtered, hasFake };\n        }\n\n        const rawDiet = [\n          ['Invert\u00e9br\u00e9s', parseFloat(espece.diet_inv) || 0],\n          ['Vert\u00e9br\u00e9s endothermes', parseFloat(espece.diet_vend) || 0],\n          ['Vert\u00e9br\u00e9s ectothermes', parseFloat(espece.diect_vect) || 0],\n          ['Poissons', parseFloat(espece.diet_vfish) || 0],\n          ['Vert\u00e9br\u00e9s inconnus', parseFloat(espece.diet_vunk) || 0],\n          ['Charognes', parseFloat(espece.diet_scav) || 0],\n          ['Fruits', parseFloat(espece.diet_fruit) || 0],\n          ['Nectars', parseFloat(espece.diet_nect) || 0],\n          ['Graines', parseFloat(espece.diet_seed) || 0],\n          ['V\u00e9g\u00e9taux', parseFloat(espece.diet_planto) || 0]\n        ].filter(d => d[1] > 0).sort((a, b) => b[1] - a[1]);\n\n        const rawStrat = [\n          ['Sous l\u2019eau', parseFloat(espece.forstrat_watbelowsurf) || 0],\n          ['Autour surface eau', parseFloat(espece.forstrat_wataroundsurf) || 0],\n          ['Au sol', parseFloat(espece.forstrat_ground) || 0],\n          ['Sous-bois', parseFloat(espece.forstrat_understory) || 0],\n          ['Milieu de canop\u00e9e', parseFloat(espece.forstrat_midhigh) || 0],\n          ['Canop\u00e9e', parseFloat(espece.forstrat_canopy) || 0],\n          ['Dans les airs', parseFloat(espece.forstrat_aerial) || 0]\n        ].filter(d => d[1] > 0).sort((a, b) => b[1] - a[1]);\n\n        const preparedDiet = prepareData(rawDiet);\n        const preparedStrat = prepareData(rawStrat);\n\n        const dataDiet = google.visualization.arrayToDataTable([\n          ['Type', 'Pourcentage'],\n          ...preparedDiet.data\n        ]);\n\n        const dataStrat = google.visualization.arrayToDataTable([\n          ['Strate', 'Pourcentage'],\n          ...preparedStrat.data\n        ]);\n\n        const chartDiet = new google.visualization.PieChart(document.getElementById('chart-diet'));\n        const chartStrat = new google.visualization.PieChart(document.getElementById('chart-strat'));\n\n        const containerWidth = document.getElementById('chart-diet')?.offsetWidth || 300;\n\n        function getColorsFromMap(dataTable, colorMap) {\n          const colors = [];\n          for (let i = 0; i < dataTable.getNumberOfRows(); i++) {\n            const label = dataTable.getValue(i, 0);\n            colors.push(!label ? 'transparent' : (colorMap[label] || '#cccccc'));\n          }\n          return colors;\n        }\n\n        function makeSlices(dataTable, hasFake) {\n          const slices = {};\n          if (hasFake) {\n            const lastIndex = dataTable.getNumberOfRows() - 1;\n            slices[lastIndex] = { color: 'transparent', textStyle: { color: 'transparent' } };\n          }\n          return slices;\n        }\n\n        const baseOptions = {\n  pieHole: 0.4,\n  width: containerWidth,\n  height: Math.round(containerWidth * 0.75),\n  legend: 'none',\n  pieSliceText: 'percentage', \/\/ % dans les parts\n  tooltip: { text: 'value' }, \/\/ ou 'none' pour ne rien avoir au hover\n  chartArea: { width: '90%', height: '80%' },\n  pieSliceBorderColor: 'transparent',\n  sliceVisibilityThreshold: 0\n};\n\n\n        chartDiet.draw(dataDiet, {\n          ...baseOptions,\n          colors: getColorsFromMap(dataDiet, dietColorMap),\n          slices: makeSlices(dataDiet, preparedDiet.hasFake)\n        });\n\n        chartStrat.draw(dataStrat, {\n          ...baseOptions,\n          colors: getColorsFromMap(dataStrat, stratColorMap),\n          slices: makeSlices(dataStrat, preparedStrat.hasFake)\n        });\n\n       function generateLegend(elementId, dataTable, colorMap) {\n  const container = document.getElementById(elementId);\n  if (!container) return;\n\n  container.innerHTML = '';\n\n  for (let i = 0; i < dataTable.getNumberOfRows(); i++) {\n    const label = dataTable.getValue(i, 0);\n    const value = dataTable.getValue(i, 1);\n    const color = colorMap[label] || '#ccc';\n\n    if (!label || !value || isNaN(value) || value === 0) continue;\n\n    const item = document.createElement('div');\n    item.className = 'legend-item';\n    item.innerHTML = `\n      <span class=\"legend-color\" style=\"background:${color};\"><\/span>\n      <span><span class=\"spes-legend-category\">${label}<\/span> \u2013 ${value}%<\/span>\n    `;\n    container.appendChild(item);\n  }\n}\n\n\n        generateLegend('legend-diet', dataDiet, dietColorMap);\n        generateLegend('legend-strat', dataStrat, stratColorMap);\n      }\n\n      drawCharts();\nafficherRegimeMensuel(espece);\nafficherAvoniche(espece);\nwindow.addEventListener('resize', drawCharts);\n    });\n  }\n\n\nlet avonicheRequestId = 0;\n\nasync function afficherAvoniche(espece) {\n  const requestId = ++avonicheRequestId;\n  const container = document.getElementById('chart-avoniche');\n  const legend = document.getElementById('legend-avoniche');\n  if (!container) return;\n\n  container.classList.add('spes-acquisition-chart');\n  container.textContent = 'Chargement des donn\u00e9es AVONICHE\u2026';\n  if (legend) legend.replaceChildren(); \/\/ Plus de l\u00e9gende s\u00e9par\u00e9e : chaque barre est \u00e9tiquet\u00e9e.\n\n  const scientific = String(espece.scientific || '').trim();\n  const latin = String(espece.latin || '').trim();\n  if (!scientific && !latin) {\n    container.textContent = 'Nom scientifique indisponible.';\n    return;\n  }\n  if (!window.FICHES_API?.restUrl || !window.FICHES_API?.nonce) {\n    container.textContent = 'API SPES indisponible.';\n    return;\n  }\n\n  const traductionsAvoniche = {\n    PAG: 'V\u00e9g\u00e9taux aquatiques \u2013 pr\u00e9l\u00e8vement en eau peu profonde',\n    PAS: 'V\u00e9g\u00e9taux aquatiques \u2013 pr\u00e9l\u00e8vement en nageant',\n    PAD: 'V\u00e9g\u00e9taux aquatiques \u2013 pr\u00e9l\u00e8vement en plong\u00e9e',\n    PEL: 'V\u00e9g\u00e9taux terrestres \u2013 pr\u00e9l\u00e8vement en hauteur',\n    PGR: 'V\u00e9g\u00e9taux terrestres \u2013 pr\u00e9l\u00e8vement au sol',\n    NAE: 'Nectar \u2013 pr\u00e9l\u00e8vement en vol',\n    NGL: 'Nectar \u2013 pr\u00e9l\u00e8vement depuis un perchoir',\n    SEL: 'Graines \u2013 pr\u00e9l\u00e8vement sur la v\u00e9g\u00e9tation',\n    SGR: 'Graines \u2013 pr\u00e9l\u00e8vement au sol',\n    FAE: 'Fruits \u2013 pr\u00e9l\u00e8vement en vol',\n    FGL: 'Fruits \u2013 pr\u00e9l\u00e8vement depuis un perchoir',\n    FGR: 'Fruits \u2013 pr\u00e9l\u00e8vement au sol',\n    IAE: 'Invert\u00e9br\u00e9s \u2013 capture en vol continu',\n    ISA: 'Invert\u00e9br\u00e9s \u2013 capture a\u00e9rienne depuis un perchoir',\n    ISS: 'Invert\u00e9br\u00e9s \u2013 capture sur un substrat par attaque a\u00e9rienne',\n    ISG: 'Invert\u00e9br\u00e9s \u2013 capture au sol par attaque a\u00e9rienne',\n    IVS: 'Invert\u00e9br\u00e9s \u2013 capture sur substrat vertical',\n    IGE: 'Invert\u00e9br\u00e9s \u2013 pr\u00e9l\u00e8vement sur la v\u00e9g\u00e9tation en hauteur',\n    IGG: 'Invert\u00e9br\u00e9s \u2013 pr\u00e9l\u00e8vement au sol',\n    APA: 'Proies aquatiques \u2013 capture en vol',\n    APL: 'Proies aquatiques \u2013 capture par plongeon a\u00e9rien',\n    APP: 'Proies aquatiques \u2013 capture depuis un perchoir',\n    APG: 'Proies aquatiques \u2013 capture en eau peu profonde',\n    APS: 'Proies aquatiques \u2013 capture \u00e0 la surface de l\u2019eau',\n    APD: 'Proies aquatiques \u2013 capture en plong\u00e9e',\n    VAE: 'Vert\u00e9br\u00e9s \u2013 capture en vol',\n    VAS: 'Vert\u00e9br\u00e9s \u2013 capture au sol ou sur support depuis le vol',\n    VPE: 'Vert\u00e9br\u00e9s \u2013 capture depuis un perchoir',\n    VGE: 'Vert\u00e9br\u00e9s \u2013 pr\u00e9l\u00e8vement sur support en hauteur',\n    VGG: 'Vert\u00e9br\u00e9s \u2013 capture au sol',\n    CAQ: 'Charognes \u2013 consommation en milieu aquatique',\n    CGR: 'Charognes \u2013 consommation au sol'\n  };\n\n  \/\/ Couleurs stables par groupe alimentaire, quelle que soit l'esp\u00e8ce.\n  const couleurs = {\n    P: '#589b81', N: '#dbac47', S: '#b99556', F: '#d97861',\n    I: '#8066ae', A: '#438fb7', V: '#a96a6b', C: '#78836b'\n  };\n\n  try {\n    const url = window.FICHES_API.restUrl\n      + 'fiches\/v1\/avoniche?scientific=' + encodeURIComponent(scientific || latin)\n      + '&latin=' + encodeURIComponent(latin);\n    const response = await fetch(url, {\n      credentials: 'same-origin',\n      headers: { 'Accept': 'application\/json', 'X-WP-Nonce': window.FICHES_API.nonce }\n    });\n    if (requestId !== avonicheRequestId) return;\n    if (!response.ok) throw new Error('Erreur API AVONICHE : ' + response.status);\n\n    const result = await response.json();\n    if (requestId !== avonicheRequestId) return;\n    if (!result.ok || !Array.isArray(result.niches)) {\n      container.textContent = 'Donn\u00e9es indisponibles pour cette esp\u00e8ce.';\n      return;\n    }\n\n    const niches = result.niches\n      .filter(item => Object.prototype.hasOwnProperty.call(traductionsAvoniche, item.code)\n        && item.value !== null && item.value !== ''\n        && Number.isFinite(Number(item.value)) && Number(item.value) > 0)\n      .map(item => ({ code: item.code, value: Number(item.value) }))\n      .sort((a, b) => b.value - a.value || a.code.localeCompare(b.code));\n\n    if (!niches.length) {\n      container.textContent = 'Aucune strat\u00e9gie alimentaire renseign\u00e9e.';\n      return;\n    }\n\n    const fragment = document.createDocumentFragment();\n    const formatPourcent = n => new Intl.NumberFormat('fr-FR', { maximumFractionDigits: 2 }).format(n) + ' %';\n\n    niches.forEach(item => {\n      const row = document.createElement('div');\n      row.className = 'spes-acquisition-row';\n      const head = document.createElement('div');\n      head.className = 'spes-acquisition-head';\n      const label = document.createElement('span');\n      label.className = 'spes-acquisition-label';\n      label.textContent = traductionsAvoniche[item.code];\n      const value = document.createElement('span');\n      value.className = 'spes-acquisition-value';\n      value.textContent = formatPourcent(item.value);\n      head.append(label, value);\n      const track = document.createElement('div');\n      track.className = 'spes-acquisition-track';\n      track.setAttribute('role', 'img');\n      track.setAttribute('aria-label', label.textContent + ' : ' + value.textContent);\n      const fill = document.createElement('div');\n      fill.className = 'spes-acquisition-fill';\n      fill.style.width = Math.max(0, Math.min(100, item.value)) + '%';\n      fill.style.backgroundColor = couleurs[item.code[0]] || '#1A5641';\n      track.appendChild(fill);\n      row.append(head, track);\n      fragment.appendChild(row);\n    });\n\n    const note = document.createElement('p');\n    note.className = 'spes-acquisition-note';\n\n    fragment.appendChild(note);\n    if (requestId !== avonicheRequestId) return;\n    container.replaceChildren(fragment);\n  } catch (error) {\n    if (requestId !== avonicheRequestId) return;\n    console.error('Erreur AVONICHE :', error);\n    container.textContent = 'Impossible de charger les donn\u00e9es AVONICHE.';\n  }\n}\n\nlet dietMonthlyChartInstance = null;\nlet dietMonthlyRequestId = 0;\n\nconst dietMonthlyColors = {\n  'Invert\u00e9br\u00e9s': '#C7F464',\n  'Vert\u00e9br\u00e9s endothermes': '#FF6B6B',\n  'Vert\u00e9br\u00e9s ectothermes': '#C3B1E1',\n  'Poissons': '#4ECDC4',\n  'Vert\u00e9br\u00e9s inconnus': '#FFB6B9',\n  'Charognes': '#6C5B7B',\n  'Fruits': '#F67280',\n  'Nectars': '#FF9F1C',\n  'Graines': '#FFE66D',\n  'V\u00e9g\u00e9taux': '#B5EAD7'\n};\n\nasync function afficherRegimeMensuel(espece) {\n  const requestId = ++dietMonthlyRequestId;\n  const container = document.getElementById('chart-diet-monthly');\n  const legend = document.getElementById('legend-diet-monthly');\n\n  if (dietMonthlyChartInstance) {\n    dietMonthlyChartInstance.destroy();\n    dietMonthlyChartInstance = null;\n  }\n\n  if (!container || !legend) return;\n\n  container.innerHTML = '<p>Chargement des variations mensuelles\u2026<\/p>';\n  legend.innerHTML = '';\n\n  \nconst scientific = String(espece.scientific || '').trim();\nconst latin = String(espece.latin || '').trim();\n\nif (!scientific && !latin) {\n  container.textContent = 'Nom scientifique indisponible.';\n  return;\n}\n\ntry {\n  const params = new URLSearchParams({\n    scientific: scientific || latin,\n    latin: latin\n  });\n\n  const response = await fetch(\n    window.FICHES_API.restUrl +\n    'fiches\/v1\/regime-mensuel?' + params.toString(),\n    {\n      credentials: 'same-origin',\n      headers: {\n        'Accept': 'application\/json',\n        'X-WP-Nonce': window.FICHES_API.nonce\n      }\n    }\n  );\n\n\n    if (!response.ok) {\n      throw new Error('Erreur API : ' + response.status);\n    }\n\n    const result = await response.json();\n\n    \/\/ \u00c9vite d'afficher les donn\u00e9es d'une esp\u00e8ce pr\u00e9c\u00e9dente.\n    if (requestId !== dietMonthlyRequestId) return;\n\n    if (!result.ok || !Array.isArray(result.series)) {\n      throw new Error('Donn\u00e9es alimentaires invalides.');\n    }\n\n    if (typeof Chart === 'undefined') {\n      throw new Error('Chart.js est indisponible.');\n    }\n\n    container.innerHTML = '';\n\n    const canvas = document.createElement('canvas');\n    container.appendChild(canvas);\n\n    const months = [\n      'Jan', 'F\u00e9v', 'Mar', 'Avr', 'Mai', 'Juin',\n      'Juil', 'Ao\u00fbt', 'Sep', 'Oct', 'Nov', 'D\u00e9c'\n    ];\n\n    const monthKeys = [\n      'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',\n      'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'\n    ];\n\n    const datasets = result.series.map(serie => ({\n      label: serie.label,\n      data: monthKeys.map(month => {\n        const value = serie.values?.[month];\n        return value === null || value === undefined || value === ''\n          ? null\n          : Number(value);\n      }),\n      backgroundColor: dietMonthlyColors[serie.label] || '#cccccc',\n      borderWidth: 0,\n      stack: 'diet'\n    }));\n\n    dietMonthlyChartInstance = new Chart(canvas, {\n      type: 'bar',\n      data: {\n        labels: months,\n        datasets\n      },\n      options: {\n        responsive: true,\n        maintainAspectRatio: false,\n        plugins: {\n          legend: {\n            display: false\n          },\n          tooltip: {\n            callbacks: {\n              label: context =>\n                context.dataset.label + ' : ' + context.parsed.y + ' %'\n            }\n          }\n        },\n        scales: {\n          x: {\n            stacked: true,\n            grid: { display: false },\n            ticks: { maxRotation: 45, minRotation: 45 }\n          },\n          y: {\n            stacked: true,\n            min: 0,\n            max: 100,\n            title: {\n              display: true,\n              text: 'Pourcentage (%)'\n            },\n            ticks: {\n              callback: value => value + ' %'\n            }\n          }\n        }\n      }\n    });\n\n    if (window.spesApplyLanguage) window.spesApplyLanguage();\n\n    \/\/ L\u00e9gende des dix cat\u00e9gories.\n    result.series\n  .filter(serie =>\n    Object.values(serie.values || {}).some(value => Number(value) > 0)\n  )\n  .forEach(serie => {\n      const item = document.createElement('div');\n      item.className = 'legend-item';\n\n      const color = document.createElement('span');\n      color.className = 'legend-color';\n      color.style.backgroundColor =\n        dietMonthlyColors[serie.label] || '#cccccc';\n\n      const label = document.createElement('span');\n      label.textContent = serie.label;\n\n      item.appendChild(color);\n      item.appendChild(label);\n      legend.appendChild(item);\n    });\n\n    if (Array.isArray(result.warnings) && result.warnings.length) {\n      console.warn('SPES \u2014 r\u00e9gime mensuel :', result.warnings);\n    }\n\n  } catch (error) {\n    if (requestId !== dietMonthlyRequestId) return;\n    console.error('SPES \u2014 r\u00e9gime mensuel :', error);\n    container.textContent = 'Donn\u00e9es mensuelles indisponibles.';\n  }\n}\n\n  let temperatureChartInstance = null;\n  let aridityChartInstance = null;\n\n  window.spesUpdateChartLanguage = function(lang, translate) {\n    const en = lang === 'en';\n    const months = en ? ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'] : ['Jan','F\u00e9v','Mar','Avr','Mai','Juin','Juil','Ao\u00fbt','Sep','Oct','Nov','D\u00e9c'];\n    for (const chart of [dietMonthlyChartInstance, temperatureChartInstance, aridityChartInstance]) {\n      if (!chart) continue;\n      chart.data.labels = months;\n      chart.data.datasets.forEach(ds => {\n        if (!ds._spesOriginalLabel) ds._spesOriginalLabel = ds.label;\n        if (ds.label) ds.label = en ? translate(ds._spesOriginalLabel) : ds._spesOriginalLabel;\n      });\n      if (chart.options?.scales?.y?.title?.text) {\n        const t = chart.options.scales.y.title;\n        if (!t._spesOriginalText) t._spesOriginalText = t.text;\n        t.text = en ? translate(t._spesOriginalText) : t._spesOriginalText;\n      }\n      chart.update('none');\n    }\n  };\n\n  function parseClimateValue(value) {\n    if (value === undefined || value === null) return null;\n\n    const clean = value\n      .toString()\n      .trim()\n      .replace(',', '.');\n\n    if (!clean || clean === '#N\/A' || clean.toLowerCase() === 'na') return null;\n\n    const number = parseFloat(clean);\n    return isNaN(number) ? null : number;\n  }\n\n  function getClimateValue(espece, month, type, minMax) {\n    const m1 = String(month).padStart(2, '0');\n    const m2 = String(month);\n\n    const possibleKeys = [\n      `${m1}_${type}${minMax}`,\n      `${m2}_${type}${minMax}`,\n      `${m1}${type}${minMax}`,\n      `${m2}${type}${minMax}`,\n      `${m1}_${type}_${minMax}`,\n      `${m2}_${type}_${minMax}`\n    ];\n\n    for (const key of possibleKeys) {\n      if (Object.prototype.hasOwnProperty.call(espece, key)) {\n        return parseClimateValue(espece[key]);\n      }\n    }\n\n    return null;\n  }\n\n  function createClimateCharts(espece) {\n    const tempCanvas = document.getElementById('temperatureChart');\n    const aridCanvas = document.getElementById('aridityChart');\n\n    if (!tempCanvas || !aridCanvas || typeof Chart === 'undefined') return;\n\n    if (temperatureChartInstance) temperatureChartInstance.destroy();\n    if (aridityChartInstance) aridityChartInstance.destroy();\n\n    const mois = ['Jan', 'F\u00e9v', 'Mar', 'Avr', 'Mai', 'Juin', 'Juil', 'Ao\u00fbt', 'Sep', 'Oct', 'Nov', 'D\u00e9c'];\n\n    const tempMin = [];\n    const tempMax = [];\n    const tempMean = [];\n\n    const aridMin = [];\n    const aridMax = [];\n    const aridMean = [];\n\n    for (let i = 1; i <= 12; i++) {\n      const tMin = getClimateValue(espece, i, 'T', 'Min');\n      const tMax = getClimateValue(espece, i, 'T', 'Max');\n      const aMin = getClimateValue(espece, i, 'A', 'Min');\n      const aMax = getClimateValue(espece, i, 'A', 'Max');\n\n      tempMin.push(tMin);\n      tempMax.push(tMax);\n      tempMean.push(tMin !== null && tMax !== null ? (tMin + tMax) \/ 2 : null);\n\n      aridMin.push(aMin);\n      aridMax.push(aMax);\n      aridMean.push(aMin !== null && aMax !== null ? (aMin + aMax) \/ 2 : null);\n    }\n\n    temperatureChartInstance = drawRangeChart(\n      tempCanvas,\n      mois,\n      tempMin,\n      tempMax,\n      tempMean,\n      'Temp\u00e9rature moyenne',\n      'Temp\u00e9rature (\u00b0C)',\n      '#2f7d32',\n      'rgba(82,170,82,0.25)'\n    );\n\n   aridityChartInstance = drawRangeChart(\n  aridCanvas,\n  mois,\n  aridMin,\n  aridMax,\n  aridMean,\n  'Aridit\u00e9 moyenne',\n  'Indice d\u2019aridit\u00e9',\n  '#b7791f',\n  'rgba(211,151,45,0.25)',\n  true\n);\n  }\n\nfunction drawRangeChart(\n  canvas,\n  labels,\n  minData,\n  maxData,\n  meanData,\n  label,\n  yTitle,\n  darkColor,\n  lightColor,\n  autoScale = false\n) {\n  return new Chart(canvas, {\n    type: 'line',\n\n    data: {\n      labels: labels,\n\n      datasets: [\n        {\n          label: 'Maximum',\n          data: maxData,\n          borderColor: 'transparent',\n          backgroundColor: lightColor,\n          pointRadius: 0,\n          fill: '+1',\n          tension: 0.35\n        },\n\n        {\n          label: 'Minimum',\n          data: minData,\n          borderColor: 'transparent',\n          backgroundColor: lightColor,\n          pointRadius: 0,\n          fill: false,\n          tension: 0.35\n        }\n      ]\n    },\n\n    options: {\n      responsive: true,\n      maintainAspectRatio: false,\n\n      interaction: {\n        mode: 'index',\n        intersect: false\n      },\n\n      plugins: {\n        legend: {\n          display: false\n        },\n\n        tooltip: {\n          callbacks: {\n            label: function(context) {\n              if (context.raw === null) return '';\n              return `${context.dataset.label} : ${Number(context.raw).toFixed(2)}`;\n            }\n          }\n        }\n      },\n\n      scales: {\n        y: {\n          beginAtZero: !autoScale,\n          suggestedMin: autoScale ? undefined : 0,\n\n          title: {\n            display: true,\n            text: yTitle,\n            font: {\n              size: 18,\n              weight: '600'\n            }\n          },\n\n          ticks: {\n            font: {\n              size: 16\n            }\n          },\n\n          grid: {\n            color: 'rgba(0,0,0,0.08)'\n          }\n        },\n\n        x: {\n          ticks: {\n            font: {\n              size: 16\n            }\n          },\n\n          grid: {\n            color: 'rgba(0,0,0,0.05)'\n          }\n        }\n      }\n    }\n  });\n}\n  \n  \/\/ =======================================================\n  \/\/ \u2705 Recherche (inchang\u00e9e) + blocage par disabled input\n  \/\/ =======================================================\n  let selectedIndex = -1;\n  let currentSuggestions = [];\n\n  input.addEventListener('input', function () {\n    \/\/ si disabled, on sort\n    if (input.disabled) return;\n\n    const saisie = removeDiacritics(input.value.trim());\n    suggestionBox.innerHTML = '';\n    selectedIndex = -1;\n    currentSuggestions = [];\n\n    if (!saisie || !fuse) return;\n\n    let resultats = fuse.search(saisie);\n\n    resultats.sort((a, b) => {\n      const en = document.documentElement.getAttribute('data-spes-language') === 'en';\n      const aStr = removeDiacritics((en ? a.item.english2 : a.item.french) || a.item.latin || '');\n      const bStr = removeDiacritics((en ? b.item.english2 : b.item.french) || b.item.latin || '');\n\n      const aStarts = aStr.startsWith(saisie);\n      const bStarts = bStr.startsWith(saisie);\n\n      if (aStarts && !bStarts) return -1;\n      if (!aStarts && bStarts) return 1;\n      return 0;\n    });\n\n    currentSuggestions = resultats.slice(0, 10);\n\n    currentSuggestions.forEach((result, index) => {\n      const nom = decodeTexte((document.documentElement.getAttribute('data-spes-language') === 'en' ? result.item.english2 : result.item.french) || result.item.latin || '\u2013');\n      const nomLatin = decodeTexte(result.item.latin || '\u2013');\n\n      const div = document.createElement('div');\n      div.innerHTML = `<strong>${nom}<\/strong> \u2013 <em>${nomLatin}<\/em>`;\n      div.setAttribute('data-index', index);\n      div.addEventListener('click', () => {\n        input.value = nom;\n        suggestionBox.innerHTML = '';\n        chargerFicheEspece(result.item);\n      });\n      suggestionBox.appendChild(div);\n    });\n  });\n\n  input.addEventListener('keydown', function (e) {\n    if (input.disabled) return;\n\n    const suggestionItems = suggestionBox.querySelectorAll('div');\n    if (e.key === 'ArrowDown') {\n      e.preventDefault();\n      if (currentSuggestions.length === 0) return;\n      selectedIndex = (selectedIndex + 1) % currentSuggestions.length;\n      updateHighlight(suggestionItems);\n    } else if (e.key === 'ArrowUp') {\n      e.preventDefault();\n      if (currentSuggestions.length === 0) return;\n      selectedIndex = (selectedIndex - 1 + currentSuggestions.length) % currentSuggestions.length;\n      updateHighlight(suggestionItems);\n    } else if (e.key === 'Enter') {\n      e.preventDefault();\n      if (selectedIndex >= 0 && currentSuggestions[selectedIndex]) {\n        const selectedItem = currentSuggestions[selectedIndex].item;\n        input.value = decodeTexte((document.documentElement.getAttribute('data-spes-language') === 'en' ? selectedItem.english2 : selectedItem.french) || selectedItem.latin || '');\n        suggestionBox.innerHTML = '';\n        chargerFicheEspece(selectedItem);\n      } else {\n        const saisie = removeDiacritics(input.value.trim());\n        if (!saisie || !fuse) return;\n        const resultats = fuse.search(saisie);\n        if (resultats.length > 0) {\n          input.value = decodeTexte((document.documentElement.getAttribute('data-spes-language') === 'en' ? resultats[0].item.english2 : resultats[0].item.french) || resultats[0].item.latin || '');\n          chargerFicheEspece(resultats[0].item);\n          suggestionBox.innerHTML = '';\n        }\n      }\n    }\n  });\n\n      function chargerEspeceDepuisURL() {\n    const params = new URLSearchParams(window.location.search);\n    const especeParam = params.get('espece');\n    if (!especeParam || !donneesCSV || !donneesCSV.length) return;\n\n    const valeur = removeDiacritics(especeParam.trim());\n\n    const matchExact = donneesCSV.find(item =>\n      removeDiacritics(item.french || '') === valeur ||\n      removeDiacritics(item.english2 || '') === valeur ||\n      removeDiacritics(item.latin || '') === valeur\n    );\n\n    if (matchExact) {\n      chargerFicheEspece(matchExact);\n      return;\n    }\n\n    if (fuse) {\n      const resultats = fuse.search(valeur);\n      if (resultats.length > 0) {\n        chargerFicheEspece(resultats[0].item);\n      }\n    }\n  }\n  \n  function updateHighlight(items) {\n    items.forEach((item, index) => {\n      const isActive = index === selectedIndex;\n      item.classList.toggle('highlight', isActive);\n      if (isActive) {\n        item.scrollIntoView({ block: 'nearest', inline: 'nearest', behavior: 'smooth' });\n      }\n    });\n  }\n\n});\n<\/script>\n\n\n\n<script>\n\/* SPES FR\/EN: traductions de l'interface et des cat\u00e9gories normalis\u00e9es.\n   Le CSV et les appels REST sont inchang\u00e9s. *\/\n(function () {\n  const dictionary = {\n    'Description':'Description','Morphologie':'Morphology','Protection':'Conservation',\n    'R\u00e9gime alimentaire':'Diet','Strat\u00e9gies alimentaires':'Foraging strategies',\n    'Reproduction':'Breeding','Traits fonctionnels':'Functional traits',\n    'Noms':'Names','Classification':'Taxonomy','Carte de distribution':'Distribution map',\n    'R\u00e9gion':'Region','Altitude moyenne':'Mean altitude',\n    'Tropicale':'Tropical','Tropicale \u00e0 temp\u00e9r\u00e9e':'Tropical to temperate',\n    'Temp\u00e9r\u00e9e':'Temperate','Temp\u00e9r\u00e9e \u00e0 polaire':'Temperate to polar',\n    'Tropicale \u00e0 polaire':'Tropical to polar',\n    'R\u00e8gne':'Kingdom','Embranchement':'Phylum','Sous-embranchement':'Subphylum',\n    'Classe':'Class','Ordre':'Order','Famille':'Family',\n    'Donn\u00e9es morphologiques':'Morphological data','Envergure':'Wingspan','Poids':'Body mass',\n    'Mensurations':'Measurements','Donn\u00e9e indisponible':'Data unavailable',\n    '1 - Longueur du bec (culmen)':'1 - Bill length (culmen)',\n    '2 - Longueur du bec (narines)':'2 - Bill length (nares)',\n    '3 - Largeur du bec':'3 - Bill width',\n    '4 - \u00c9paisseur du bec':'4 - Bill depth',\n    '5 - Longueur du tarse':'5 - Tarsus length',\n    '6 - Longueur de l\u2019aile':'6 - Wing length',\n    '7 - Distance de Kipps':'7 - Kipp\u2019s distance',\n    '8 - Hand-wing index':'8 - Hand-wing index',\n    '9 - Longueur de la queue':'9 - Tail length',\n    'Envergure rapport\u00e9e':'Reported wingspan','Envergure estim\u00e9e':'Estimated wingspan',\n    'Statut de protection':'Conservation status','Tendance des populations':'Population trend',\n    'Statut UICN':'IUCN status','Annexes':'Appendices',\n    'Pr\u00e9occupation mineure':'Least Concern','Quasi menac\u00e9e':'Near Threatened',\n    'Vuln\u00e9rable':'Vulnerable','En danger':'Endangered',\n    'En danger critique':'Critically Endangered','\u00c9teint':'Extinct',\n    '\u00c9teint dans la nature':'Extinct in the Wild','Donn\u00e9es insuffisantes':'Data Deficient',\n    'En baisse':'Decreasing','En hausse':'Increasing','Stable':'Stable',\n    'Inconnue':'Unknown','Inconnu':'Unknown','Ordre inconnu':'Unknown order',\n    'Famille inconnue':'Unknown family',\n    'RYTHME CIRCADIEN':'CIRCADIAN RHYTHM','HABITAT':'HABITAT',\n    \/\/ Habitats : valeurs fran\u00e7aises affich\u00e9es \u00e0 partir du CSV.\n    'Ouvert':'Open','Ferm\u00e9':'Closed','Semi-ouvert':'Semi-open',\n    'Semi ouvert':'Semi-open','Milieu ouvert':'Open habitat',\n    'Milieu ferm\u00e9':'Closed habitat','Milieux ouverts':'Open habitats',\n    'Milieux ferm\u00e9s':'Closed habitats','For\u00eats denses':'Dense forests',\n    'For\u00eat dense':'Dense forest','For\u00eats claires':'Open forests',\n    'For\u00eat claire':'Open forest','For\u00eats ouvertes':'Open forests','For\u00eat ouverte':'Open forest','For\u00eat':'Forest','For\u00eats':'Forests',\n    'Zones humides':'Wetlands','Zone humide':'Wetland',\n    'Prairies':'Grasslands','Prairie':'Grassland',\n    'Savanes':'Savannas','Savane':'Savanna',\n    'D\u00e9serts':'Deserts','D\u00e9sert':'Desert',\n    'Montagnes':'Mountains','Montagne':'Mountain',\n    'Milieux aquatiques':'Aquatic habitats',\n    'Milieux agricoles':'Agricultural habitats',\n    'Milieux urbains':'Urban habitats',\n    'Buissons':'Shrublands','Broussailles':'Scrublands',\n    'Arbustif':'Shrubland','Arboricole':'Arboreal',\n    'Terrestre':'Terrestrial','Aquatique':'Aquatic',\n    'MIGRATION':'MIGRATION','SOCIALIT\u00c9':'SOCIALITY','LONG\u00c9VIT\u00c9':'LONGEVITY',\n    'Niche thermique':'Thermal niche','Niche d\u2019aridit\u00e9':'Aridity niche',\n    'Temp\u00e9rature moyenne':'Mean temperature','Temp\u00e9rature (\u00b0C)':'Temperature (\u00b0C)',\n    'Aridit\u00e9 moyenne':'Mean aridity','Indice d\u2019aridit\u00e9':'Aridity index',\n    'Pourcentage (%)':'Percentage (%)',\n    '\u00c9tat naturel':'In the wild','En captivit\u00e9':'In captivity',\n    'Esp\u00e8ce coloniale':'Colonial species','Esp\u00e8ce sociale':'Social species',\n    'Esp\u00e8ce vivant en couple ou groupes familiaux':'Lives in pairs or family groups',\n    'Esp\u00e8ce vivant seule ou en couple':'Lives alone or in pairs',\n    'Esp\u00e8ce vivant seule':'Solitary species','Esp\u00e8ce avec syst\u00e8me de lek':'Lek-breeding species',\n    \/\/ Bloc reproduction : libell\u00e9s et cat\u00e9gories provenant des codes du CSV.\n    'Nid':'Nest','Type de nid':'Nest type','Substrat':'Substrate',\n    'Envol des jeunes':'Fledging age',\n    'Elevage solitaire':'Unassisted parental care',\n    '\u00c9levage solitaire':'Unassisted parental care',\n    'Monogame et polygame':'Monogamous and polygamous',\n    'Terrier':'Burrow','Nid en coupe':'Cup nest',\n    'Demi-coupe peu profonde':'Shallow half-cup nest',\n    'Pas de nid':'No nest','Plateforme':'Platform nest',\n    'Nid suspendu (en sac)':'Pendant nest',\n    'Nid en soucoupe':'Saucer nest','Nid en monticule':'Mound nest',\n    'Bambou':'Bamboo','Souche':'Tree stump','Sol':'Ground',\n    'Cactus':'Cactus','Rocher \/ paroi rocheuse':'Rock \/ cliff',\n    'Arbuste, buisson ou liane':'Shrub, bush or vine',\n    'Arbre':'Tree',\n    'Strat\u00e9gie de reproduction':'Breeding strategy','Nombre d\u2019\u0153ufs':'Clutch size',\n    \"Nombre d'\u0153ufs\":'Clutch size','Syst\u00e8me d\u2019accouplement':'Mating system',\n    \"M\u00e9thode d'\u00e9levage\":'Parental care',\n    '\u00c9levage coop\u00e9ratif':'Cooperative breeding','Elevage coop\u00e9ratif':'Cooperative breeding',\n    'Aid\u00e9e par la famille':'Assisted by family','Aid\u00e9e occasionnellement':'Occasional helpers',\n    'Fissure \/ anfractuosit\u00e9 rocheuse':'Rock crevice',\n    'Cavit\u00e9 arboricole':'Tree cavity','D\u00f4me':'Dome',\n    'Nid d\u2019une autre esp\u00e8ce':'Nest of another species',\n    'Grattage \/ d\u00e9pression sommaire':'Scrape \/ shallow depression',\n    'Nid sph\u00e9rique \/ globulaire':'Spherical \/ globular nest',\n    'B\u00e2timent \/ construction humaine':'Building \/ human-made structure',\n    'Nid pr\u00e9existant':'Existing nest','Poteau \/ m\u00e2t':'Pole \/ mast',\n    'Eau (flottant ou v\u00e9g\u00e9tation aquatique)':'Water (floating or aquatic vegetation)',\n    'Herbac\u00e9es \/ gramin\u00e9es':'Herbaceous vegetation \/ grasses',\n    'Composition du r\u00e9gime alimentaire':'Diet composition',\n    'Variations saisonni\u00e8res du r\u00e9gime alimentaire':'Seasonal variation in diet',\n    'Strat\u00e9gie de recherche alimentaire':'Foraging strategy',\n    'Lieux de prise alimentaire':'Foraging locations',\n    \"Strat\u00e9gies d'acquisition alimentaire\":'Food acquisition strategies',\n    'Invert\u00e9br\u00e9s':'Invertebrates','Vert\u00e9br\u00e9s endothermes':'Endothermic vertebrates',\n    'Vert\u00e9br\u00e9s ectothermes':'Ectothermic vertebrates','Poissons':'Fish',\n    'Vert\u00e9br\u00e9s inconnus':'Unidentified vertebrates','Charognes':'Carrion',\n    'Fruits':'Fruit','Fruit':'Fruit','Nectars':'Nectar','Graines':'Seeds','V\u00e9g\u00e9taux':'Plant material',\n    'Sous l\u2019eau':'Underwater','Autour surface eau':'At the water surface',\n    'Au sol':'On the ground','Sous-bois':'Understorey',\n    'Milieu de canop\u00e9e':'Mid-canopy','Canop\u00e9e':'Canopy','Dans les airs':'In flight',\n    'Femelle':'Female','M\u00e2le':'Male','Poids non sex\u00e9':'Unsexed mass',\n    'Chargement\u2026':'Loading\u2026','Chargement des donn\u00e9es AVONICHE\u2026':'Loading AVONICHE data\u2026',\n    'Chargement des variations mensuelles\u2026':'Loading monthly variation\u2026',\n    'Donn\u00e9es mensuelles indisponibles.':'Monthly data unavailable.',\n    'Aucune strat\u00e9gie alimentaire renseign\u00e9e.':'No foraging strategy available.',\n    'Donn\u00e9es indisponibles pour cette esp\u00e8ce.':'No data available for this species.',\n    'Impossible de charger les donn\u00e9es AVONICHE.':'Unable to load AVONICHE data.',\n    'Nom scientifique indisponible.':'Scientific name unavailable.',\n    'Carte prochainement disponible':'Map coming soon',\n    'Pr\u00e9paration des fiches\u2026':'Preparing species sheets\u2026',\n    'La recherche s\u2019active \u00e0 100%.':'Search will be available when loading is complete.',\n    'On apprivoise les donn\u00e9es\u2026':'Preparing the data\u2026',\n    'Maximum':'Maximum','Minimum':'Minimum',\n    'Jan':'Jan','F\u00e9v':'Feb','Mar':'Mar','Avr':'Apr','Mai':'May','Juin':'Jun',\n    'Juil':'Jul','Ao\u00fbt':'Aug','Sep':'Sep','Oct':'Oct','Nov':'Nov','D\u00e9c':'Dec',\n    'Diurne':'Diurnal','Nocturne':'Nocturnal','Cr\u00e9pusculaire':'Crepuscular',\n    'S\u00e9dentaire':'Resident','Migrateur':'Migratory','Migratrice':'Migratory',\n    'For\u00eat':'Forest','For\u00eats':'Forests','Prairie':'Grassland',\n    'Monogame':'Monogamous','Polygame':'Polygamous'\n  };\n  const avoniche = {\n    'V\u00e9g\u00e9taux aquatiques':'Aquatic plants','V\u00e9g\u00e9taux terrestres':'Terrestrial plants',\n    'Nectar':'Nectar','Graines':'Seeds','Fruits':'Fruit','Fruit':'Fruit',\n    'Invert\u00e9br\u00e9s':'Invertebrates','Proies aquatiques':'Aquatic prey',\n    'Vert\u00e9br\u00e9s':'Vertebrates','Charognes':'Carrion',\n    'pr\u00e9l\u00e8vement en eau peu profonde':'collected in shallow water',\n    'pr\u00e9l\u00e8vement en nageant':'collected while swimming',\n    'pr\u00e9l\u00e8vement en plong\u00e9e':'collected while diving',\n    'pr\u00e9l\u00e8vement en hauteur':'collected above ground',\n    'pr\u00e9l\u00e8vement au sol':'collected on the ground',\n    'pr\u00e9l\u00e8vement en vol':'collected in flight',\n    'pr\u00e9l\u00e8vement depuis un perchoir':'collected from a perch',\n    'pr\u00e9l\u00e8vement sur la v\u00e9g\u00e9tation':'collected from vegetation',\n    'capture en vol continu':'captured during continuous flight',\n    'capture a\u00e9rienne depuis un perchoir':'aerial capture from a perch',\n    'capture sur un substrat par attaque a\u00e9rienne':'captured on a substrate by aerial attack',\n    'capture au sol par attaque a\u00e9rienne':'captured on the ground by aerial attack',\n    'capture sur substrat vertical':'captured on a vertical substrate',\n    'capture en vol':'captured in flight',\n    'capture par plongeon a\u00e9rien':'captured by plunge-diving',\n    'capture depuis un perchoir':'captured from a perch',\n    'capture en eau peu profonde':'captured in shallow water',\n    'capture \u00e0 la surface de l\u2019eau':'captured at the water surface',\n    'capture en plong\u00e9e':'captured while diving',\n    'capture au sol ou sur support depuis le vol':'captured on the ground or a substrate from flight',\n    'capture au sol':'captured on the ground',\n    'pr\u00e9l\u00e8vement sur support en hauteur':'collected on elevated substrate',\n    'consommation en milieu aquatique':'consumed in aquatic habitat',\n    'consommation au sol':'consumed on the ground'\n  };\n  let language = 'fr';\n  try {language = localStorage.getItem('spes-language') === 'en' ? 'en' : 'fr';} catch (_) {}\n  const originals = new WeakMap();\n  function translate(value) {\n    const raw = String(value);\n    const trimmed = raw.trim();\n    if (!trimmed) return raw;\n    if (\/^(?:#N\\\/A|N\\\/A|NA)$\/i.test(trimmed)) return raw.replace(trimmed, '\u2013');\n    let translated = dictionary[trimmed];\n    \/\/ Les libell\u00e9s des cartes contiennent un deux-points (ex. \u00ab R\u00e9gion : \u00bb).\n    \/\/ Retirer temporairement la ponctuation pour rechercher la traduction.\n    if (!translated) {\n      const matchLabel = trimmed.match(\/^(.+?)(\\s*[:\uff1a])$\/);\n      if (matchLabel && dictionary[matchLabel[1].trim()]) {\n        translated = dictionary[matchLabel[1].trim()] + matchLabel[2];\n      }\n    }\n    \/\/ Les valeurs multichoix du CSV sont souvent s\u00e9par\u00e9es par des virgules\n    \/\/ (ex. \u00ab D\u00f4me, Nid suspendu (en sac) \u00bb) : traduire chaque cat\u00e9gorie.\n    if (!translated && trimmed.includes(',')) {\n      const segments = trimmed.split(\/(,\\s*)\/);\n      let changed = false;\n      const mapped = segments.map(segment => {\n        if (\/^,\\s*$\/.test(segment)) return segment;\n        const key = segment.trim();\n        const replacement = dictionary[key] || avoniche[key];\n        if (!replacement) return segment;\n        changed = true;\n        return segment.replace(key, replacement);\n      });\n      if (changed) translated = mapped.join('');\n    }\n    \/\/ L\u00e9gendes de graphiques : \u00ab Fruit \u2013 10 % \u00bb, \u00ab Fruits \u2013 10 % \u00bb.\n    if (!translated) {\n      const percentageLegend = trimmed.match(\/^(.+?)(\\s*[\u2013-]\\s*\\d+(?:[.,]\\d+)?\\s*%)$\/);\n      if (percentageLegend) {\n        const label = percentageLegend[1].trim();\n        const mapped = dictionary[label] || avoniche[label];\n        if (mapped) translated = mapped + percentageLegend[2];\n      }\n    }\n    if (!translated && trimmed.includes(' \u2013 ')) {\n      const parts = trimmed.split(' \u2013 ');\n      if (parts.length === 2 && avoniche[parts[0]] && avoniche[parts[1]])\n        translated = avoniche[parts[0]] + ' \u2013 ' + avoniche[parts[1]];\n    }\n    if (!translated) {\n      const longevity = trimmed.match(\/^(\\d+(?:[.,]\\d+)?) ans$\/);\n      if (longevity) translated = longevity[1] + ' years';\n      const days = trimmed.match(\/^(\\d+(?:[.,]\\d+)?(?:[\u2013-]\\d+(?:[.,]\\d+)?)?) j$\/);\n      if (days) translated = days[1] + ' days';\n    }\n    if (!translated || translated === trimmed) return raw;\n    return raw.replace(trimmed,translated);\n  }\n  function walk(node) {\n    if (node.nodeType === 3) {\n      const parent = node.parentElement;\n      if (!parent || parent.closest('script,style,textarea,input,select,option')) return;\n      if (!originals.has(node)) originals.set(node,node.nodeValue);\n      const original = originals.get(node);\n      \/\/ Une donn\u00e9e manquante doit appara\u00eetre sous forme de tiret dans les deux langues.\n      const missing = \/^(?:#N\\\/A|N\\\/A|NA)$\/i.test(original.trim());\n      const wanted = missing ? original.replace(original.trim(), '\u2013') : (language === 'en' ? translate(original) : original);\n      if (node.nodeValue !== wanted) node.nodeValue = wanted;\n      return;\n    }\n    if (node.nodeType !== 1) return;\n    if (node.matches('script,style,textarea,input,select,option')) return;\n    for (const child of node.childNodes) walk(child);\n    if (node.hasAttribute('aria-label')) {\n      if (!node.dataset.spesOriginalAria) node.dataset.spesOriginalAria = node.getAttribute('aria-label');\n      node.setAttribute('aria-label', language === 'en' ? translate(node.dataset.spesOriginalAria) : node.dataset.spesOriginalAria);\n    }\n  }\n  function syncSpeciesName() {\n    const s = window.spesCurrentSpecies;\n    const node = document.querySelector('#nom-principal .nom-fr');\n    if (!node || !s) return;\n    const wanted = language === 'en' ? (s.english2 || s.french || '\u2013') : (s.french || '\u2013');\n    if (node.textContent !== wanted) node.textContent = wanted;\n    \/\/ Dans NAMES, montrer l'autre langue : fran\u00e7ais en EN, anglais en FR.\n    const namesRow = document.querySelector('#bloc-noms p');\n    if (namesRow) {\n      const flag = namesRow.querySelector('.flag');\n      if (flag) flag.textContent = language === 'en' ? '\ud83c\uddeb\ud83c\uddf7' : '\ud83c\uddec\ud83c\udde7';\n      \/\/ Pr\u00e9server la structure et \u00e9viter de remplacer le nom dans le titre principal.\n      let nameText = Array.from(namesRow.childNodes).find(n => n.nodeType === 3);\n      if (!nameText) {\n        nameText = document.createTextNode('');\n        namesRow.appendChild(nameText);\n      }\n      const alternate = language === 'en' ? (s.french || '\u2013') : (s.english2 || '\u2013');\n      if (nameText.nodeValue !== ' ' + alternate) nameText.nodeValue = ' ' + alternate;\n    }\n  }\n  function apply() {\n    const fiche = document.getElementById('fiche-espece');\n    if (fiche) walk(fiche);\n    const overlay = document.getElementById('loading-overlay');\n    if (overlay) walk(overlay);\n    const btns = document.querySelectorAll('#spes-language-switch [data-spes-lang]');\n    btns.forEach(btn => btn.setAttribute('aria-pressed',String(btn.dataset.spesLang === language)));\n    syncSpeciesName();\n    \/\/ Basculer l'image UICN selon la langue, sans toucher aux indicateurs superpos\u00e9s.\n    const uicnImage = document.querySelector('#bloc-uicn .uicn-bar-image');\n    if (uicnImage) {\n      const imageFR = 'https:\/\/fiches-especes.simonpotier.fr\/wp-content\/uploads\/2025\/12\/UICN-Categories-scaled.png';\n      const imageEN = 'https:\/\/spes.anivise.fr\/wp-content\/uploads\/2026\/10\/UICN-Categories-ENG-scaled.png';\n      const desired = language === 'en' ? imageEN : imageFR;\n      if (uicnImage.getAttribute('src') !== desired) uicnImage.setAttribute('src', desired);\n      uicnImage.alt = language === 'en' ? 'IUCN Red List categories' : 'Barre des statuts UICN';\n    }\n    document.documentElement.setAttribute('data-spes-language', language);\n    \/\/ Placeholder du moteur : attribut HTML, donc non traduit par le parcours des textes.\n    document.querySelectorAll('#search-espece input[type=\"text\"], #search-espece input:not([type])').forEach(field => {\n      if (!field.dataset.spesPlaceholderFR) field.dataset.spesPlaceholderFR = field.getAttribute('placeholder') || '\u00c9crire ici';\n      const desired = language === 'en' ? 'Type here' : field.dataset.spesPlaceholderFR;\n      if (field.getAttribute('placeholder') !== desired) field.setAttribute('placeholder', desired);\n    });\n    if (typeof window.spesUpdateChartLanguage === 'function') window.spesUpdateChartLanguage(language,translate);\n  }\n  window.spesApplyLanguage = apply;\n  document.querySelectorAll('#spes-language-switch [data-spes-lang]').forEach(btn => {\n    btn.addEventListener('click', () => {\n      language = btn.dataset.spesLang;\n      try {localStorage.setItem('spes-language',language);} catch (_) {}\n      apply();\n      if (typeof window.spesUpdateLoadingLine === 'function') window.spesUpdateLoadingLine();\n      window.dispatchEvent(new Event('spes-language-changed'));\n    });\n  });\n  const target = document.getElementById('fiche-espece');\n  if (target) {\n    let scheduled = false;\n    new MutationObserver(() => {\n      if (scheduled) return;\n      scheduled = true;\n      requestAnimationFrame(() => {scheduled = false;apply();});\n    }).observe(target,{subtree:true,childList:true});\n  }\n  apply();\n  window.dispatchEvent(new Event('spes-language-changed'));\n})();\n<\/script>\n\n<script>\n\/* SPES : navigation fixe au d\u00e9filement, m\u00eame avec les transforms d'Elementor \/ Bridge. *\/\n(function () {\n  const nav = document.getElementById('spes-nav-fiche');\n  const fiche = document.getElementById('fiche-espece');\n  if (!nav || !fiche) return;\n\n  const originalParent = nav.parentNode;\n  const spacer = document.createElement('div');\n  spacer.id = 'spes-nav-placeholder';\n  spacer.style.display = 'none';\n  originalParent.insertBefore(spacer, nav);\n  let fixed = false;\n  const topOffset = () => 0; \/\/ Modifier si l'en-t\u00eate Bridge est fixe.\n\n  function unfix() {\n    if (!fixed) return;\n    nav.classList.remove('spes-nav-fixed');\n    nav.style.removeProperty('--spes-nav-left');\n    nav.style.removeProperty('--spes-nav-width');\n    nav.style.removeProperty('--spes-nav-top');\n    originalParent.insertBefore(nav, spacer.nextSibling);\n    spacer.style.display = 'none';\n    fixed = false;\n  }\n\n  function updateFixed() {\n    if (getComputedStyle(nav).display === 'none') {\n      unfix();\n      return;\n    }\n    \/\/ Le rep\u00e8re reste dans le flux de la fiche, contrairement \u00e0 une barre sticky.\n    const trigger = fixed ? spacer.getBoundingClientRect().top : nav.getBoundingClientRect().top;\n    const shouldFix = trigger <= topOffset();\n    if (shouldFix && !fixed) {\n      spacer.style.height = nav.offsetHeight + 'px';\n      spacer.style.display = 'block';\n      \/\/ Sortir du parent transform\u00e9 : position:fixed est sinon relative \u00e0 Elementor.\n      document.body.appendChild(nav);\n      nav.classList.add('spes-nav-fixed');\n      fixed = true;\n    } else if (!shouldFix && fixed) {\n      unfix();\n    }\n    if (fixed) {\n      const rect = fiche.getBoundingClientRect();\n      nav.style.setProperty('--spes-nav-left', rect.left + 'px');\n      nav.style.setProperty('--spes-nav-width', rect.width + 'px');\n      nav.style.setProperty('--spes-nav-top', topOffset() + 'px');\n      spacer.style.height = nav.offsetHeight + 'px';\n    }\n  }\n\n  const links = Array.from(nav.querySelectorAll('a[href^=\"#\"]'));\n  const targetOf = link => document.getElementById(link.getAttribute('href').slice(1));\n  const offset = () => nav.offsetHeight + topOffset() + 18;\n  function setActive(link) {\n    links.forEach(a => {\n      a.classList.toggle('is-active', a === link);\n      if (a === link) a.setAttribute('aria-current', 'location');\n      else a.removeAttribute('aria-current');\n    });\n  }\n  links.forEach(link => link.addEventListener('click', event => {\n    const target = targetOf(link);\n    if (!target) return;\n    event.preventDefault();\n    setActive(link);\n    const y = target.getBoundingClientRect().top + window.scrollY - offset();\n    window.scrollTo({top: Math.max(0, y), behavior: window.matchMedia('(prefers-reduced-motion: reduce)').matches ? 'auto' : 'smooth'});\n  }));\n\n  function updateActive() {\n    if (getComputedStyle(nav).display === 'none') return;\n    const threshold = offset() + 35;\n    let current = links[0];\n    links.forEach(link => {\n      const target = targetOf(link);\n      if (target && target.getClientRects().length && target.getBoundingClientRect().top <= threshold) current = link;\n    });\n    setActive(current);\n  }\n  let pending = false;\n  function schedule() {\n    if (pending) return;\n    pending = true;\n    requestAnimationFrame(() => { pending = false; updateFixed(); updateActive(); });\n  }\n  window.addEventListener('scroll', schedule, {passive:true});\n  window.addEventListener('resize', schedule, {passive:true});\n  schedule();\n})();\n<\/script>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-ffc61b9 elementor-widget elementor-widget-html\" data-id=\"ffc61b9\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<!-- Librairies pour l'export -->\r\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/jspdf\/2.5.1\/jspdf.umd.min.js\"><\/script>\r\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/html2canvas\/1.4.1\/html2canvas.min.js\"><\/script>\r\n\r\n<script>\r\n  document.addEventListener('DOMContentLoaded', function () {\r\n    const btnExport = document.getElementById('btn-export-pdf');\r\n\r\n    if (!btnExport) {\r\n      console.warn(\"\ud83d\udd34 Bouton #btn-export-pdf introuvable\");\r\n      return;\r\n    }\r\n\r\n    btnExport.addEventListener('click', async () => {\r\n      const elements = [\r\n        document.getElementById('bloc-noms'),\r\n        document.getElementById('bloc-classification'),\r\n        document.getElementById('bloc-rythme'),\r\n        document.getElementById('bloc-regime'),\r\n        document.getElementById('bloc-uicn'),\r\n        document.getElementById('morphologie') \/\/ \u2705 Bloc morphologie ajout\u00e9 ici\r\n      ];\r\n\r\n      const { jsPDF } = window.jspdf;\r\n      const doc = new jsPDF('p', 'mm', 'a4');\r\n      let positionY = 10;\r\n\r\n      for (const el of elements) {\r\n        if (!el) continue;\r\n\r\n        const canvas = await html2canvas(el, { scale: 2 });\r\n        const imgData = canvas.toDataURL('image\/png');\r\n\r\n        const imgWidth = 180;\r\n        const imgHeight = (canvas.height * imgWidth) \/ canvas.width;\r\n\r\n        if (positionY + imgHeight > 280) {\r\n          doc.addPage();\r\n          positionY = 10;\r\n        }\r\n\r\n        doc.addImage(imgData, 'PNG', 15, positionY, imgWidth, imgHeight);\r\n        positionY += imgHeight + 10;\r\n      }\r\n\r\n      doc.save('fiche-espece.pdf');\r\n    });\r\n  });\r\n<\/script>\r\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-b63641d e-flex e-con-boxed parallax_section_no qode_elementor_container_no e-con e-parent\" data-id=\"b63641d\" data-element_type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-9e7cade elementor-widget elementor-widget-html\" data-id=\"9e7cade\" data-element_type=\"widget\" id=\"bloc-noms\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t \t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-3bffc9e elementor-widget elementor-widget-html\" data-id=\"3bffc9e\" data-element_type=\"widget\" id=\"bloc-classification\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t \t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-d6485e4 e-flex e-con-boxed parallax_section_no qode_elementor_container_no e-con e-parent\" data-id=\"d6485e4\" data-element_type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-2e9f183 elementor-widget__width-inherit elementor-widget elementor-widget-html\" data-id=\"2e9f183\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<div id=\"bloc-uicn\"><\/div>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-78be4bb elementor-widget__width-inherit elementor-widget elementor-widget-html\" data-id=\"78be4bb\" data-element_type=\"widget\" id=\"bloc-regime\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t \t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-82eb339 elementor-widget__width-inherit elementor-widget elementor-widget-html\" data-id=\"82eb339\" data-element_type=\"widget\" id=\"bloc-rythme\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t \t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-49ceeda e-flex e-con-boxed parallax_section_no qode_elementor_container_no e-con e-parent\" data-id=\"49ceeda\" data-element_type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-e4ee36f elementor-widget__width-inherit elementor-widget elementor-widget-html\" data-id=\"e4ee36f\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\r\n<div id=\"morphologie\">\r\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-cb0cc33 e-flex e-con-boxed parallax_section_no qode_elementor_container_no e-con e-parent\" data-id=\"cb0cc33\" data-element_type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-0bcfb53 elementor-widget elementor-widget-html\" data-id=\"0bcfb53\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<script>\r\n  \/\/ \u274c D\u00e9sactiver s\u00e9lection de texte\r\n  document.addEventListener('selectstart', e => e.preventDefault());\r\n\r\n  \/\/ \u274c D\u00e9sactiver glisser-d\u00e9poser\r\n  document.addEventListener('dragstart', e => e.preventDefault());\r\n\r\n  \/\/ \u274c R\u00e9action \u00e0 la touche \"Print Screen\"\r\n  document.addEventListener('keydown', function(e) {\r\n    if (e.key === 'PrintScreen') {\r\n      alert(\"La capture d\u2019\u00e9cran est d\u00e9sactiv\u00e9e sur ce site.\");\r\n      document.body.style.filter = \"blur(10px)\";\r\n      setTimeout(() => {\r\n        document.body.style.filter = \"none\";\r\n      }, 2000);\r\n    }\r\n  });\r\n  \r\n    \/\/ \u274c R\u00e9sactiver  Ctrl+P\r\n  document.addEventListener('keydown', function(e) {\r\n  if ((e.ctrlKey || e.metaKey) && e.key === 'p') {\r\n    e.preventDefault();\r\n    alert(\"L'impression est d\u00e9sactiv\u00e9e sur ce site.\");\r\n  }\r\n});\r\n\r\n<\/script>\r\n\r\n\r\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-9f4a56e e-flex e-con-boxed parallax_section_no qode_elementor_container_no e-con e-parent\" data-id=\"9f4a56e\" data-element_type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-7b6ccf5 elementor-widget elementor-widget-html\" data-id=\"7b6ccf5\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<?php echo do_shortcode('[fiches_rest_nonce]'); ?>\r\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Pr\u00e9paration des fiches\u2026 0% On apprivoise les donn\u00e9es\u2026 La recherche s\u2019active \u00e0 100%. FR EN Description Morphologie Protection R\u00e9gime alimentaire Strat\u00e9gies alimentaires Reproduction Traits fonctionnels<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"pmpro_default_level":"","footnotes":""},"class_list":["post-1868","page","type-page","status-publish","hentry","pmpro-has-access"],"_links":{"self":[{"href":"https:\/\/spes.anivise.fr\/index.php\/wp-json\/wp\/v2\/pages\/1868","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/spes.anivise.fr\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/spes.anivise.fr\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/spes.anivise.fr\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/spes.anivise.fr\/index.php\/wp-json\/wp\/v2\/comments?post=1868"}],"version-history":[{"count":244,"href":"https:\/\/spes.anivise.fr\/index.php\/wp-json\/wp\/v2\/pages\/1868\/revisions"}],"predecessor-version":[{"id":2232,"href":"https:\/\/spes.anivise.fr\/index.php\/wp-json\/wp\/v2\/pages\/1868\/revisions\/2232"}],"wp:attachment":[{"href":"https:\/\/spes.anivise.fr\/index.php\/wp-json\/wp\/v2\/media?parent=1868"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}