{"id":62006,"date":"2026-08-04T19:07:46","date_gmt":"2026-08-04T17:07:46","guid":{"rendered":"https:\/\/alertes-meteo.com\/?page_id=62006"},"modified":"2026-08-05T07:53:55","modified_gmt":"2026-08-05T05:53:55","slug":"carte-historique-des-incendies","status":"publish","type":"page","link":"https:\/\/alertes-meteo.com\/index.php\/la-france-face-aux-feux-de-foret\/carte-historique-des-incendies\/","title":{"rendered":"CARTE HISTORIQUE DES INCENDIES"},"content":{"rendered":"<div class=\"fusion-fullwidth fullwidth-box fusion-builder-row-1 fusion-flex-container nonhundred-percent-fullwidth non-hundred-percent-height-scrolling\" style=\"--awb-border-radius-top-left:0px;--awb-border-radius-top-right:0px;--awb-border-radius-bottom-right:0px;--awb-border-radius-bottom-left:0px;--awb-flex-wrap:wrap;\" ><div class=\"fusion-builder-row fusion-row fusion-flex-align-items-flex-start fusion-flex-content-wrap\" style=\"max-width:1575px;margin-left: calc(-5% \/ 2 );margin-right: calc(-5% \/ 2 );\"><div class=\"fusion-layout-column fusion_builder_column fusion-builder-column-0 fusion_builder_column_1_1 1_1 fusion-flex-column fusion-flex-align-self-center contenu-echelle\" style=\"--awb-bg-blend:overlay;--awb-bg-size:cover;--awb-width-large:100%;--awb-margin-top-large:0px;--awb-spacing-right-large:2.375%;--awb-margin-bottom-large:0px;--awb-spacing-left-large:2.375%;--awb-width-medium:100%;--awb-order-medium:0;--awb-spacing-right-medium:2.375%;--awb-spacing-left-medium:2.375%;--awb-width-small:100%;--awb-order-small:0;--awb-spacing-right-small:2.375%;--awb-spacing-left-small:2.375%;\" data-scroll-devices=\"small-visibility,medium-visibility,large-visibility\"><div class=\"fusion-column-wrapper fusion-column-has-shadow fusion-flex-justify-content-flex-start fusion-content-layout-column\"><div class=\"fusion-text fusion-text-1\"><p class=\"my-2 &#091;&amp;+p&#093;:mt-4 &#091;&amp;_strong:has(+br)&#093;:inline-block &#091;&amp;_strong:has(+br)&#093;:align-top\">CARTE HISTORIQUE DES INCENDIES<\/p>\n<\/div><!--\n  CARTE HISTORIQUE DES INCENDIES \u2014 HTML \/ WORDPRESS \/ AVADA\n  Version 1.2 \u2014 compatibilit\u00e9 Avada, d\u00e9marrage s\u00e9curis\u00e9 et sources CDN de secours\n\n  Installation :\n  1) Collez tout ce fichier dans un \u00e9l\u00e9ment \u00ab Code Block \u00bb Avada ou un bloc HTML personnalis\u00e9.\n  2) Par d\u00e9faut, la carte charge la couche publique BDIFF 2022 diffus\u00e9e par IGN\/G\u00e9orisques\/Esri France.\n  3) Pour exploiter votre export BDIFF complet, t\u00e9l\u00e9versez le CSV dans WordPress puis renseignez\n     son URL dans CONFIG.csvUrl, au d\u00e9but du script. Le parseur reconna\u00eet les principaux intitul\u00e9s\n     de colonnes BDIFF (ann\u00e9e, d\u00e9partement, code INSEE, commune, date, surface, cause, d\u00e9c\u00e8s,\n     b\u00e2timents, latitude et longitude).\n  4) Apr\u00e8s remplacement du code, videz les caches Avada\/WordPress\/CDN. Le bloc utilis\u00e9 doit\n     autoriser les balises <script> (Code Block Avada ou HTML personnalis\u00e9 avec droits administrateur).\n\n  D\u00e9pendances charg\u00e9es depuis jsDelivr : Leaflet 1.9.4, Chart.js 4.4.7 et Papa Parse 5.4.1.\n-->\n\n<div id=\"mcpfh-root\" class=\"mcpfh\" data-ready=\"false\">\n  <section class=\"mcpfh-stage\" aria-label=\"Historique cartographique des incendies\">\n    <header class=\"mcpfh-map-title\">\n      <span class=\"mcpfh-map-title-icon\" aria-hidden=\"true\">\ud83d\udd25<\/span>\n      <span>\n        <strong>Historique des incendies en France<\/strong>\n        <small id=\"mcpfh-source-badge\">Chargement des donn\u00e9es\u2026<\/small>\n      <\/span>\n    <\/header>\n\n    <button class=\"mcpfh-mobile-filter\" id=\"mcpfh-mobile-filter\" type=\"button\" aria-controls=\"mcpfh-filters\" aria-expanded=\"false\">\n      <span aria-hidden=\"true\">\u2630<\/span> Filtres\n    <\/button>\n\n    <div id=\"mcpfh-map\" class=\"mcpfh-map\" aria-label=\"Carte interactive des incendies\"><\/div>\n\n    <aside class=\"mcpfh-filters\" id=\"mcpfh-filters\" aria-label=\"Filtres de la carte\">\n      <div class=\"mcpfh-panel-head\">\n        <h2>Filtres<\/h2>\n        <button id=\"mcpfh-filter-close\" class=\"mcpfh-icon-btn\" type=\"button\" aria-label=\"Fermer les filtres\">\u00d7<\/button>\n      <\/div>\n\n      <div class=\"mcpfh-segmented\" role=\"group\" aria-label=\"Indicateur cartographique\">\n        <button type=\"button\" class=\"is-active\" data-mcpfh-metric=\"count\" aria-pressed=\"true\">\n          <span aria-hidden=\"true\">\u25cf<\/span> Feux\n        <\/button>\n        <button type=\"button\" data-mcpfh-metric=\"surface\" aria-pressed=\"false\">\n          <span aria-hidden=\"true\">\u25a6<\/span> Surface\n        <\/button>\n      <\/div>\n\n      <div class=\"mcpfh-kpis\" aria-live=\"polite\">\n        <div><strong id=\"mcpfh-kpi-count\">\u2014<\/strong><span>incendies<\/span><\/div>\n        <div><strong id=\"mcpfh-kpi-surface\">\u2014<\/strong><span>surface (ha)<\/span><\/div>\n        <div><strong id=\"mcpfh-kpi-deaths\">\u2014<\/strong><span>d\u00e9c\u00e8s<\/span><\/div>\n        <div><strong id=\"mcpfh-kpi-buildings\">\u2014<\/strong><span>b\u00e2timents<\/span><\/div>\n      <\/div>\n\n      <form id=\"mcpfh-form\" class=\"mcpfh-form\">\n        <fieldset class=\"mcpfh-fieldset\">\n          <legend>P\u00e9riode<\/legend>\n          <div class=\"mcpfh-year-values\">\n            <label>De <output id=\"mcpfh-year-min-output\">\u2014<\/output><\/label>\n            <label>\u00e0 <output id=\"mcpfh-year-max-output\">\u2014<\/output><\/label>\n          <\/div>\n          <div class=\"mcpfh-dual-range\" id=\"mcpfh-year-range\">\n            <span class=\"mcpfh-range-track\" aria-hidden=\"true\"><span id=\"mcpfh-range-fill\"><\/span><\/span>\n            <input id=\"mcpfh-year-min\" type=\"range\" min=\"2000\" max=\"2025\" value=\"2000\" aria-label=\"Ann\u00e9e de d\u00e9but\">\n            <input id=\"mcpfh-year-max\" type=\"range\" min=\"2000\" max=\"2025\" value=\"2025\" aria-label=\"Ann\u00e9e de fin\">\n          <\/div>\n        <\/fieldset>\n\n        <div class=\"mcpfh-field\">\n          <label for=\"mcpfh-dept\">D\u00e9partement<\/label>\n          <select id=\"mcpfh-dept\">\n            <option value=\"\">Tous les d\u00e9partements<\/option>\n          <\/select>\n        <\/div>\n\n        <div class=\"mcpfh-field\">\n          <label for=\"mcpfh-commune\">Commune<\/label>\n          <input id=\"mcpfh-commune\" type=\"search\" list=\"mcpfh-communes\" placeholder=\"Rechercher une commune\u2026\" autocomplete=\"off\">\n          <datalist id=\"mcpfh-communes\"><\/datalist>\n        <\/div>\n\n        <fieldset class=\"mcpfh-fieldset\">\n          <legend>Causes<\/legend>\n          <div class=\"mcpfh-chips\" id=\"mcpfh-causes\">\n            <label><input type=\"checkbox\" value=\"Involontaire (particulier)\"><span>Involontaire (particulier)<\/span><\/label>\n            <label><input type=\"checkbox\" value=\"Involontaire (travaux)\"><span>Involontaire (travaux)<\/span><\/label>\n            <label><input type=\"checkbox\" value=\"Accidentelle\"><span>Accidentelle<\/span><\/label>\n            <label><input type=\"checkbox\" value=\"Malveillance\"><span>Malveillance<\/span><\/label>\n            <label><input type=\"checkbox\" value=\"Naturelle\"><span>Naturelle<\/span><\/label>\n            <label><input type=\"checkbox\" value=\"Inconnue\"><span>Inconnue<\/span><\/label>\n          <\/div>\n        <\/fieldset>\n\n        <fieldset class=\"mcpfh-fieldset\">\n          <legend>Surface br\u00fbl\u00e9e par feu<\/legend>\n          <div class=\"mcpfh-number-pair\">\n            <label><span>Minimum (ha)<\/span><input id=\"mcpfh-surface-min\" type=\"number\" min=\"0\" step=\"0.01\" placeholder=\"0\"><\/label>\n            <label><span>Maximum (ha)<\/span><input id=\"mcpfh-surface-max\" type=\"number\" min=\"0\" step=\"0.01\" placeholder=\"Toutes\"><\/label>\n          <\/div>\n        <\/fieldset>\n\n        <div class=\"mcpfh-actions\">\n          <button class=\"mcpfh-primary\" type=\"submit\">Appliquer<\/button>\n          <button class=\"mcpfh-secondary\" id=\"mcpfh-reset\" type=\"button\">R\u00e9initialiser<\/button>\n        <\/div>\n      <\/form>\n    <\/aside>\n\n    <div class=\"mcpfh-backdrop\" id=\"mcpfh-backdrop\" hidden><\/div>\n\n    <div class=\"mcpfh-legend\" id=\"mcpfh-legend\" aria-label=\"L\u00e9gende cartographique\"><\/div>\n\n    <div class=\"mcpfh-status\" id=\"mcpfh-status\" role=\"status\" aria-live=\"polite\">\n      <span class=\"mcpfh-spinner\" aria-hidden=\"true\"><\/span>\n      <strong id=\"mcpfh-status-title\">Chargement de la carte<\/strong>\n      <span id=\"mcpfh-status-text\">R\u00e9cup\u00e9ration des donn\u00e9es et des contours administratifs\u2026<\/span>\n      <small class=\"mcpfh-status-help\">Si ce message reste affich\u00e9 plus de 20 secondes, v\u00e9rifiez que le bloc WordPress autorise l\u2019ex\u00e9cution du JavaScript.<\/small>\n      <button id=\"mcpfh-retry\" type=\"button\" hidden>R\u00e9essayer<\/button>\n    <\/div>\n\n    <section class=\"mcpfh-drawer\" id=\"mcpfh-drawer\" aria-label=\"Statistiques et tableau\">\n      <div class=\"mcpfh-drawer-bar\">\n        <div class=\"mcpfh-tabs\" role=\"tablist\" aria-label=\"Sections de donn\u00e9es\">\n          <button type=\"button\" class=\"is-active\" data-mcpfh-tab=\"stats\" role=\"tab\" aria-selected=\"true\">\u25a5 <span>Statistiques<\/span><\/button>\n          <button type=\"button\" data-mcpfh-tab=\"table\" role=\"tab\" aria-selected=\"false\">\u2637 <span>Tableau<\/span><\/button>\n          <button type=\"button\" data-mcpfh-tab=\"about\" role=\"tab\" aria-selected=\"false\">\u24d8 <span>\u00c0 propos<\/span><\/button>\n        <\/div>\n        <button id=\"mcpfh-drawer-toggle\" class=\"mcpfh-icon-btn\" type=\"button\" aria-expanded=\"false\" aria-label=\"Ouvrir ou fermer le panneau\">\u2303<\/button>\n      <\/div>\n\n      <div class=\"mcpfh-drawer-content\">\n        <div class=\"mcpfh-pane is-active\" data-mcpfh-pane=\"stats\" role=\"tabpanel\">\n          <div class=\"mcpfh-chart-grid\">\n            <article><h3>\u00c9volution annuelle<\/h3><div><canvas id=\"mcpfh-chart-year\"><\/canvas><\/div><\/article>\n            <article><h3>R\u00e9partition des causes<\/h3><div><canvas id=\"mcpfh-chart-causes\"><\/canvas><\/div><\/article>\n            <article><h3>D\u00e9partements les plus touch\u00e9s<\/h3><div><canvas id=\"mcpfh-chart-depts\"><\/canvas><\/div><\/article>\n          <\/div>\n        <\/div>\n\n        <div class=\"mcpfh-pane\" data-mcpfh-pane=\"table\" role=\"tabpanel\" hidden>\n          <div class=\"mcpfh-table-wrap\">\n            <table class=\"mcpfh-table\">\n              <thead>\n                <tr>\n                  <th><button type=\"button\" data-mcpfh-sort=\"date\">Date <span>\u2193<\/span><\/button><\/th>\n                  <th><button type=\"button\" data-mcpfh-sort=\"commune\">Commune <span><\/span><\/button><\/th>\n                  <th><button type=\"button\" data-mcpfh-sort=\"dept\">D\u00e9p. <span><\/span><\/button><\/th>\n                  <th><button type=\"button\" data-mcpfh-sort=\"surfaceM2\">Surface (ha) <span><\/span><\/button><\/th>\n                  <th>Cause<\/th>\n                <\/tr>\n              <\/thead>\n              <tbody id=\"mcpfh-table-body\"><\/tbody>\n            <\/table>\n          <\/div>\n          <nav class=\"mcpfh-pager\" aria-label=\"Pagination du tableau\">\n            <button id=\"mcpfh-prev\" type=\"button\">Pr\u00e9c\u00e9dent<\/button>\n            <span id=\"mcpfh-page-info\">\u2014<\/span>\n            <button id=\"mcpfh-next\" type=\"button\">Suivant<\/button>\n          <\/nav>\n        <\/div>\n\n        <div class=\"mcpfh-pane\" data-mcpfh-pane=\"about\" role=\"tabpanel\" hidden>\n          <article class=\"mcpfh-about\">\n            <h3>\u00c0 propos de cette carte<\/h3>\n            <p>Cette r\u00e9alisation ind\u00e9pendante reprend les fonctions essentielles d\u2019un explorateur d\u2019incendies : s\u00e9lection temporelle, d\u00e9partement, commune, cause et surface, carte interactive, statistiques et tableau d\u00e9taill\u00e9.<\/p>\n            <p><strong>Affichage par d\u00e9faut :<\/strong> incendies d\u00e9clar\u00e9s en 2022 issus de la BDIFF, diffus\u00e9s par IGN\/G\u00e9orisques et adapt\u00e9s dans une couche publique Esri France. Pour couvrir toutes les ann\u00e9es disponibles, renseignez l\u2019URL de votre export CSV BDIFF dans <code>CONFIG.csvUrl<\/code>.<\/p>\n            <p class=\"mcpfh-sources\"><a href=\"https:\/\/bdiff.agriculture.gouv.fr\/\" target=\"_blank\" rel=\"noopener\">BDIFF<\/a><a href=\"https:\/\/www.data.gouv.fr\/datasets\/base-de-donnees-sur-les-incendies-de-forets-en-france-bdiff\" target=\"_blank\" rel=\"noopener\">M\u00e9tadonn\u00e9es data.gouv.fr<\/a><a href=\"https:\/\/www.arcgis.com\/home\/item.html?id=f3daa1d05c214fdca2bec2866a9518fa\" target=\"_blank\" rel=\"noopener\">Couche IGN\/G\u00e9orisques<\/a><\/p>\n          <\/article>\n        <\/div>\n      <\/div>\n    <\/section>\n  <\/section>\n<\/div>\n\n<style>\n  #mcpfh-root,\n  #mcpfh-root * { box-sizing: border-box; }\n\n  #mcpfh-root {\n    --mcpfh-red: #d20a11;\n    --mcpfh-red-dark: #9f0710;\n    --mcpfh-ink: #18181b;\n    --mcpfh-muted: #666a73;\n    --mcpfh-line: #e3e5e9;\n    --mcpfh-panel: rgba(255,255,255,.97);\n    --mcpfh-shadow: 0 18px 45px rgba(28,32,39,.18);\n    width: 100%;\n    color: var(--mcpfh-ink);\n    font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n  }\n\n  #mcpfh-root button,\n  #mcpfh-root input,\n  #mcpfh-root select { font: inherit; }\n\n  #mcpfh-root button { cursor: pointer; }\n\n  #mcpfh-root .mcpfh-stage {\n    position: relative;\n    isolation: isolate;\n    width: 100%;\n    height: clamp(650px, 78vh, 820px);\n    min-height: 650px;\n    overflow: hidden;\n    border: 1px solid var(--mcpfh-line);\n    border-radius: 24px;\n    background: #e9eef2;\n    box-shadow: 0 10px 35px rgba(17,24,39,.10);\n  }\n\n  #mcpfh-root .mcpfh-map { position: absolute; inset: 0; z-index: 1; background: #e9eef2; }\n\n  #mcpfh-root .mcpfh-map-title {\n    position: absolute;\n    z-index: 510;\n    top: 18px;\n    left: 18px;\n    display: flex;\n    align-items: center;\n    gap: 10px;\n    max-width: min(560px, calc(100% - 36px));\n    padding: 11px 18px;\n    border: 1px solid rgba(255,255,255,.85);\n    border-radius: 999px;\n    background: rgba(255,255,255,.94);\n    box-shadow: 0 9px 25px rgba(17,24,39,.15);\n    backdrop-filter: blur(8px);\n  }\n\n  #mcpfh-root .mcpfh-map-title-icon { font-size: 23px; line-height: 1; }\n  #mcpfh-root .mcpfh-map-title strong { display: block; font-size: 17px; line-height: 1.22; }\n  #mcpfh-root .mcpfh-map-title small { display: block; margin-top: 2px; color: var(--mcpfh-muted); font-size: 11px; line-height: 1.25; }\n\n  #mcpfh-root .mcpfh-filters {\n    position: absolute;\n    z-index: 520;\n    top: 76px;\n    left: 18px;\n    width: 330px;\n    max-height: calc(100% - 150px);\n    overflow: auto;\n    overscroll-behavior: contain;\n    padding: 14px;\n    border: 1px solid rgba(255,255,255,.95);\n    border-radius: 18px;\n    background: var(--mcpfh-panel);\n    box-shadow: var(--mcpfh-shadow);\n    scrollbar-width: thin;\n  }\n\n  #mcpfh-root .mcpfh-panel-head,\n  #mcpfh-root .mcpfh-drawer-bar { display: flex; align-items: center; justify-content: space-between; gap: 12px; }\n  #mcpfh-root .mcpfh-panel-head h2 { margin: 0; font-size: 13px; letter-spacing: .08em; text-transform: uppercase; }\n  #mcpfh-root .mcpfh-icon-btn { width: 32px; height: 32px; border: 0; border-radius: 9px; background: #f2f3f5; color: #444; font-weight: 800; }\n  #mcpfh-root #mcpfh-filter-close { display: none; font-size: 22px; line-height: 1; }\n\n  #mcpfh-root .mcpfh-segmented { display: grid; grid-template-columns: 1fr 1fr; gap: 3px; margin-top: 10px; padding: 3px; border: 1px solid var(--mcpfh-line); border-radius: 999px; background: #f6f7f8; }\n  #mcpfh-root .mcpfh-segmented button { min-height: 39px; border: 0; border-radius: 999px; background: transparent; color: #5c6068; font-size: 13px; font-weight: 750; }\n  #mcpfh-root .mcpfh-segmented button.is-active { background: #111; color: #fff; box-shadow: 0 4px 12px rgba(0,0,0,.16); }\n  #mcpfh-root .mcpfh-segmented button.is-active span { color: #ef1520; }\n\n  #mcpfh-root .mcpfh-kpis { display: grid; grid-template-columns: repeat(4, 1fr); margin-top: 11px; border: 1px solid var(--mcpfh-line); border-radius: 12px; overflow: hidden; background: #fff; }\n  #mcpfh-root .mcpfh-kpis div { min-width: 0; padding: 9px 4px 8px; text-align: center; border-right: 1px solid var(--mcpfh-line); }\n  #mcpfh-root .mcpfh-kpis div:last-child { border-right: 0; }\n  #mcpfh-root .mcpfh-kpis strong { display: block; overflow: hidden; text-overflow: ellipsis; font-size: 15px; line-height: 1.05; white-space: nowrap; }\n  #mcpfh-root .mcpfh-kpis span { display: block; margin-top: 3px; color: var(--mcpfh-muted); font-size: 8px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase; }\n\n  #mcpfh-root .mcpfh-form { display: grid; gap: 13px; margin-top: 13px; }\n  #mcpfh-root .mcpfh-field,\n  #mcpfh-root .mcpfh-fieldset { min-width: 0; margin: 0; padding: 0; border: 0; }\n  #mcpfh-root .mcpfh-field > label,\n  #mcpfh-root .mcpfh-fieldset > legend { display: block; margin: 0 0 6px; padding: 0; color: #6b7078; font-size: 10px; font-weight: 850; letter-spacing: .055em; text-transform: uppercase; }\n  #mcpfh-root .mcpfh-field input,\n  #mcpfh-root .mcpfh-field select,\n  #mcpfh-root .mcpfh-number-pair input { width: 100%; min-height: 38px; padding: 8px 10px; border: 1px solid #d9dce1; border-radius: 9px; background: #fff; color: #24262b; outline: none; font-size: 13px; }\n  #mcpfh-root .mcpfh-field input:focus,\n  #mcpfh-root .mcpfh-field select:focus,\n  #mcpfh-root .mcpfh-number-pair input:focus { border-color: var(--mcpfh-red); box-shadow: 0 0 0 3px rgba(210,10,17,.12); }\n\n  #mcpfh-root .mcpfh-year-values { display: flex; justify-content: space-between; color: #555a62; font-size: 11px; }\n  #mcpfh-root .mcpfh-year-values output { display: inline-block; min-width: 38px; padding: 3px 5px; border-radius: 6px; background: #33363c; color: #fff; text-align: center; font-weight: 750; }\n  #mcpfh-root .mcpfh-dual-range { position: relative; height: 30px; margin: 4px 7px 0; }\n  #mcpfh-root .mcpfh-range-track { position: absolute; top: 13px; right: 0; left: 0; height: 5px; overflow: hidden; border-radius: 999px; background: #d8dce2; }\n  #mcpfh-root .mcpfh-range-track span { position: absolute; top: 0; bottom: 0; background: var(--mcpfh-red); }\n  #mcpfh-root .mcpfh-dual-range input[type=\"range\"] { position: absolute; inset: 0; width: 100%; height: 30px; margin: 0; padding: 0; border: 0; background: transparent; appearance: none; pointer-events: none; }\n  #mcpfh-root .mcpfh-dual-range input[type=\"range\"]::-webkit-slider-runnable-track { height: 5px; background: transparent; }\n  #mcpfh-root .mcpfh-dual-range input[type=\"range\"]::-webkit-slider-thumb { width: 18px; height: 18px; margin-top: -6px; border: 2px solid var(--mcpfh-red); border-radius: 50%; background: #fff; appearance: none; box-shadow: 0 2px 7px rgba(0,0,0,.18); pointer-events: auto; }\n  #mcpfh-root .mcpfh-dual-range input[type=\"range\"]::-moz-range-track { height: 5px; background: transparent; }\n  #mcpfh-root .mcpfh-dual-range input[type=\"range\"]::-moz-range-thumb { width: 16px; height: 16px; border: 2px solid var(--mcpfh-red); border-radius: 50%; background: #fff; box-shadow: 0 2px 7px rgba(0,0,0,.18); pointer-events: auto; }\n\n  #mcpfh-root .mcpfh-chips { display: flex; flex-wrap: wrap; gap: 6px; }\n  #mcpfh-root .mcpfh-chips label { position: relative; cursor: pointer; }\n  #mcpfh-root .mcpfh-chips input { position: absolute; opacity: 0; pointer-events: none; }\n  #mcpfh-root .mcpfh-chips span { display: block; padding: 6px 8px; border: 1px solid #dadde2; border-radius: 999px; background: #fff; color: #5b6067; font-size: 10px; font-weight: 750; line-height: 1.1; }\n  #mcpfh-root .mcpfh-chips input:checked + span { border-color: var(--mcpfh-red); background: #fff0f0; color: var(--mcpfh-red-dark); box-shadow: inset 0 0 0 1px var(--mcpfh-red); }\n  #mcpfh-root .mcpfh-chips input:focus-visible + span { outline: 3px solid rgba(210,10,17,.18); outline-offset: 2px; }\n\n  #mcpfh-root .mcpfh-number-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }\n  #mcpfh-root .mcpfh-number-pair label span { display: block; margin-bottom: 4px; color: #777b82; font-size: 10px; }\n\n  #mcpfh-root .mcpfh-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }\n  #mcpfh-root .mcpfh-actions button,\n  #mcpfh-root .mcpfh-pager button,\n  #mcpfh-root .mcpfh-status button { min-height: 40px; padding: 8px 13px; border-radius: 10px; font-weight: 800; font-size: 12px; }\n  #mcpfh-root .mcpfh-primary { border: 1px solid var(--mcpfh-red); background: var(--mcpfh-red); color: #fff; }\n  #mcpfh-root .mcpfh-primary:hover { background: var(--mcpfh-red-dark); }\n  #mcpfh-root .mcpfh-secondary,\n  #mcpfh-root .mcpfh-pager button { border: 1px solid #d7dae0; background: #fff; color: #4b4f56; }\n\n  #mcpfh-root .mcpfh-legend { position: absolute; z-index: 510; right: 14px; bottom: 72px; min-width: 170px; padding: 10px 12px; border: 1px solid rgba(255,255,255,.9); border-radius: 12px; background: rgba(255,255,255,.94); box-shadow: 0 8px 22px rgba(17,24,39,.15); font-size: 10px; backdrop-filter: blur(7px); }\n  #mcpfh-root .mcpfh-legend strong { display: block; margin-bottom: 7px; font-size: 11px; }\n  #mcpfh-root .mcpfh-legend-row { display: flex; align-items: center; gap: 7px; margin-top: 4px; color: #5d6168; }\n  #mcpfh-root .mcpfh-legend-swatch { width: 18px; height: 10px; flex: 0 0 auto; border: 1px solid rgba(0,0,0,.08); border-radius: 2px; }\n\n  #mcpfh-root .mcpfh-status { position: absolute; z-index: 800; top: 50%; left: 50%; display: flex; width: min(360px, calc(100% - 40px)); transform: translate(-50%,-50%); flex-direction: column; align-items: center; gap: 7px; padding: 24px; border: 1px solid rgba(255,255,255,.95); border-radius: 18px; background: rgba(255,255,255,.96); box-shadow: var(--mcpfh-shadow); text-align: center; }\n  #mcpfh-root .mcpfh-status[hidden] { display: none; }\n  #mcpfh-root .mcpfh-status strong { font-size: 16px; }\n  #mcpfh-root .mcpfh-status > span:not(.mcpfh-spinner) { color: var(--mcpfh-muted); font-size: 12px; line-height: 1.45; }\n  #mcpfh-root .mcpfh-status-help { max-height: 0; overflow: hidden; color: #8b4b00; font-size: 10px; line-height: 1.4; opacity: 0; animation: mcpfh-help-in .25s ease 20s forwards; }\n  #mcpfh-root .mcpfh-status.is-error .mcpfh-status-help { max-height: 90px; opacity: 1; animation: none; }\n  #mcpfh-root .mcpfh-status button { border: 0; background: var(--mcpfh-red); color: #fff; }\n  #mcpfh-root .mcpfh-spinner { width: 30px; height: 30px; border: 3px solid #eceef1; border-top-color: var(--mcpfh-red); border-radius: 50%; animation: mcpfh-spin .8s linear infinite; }\n  @keyframes mcpfh-spin { to { transform: rotate(360deg); } }\n  @keyframes mcpfh-help-in { to { max-height: 90px; opacity: 1; } }\n\n  #mcpfh-root .mcpfh-drawer { position: absolute; z-index: 530; right: 14px; bottom: 14px; left: 370px; height: 48px; overflow: hidden; border: 1px solid rgba(255,255,255,.92); border-radius: 16px; background: rgba(255,255,255,.97); box-shadow: var(--mcpfh-shadow); transition: height .24s ease; backdrop-filter: blur(8px); }\n  #mcpfh-root .mcpfh-drawer.is-open { height: min(330px, calc(100% - 90px)); }\n  #mcpfh-root .mcpfh-drawer-bar { height: 47px; padding: 5px 7px 5px 10px; border-bottom: 1px solid transparent; }\n  #mcpfh-root .mcpfh-drawer.is-open .mcpfh-drawer-bar { border-bottom-color: var(--mcpfh-line); }\n  #mcpfh-root .mcpfh-tabs { display: flex; align-items: center; gap: 3px; }\n  #mcpfh-root .mcpfh-tabs button { min-height: 36px; padding: 7px 12px; border: 0; border-radius: 999px; background: transparent; color: #555a62; font-size: 12px; font-weight: 750; }\n  #mcpfh-root .mcpfh-tabs button.is-active { background: #f4f4f5; color: var(--mcpfh-red); }\n  #mcpfh-root .mcpfh-drawer.is-open #mcpfh-drawer-toggle { transform: rotate(180deg); }\n  #mcpfh-root .mcpfh-drawer-content { height: calc(100% - 48px); overflow: auto; padding: 13px; }\n  #mcpfh-root .mcpfh-pane[hidden] { display: none; }\n\n  #mcpfh-root .mcpfh-chart-grid { display: grid; grid-template-columns: 1.15fr .9fr 1.15fr; gap: 12px; height: 100%; }\n  #mcpfh-root .mcpfh-chart-grid article { min-width: 0; min-height: 235px; padding: 11px; border: 1px solid var(--mcpfh-line); border-radius: 12px; background: #fff; }\n  #mcpfh-root .mcpfh-chart-grid h3 { margin: 0 0 7px; font-size: 12px; }\n  #mcpfh-root .mcpfh-chart-grid article > div { position: relative; height: 190px; }\n\n  #mcpfh-root .mcpfh-table-wrap { overflow: auto; border: 1px solid var(--mcpfh-line); border-radius: 10px; }\n  #mcpfh-root .mcpfh-table { width: 100%; border-collapse: collapse; background: #fff; font-size: 11px; }\n  #mcpfh-root .mcpfh-table th,\n  #mcpfh-root .mcpfh-table td { padding: 8px 10px; border-bottom: 1px solid #eceef1; text-align: left; white-space: nowrap; }\n  #mcpfh-root .mcpfh-table th { position: sticky; top: 0; z-index: 1; background: #f7f7f8; color: #5b6067; font-size: 10px; text-transform: uppercase; }\n  #mcpfh-root .mcpfh-table th button { padding: 0; border: 0; background: none; color: inherit; font-size: inherit; font-weight: 800; text-transform: inherit; }\n  #mcpfh-root .mcpfh-table td:nth-child(2) { font-weight: 700; }\n  #mcpfh-root .mcpfh-table-empty { padding: 28px !important; color: var(--mcpfh-muted); text-align: center !important; }\n  #mcpfh-root .mcpfh-pager { display: flex; align-items: center; justify-content: center; gap: 12px; margin-top: 10px; }\n  #mcpfh-root .mcpfh-pager button { min-height: 34px; }\n  #mcpfh-root .mcpfh-pager button:disabled { cursor: not-allowed; opacity: .45; }\n  #mcpfh-root .mcpfh-pager span { color: #666b73; font-size: 11px; }\n\n  #mcpfh-root .mcpfh-about { max-width: 920px; margin: 0 auto; color: #555a62; font-size: 12px; line-height: 1.55; }\n  #mcpfh-root .mcpfh-about h3 { margin: 0 0 7px; color: var(--mcpfh-ink); font-size: 16px; }\n  #mcpfh-root .mcpfh-about p { margin: 7px 0; }\n  #mcpfh-root .mcpfh-about code { padding: 2px 5px; border-radius: 5px; background: #f0f1f3; color: #a10a10; }\n  #mcpfh-root .mcpfh-sources { display: flex; flex-wrap: wrap; gap: 7px; }\n  #mcpfh-root .mcpfh-sources a { padding: 6px 9px; border: 1px solid #dddfe3; border-radius: 999px; color: var(--mcpfh-red-dark); text-decoration: none; }\n\n  #mcpfh-root .mcpfh-mobile-filter { display: none; position: absolute; z-index: 525; top: 79px; left: 12px; min-height: 40px; padding: 8px 13px; border: 0; border-radius: 999px; background: #111; color: #fff; box-shadow: 0 7px 18px rgba(0,0,0,.2); font-size: 12px; font-weight: 800; }\n  #mcpfh-root .mcpfh-backdrop { position: absolute; z-index: 515; inset: 0; background: rgba(16,21,28,.48); backdrop-filter: blur(2px); }\n  #mcpfh-root .mcpfh-backdrop[hidden] { display: none; }\n\n  #mcpfh-root .leaflet-control-zoom { overflow: hidden; border: 0; border-radius: 11px; box-shadow: 0 7px 20px rgba(17,24,39,.16); }\n  #mcpfh-root .leaflet-control-zoom a { border-bottom-color: #eee; }\n  #mcpfh-root .leaflet-popup-content-wrapper { border-radius: 12px; box-shadow: 0 14px 35px rgba(17,24,39,.22); }\n  #mcpfh-root .mcpfh-popup h4 { margin: 0 0 5px; font-size: 14px; }\n  #mcpfh-root .mcpfh-popup p { margin: 3px 0; font-size: 11px; }\n  #mcpfh-root .mcpfh-popup strong { color: var(--mcpfh-red-dark); }\n\n  @media (max-width: 980px) {\n    #mcpfh-root .mcpfh-stage { height: 760px; }\n    #mcpfh-root .mcpfh-mobile-filter { display: block; }\n    #mcpfh-root .mcpfh-filters { z-index: 540; top: 12px; bottom: 12px; left: 12px; width: min(345px, calc(100% - 24px)); max-height: none; transform: translateX(calc(-100% - 28px)); transition: transform .22s ease; }\n    #mcpfh-root .mcpfh-filters.is-open { transform: translateX(0); }\n    #mcpfh-root #mcpfh-filter-close { display: inline-grid; place-items: center; }\n    #mcpfh-root .mcpfh-drawer { right: 8px; bottom: 8px; left: 8px; }\n    #mcpfh-root .mcpfh-drawer.is-open { height: min(430px, 58%); }\n    #mcpfh-root .mcpfh-chart-grid { grid-template-columns: 1fr 1fr; }\n    #mcpfh-root .mcpfh-chart-grid article:last-child { grid-column: 1 \/ -1; }\n    #mcpfh-root .mcpfh-legend { right: 10px; bottom: 66px; }\n  }\n\n  @media (max-width: 620px) {\n    #mcpfh-root .mcpfh-stage { height: 760px; min-height: 680px; border-radius: 16px; }\n    #mcpfh-root .mcpfh-map-title { top: 10px; left: 10px; max-width: calc(100% - 20px); padding: 9px 13px; border-radius: 14px; }\n    #mcpfh-root .mcpfh-map-title strong { font-size: 14px; }\n    #mcpfh-root .mcpfh-map-title small { max-width: 250px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n    #mcpfh-root .mcpfh-mobile-filter { top: 72px; left: 9px; }\n    #mcpfh-root .mcpfh-legend { min-width: 145px; padding: 8px 9px; }\n    #mcpfh-root .mcpfh-tabs button { padding: 7px 9px; }\n    #mcpfh-root .mcpfh-tabs button span { display: none; }\n    #mcpfh-root .mcpfh-drawer.is-open { height: 62%; }\n    #mcpfh-root .mcpfh-chart-grid { grid-template-columns: 1fr; }\n    #mcpfh-root .mcpfh-chart-grid article:last-child { grid-column: auto; }\n    #mcpfh-root .mcpfh-number-pair { grid-template-columns: 1fr; }\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    #mcpfh-root *, #mcpfh-root *::before, #mcpfh-root *::after { scroll-behavior: auto !important; animation-duration: .01ms !important; transition-duration: .01ms !important; }\n  }\n<\/style>\n\n<script type=\"text\/javascript\" data-cfasync=\"false\">\n(() => {\n  'use strict';\n\n  const root = document.getElementById('mcpfh-root');\n  if (!root || root.dataset.booted === 'true') return;\n  root.dataset.booted = 'true';\n\n  const CONFIG = {\n    \/*\n      Pour utiliser l'historique complet : t\u00e9l\u00e9versez votre export CSV BDIFF\n      dans WordPress et collez son URL entre les guillemets ci-dessous.\n      Exemple : csvUrl: 'https:\/\/votresite.fr\/wp-content\/uploads\/2026\/08\/bdiff.csv'\n    *\/\n    csvUrl: '',\n    csvSurfaceUnit: 'm2',\n    arcgisUrl: 'https:\/\/services.arcgis.com\/d3voDfTFbHOCRwVR\/arcgis\/rest\/services\/G%C3%A9orisques___Les_feux_de_For%C3%AAt\/FeatureServer\/10\/query',\n    departmentsUrl: 'https:\/\/cdn.jsdelivr.net\/gh\/gregoiredavid\/france-geojson@5d34ee6\/departements-version-simplifiee.geojson',\n    leafletCss: [\n      'https:\/\/cdn.jsdelivr.net\/npm\/leaflet@1.9.4\/dist\/leaflet.css',\n      'https:\/\/unpkg.com\/leaflet@1.9.4\/dist\/leaflet.css'\n    ],\n    leafletJs: [\n      'https:\/\/cdn.jsdelivr.net\/npm\/leaflet@1.9.4\/dist\/leaflet.js',\n      'https:\/\/unpkg.com\/leaflet@1.9.4\/dist\/leaflet.js'\n    ],\n    chartJs: [\n      'https:\/\/cdn.jsdelivr.net\/npm\/chart.js@4.4.7\/dist\/chart.umd.min.js',\n      'https:\/\/unpkg.com\/chart.js@4.4.7\/dist\/chart.umd.js'\n    ],\n    papaJs: [\n      'https:\/\/cdn.jsdelivr.net\/npm\/papaparse@5.4.1\/papaparse.min.js',\n      'https:\/\/unpkg.com\/papaparse@5.4.1\/papaparse.min.js'\n    ],\n    dependencyTimeoutMs: 8000,\n    requestTimeoutMs: 18000,\n    bootTimeoutMs: 38000,\n    pageSize: 20\n  };\n\n  const COLORS = ['#fee5d9', '#fcae91', '#fb6a4a', '#de2d26', '#a50f15'];\n  const CAUSE_COLORS = {\n    'Involontaire (particulier)': '#ef7b45',\n    'Involontaire (travaux)': '#f0b429',\n    'Accidentelle': '#5271ff',\n    'Malveillance': '#d20a11',\n    'Naturelle': '#2f9d63',\n    'Inconnue': '#9ca3af'\n  };\n\n  const state = {\n    events: [],\n    filtered: [],\n    boundaries: null,\n    departmentNames: {},\n    metric: 'count',\n    map: null,\n    deptLayer: null,\n    communeLayer: null,\n    charts: {},\n    page: 1,\n    sort: { key: 'date', dir: 'desc' },\n    years: { min: 2022, max: 2022 },\n    sourceLabel: ''\n  };\n\n  const $ = (selector) => root.querySelector(selector);\n  const $$ = (selector) => Array.from(root.querySelectorAll(selector));\n  const els = {\n    map: $('#mcpfh-map'),\n    filters: $('#mcpfh-filters'),\n    mobileFilter: $('#mcpfh-mobile-filter'),\n    filterClose: $('#mcpfh-filter-close'),\n    backdrop: $('#mcpfh-backdrop'),\n    form: $('#mcpfh-form'),\n    reset: $('#mcpfh-reset'),\n    dept: $('#mcpfh-dept'),\n    commune: $('#mcpfh-commune'),\n    communes: $('#mcpfh-communes'),\n    yearMin: $('#mcpfh-year-min'),\n    yearMax: $('#mcpfh-year-max'),\n    yearMinOutput: $('#mcpfh-year-min-output'),\n    yearMaxOutput: $('#mcpfh-year-max-output'),\n    rangeFill: $('#mcpfh-range-fill'),\n    surfaceMin: $('#mcpfh-surface-min'),\n    surfaceMax: $('#mcpfh-surface-max'),\n    legend: $('#mcpfh-legend'),\n    status: $('#mcpfh-status'),\n    statusTitle: $('#mcpfh-status-title'),\n    statusText: $('#mcpfh-status-text'),\n    retry: $('#mcpfh-retry'),\n    sourceBadge: $('#mcpfh-source-badge'),\n    drawer: $('#mcpfh-drawer'),\n    drawerToggle: $('#mcpfh-drawer-toggle'),\n    tableBody: $('#mcpfh-table-body'),\n    prev: $('#mcpfh-prev'),\n    next: $('#mcpfh-next'),\n    pageInfo: $('#mcpfh-page-info')\n  };\n\n  async function loadCss(id, sources) {\n    const urls = Array.isArray(sources) ? sources : [sources];\n    let lastError = null;\n    const existing = document.getElementById(id);\n    if (existing) existing.remove();\n    for (const href of urls) {\n      try {\n        await new Promise((resolve, reject) => {\n          const link = document.createElement('link');\n          let settled = false;\n          const finish = (error) => {\n            if (settled) return;\n            settled = true;\n            window.clearTimeout(timer);\n            if (error) { link.remove(); reject(error); }\n            else resolve();\n          };\n          const timer = window.setTimeout(() => finish(new Error('D\u00e9lai d\u00e9pass\u00e9 pour la feuille de style.')), CONFIG.dependencyTimeoutMs);\n          link.id = id;\n          link.rel = 'stylesheet';\n          link.href = href;\n          link.addEventListener('load', () => finish(), { once: true });\n          link.addEventListener('error', () => finish(new Error('Feuille de style indisponible.')), { once: true });\n          document.head.appendChild(link);\n        });\n        return;\n      } catch (error) {\n        lastError = error;\n      }\n    }\n    throw lastError || new Error('Impossible de charger la feuille de style de la carte.');\n  }\n\n  async function loadScript(id, sources, globalName) {\n    if (globalName && window[globalName]) return;\n    const urls = Array.isArray(sources) ? sources : [sources];\n    let lastError = null;\n    const existing = document.getElementById(id);\n    if (existing) existing.remove();\n    for (const src of urls) {\n      try {\n        await new Promise((resolve, reject) => {\n          const script = document.createElement('script');\n          let settled = false;\n          const finish = (error) => {\n            if (settled) return;\n            settled = true;\n            window.clearTimeout(timer);\n            if (error) { script.remove(); reject(error); }\n            else resolve();\n          };\n          const timer = window.setTimeout(() => finish(new Error('D\u00e9lai d\u00e9pass\u00e9 pour ' + src)), CONFIG.dependencyTimeoutMs);\n          script.id = id;\n          script.src = src;\n          script.async = true;\n          script.addEventListener('load', () => {\n            if (globalName && !window[globalName]) finish(new Error('Biblioth\u00e8que charg\u00e9e mais indisponible.'));\n            else finish();\n          }, { once: true });\n          script.addEventListener('error', () => finish(new Error('Impossible de charger ' + src)), { once: true });\n          document.head.appendChild(script);\n        });\n        return;\n      } catch (error) {\n        lastError = error;\n      }\n    }\n    throw lastError || new Error('Biblioth\u00e8que JavaScript indisponible.');\n  }\n\n  function normalizeText(value) {\n    return String(value ?? '')\n      .normalize('NFD')\n      .replace(\/[\\u0300-\\u036f]\/g, '')\n      .toLowerCase()\n      .replace(\/[^a-z0-9]+\/g, ' ')\n      .trim();\n  }\n\n  function parseNumber(value) {\n    if (typeof value === 'number') return Number.isFinite(value) ? value : 0;\n    const cleaned = String(value ?? '')\n      .replace(\/[\\s\\u00a0\\u202f]\/g, '')\n      .replace(',', '.')\n      .replace(\/[^0-9+\\-.eE]\/g, '');\n    const number = Number(cleaned);\n    return Number.isFinite(number) ? number : 0;\n  }\n\n  function parseDate(value, yearFallback) {\n    if (value instanceof Date && !Number.isNaN(value.valueOf())) return value;\n    if (typeof value === 'number' && value > 1000000000) {\n      const d = new Date(value);\n      if (!Number.isNaN(d.valueOf())) return d;\n    }\n    const raw = String(value ?? '').trim();\n    const fr = raw.match(\/^(\\d{1,2})[\\\/\\-.](\\d{1,2})[\\\/\\-.](\\d{4})\/);\n    if (fr) {\n      const d = new Date(Date.UTC(Number(fr[3]), Number(fr[2]) - 1, Number(fr[1])));\n      if (!Number.isNaN(d.valueOf())) return d;\n    }\n    if (raw) {\n      const d = new Date(raw);\n      if (!Number.isNaN(d.valueOf())) return d;\n    }\n    return new Date(Date.UTC(Number(yearFallback) || 2000, 0, 1));\n  }\n\n  function normalizeCause(value) {\n    const raw = String(value ?? '').trim();\n    const n = normalizeText(raw);\n    if (!n || n === '0' || n.includes('inconnu') || n.includes('non renseigne')) return 'Inconnue';\n    if (n.includes('malveill') || n.includes('intention')) return 'Malveillance';\n    if (n.includes('naturell') || n.includes('foudre')) return 'Naturelle';\n    if (n.includes('travaux') || n.includes('profession') || n.includes('infrastructure')) return 'Involontaire (travaux)';\n    if (n.includes('accident')) return 'Accidentelle';\n    if (n.includes('involontaire') || n.includes('imprud') || n.includes('particulier')) return 'Involontaire (particulier)';\n    return raw || 'Inconnue';\n  }\n\n  function formatNumber(value, maximumFractionDigits = 0) {\n    return new Intl.NumberFormat('fr-FR', { maximumFractionDigits }).format(Number(value) || 0);\n  }\n\n  function formatCompact(value) {\n    const number = Number(value) || 0;\n    if (Math.abs(number) < 1000) return formatNumber(number, 0);\n    return new Intl.NumberFormat('fr-FR', { notation: 'compact', maximumFractionDigits: 1 }).format(number);\n  }\n\n  function formatDate(date) {\n    if (!(date instanceof Date) || Number.isNaN(date.valueOf())) return '\u2014';\n    return new Intl.DateTimeFormat('fr-FR', { day: '2-digit', month: '2-digit', year: 'numeric' }).format(date);\n  }\n\n  function showStatus(title, text, isError = false) {\n    els.status.hidden = false;\n    els.status.classList.toggle('is-error', isError);\n    els.statusTitle.textContent = title;\n    els.statusText.textContent = text;\n    $('.mcpfh-spinner').hidden = isError;\n    els.retry.hidden = !isError;\n  }\n\n  function hideStatus() {\n    els.status.hidden = true;\n  }\n\n  async function fetchWithTimeout(url, options = {}) {\n    const { timeoutMs = CONFIG.requestTimeoutMs, ...fetchOptions } = options;\n    const controller = new AbortController();\n    const timer = window.setTimeout(() => controller.abort(), timeoutMs);\n    try {\n      return await fetch(url, { ...fetchOptions, signal: controller.signal });\n    } catch (error) {\n      if (error && error.name === 'AbortError') throw new Error('Le serveur de donn\u00e9es ne r\u00e9pond pas dans le d\u00e9lai pr\u00e9vu.');\n      throw error;\n    } finally {\n      window.clearTimeout(timer);\n    }\n  }\n\n  async function fetchJson(url, options = {}) {\n    const response = await fetchWithTimeout(url, { ...options, headers: { Accept: 'application\/json', ...(options.headers || {}) } });\n    if (!response.ok) throw new Error('R\u00e9ponse HTTP ' + response.status + ' depuis la source de donn\u00e9es.');\n    return response.json();\n  }\n\n  function buildArcgisUrl(params) {\n    const url = new URL(CONFIG.arcgisUrl);\n    Object.entries(params).forEach(([key, value]) => url.searchParams.set(key, value));\n    return url.toString();\n  }\n\n  async function loadArcgisEvents() {\n    const countData = await fetchJson(buildArcgisUrl({ where: '1=1', returnCountOnly: 'true', f: 'json' }));\n    const count = Number(countData.count) || 0;\n    const pageSize = 2000;\n    const offsets = [];\n    for (let offset = 0; offset < count; offset += pageSize) offsets.push(offset);\n\n    const outFields = [\n      'OBJECTID',\n      'Annee', 'Departement', 'Code_INSEE', 'Nom_de_la_commune',\n      'Date_de_premiere_alerte', 'Surface_parcourue', 'Nature',\n      'Nombre_de_deces', 'NombreBatTotalementDetruits', 'NombreBatPartielDetruits'\n    ].join(',');\n\n    const pages = await Promise.all(offsets.map((offset) => fetchJson(buildArcgisUrl({\n      where: '1=1',\n      outFields,\n      returnGeometry: 'true',\n      outSR: '4326',\n      f: 'geojson',\n      resultOffset: String(offset),\n      resultRecordCount: String(pageSize),\n      orderByFields: 'Date_de_premiere_alerte DESC'\n    }))));\n\n    const events = [];\n    pages.forEach((collection) => {\n      (collection.features || []).forEach((feature, index) => {\n        const p = feature.properties || {};\n        const coordinates = feature.geometry && feature.geometry.type === 'Point' ? feature.geometry.coordinates : [];\n        const year = Number(p.Annee) || new Date(p.Date_de_premiere_alerte).getUTCFullYear();\n        events.push({\n          id: 'arcgis-' + (p.OBJECTID || events.length + index),\n          year,\n          dept: String(p.Departement || '').padStart(2, '0'),\n          insee: String(p.Code_INSEE || ''),\n          commune: String(p.Nom_de_la_commune || 'Commune non renseign\u00e9e'),\n          date: parseDate(p.Date_de_premiere_alerte, year),\n          surfaceM2: Math.max(0, parseNumber(p.Surface_parcourue)),\n          cause: normalizeCause(p.Nature),\n          deaths: Math.max(0, parseNumber(p.Nombre_de_deces)),\n          buildings: Math.max(0, parseNumber(p.NombreBatTotalementDetruits)) + Math.max(0, parseNumber(p.NombreBatPartielDetruits)),\n          lat: Number(coordinates[1]),\n          lon: Number(coordinates[0])\n        });\n      });\n    });\n    state.sourceLabel = 'BDIFF 2022 \u00b7 couche publique IGN\/G\u00e9orisques';\n    return events;\n  }\n\n  const CSV_ALIASES = {\n    year: ['annee', 'annee incident', 'annee de l incident'],\n    dept: ['departement', 'code departement', 'deprt'],\n    insee: ['code insee', 'code commune', 'insee'],\n    commune: ['commune', 'nom de la commune', 'nom commune'],\n    date: ['date de premiere alerte', 'date premiere alerte', 'date alerte', 'date'],\n    surface: ['surface parcourue', 'surface totale m2', 'surface totale', 'surface m2', 'surface ha'],\n    cause: ['nature', 'cause', 'nature du feu', 'cause supposee'],\n    deaths: ['nombre de deces', 'deces', 'nb deces'],\n    buildingsDestroyed: ['nombre bat totalement detruits', 'batiments detruits', 'nb bat detruits'],\n    buildingsPartial: ['nombre bat partiel detruits', 'batiments partiellement detruits', 'nb bat partiels'],\n    lat: ['latitude', 'lat'],\n    lon: ['longitude', 'lon', 'lng']\n  };\n\n  function detectCsvFields(headers) {\n    const normalized = headers.map((header) => ({ original: header, normalized: normalizeText(header) }));\n    const found = {};\n    Object.entries(CSV_ALIASES).forEach(([key, aliases]) => {\n      const exact = normalized.find((h) => aliases.includes(h.normalized));\n      const partial = normalized.find((h) => aliases.some((alias) => h.normalized.includes(alias)));\n      found[key] = (exact || partial || {}).original || '';\n    });\n    return found;\n  }\n\n  async function loadCsvEvents() {\n    if (!window.Papa) throw new Error('Le lecteur CSV n\u2019a pas pu \u00eatre charg\u00e9.');\n    const response = await fetchWithTimeout(CONFIG.csvUrl, { credentials: 'omit' });\n    if (!response.ok) throw new Error('Impossible de t\u00e9l\u00e9charger le CSV (' + response.status + ').');\n    const text = await response.text();\n    const parsed = window.Papa.parse(text, { header: true, skipEmptyLines: 'greedy', transformHeader: (h) => h.replace(\/^\\uFEFF\/, '').trim() });\n    if (parsed.errors && parsed.errors.length && !parsed.data.length) throw new Error(parsed.errors[0].message || 'CSV illisible.');\n    const fields = detectCsvFields(parsed.meta.fields || []);\n    if (!fields.year && !fields.date) throw new Error('Colonnes ann\u00e9e\/date non reconnues dans le CSV.');\n    if (!fields.dept) throw new Error('Colonne d\u00e9partement non reconnue dans le CSV.');\n\n    const surfaceHeader = normalizeText(fields.surface);\n    const surfaceIsHa = surfaceHeader.includes(' ha') || surfaceHeader.endsWith('ha') || CONFIG.csvSurfaceUnit === 'ha';\n    const events = [];\n    parsed.data.forEach((row, index) => {\n      const rawYear = fields.year ? parseNumber(row[fields.year]) : 0;\n      const date = parseDate(fields.date ? row[fields.date] : '', rawYear);\n      const year = rawYear || date.getUTCFullYear();\n      if (!year) return;\n      const surfaceRaw = fields.surface ? parseNumber(row[fields.surface]) : 0;\n      const deptRaw = String(row[fields.dept] ?? '').trim().replace(\/^0+(?=\\d{2,3}$)\/, '');\n      const dept = \/^(2A|2B)$\/i.test(deptRaw) ? deptRaw.toUpperCase() : deptRaw.padStart(2, '0');\n      events.push({\n        id: 'csv-' + index,\n        year,\n        dept,\n        insee: fields.insee ? String(row[fields.insee] ?? '').trim() : '',\n        commune: fields.commune ? String(row[fields.commune] ?? 'Commune non renseign\u00e9e').trim() : 'Commune non renseign\u00e9e',\n        date,\n        surfaceM2: Math.max(0, surfaceRaw * (surfaceIsHa ? 10000 : 1)),\n        cause: normalizeCause(fields.cause ? row[fields.cause] : ''),\n        deaths: Math.max(0, fields.deaths ? parseNumber(row[fields.deaths]) : 0),\n        buildings: Math.max(0, fields.buildingsDestroyed ? parseNumber(row[fields.buildingsDestroyed]) : 0) + Math.max(0, fields.buildingsPartial ? parseNumber(row[fields.buildingsPartial]) : 0),\n        lat: fields.lat ? parseNumber(row[fields.lat]) : NaN,\n        lon: fields.lon ? parseNumber(row[fields.lon]) : NaN\n      });\n    });\n    state.sourceLabel = 'Export CSV BDIFF configur\u00e9 \u00b7 ' + formatNumber(events.length) + ' fiches';\n    return events;\n  }\n\n  async function loadData() {\n    const [events, boundaries] = await Promise.all([\n      CONFIG.csvUrl ? loadCsvEvents() : loadArcgisEvents(),\n      fetchJson(CONFIG.departmentsUrl, { timeoutMs: 12000 }).catch(() => null)\n    ]);\n    if (!events.length) throw new Error('Aucune fiche incendie exploitable.');\n    state.events = events;\n    state.boundaries = boundaries;\n    (boundaries && boundaries.features || []).forEach((feature) => {\n      const p = feature.properties || {};\n      state.departmentNames[String(p.code || '')] = String(p.nom || p.code || '');\n    });\n  }\n\n  function initializeFilterOptions() {\n    els.dept.innerHTML = '<option value=\"\">Tous les d\u00e9partements<\/option>';\n    els.communes.replaceChildren();\n\n    const years = state.events.map((event) => event.year).filter(Number.isFinite);\n    state.years.min = Math.min(...years);\n    state.years.max = Math.max(...years);\n    [els.yearMin, els.yearMax].forEach((input) => {\n      input.min = String(state.years.min);\n      input.max = String(state.years.max);\n    });\n    els.yearMin.value = String(state.years.min);\n    els.yearMax.value = String(state.years.max);\n    els.yearMin.disabled = state.years.min === state.years.max;\n    els.yearMax.disabled = state.years.min === state.years.max;\n\n    const departments = new Set(state.events.map((event) => event.dept).filter(Boolean));\n    Array.from(departments).sort((a, b) => a.localeCompare(b, 'fr', { numeric: true })).forEach((code) => {\n      const option = document.createElement('option');\n      option.value = code;\n      option.textContent = code + ' \u2014 ' + (state.departmentNames[code] || 'D\u00e9partement ' + code);\n      els.dept.appendChild(option);\n    });\n\n    const communes = new Map();\n    state.events.forEach((event) => {\n      const key = normalizeText(event.commune) + '|' + event.dept;\n      if (!communes.has(key)) communes.set(key, event.commune + ' (' + event.dept + ')');\n    });\n    Array.from(communes.values()).sort((a, b) => a.localeCompare(b, 'fr')).slice(0, 12000).forEach((label) => {\n      const option = document.createElement('option');\n      option.value = label;\n      els.communes.appendChild(option);\n    });\n    updateYearRangeUi();\n  }\n\n  function updateYearRangeUi(changed) {\n    let min = Number(els.yearMin.value);\n    let max = Number(els.yearMax.value);\n    if (min > max) {\n      if (changed === 'min') max = min;\n      else min = max;\n      els.yearMin.value = String(min);\n      els.yearMax.value = String(max);\n    }\n    els.yearMinOutput.textContent = String(min);\n    els.yearMaxOutput.textContent = String(max);\n    const span = Math.max(1, state.years.max - state.years.min);\n    const left = ((min - state.years.min) \/ span) * 100;\n    const right = 100 - ((max - state.years.min) \/ span) * 100;\n    els.rangeFill.style.left = left + '%';\n    els.rangeFill.style.right = right + '%';\n  }\n\n  function selectedCauses() {\n    return $$('#mcpfh-causes input:checked').map((input) => input.value);\n  }\n\n  function filterEvents() {\n    const yearMin = Number(els.yearMin.value);\n    const yearMax = Number(els.yearMax.value);\n    const dept = els.dept.value;\n    const communeQuery = normalizeText(els.commune.value.replace(\/\\s*\\([^)]*\\)\\s*$\/, ''));\n    const causes = selectedCauses();\n    const surfaceMinM2 = Math.max(0, parseNumber(els.surfaceMin.value) * 10000);\n    const surfaceMaxM2 = els.surfaceMax.value === '' ? Infinity : Math.max(0, parseNumber(els.surfaceMax.value) * 10000);\n\n    state.filtered = state.events.filter((event) => {\n      if (event.year < yearMin || event.year > yearMax) return false;\n      if (dept && event.dept !== dept) return false;\n      if (communeQuery && !normalizeText(event.commune).includes(communeQuery)) return false;\n      if (causes.length && !causes.includes(event.cause)) return false;\n      if (event.surfaceM2 < surfaceMinM2 || event.surfaceM2 > surfaceMaxM2) return false;\n      return true;\n    });\n  }\n\n  function groupByDepartment(events) {\n    const result = {};\n    events.forEach((event) => {\n      if (!event.dept) return;\n      const item = result[event.dept] || { count: 0, surfaceM2: 0, deaths: 0, buildings: 0 };\n      item.count += 1;\n      item.surfaceM2 += event.surfaceM2;\n      item.deaths += event.deaths;\n      item.buildings += event.buildings;\n      result[event.dept] = item;\n    });\n    return result;\n  }\n\n  function groupByCommune(events) {\n    const result = new Map();\n    events.forEach((event) => {\n      const key = event.insee || normalizeText(event.commune) + '|' + event.dept;\n      const current = result.get(key) || {\n        insee: event.insee,\n        commune: event.commune,\n        dept: event.dept,\n        count: 0,\n        surfaceM2: 0,\n        lat: event.lat,\n        lon: event.lon,\n        causes: {}\n      };\n      current.count += 1;\n      current.surfaceM2 += event.surfaceM2;\n      current.causes[event.cause] = (current.causes[event.cause] || 0) + 1;\n      if (!Number.isFinite(current.lat) && Number.isFinite(event.lat)) current.lat = event.lat;\n      if (!Number.isFinite(current.lon) && Number.isFinite(event.lon)) current.lon = event.lon;\n      result.set(key, current);\n    });\n    return Array.from(result.values());\n  }\n\n  function quantileBreaks(values) {\n    const sorted = values.filter((v) => Number.isFinite(v) && v > 0).sort((a, b) => a - b);\n    if (!sorted.length) return [0, 0, 0, 0];\n    return [0.2, 0.4, 0.6, 0.8].map((q) => sorted[Math.min(sorted.length - 1, Math.floor((sorted.length - 1) * q))]);\n  }\n\n  function bucketColor(value, breaks) {\n    if (!value) return '#e6e9ed';\n    let index = 0;\n    while (index < breaks.length && value > breaks[index]) index += 1;\n    return COLORS[Math.min(index, COLORS.length - 1)];\n  }\n\n  function renderLegend(breaks) {\n    els.legend.replaceChildren();\n    const title = document.createElement('strong');\n    title.textContent = state.metric === 'count' ? 'Nombre d\u2019incendies' : 'Surface br\u00fbl\u00e9e (ha)';\n    els.legend.appendChild(title);\n    const limits = [0, ...breaks];\n    COLORS.forEach((color, index) => {\n      const row = document.createElement('div');\n      row.className = 'mcpfh-legend-row';\n      const swatch = document.createElement('span');\n      swatch.className = 'mcpfh-legend-swatch';\n      swatch.style.background = color;\n      const label = document.createElement('span');\n      const low = limits[index];\n      const high = breaks[index];\n      if (index === COLORS.length - 1) {\n        label.textContent = 'Plus de ' + formatNumber(low, state.metric === 'surface' ? 1 : 0);\n      } else {\n        label.textContent = formatNumber(low, state.metric === 'surface' ? 1 : 0) + ' \u00e0 ' + formatNumber(high, state.metric === 'surface' ? 1 : 0);\n      }\n      row.append(swatch, label);\n      els.legend.appendChild(row);\n    });\n  }\n\n  function initMap() {\n    state.map = window.L.map(els.map, { zoomControl: false, minZoom: 4, maxZoom: 16, preferCanvas: true }).setView([46.55, 2.35], 5.5);\n    window.L.control.zoom({ position: 'topright' }).addTo(state.map);\n    window.L.tileLayer('https:\/\/{s}.basemaps.cartocdn.com\/light_all\/{z}\/{x}\/{y}{r}.png', {\n      maxZoom: 20,\n      subdomains: 'abcd',\n      attribution: '\u00a9 OpenStreetMap \u00a9 CARTO'\n    }).addTo(state.map);\n    window.L.control.scale({ imperial: false, position: 'bottomright' }).addTo(state.map);\n    state.communeLayer = window.L.layerGroup().addTo(state.map);\n    window.setTimeout(() => state.map.invalidateSize(), 100);\n  }\n\n  function renderMap() {\n    if (!state.map) return;\n    const byDept = groupByDepartment(state.filtered);\n    const values = Object.values(byDept).map((item) => state.metric === 'count' ? item.count : item.surfaceM2 \/ 10000);\n    const breaks = quantileBreaks(values);\n    renderLegend(breaks);\n\n    if (state.deptLayer) state.deptLayer.remove();\n    if (state.boundaries) {\n      state.deptLayer = window.L.geoJSON(state.boundaries, {\n        style: (feature) => {\n          const code = String((feature.properties || {}).code || '');\n          const item = byDept[code];\n          const value = item ? (state.metric === 'count' ? item.count : item.surfaceM2 \/ 10000) : 0;\n          const selected = !els.dept.value || els.dept.value === code;\n          return {\n            color: selected ? '#ffffff' : '#bfc4ca',\n            weight: selected ? 1.2 : 0.6,\n            fillColor: bucketColor(value, breaks),\n            fillOpacity: selected ? 0.78 : 0.22\n          };\n        },\n        onEachFeature: (feature, layer) => {\n          const p = feature.properties || {};\n          const code = String(p.code || '');\n          const item = byDept[code] || { count: 0, surfaceM2: 0 };\n          layer.bindTooltip('<strong>' + escapeHtml(p.nom || code) + '<\/strong><br>' + formatNumber(item.count) + ' incendies<br>' + formatNumber(item.surfaceM2 \/ 10000, 1) + ' ha', { sticky: true });\n          layer.on('click', () => {\n            els.dept.value = code;\n            applyFilters(false);\n            if (layer.getBounds) state.map.fitBounds(layer.getBounds(), { padding: [30, 30], maxZoom: 9 });\n          });\n        }\n      }).addTo(state.map);\n      state.deptLayer.bringToBack();\n    }\n\n    state.communeLayer.clearLayers();\n    const shouldShowCommunes = Boolean(!state.boundaries || els.dept.value || normalizeText(els.commune.value));\n    if (shouldShowCommunes) {\n      groupByCommune(state.filtered).forEach((item) => {\n        if (!Number.isFinite(item.lat) || !Number.isFinite(item.lon)) return;\n        const value = state.metric === 'count' ? item.count : item.surfaceM2 \/ 10000;\n        const radius = Math.max(5, Math.min(20, 4 + Math.log1p(value) * 2.2));\n        const marker = window.L.circleMarker([item.lat, item.lon], {\n          radius,\n          color: '#fff',\n          weight: 1.5,\n          fillColor: bucketColor(value, breaks),\n          fillOpacity: .9\n        });\n        marker.bindPopup(makeCommunePopup(item));\n        marker.on('click', () => marker.openPopup());\n        state.communeLayer.addLayer(marker);\n      });\n    }\n  }\n\n  function focusFilteredArea() {\n    if (!state.map || (!els.dept.value && !normalizeText(els.commune.value))) return;\n    const points = state.filtered\n      .filter((event) => Number.isFinite(event.lat) && Number.isFinite(event.lon))\n      .map((event) => [event.lat, event.lon]);\n    if (points.length) {\n      const bounds = window.L.latLngBounds(points);\n      if (bounds.isValid()) {\n        state.map.fitBounds(bounds, {\n          padding: [36, 36],\n          maxZoom: normalizeText(els.commune.value) ? 11 : 8\n        });\n        return;\n      }\n    }\n    if (els.dept.value && state.deptLayer) {\n      state.deptLayer.eachLayer((layer) => {\n        if (String((layer.feature && layer.feature.properties || {}).code || '') === els.dept.value && layer.getBounds) {\n          state.map.fitBounds(layer.getBounds(), { padding: [36, 36], maxZoom: 8 });\n        }\n      });\n    }\n  }\n\n  function escapeHtml(value) {\n    const holder = document.createElement('span');\n    holder.textContent = String(value ?? '');\n    return holder.innerHTML;\n  }\n\n  function makeCommunePopup(item) {\n    const topCause = Object.entries(item.causes).sort((a, b) => b[1] - a[1])[0];\n    return '<div class=\"mcpfh-popup\">' +\n      '<h4>' + escapeHtml(item.commune) + ' (' + escapeHtml(item.dept) + ')<\/h4>' +\n      '<p><strong>' + formatNumber(item.count) + '<\/strong> incendies<\/p>' +\n      '<p><strong>' + formatNumber(item.surfaceM2 \/ 10000, 2) + ' ha<\/strong> parcourus<\/p>' +\n      '<p>Cause dominante : ' + escapeHtml(topCause ? topCause[0] : 'Inconnue') + '<\/p>' +\n      '<\/div>';\n  }\n\n  function renderKpis() {\n    const totals = state.filtered.reduce((acc, event) => {\n      acc.surfaceM2 += event.surfaceM2;\n      acc.deaths += event.deaths;\n      acc.buildings += event.buildings;\n      return acc;\n    }, { surfaceM2: 0, deaths: 0, buildings: 0 });\n    $('#mcpfh-kpi-count').textContent = formatCompact(state.filtered.length);\n    $('#mcpfh-kpi-surface').textContent = formatCompact(totals.surfaceM2 \/ 10000);\n    $('#mcpfh-kpi-deaths').textContent = formatNumber(totals.deaths);\n    $('#mcpfh-kpi-buildings').textContent = formatNumber(totals.buildings);\n  }\n\n  function destroyCharts() {\n    Object.values(state.charts).forEach((chart) => chart && chart.destroy());\n    state.charts = {};\n  }\n\n  function renderCharts() {\n    if (!window.Chart) return;\n    destroyCharts();\n    const byYear = {};\n    const byCause = {};\n    state.filtered.forEach((event) => {\n      const year = byYear[event.year] || { count: 0, surfaceM2: 0 };\n      year.count += 1;\n      year.surfaceM2 += event.surfaceM2;\n      byYear[event.year] = year;\n      byCause[event.cause] = (byCause[event.cause] || 0) + 1;\n    });\n    const byDept = groupByDepartment(state.filtered);\n    const yearLabels = Object.keys(byYear).sort((a, b) => Number(a) - Number(b));\n    const causeEntries = Object.entries(byCause).sort((a, b) => b[1] - a[1]);\n    const deptEntries = Object.entries(byDept)\n      .sort((a, b) => (state.metric === 'count' ? b[1].count - a[1].count : b[1].surfaceM2 - a[1].surfaceM2))\n      .slice(0, 12);\n    const common = {\n      responsive: true,\n      maintainAspectRatio: false,\n      animation: { duration: 250 },\n      plugins: { legend: { labels: { boxWidth: 10, font: { size: 9 } } } }\n    };\n\n    state.charts.year = new window.Chart($('#mcpfh-chart-year'), {\n      type: 'bar',\n      data: { labels: yearLabels, datasets: [{\n        label: state.metric === 'count' ? 'Incendies' : 'Surface (ha)',\n        data: yearLabels.map((year) => state.metric === 'count' ? byYear[year].count : byYear[year].surfaceM2 \/ 10000),\n        backgroundColor: '#22252a',\n        borderRadius: 3\n      }] },\n      options: { ...common, plugins: { legend: { display: false } }, scales: { x: { ticks: { font: { size: 8 }, maxRotation: 45 } }, y: { beginAtZero: true, ticks: { font: { size: 8 } } } } }\n    });\n\n    state.charts.causes = new window.Chart($('#mcpfh-chart-causes'), {\n      type: 'doughnut',\n      data: {\n        labels: causeEntries.map(([cause]) => cause),\n        datasets: [{ data: causeEntries.map(([, value]) => value), backgroundColor: causeEntries.map(([cause]) => CAUSE_COLORS[cause] || '#7c818a'), borderWidth: 1 }]\n      },\n      options: { ...common, cutout: '58%', plugins: { legend: { position: 'bottom', labels: { boxWidth: 9, font: { size: 8 } } } } }\n    });\n\n    state.charts.depts = new window.Chart($('#mcpfh-chart-depts'), {\n      type: 'bar',\n      data: {\n        labels: deptEntries.map(([code]) => state.departmentNames[code] || code),\n        datasets: [{\n          label: state.metric === 'count' ? 'Incendies' : 'Surface (ha)',\n          data: deptEntries.map(([, item]) => state.metric === 'count' ? item.count : item.surfaceM2 \/ 10000),\n          backgroundColor: '#d20a11',\n          borderRadius: 3\n        }]\n      },\n      options: { ...common, indexAxis: 'y', plugins: { legend: { display: false } }, scales: { x: { beginAtZero: true, ticks: { font: { size: 8 } } }, y: { ticks: { font: { size: 8 } } } } }\n    });\n  }\n\n  function sortedEvents() {\n    const events = state.filtered.slice();\n    const { key, dir } = state.sort;\n    const sign = dir === 'asc' ? 1 : -1;\n    events.sort((a, b) => {\n      let av = a[key];\n      let bv = b[key];\n      if (key === 'date') { av = a.date.valueOf(); bv = b.date.valueOf(); }\n      if (typeof av === 'string') return av.localeCompare(String(bv), 'fr', { numeric: true }) * sign;\n      return ((Number(av) || 0) - (Number(bv) || 0)) * sign;\n    });\n    return events;\n  }\n\n  function renderTable() {\n    const events = sortedEvents();\n    const pages = Math.max(1, Math.ceil(events.length \/ CONFIG.pageSize));\n    state.page = Math.min(Math.max(1, state.page), pages);\n    const start = (state.page - 1) * CONFIG.pageSize;\n    const rows = events.slice(start, start + CONFIG.pageSize);\n    els.tableBody.replaceChildren();\n    if (!rows.length) {\n      const tr = document.createElement('tr');\n      const td = document.createElement('td');\n      td.colSpan = 5;\n      td.className = 'mcpfh-table-empty';\n      td.textContent = 'Aucun incendie ne correspond aux filtres s\u00e9lectionn\u00e9s.';\n      tr.appendChild(td);\n      els.tableBody.appendChild(tr);\n    } else {\n      rows.forEach((event) => {\n        const tr = document.createElement('tr');\n        [formatDate(event.date), event.commune, event.dept, formatNumber(event.surfaceM2 \/ 10000, 4), event.cause].forEach((value) => {\n          const td = document.createElement('td');\n          td.textContent = value;\n          tr.appendChild(td);\n        });\n        els.tableBody.appendChild(tr);\n      });\n    }\n    els.pageInfo.textContent = state.page + ' \/ ' + pages + ' \u2014 ' + formatNumber(events.length) + ' r\u00e9sultats';\n    els.prev.disabled = state.page <= 1;\n    els.next.disabled = state.page >= pages;\n    $$('.mcpfh-table th [data-mcpfh-sort]').forEach((button) => {\n      const indicator = button.querySelector('span');\n      indicator.textContent = button.dataset.mcpfhSort === state.sort.key ? (state.sort.dir === 'asc' ? '\u2191' : '\u2193') : '';\n    });\n  }\n\n  function applyFilters(closeMobile = true) {\n    filterEvents();\n    state.page = 1;\n    renderKpis();\n    renderMap();\n    focusFilteredArea();\n    renderCharts();\n    renderTable();\n    if (closeMobile) closeMobileFilters();\n  }\n\n  function resetFilters() {\n    els.yearMin.value = String(state.years.min);\n    els.yearMax.value = String(state.years.max);\n    els.dept.value = '';\n    els.commune.value = '';\n    els.surfaceMin.value = '';\n    els.surfaceMax.value = '';\n    $$('#mcpfh-causes input').forEach((input) => { input.checked = false; });\n    updateYearRangeUi();\n    applyFilters();\n    if (state.map) state.map.setView([46.55, 2.35], 5.5);\n  }\n\n  function openMobileFilters() {\n    els.filters.classList.add('is-open');\n    els.backdrop.hidden = false;\n    els.mobileFilter.setAttribute('aria-expanded', 'true');\n  }\n\n  function closeMobileFilters() {\n    els.filters.classList.remove('is-open');\n    els.backdrop.hidden = true;\n    els.mobileFilter.setAttribute('aria-expanded', 'false');\n  }\n\n  function setDrawer(open, tab) {\n    if (tab) {\n      $$('[data-mcpfh-tab]').forEach((button) => {\n        const active = button.dataset.mcpfhTab === tab;\n        button.classList.toggle('is-active', active);\n        button.setAttribute('aria-selected', active ? 'true' : 'false');\n      });\n      $$('[data-mcpfh-pane]').forEach((pane) => {\n        const active = pane.dataset.mcpfhPane === tab;\n        pane.classList.toggle('is-active', active);\n        pane.hidden = !active;\n      });\n    }\n    els.drawer.classList.toggle('is-open', open);\n    els.drawerToggle.setAttribute('aria-expanded', open ? 'true' : 'false');\n    if (open) window.setTimeout(() => Object.values(state.charts).forEach((chart) => chart && chart.resize()), 260);\n  }\n\n  function bindEvents() {\n    els.form.addEventListener('submit', (event) => { event.preventDefault(); applyFilters(); });\n    els.reset.addEventListener('click', resetFilters);\n    els.yearMin.addEventListener('input', () => updateYearRangeUi('min'));\n    els.yearMax.addEventListener('input', () => updateYearRangeUi('max'));\n    els.mobileFilter.addEventListener('click', openMobileFilters);\n    els.filterClose.addEventListener('click', closeMobileFilters);\n    els.backdrop.addEventListener('click', closeMobileFilters);\n    els.retry.addEventListener('click', boot);\n    els.prev.addEventListener('click', () => { state.page -= 1; renderTable(); });\n    els.next.addEventListener('click', () => { state.page += 1; renderTable(); });\n\n    $$('[data-mcpfh-metric]').forEach((button) => {\n      button.addEventListener('click', () => {\n        state.metric = button.dataset.mcpfhMetric;\n        $$('[data-mcpfh-metric]').forEach((item) => {\n          const active = item === button;\n          item.classList.toggle('is-active', active);\n          item.setAttribute('aria-pressed', active ? 'true' : 'false');\n        });\n        renderMap();\n        renderCharts();\n      });\n    });\n\n    $$('[data-mcpfh-tab]').forEach((button) => {\n      button.addEventListener('click', () => setDrawer(true, button.dataset.mcpfhTab));\n    });\n    els.drawerToggle.addEventListener('click', () => setDrawer(!els.drawer.classList.contains('is-open')));\n\n    $$('[data-mcpfh-sort]').forEach((button) => {\n      button.addEventListener('click', () => {\n        const key = button.dataset.mcpfhSort;\n        if (state.sort.key === key) state.sort.dir = state.sort.dir === 'asc' ? 'desc' : 'asc';\n        else state.sort = { key, dir: key === 'commune' || key === 'dept' ? 'asc' : 'desc' };\n        state.page = 1;\n        renderTable();\n      });\n    });\n\n    window.addEventListener('resize', () => state.map && state.map.invalidateSize());\n  }\n\n  let bootAttempt = 0;\n  let bootWatchdog = null;\n\n  async function boot() {\n    const attempt = ++bootAttempt;\n    window.clearTimeout(bootWatchdog);\n    root.dataset.ready = 'false';\n    bootWatchdog = window.setTimeout(() => {\n      if (attempt !== bootAttempt || root.dataset.ready === 'true') return;\n      bootAttempt += 1;\n      showStatus(\n        'Chargement interrompu',\n        'Une ressource distante est bloqu\u00e9e ou trop lente. Cliquez sur \u00ab R\u00e9essayer \u00bb. Si le probl\u00e8me persiste, v\u00e9rifiez les r\u00e9glages de cache et de s\u00e9curit\u00e9 WordPress.',\n        true\n      );\n    }, CONFIG.bootTimeoutMs);\n\n    try {\n      showStatus('Chargement de la carte', 'Initialisation et r\u00e9cup\u00e9ration des donn\u00e9es BDIFF\u2026');\n      const dependencies = [\n        loadCss('mcpfh-leaflet-css', CONFIG.leafletCss),\n        loadScript('mcpfh-leaflet-js', CONFIG.leafletJs, 'L'),\n        loadScript('mcpfh-chart-js', CONFIG.chartJs, 'Chart').catch((error) => {\n          console.warn('[Carte historique incendies] Graphiques indisponibles :', error);\n        })\n      ];\n      if (CONFIG.csvUrl) dependencies.push(loadScript('mcpfh-papa-js', CONFIG.papaJs, 'Papa'));\n      else dependencies.push(loadData());\n      await Promise.all(dependencies);\n      if (attempt !== bootAttempt) return;\n\n      if (CONFIG.csvUrl) {\n        showStatus('Chargement des incendies', 'Lecture du fichier CSV BDIFF\u2026');\n        await loadData();\n        if (attempt !== bootAttempt) return;\n      }\n\n      showStatus('Pr\u00e9paration de la carte', 'Calcul des indicateurs et construction des filtres\u2026');\n      initializeFilterOptions();\n      if (!state.map) initMap();\n      state.filtered = state.events.slice();\n      els.sourceBadge.textContent = state.sourceLabel;\n      renderKpis();\n      renderMap();\n      renderCharts();\n      renderTable();\n      root.dataset.ready = 'true';\n      hideStatus();\n    } catch (error) {\n      if (attempt !== bootAttempt) return;\n      console.error('[Carte historique incendies]', error);\n      showStatus('Impossible de charger la carte', error && error.message ? error.message : 'Une erreur inconnue est survenue.', true);\n    } finally {\n      if (attempt === bootAttempt) window.clearTimeout(bootWatchdog);\n    }\n  }\n\n  try {\n    bindEvents();\n    boot();\n  } catch (error) {\n    console.error('[Carte historique incendies] Initialisation impossible :', error);\n    showStatus('Initialisation impossible', 'Le bloc WordPress a modifi\u00e9 une partie du code HTML ou JavaScript.', true);\n  }\n})();\n<\/script><\/div><\/div><\/div><\/div>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":62010,"parent":61253,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"100-width.php","meta":{"_seopress_titles_title":"| guide pratique","_seopress_titles_desc":"","_seopress_robots_index":"","_seopress_robots_follow":"","_seopress_robots_imageindex":"","_seopress_robots_snippet":"","_seopress_robots_primary_cat":"","_seopress_robots_breadcrumbs":"","_seopress_robots_freeze_modified_date":"","_seopress_robots_custom_modified_date":"","_seopress_robots_canonical":"","_seopress_social_fb_title":"","_seopress_social_fb_desc":"","_seopress_social_fb_img":"","_seopress_social_fb_img_attachment_id":0,"_seopress_social_fb_img_width":0,"_seopress_social_fb_img_height":0,"_seopress_social_twitter_title":"","_seopress_social_twitter_desc":"","_seopress_social_twitter_img":"","_seopress_social_twitter_img_attachment_id":0,"_seopress_social_twitter_img_width":0,"_seopress_social_twitter_img_height":0,"_seopress_redirections_value":"","_seopress_redirections_enabled":"","_seopress_redirections_enabled_regex":"","_seopress_redirections_logged_status":"both","_seopress_redirections_param":"","_seopress_redirections_type":301,"_seopress_analysis_target_kw":"temperature mer","footnotes":""},"class_list":["post-62006","page","type-page","status-publish","has-post-thumbnail","hentry"],"_links":{"self":[{"href":"https:\/\/alertes-meteo.com\/index.php\/wp-json\/wp\/v2\/pages\/62006","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/alertes-meteo.com\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/alertes-meteo.com\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/alertes-meteo.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/alertes-meteo.com\/index.php\/wp-json\/wp\/v2\/comments?post=62006"}],"version-history":[{"count":5,"href":"https:\/\/alertes-meteo.com\/index.php\/wp-json\/wp\/v2\/pages\/62006\/revisions"}],"predecessor-version":[{"id":62027,"href":"https:\/\/alertes-meteo.com\/index.php\/wp-json\/wp\/v2\/pages\/62006\/revisions\/62027"}],"up":[{"embeddable":true,"href":"https:\/\/alertes-meteo.com\/index.php\/wp-json\/wp\/v2\/pages\/61253"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/alertes-meteo.com\/index.php\/wp-json\/wp\/v2\/media\/62010"}],"wp:attachment":[{"href":"https:\/\/alertes-meteo.com\/index.php\/wp-json\/wp\/v2\/media?parent=62006"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}