{"id":23814165,"date":"2026-09-21T17:59:28","date_gmt":"2026-09-21T17:59:28","guid":{"rendered":"https:\/\/socialhackerslab.com\/?page_id=23814165"},"modified":"2026-09-21T17:59:29","modified_gmt":"2026-09-21T17:59:29","slug":"carte-herault","status":"publish","type":"page","link":"https:\/\/socialhackerslab.com\/en\/carte-herault\/","title":{"rendered":"carte herault"},"content":{"rendered":"<!-- ============================================================\r\n      CARTE H\u00c9RAULT \u2014 Associations accompagn\u00e9es\r\n      - Vraie g\u00e9om\u00e9trie (contours des 341 communes du d\u00e9partement, charg\u00e9s au\r\n        runtime ; la liste officielle vient de geo.api.gouv.fr)\r\n      - Les 31 communes de Montpellier M\u00e9diterran\u00e9e M\u00e9tropole ressortent en\r\n        surbrillance, avec un raccourci de zoom dans le rail\r\n      - Deux vues : les communes du d\u00e9partement, ou les 21 quartiers\r\n        prioritaires (QPV) de l'H\u00e9rault, charg\u00e9s depuis l'open data ANCT\r\n      - Zoom \/ d\u00e9zoom anim\u00e9s (D3)\r\n      - \u00c0 coller dans un bloc \"HTML personnalis\u00e9\" WordPress\r\n      \u25ba Pour ajouter les assos : remplir l'objet ASSOS plus bas (code INSEE -> [assos])\r\n    ============================================================ -->\r\n  <div id=\"mtp-map\">\r\n    <!-- Rail : titre, vues et rep\u00e8res. Sous 900px, il redevient un bandeau. -->\r\n    <aside class=\"mtp-side\">\r\n      <div class=\"mtp-head\">\r\n        <div class=\"mtp-eyebrow\">SOCIAL HACKERS LAB<\/div>\r\n        <div class=\"mtp-title\">Associations accompagn\u00e9es<\/div>\r\n        <div class=\"mtp-sub\">H\u00e9rault<\/div>\r\n      <\/div>\r\n      <div class=\"mtp-ctrl\">\r\n        <button class=\"mtp-chip mtp-on\" data-view=\"com\">Communes de l'H\u00e9rault<\/button>\r\n        <button class=\"mtp-chip\" data-view=\"qpv\">Quartiers prioritaires<\/button>\r\n        <button class=\"mtp-chip\" data-view=\"metro\">M\u00e9tropole de Montpellier<\/button>\r\n        <button class=\"mtp-chip mtp-reset\" data-view=\"reset\">\u21ba Vue d'ensemble<\/button>\r\n      <\/div>\r\n      <div class=\"mtp-stat\" id=\"mtp-stat\"><\/div>\r\n      <div class=\"mtp-legend\" id=\"mtp-legend\"><\/div>\r\n    <\/aside>\r\n\r\n    <div class=\"mtp-stage\" id=\"mtp-stage\">\r\n      <svg id=\"mtp-svg\"><\/svg>\r\n      <div class=\"mtp-tip\" id=\"mtp-tip\"><\/div>\r\n      <aside class=\"mtp-panel\" id=\"mtp-panel\" aria-hidden=\"true\">\r\n        <div class=\"mtp-panel-bar\" id=\"mtp-panel-bar\"><\/div>\r\n        <div class=\"mtp-panel-hd\" id=\"mtp-panel-hd\">\r\n          <button class=\"mtp-panel-close\" id=\"mtp-panel-close\" aria-label=\"Fermer\">\u2190 Retour<\/button>\r\n          <div class=\"mtp-panel-eyebrow\" id=\"mtp-panel-eyebrow\"><\/div>\r\n          <div class=\"mtp-panel-title\" id=\"mtp-panel-title\"><\/div>\r\n          <div class=\"mtp-panel-count\" id=\"mtp-panel-count\"><\/div>\r\n        <\/div>\r\n        <div class=\"mtp-panel-list\" id=\"mtp-panel-list\"><\/div>\r\n      <\/aside>\r\n      <div class=\"mtp-loader\" id=\"mtp-loader\">Chargement de la carte\u2026<\/div>\r\n    <\/div>\r\n  <\/div>\r\n\r\n  <style>\r\n\r\n\r\n  @import url('https:\/\/fonts.googleapis.com\/css2?family=Manrope:wght@500;600;700;800&display=swap');\r\n  #mtp-map{\r\n    --bg:#fbfaf8; --ink:#24476d; --muted:#6d7580; --border:#e7dfd9; --accent:#6d4f84;\r\n    --marker:#e0a32a; --font:'Manrope',system-ui,sans-serif;\r\n    font-family:var(--font); color:var(--ink); background:var(--bg);\r\n    \/* Plein \u00e9cran (uniquement l\u00e0 o\u00f9 le bloc est pos\u00e9) *\/\r\n    position:fixed !important; inset:0 !important;\r\n    width:100vw !important; height:100vh !important; max-width:none !important;\r\n    margin:0 !important; padding:20px 24px !important;\r\n    border:none !important; border-radius:0 !important;\r\n    box-sizing:border-box; z-index:999999; isolation:isolate;\r\n    display:flex; flex-direction:row; gap:22px; overflow:hidden;\r\n  }\r\n  body.admin-bar #mtp-map{top:32px !important; height:calc(100vh - 32px) !important;}\r\n  @media(max-width:782px){ body.admin-bar #mtp-map{top:46px !important; height:calc(100vh - 46px) !important;} }\r\n  html:has(#mtp-map), body:has(#mtp-map){margin:0 !important;padding:0 !important;overflow:hidden !important;}\r\n  \/* Masque le chrome du th\u00e8me UNIQUEMENT sur la page qui h\u00e9berge la carte (via :has) *\/\r\n  body:has(#mtp-map) .entry-title,\r\n  body:has(#mtp-map) .page-title,\r\n  body:has(#mtp-map) h1.elementor-heading-title,\r\n  body:has(#mtp-map) .nv-page-title,\r\n  body:has(#mtp-map) .site-header,\r\n  body:has(#mtp-map) header.site-header,\r\n  body:has(#mtp-map) .site-footer,\r\n  body:has(#mtp-map) footer.site-footer{display:none !important;}\r\n  #mtp-map *{box-sizing:border-box}\r\n  #mtp-map .mtp-side{\r\n    flex:0 0 244px;display:flex;flex-direction:column;gap:18px;\r\n    min-height:0;overflow-y:auto;padding-right:4px;\r\n    scrollbar-width:thin;scrollbar-color:var(--border) transparent;\r\n  }\r\n  #mtp-map .mtp-head{margin-bottom:0}\r\n  #mtp-map .mtp-eyebrow{font-size:.62rem;font-weight:800;letter-spacing:.14em;color:var(--muted)}\r\n  #mtp-map .mtp-title{font-size:1.5rem;font-weight:800;line-height:1.08;margin-top:3px;letter-spacing:-.01em}\r\n  #mtp-map .mtp-sub{font-size:.82rem;font-weight:700;color:var(--accent);margin-top:3px}\r\n  #mtp-map .mtp-ctrl{display:flex;flex-direction:column;align-items:stretch;gap:.4rem}\r\n  #mtp-map .mtp-chip{\r\n    font-family:var(--font);font-size:.7rem;font-weight:800;letter-spacing:.04em;\r\n    background:#fff;border:1.5px solid var(--border);color:var(--ink);\r\n    padding:.52rem .9rem;border-radius:999px;cursor:pointer;transition:.16s;text-align:left;\r\n  }\r\n  #mtp-map .mtp-chip:hover{border-color:var(--accent);color:var(--accent)}\r\n  #mtp-map .mtp-chip.mtp-on{background:var(--accent);border-color:var(--accent);color:#fff}\r\n  #mtp-map .mtp-reset{color:var(--muted)}\r\n  \/* Pas de cadre : la carte respire dans le fond de la page, sans bordure ni\r\n    d\u00e9grad\u00e9. Tout le bloc \u2014 en-t\u00eate, carte, l\u00e9gende \u2014 tient sur le m\u00eame fond. *\/\r\n  #mtp-map .mtp-stage{\r\n    position:relative;width:100%;flex:1 1 auto;min-height:0;\r\n    background:transparent;border:0;border-radius:0;overflow:hidden;\r\n  }\r\n  \/* Cible UNIQUEMENT le SVG de la carte : sinon la r\u00e8gle width\/height:100%\r\n    \u00e9tire aussi les petites ic\u00f4nes inline du panneau (pin secteur, lien Erasmus\u2026). *\/\r\n  #mtp-map #mtp-svg{display:block;width:100%;height:100%;cursor:grab}\r\n  #mtp-map #mtp-svg:active{cursor:grabbing}\r\n  \/* Ic\u00f4nes inline (fiches, contact, meta) : garder leur taille intrins\u00e8que *\/\r\n  #mtp-map .mtp-panel svg{width:auto;height:auto;display:inline-block}\r\n  #mtp-map .mtp-zone{stroke:#a7bacb;stroke-width:.9px;vector-effect:non-scaling-stroke;cursor:pointer;transition:fill-opacity .2s,filter .2s}\r\n  #mtp-map .mtp-zone:hover{filter:brightness(1.12)}\r\n  #mtp-map .mtp-zborder{fill:none;stroke:#fff;stroke-width:1.6px;vector-effect:non-scaling-stroke;pointer-events:none}\r\n  #mtp-map .mtp-arr{stroke:#fff;stroke-width:.6px;cursor:pointer;transition:.18s}\r\n  #mtp-map .mtp-arr:hover{filter:brightness(1.12)}\r\n  #mtp-map .mtp-zlabel{font-size:12px;font-weight:800;fill:#fff;text-anchor:middle;pointer-events:none;\r\n    paint-order:stroke;stroke:rgba(36,71,109,.55);stroke-width:3px;stroke-linejoin:round;letter-spacing:.02em}\r\n  #mtp-map .mtp-zlabel-off{fill:var(--ink);stroke:rgba(255,255,255,.92);stroke-width:2.6px}\r\n  #mtp-map .mtp-alabel{font-size:9px;font-weight:800;fill:var(--ink);text-anchor:middle;pointer-events:none;\r\n    paint-order:stroke;stroke:rgba(255,255,255,.9);stroke-width:2.4px;stroke-linejoin:round}\r\n  #mtp-map .mtp-asso circle,\r\n  #mtp-map .mtp-arr-bubble circle{fill:var(--marker);stroke:#fff;stroke-width:1.6px;cursor:pointer;\r\n    filter:drop-shadow(0 2px 3px rgba(36,71,109,.3))}\r\n  #mtp-map .mtp-asso text,\r\n  #mtp-map .mtp-arr-bubble text{font-size:10px;font-weight:800;fill:#3a2a00;text-anchor:middle;pointer-events:none;dominant-baseline:central}\r\n  #mtp-map .mtp-tip{position:absolute;z-index:30;max-width:240px;padding:.55rem .7rem;border-radius:10px;\r\n    background:rgba(255,255,255,.97);border:1px solid var(--border);box-shadow:0 10px 28px rgba(36,71,109,.16);\r\n    font-size:.76rem;pointer-events:none;opacity:0;transform:translateY(6px);transition:.14s}\r\n  #mtp-map .mtp-tip.mtp-show{opacity:1;transform:translateY(0)}\r\n  #mtp-map .mtp-tip b{color:var(--ink);font-weight:800}\r\n  #mtp-map .mtp-tip .mtp-tip-meta{color:var(--accent);font-weight:700;margin-top:2px;font-size:.7rem}\r\n  #mtp-map .mtp-tip ul{margin:.3rem 0 0;padding-left:1rem;color:var(--muted);font-weight:600}\r\n  #mtp-map .mtp-loader{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;\r\n    color:var(--muted);font-weight:700;font-size:.85rem;background:transparent}\r\n  #mtp-map .mtp-stat{\r\n    border-top:1px solid var(--border);padding-top:14px;\r\n    display:flex;flex-direction:column;gap:2px;\r\n  }\r\n  #mtp-map .mtp-stat-num{font-size:1.9rem;font-weight:800;line-height:1;color:var(--marker)}\r\n  #mtp-map .mtp-stat-lab{font-size:.74rem;font-weight:700;color:var(--ink);line-height:1.3}\r\n  #mtp-map .mtp-stat-sub{font-size:.7rem;font-weight:600;color:var(--muted)}\r\n  #mtp-map .mtp-legend{display:flex;flex-direction:column;align-items:flex-start;gap:.42rem;\r\n    padding-top:14px;border-top:1px solid var(--border);\r\n    font-size:.72rem;color:var(--muted);font-weight:700;line-height:1.25}\r\n  #mtp-map .mtp-legend span{display:inline-flex;align-items:center;gap:.45rem}\r\n  #mtp-map .mtp-legend i{width:11px;height:11px;border-radius:3px;display:inline-block;flex:0 0 auto}\r\n  #mtp-map .mtp-legend i.mtp-dot{border-radius:50%}\r\n\r\n  @media(max-width:900px){\r\n    #mtp-map{flex-direction:column;gap:12px}\r\n    #mtp-map .mtp-side{flex:0 0 auto;overflow:visible;gap:12px;\r\n      display:grid;grid-template-columns:1fr auto;align-items:end;\r\n      padding-bottom:12px;border-bottom:1px solid var(--border)}\r\n    #mtp-map .mtp-ctrl{flex-direction:row;flex-wrap:wrap;justify-content:flex-end}\r\n    #mtp-map .mtp-stat{grid-column:1\/-1;flex-direction:row;align-items:baseline;gap:.5rem;\r\n      border-top:0;padding-top:0}\r\n    #mtp-map .mtp-stat-num{font-size:1.15rem}\r\n    #mtp-map .mtp-legend{grid-column:1\/-1;flex-direction:row;flex-wrap:wrap;\r\n      align-items:center;gap:.35rem .8rem;border-top:0;padding-top:0}\r\n  }\r\n  @media(max-width:600px){\r\n    #mtp-map{padding:12px 14px !important}\r\n    #mtp-map .mtp-title{font-size:1.2rem}\r\n  }\r\n  \/* \u2500\u2500 Panel lat\u00e9ral associations \u2500\u2500 *\/\r\n  #mtp-map .mtp-panel{\r\n    position:absolute;top:0;right:0;bottom:0;width:352px;max-width:calc(100% - 16px);\r\n    background:#fff;border:1px solid var(--border);border-radius:16px;\r\n    box-shadow:0 18px 44px rgba(36,71,109,.14);\r\n    display:flex;flex-direction:column;\r\n    transform:translateX(106%);transition:transform .38s cubic-bezier(.22,1,.36,1);\r\n    z-index:20;overflow:hidden;\r\n  }\r\n  #mtp-map .mtp-panel.mtp-open{transform:translateX(0);}\r\n  #mtp-map .mtp-panel-bar{height:4px;flex:0 0 4px;background:var(--pc,var(--accent));border-radius:16px 16px 0 0;}\r\n  #mtp-map .mtp-panel-hd{\r\n    padding:1.3rem 1.5rem 1.1rem;\r\n    border-bottom:1px solid var(--border);flex:0 0 auto;\r\n  }\r\n  #mtp-map .mtp-panel-close{\r\n    display:inline-flex;align-items:center;gap:.4rem;\r\n    background:none;border:none;font-family:var(--font);\r\n    font-size:.8rem;font-weight:700;color:var(--muted);\r\n    cursor:pointer;padding:0;margin-bottom:1.1rem;transition:.14s;\r\n  }\r\n  #mtp-map .mtp-panel-close:hover{color:var(--pc,var(--accent));}\r\n  #mtp-map .mtp-panel-eyebrow{\r\n    font-size:.64rem;font-weight:800;letter-spacing:.12em;\r\n    text-transform:uppercase;color:var(--muted);\r\n  }\r\n  #mtp-map .mtp-panel-title{\r\n    font-size:1.35rem;font-weight:800;color:var(--ink);\r\n    margin-top:.25rem;line-height:1.15;\r\n  }\r\n  #mtp-map .mtp-panel-count{\r\n    font-size:.72rem;color:var(--muted);font-weight:600;margin-top:.3rem;\r\n  }\r\n  #mtp-map .mtp-panel-list{\r\n    flex:1;overflow-y:auto;\r\n    padding:1.1rem 1.5rem;\r\n    display:flex;flex-direction:column;gap:.38rem;\r\n    scrollbar-width:thin;scrollbar-color:var(--border) transparent;\r\n  }\r\n  #mtp-map .mtp-panel-list::-webkit-scrollbar{width:4px;}\r\n  #mtp-map .mtp-panel-list::-webkit-scrollbar-thumb{background:var(--border);border-radius:4px;}\r\n  #mtp-map .mtp-pitem{\r\n    display:flex;align-items:center;gap:.7rem;\r\n    background:#fff;border:1.5px solid var(--border);\r\n    border-radius:11px;padding:.65rem .8rem;\r\n    transition:.18s ease;cursor:pointer;\r\n    text-decoration:none;\r\n  }\r\n  #mtp-map .mtp-pitem:hover{\r\n    border-color:var(--pc,var(--accent));\r\n    transform:translateY(-1px);\r\n    box-shadow:0 5px 12px rgba(36,71,109,.07);\r\n  }\r\n  #mtp-map .mtp-pitem-chip{\r\n    width:16px;height:16px;border-radius:50%;flex:0 0 16px;\r\n    box-shadow:0 0 0 2px #fff,0 0 0 3px rgba(0,0,0,.07);\r\n  }\r\n  #mtp-map .mtp-pitem-body{flex:1;min-width:0;}\r\n  #mtp-map .mtp-pitem-name{\r\n    font-weight:800;font-size:.86rem;color:var(--ink);line-height:1.15;display:block;\r\n  }\r\n  #mtp-map .mtp-pitem-arrow{font-size:.9rem;color:var(--muted);flex:0 0 auto;transition:.15s;}\r\n  #mtp-map .mtp-pitem:hover .mtp-pitem-arrow{color:var(--pc,var(--accent));transform:translateX(2px);}\r\n  \/* \u2500\u2500 Profil asso (niveau 2) \u2500\u2500 *\/\r\n  #mtp-map .mtp-profile{display:flex;flex-direction:column;}\r\n  #mtp-map .mtp-profile-hero{\r\n    margin:-1.1rem -1.5rem 0;padding:1.35rem 1.5rem 1.25rem;\r\n    background:linear-gradient(135deg,var(--pc,var(--accent)),color-mix(in srgb,var(--pc,var(--accent)) 65%,#0a1520) 100%);\r\n    color:#fff;position:relative;overflow:hidden;isolation:isolate;\r\n  }\r\n  #mtp-map .mtp-profile-hero::before{\r\n    content:\"\";position:absolute;top:-55%;right:-22%;width:75%;height:200%;\r\n    background:radial-gradient(circle,rgba(255,255,255,.2),transparent 62%);\r\n    border-radius:50%;pointer-events:none;z-index:0;\r\n  }\r\n  #mtp-map .mtp-profile-hero>*{position:relative;z-index:1;}\r\n  #mtp-map .mtp-profile-back{\r\n    display:inline-flex;align-items:center;gap:.35rem;\r\n    background:rgba(255,255,255,.15);border:1px solid rgba(255,255,255,.25);\r\n    color:rgba(255,255,255,.92);font-family:var(--font);font-size:.72rem;font-weight:700;\r\n    padding:.3rem .7rem;border-radius:999px;cursor:pointer;margin-bottom:.9rem;transition:.15s;\r\n  }\r\n  #mtp-map .mtp-profile-back:hover{background:rgba(255,255,255,.25);color:#fff;}\r\n  #mtp-map .mtp-profile-domain{\r\n    display:inline-block;\r\n    background:rgba(255,255,255,.18);border:1px solid rgba(255,255,255,.3);\r\n    font-size:.65rem;font-weight:800;letter-spacing:.07em;text-transform:uppercase;\r\n    padding:.22rem .6rem;border-radius:999px;margin-bottom:.8rem;\r\n    backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);\r\n  }\r\n  #mtp-map .mtp-profile-title{\r\n    font-size:1.22rem;font-weight:800;line-height:1.12;color:#fff;\r\n    letter-spacing:-.01em;overflow-wrap:anywhere;margin:0 0 .8rem;\r\n  }\r\n  #mtp-map .mtp-profile-meta{\r\n    display:flex;flex-wrap:wrap;align-items:center;gap:.4rem;\r\n    color:rgba(255,255,255,.88);font-size:.78rem;font-weight:600;\r\n  }\r\n  #mtp-map .mtp-profile-meta-zone{\r\n    display:inline-flex;align-items:center;gap:.28rem;\r\n    background:rgba(255,255,255,.15);border:1px solid rgba(255,255,255,.2);\r\n    padding:.2rem .55rem;border-radius:999px;font-weight:700;\r\n  }\r\n  #mtp-map .mtp-profile-meta-sep{color:rgba(255,255,255,.4);font-weight:700;}\r\n  #mtp-map .mtp-profile-funding-title{\r\n    font-size:.62rem;font-weight:800;letter-spacing:.1em;text-transform:uppercase;\r\n    color:#94a3b8;margin-bottom:.5rem;\r\n  }\r\n  #mtp-map .mtp-profile-body{padding:1.2rem 0 0;display:flex;flex-direction:column;gap:1.1rem;}\r\n  #mtp-map .mtp-profile-section-label{\r\n    font-size:.62rem;font-weight:800;letter-spacing:.1em;text-transform:uppercase;\r\n    color:#94a3b8;margin-bottom:.5rem;\r\n  }\r\n  #mtp-map .mtp-profile-desc{\r\n    font-size:.88rem;font-weight:500;line-height:1.62;color:#2f3e4f;\r\n    margin:0;white-space:pre-wrap;overflow-wrap:anywhere;\r\n    border-left:3px solid var(--pc,var(--accent));padding:.05rem 0 .05rem .85rem;\r\n  }\r\n  #mtp-map .mtp-profile-no-desc{font-size:.83rem;color:var(--muted);font-style:italic;margin:0;}\r\n  #mtp-map .mtp-profile-link{\r\n    display:flex;align-items:center;gap:.7rem;\r\n    padding:.65rem .85rem;border-radius:12px;\r\n    background:#fff;border:1px solid #e6ebf1;\r\n    color:#1f2d3d;text-decoration:none;transition:.18s ease;\r\n  }\r\n  #mtp-map .mtp-profile-link:hover{\r\n    border-color:var(--pc,var(--accent));\r\n    background:color-mix(in srgb,var(--pc,var(--accent)) 6%,#fff);\r\n    transform:translateY(-1px);box-shadow:0 4px 12px rgba(36,71,109,.08);\r\n  }\r\n  #mtp-map .mtp-profile-link-icon{\r\n    width:34px;height:34px;border-radius:10px;flex:0 0 34px;\r\n    background:var(--pc,var(--accent));color:#fff;\r\n    display:inline-flex;align-items:center;justify-content:center;\r\n  }\r\n  #mtp-map .mtp-profile-link-body{flex:1;min-width:0;}\r\n  #mtp-map .mtp-profile-link-label{\r\n    display:block;font-size:.62rem;font-weight:800;letter-spacing:.08em;\r\n    text-transform:uppercase;color:#94a3b8;\r\n  }\r\n  #mtp-map .mtp-profile-link-value{\r\n    display:block;font-size:.86rem;font-weight:700;color:#1f2d3d;\r\n    overflow:hidden;text-overflow:ellipsis;white-space:nowrap;\r\n  }\r\n  #mtp-map .mtp-profile-link-arrow{flex:0 0 auto;color:#cbd5e1;font-size:1rem;transition:.18s;}\r\n  #mtp-map .mtp-profile-link:hover .mtp-profile-link-arrow{color:var(--pc,var(--accent));transform:translateX(2px);}\r\n  \/* Pills public cible etc. *\/\r\n  #mtp-map .mtp-profile-pills{display:flex;flex-wrap:wrap;gap:.4rem;}\r\n  #mtp-map .mtp-profile-pill{\r\n    background:color-mix(in srgb,var(--pc,var(--accent)) 9%,#fff);\r\n    border:1px solid color-mix(in srgb,var(--pc,var(--accent)) 22%,#fff);\r\n    border-radius:11px;padding:.48rem .7rem;display:flex;flex-direction:column;gap:.15rem;\r\n  }\r\n  #mtp-map .mtp-profile-pill-label{\r\n    font-size:.6rem;font-weight:800;letter-spacing:.09em;text-transform:uppercase;\r\n    color:var(--pc,var(--accent));\r\n  }\r\n  #mtp-map .mtp-profile-pill-value{font-size:.82rem;font-weight:700;color:#1f2d3d;line-height:1.25;}\r\n  \/* Carte projet *\/\r\n  #mtp-map .mtp-profile-projet{border:1px solid var(--border);border-radius:12px;overflow:hidden;}\r\n  #mtp-map .mtp-profile-projet-hd{\r\n    display:flex;align-items:center;gap:.5rem;flex-wrap:wrap;\r\n    padding:.5rem .75rem;background:#f8f9fb;border-bottom:1px solid var(--border);\r\n  }\r\n  #mtp-map .mtp-profile-projet-year{\r\n    font-size:.72rem;font-weight:800;color:var(--ink);\r\n    background:#fff;border:1px solid var(--border);\r\n    padding:.18rem .5rem;border-radius:6px;flex:0 0 auto;\r\n  }\r\n  #mtp-map .mtp-profile-projet-domain{font-size:.75rem;font-weight:600;color:var(--muted);min-width:0;}\r\n  #mtp-map .mtp-profile-projet-body{padding:.7rem .75rem;display:flex;flex-direction:column;gap:.55rem;}\r\n  \/* Grille financement *\/\r\n  #mtp-map .mtp-profile-funding{display:grid;grid-template-columns:1fr 1fr;gap:.4rem;}\r\n  #mtp-map .mtp-profile-funding-item{\r\n    padding:.5rem .65rem;border-radius:9px;\r\n    background:color-mix(in srgb,var(--pc,var(--accent)) 7%,#fff);\r\n    border:1px solid color-mix(in srgb,var(--pc,var(--accent)) 18%,#fff);\r\n  }\r\n  #mtp-map .mtp-profile-funding-item.full{grid-column:1\/-1;}\r\n  #mtp-map .mtp-profile-funding-label{\r\n    display:block;font-size:.6rem;font-weight:800;letter-spacing:.08em;text-transform:uppercase;\r\n    color:var(--pc,var(--accent));margin-bottom:.16rem;\r\n  }\r\n  #mtp-map .mtp-profile-funding-value{\r\n    display:block;font-size:.84rem;font-weight:800;color:#1f2d3d;line-height:1.25;overflow-wrap:anywhere;\r\n  }\r\n  #mtp-map .mtp-profile-yesno{\r\n    display:inline-block;font-size:.78rem;font-weight:800;\r\n    padding:.18rem .52rem;border-radius:6px;\r\n  }\r\n  #mtp-map .mtp-profile-yesno.yes{background:#ecf7f1;color:#207a4f;}\r\n  #mtp-map .mtp-profile-yesno.no{background:#fef2f2;color:#b91c1c;}\r\n  \/* Fiches Erasmus+ *\/\r\n  #mtp-map .mtp-profile-erasmus-list{display:flex;flex-direction:column;gap:.35rem;align-items:flex-start;}\r\n  #mtp-map .mtp-profile-erasmus{\r\n    display:inline-flex;align-items:center;gap:.4rem;\r\n    font-size:.78rem;font-weight:700;color:var(--pc,var(--accent));\r\n    text-decoration:none;overflow-wrap:anywhere;\r\n  }\r\n  #mtp-map .mtp-profile-erasmus:hover{text-decoration:underline;}\r\n  \/* Contact rows *\/\r\n  #mtp-map .mtp-profile-contacts{display:flex;flex-direction:column;gap:.4rem;}\r\n  #mtp-map .mtp-profile-contact-row{\r\n    display:flex;align-items:center;gap:.65rem;\r\n    padding:.6rem .8rem;border-radius:12px;\r\n    background:#fff;border:1px solid #e6ebf1;\r\n    color:#1f2d3d;text-decoration:none;transition:.18s ease;\r\n  }\r\n  #mtp-map .mtp-profile-contact-row:hover{\r\n    border-color:var(--pc,var(--accent));\r\n    background:color-mix(in srgb,var(--pc,var(--accent)) 6%,#fff);\r\n    transform:translateY(-1px);box-shadow:0 4px 12px rgba(36,71,109,.08);\r\n  }\r\n  #mtp-map .mtp-profile-contact-icon{\r\n    width:30px;height:30px;border-radius:8px;flex:0 0 30px;\r\n    background:var(--pc,var(--accent));color:#fff;\r\n    display:inline-flex;align-items:center;justify-content:center;\r\n  }\r\n  #mtp-map .mtp-profile-contact-body{flex:1;min-width:0;}\r\n  #mtp-map .mtp-profile-contact-label{\r\n    display:block;font-size:.6rem;font-weight:800;letter-spacing:.08em;\r\n    text-transform:uppercase;color:#94a3b8;\r\n  }\r\n  #mtp-map .mtp-profile-contact-value{\r\n    display:block;font-size:.84rem;font-weight:700;color:#1f2d3d;\r\n    overflow:hidden;text-overflow:ellipsis;white-space:nowrap;\r\n  }\r\n  \r\n  \/* \u2500\u2500 \u00c9tiquettes d'enjeux (profil) \u2500\u2500 *\/\r\n  #mtp-map .mtp-profile-tags{display:flex;flex-wrap:wrap;gap:.32rem;}\r\n  #mtp-map .mtp-profile-tag{\r\n    font-size:.72rem;font-weight:700;line-height:1.2;\r\n    color:var(--pc,var(--accent));\r\n    background:color-mix(in srgb,var(--pc,var(--accent)) 9%,#fff);\r\n    border:1px solid color-mix(in srgb,var(--pc,var(--accent)) 22%,#fff);\r\n    padding:.3rem .55rem;border-radius:999px;\r\n  }\r\n  \r\n  \/* \u2500\u2500 Quartiers prioritaires \u2500\u2500 *\/\r\n  #mtp-map .mtp-qpv{stroke:#8fa4b8;stroke-width:1.2px;vector-effect:non-scaling-stroke;cursor:pointer;transition:.18s}\r\n  #mtp-map .mtp-city{fill:none;stroke:var(--ink);stroke-width:1.4px;vector-effect:non-scaling-stroke;\r\n    stroke-dasharray:5 4;opacity:.45;pointer-events:none}\r\n  #mtp-map .mtp-qpv:hover{filter:brightness(1.12)}\r\n  #mtp-map .mtp-qpv-bubble circle{cursor:pointer;filter:drop-shadow(0 2px 3px rgba(36,71,109,.3))}\r\n  #mtp-map .mtp-qpv-bubble text{font-size:10px;font-weight:800;fill:#3a2a00;text-anchor:middle;\r\n    pointer-events:none;dominant-baseline:central}\r\n  #mtp-map .mtp-qpv-bubble text.mtp-qpv-label{font-size:9px;fill:var(--ink);dominant-baseline:auto;\r\n    paint-order:stroke;stroke:rgba(255,255,255,.92);stroke-width:2.6px;stroke-linejoin:round}\r\n  #mtp-map .mtp-legend-note{font-size:.66rem;font-weight:600;color:#9aa3ad}\r\n  \/* \u2500\u2500 \u00c9tat vide du panneau \u2500\u2500 *\/\r\n  #mtp-map .mtp-empty{font-size:.83rem;color:var(--muted);font-style:italic;margin:.2rem 0 0;line-height:1.5}\r\n  <\/style>\r\n\r\n  <script>\r\n  (function(){\r\n    \"use strict\";\r\n\r\n    \/* ===== Associations accompagn\u00e9es =====\r\n      Cl\u00e9 : code INSEE de la commune du si\u00e8ge social. Une association se d\u00e9crit\r\n      ainsi (tous les champs sont facultatifs sauf \u00ab n \u00bb) :\r\n\r\n        \"34172\": [{\r\n          n:       \"NOM DE L'ASSOCIATION\",\r\n          siege:   \"12 rue Exemple, 34000 Montpellier\",\r\n          qpv:     \"Mosson\",            \/\/ nom du QPV, affich\u00e9 dans la fiche\r\n          qpvCode: \"QN03405M\",          \/\/ code ANCT, c'est LUI qui rattache au quartier\r\n          terr:    \"Territoires d'intervention\",\r\n          objet:   \"Objet social \/ mission\",\r\n          proj:    \"Projets \u00e0 financer\",\r\n          them:    \"Coh\u00e9sion sociale et citoyennet\u00e9\",\r\n          publics: \"Jeunes, Habitants QPV\",\r\n          enjeux:  \"Stabilisation financi\u00e8re, Changement d'\u00e9chelle\",\r\n          motiv:   \"Motivations pour l'incubateur\",\r\n          contact: \"Pr\u00e9nom NOM\"\r\n        }]\r\n\r\n      Pourquoi deux champs pour le quartier : cinq QPV de l'H\u00e9rault s'appellent\r\n      \u00ab Centre Ville \u00bb (B\u00e9ziers, S\u00e8te, Lod\u00e8ve, Agde, Clermont-l'H\u00e9rault). Le nom\r\n      seul ne d\u00e9signe donc rien ; le rattachement se fait sur le code ANCT, le\r\n      nom ne sert qu'\u00e0 l'affichage.\r\n\r\n      Cohorte issue des candidatures re\u00e7ues (export \u00ab candidatures-shl \u00bb).\r\n      Le si\u00e8ge vient du r\u00e9pertoire national des associations (annuaire des\r\n      entreprises), le QPV est celui dans lequel tombe ce si\u00e8ge (contours ANCT) :\r\n      une association qui intervient en quartier prioritaire sans y \u00eatre\r\n      domicili\u00e9e garde donc un champ \u00ab qpv \u00bb vide, et n'appara\u00eet que dans la vue\r\n      communes. *\/\r\n    const ASSOS = {\r\n      \/\/ Montpellier\r\n      \"34172\": [\r\n        {\r\n          n: \"3MTKD SPORT CULTURE SOCIAL\",\r\n          siege: \"205 rue de Cheng-Du, 34070 Montpellier\",\r\n          qpv: \"Paul Val\u00e9ry - Pas Du Loup - Val De Croze\",\r\n          qpvCode: \"QN03407I\",\r\n          terr: \"Quartier prioritaire (QPV) \u00b7 Commune \u00b7 D\u00e9partement \u00b7 R\u00e9gion\",\r\n          them: \"Jeunesse, \u00e9ducation et comp\u00e9tences\",\r\n          contact: \"bellahcene Karim\"\r\n        },\r\n        {\r\n          n: \"Association Bien vivre \u00e0 Aiguelongue\",\r\n          siege: \"B\u00e2t. 16 esc. 19, 913 rue de Montasinos, 34090 Montpellier\",\r\n          qpv: \"Aiguelongue\",\r\n          qpvCode: \"QN03413I\",\r\n          terr: \"Quartier prioritaire (QPV)\",\r\n          them: \"Coh\u00e9sion sociale et citoyennet\u00e9\",\r\n          contact: \"Kenniche Zahia\"\r\n        },\r\n        {\r\n          n: \"Association Jasmin d'Orient\",\r\n          siege: \"Quartier Saint-Martin, 3 rue des Catalpas, 34070 Montpellier\",\r\n          qpv: \"Tournezy-Saint Martin\",\r\n          qpvCode: \"QN03411I\",\r\n          terr: \"Quartier prioritaire (QPV)\",\r\n          them: \"Culture et cr\u00e9ativit\u00e9 territoriale\",\r\n          contact: \"DIOT P\u00e9n\u00e9lope\"\r\n        },\r\n        {\r\n          n: \"association kaina\",\r\n          siege: \"600 avenue de Louisville, 34080 Montpellier\",\r\n          qpv: \"Mosson\",\r\n          qpvCode: \"QN03405M\",\r\n          terr: \"Quartier prioritaire (QPV)\",\r\n          them: \"Coh\u00e9sion sociale et citoyennet\u00e9\",\r\n          contact: \"Alliouat akli\"\r\n        },\r\n        {\r\n          n: \"association Tin Hinan\",\r\n          siege: \"R\u00e9sidence Le Saint-Guilhem, 2 Le Grand Mail, 34080 Montpellier\",\r\n          qpv: \"Mosson\",\r\n          qpvCode: \"QN03405M\",\r\n          terr: \"Quartier prioritaire (QPV)\",\r\n          them: \"Culture et cr\u00e9ativit\u00e9 territoriale\",\r\n          contact: \"Juret Manon et Yassa Louisa\"\r\n        },\r\n        {\r\n          n: \"Association Vivre Ensemble Citoyen\",\r\n          siege: \"Les C\u00e9vennes b\u00e2t. 4, 949 avenue du Professeur Louis Ravaz, 34000 Montpellier\",\r\n          qpv: \"C\u00e9vennes\",\r\n          qpvCode: \"QN03408I\",\r\n          terr: \"Quartier prioritaire (QPV)\",\r\n          them: \"Jeunesse, \u00e9ducation et comp\u00e9tences\",\r\n          contact: \"Tonneau Jean-Philippe\"\r\n        },\r\n        {\r\n          n: \"Centre APAJ\",\r\n          siege: \"6 rue Dom Vaissette, 34000 Montpellier\",\r\n          qpv: \"\",\r\n          qpvCode: \"\",\r\n          terr: \"Quartier prioritaire (QPV) \u00b7 Commune\",\r\n          them: \"Coh\u00e9sion sociale et citoyennet\u00e9\",\r\n          contact: \"DELAY C\u00e9line\"\r\n        },\r\n        {\r\n          n: \"Entre&Avec\",\r\n          siege: \"Loft des Assos, 77 rue du Faubourg Figuerolles, 34070 Montpellier\",\r\n          qpv: \"\",\r\n          qpvCode: \"\",\r\n          terr: \"\",\r\n          them: \"Culture et cr\u00e9ativit\u00e9 territoriale\",\r\n          contact: \"Donalie-an TRAN\"\r\n        },\r\n        {\r\n          n: \"Esprit Libre\",\r\n          siege: \"R\u00e9sidence Le Plein Ciel, 650 Le Grand Mail, 34080 Montpellier\",\r\n          qpv: \"Mosson\",\r\n          qpvCode: \"QN03405M\",\r\n          terr: \"Quartier prioritaire (QPV)\",\r\n          them: \"Culture et cr\u00e9ativit\u00e9 territoriale\",\r\n          contact: \"Hamdi alaoui Nezha\"\r\n        },\r\n        {\r\n          n: \"ESSOR SAVOIRS ET PARTAGE\",\r\n          siege: \"153 all\u00e9e de la Gardiole, 34080 Montpellier\",\r\n          qpv: \"Petit Bard Pergola\",\r\n          qpvCode: \"QN03406I\",\r\n          terr: \"Quartier prioritaire (QPV) \u00b7 R\u00e9gion\",\r\n          them: \"Culture et cr\u00e9ativit\u00e9 territoriale\",\r\n          contact: \"VIGIER ANABEL\"\r\n        },\r\n        {\r\n          n: \"UNi'SONS\",\r\n          siege: \"475 avenue du Comt\u00e9 de Nice, 34080 Montpellier\",\r\n          qpv: \"Mosson\",\r\n          qpvCode: \"QN03405M\",\r\n          terr: \"Quartier prioritaire (QPV) \u00b7 Commune \u00b7 D\u00e9partement\",\r\n          them: \"Coh\u00e9sion sociale et citoyennet\u00e9\",\r\n          contact: \"BARDIOT Estelle\"\r\n        },\r\n        {\r\n          n: \"Union Fran\u00e7aise des Oeuvres La\u00efques d'Education Physique\",\r\n          siege: \"Maison d\u00e9partementale des sports, ZAC Pierresvives, esplanade de l'\u00c9galit\u00e9, 34080 Montpellier\",\r\n          qpv: \"\",\r\n          qpvCode: \"\",\r\n          terr: \"D\u00e9partement\",\r\n          them: \"Coh\u00e9sion sociale et citoyennet\u00e9\",\r\n          contact: \"CANOVAS C\u00e9cile\"\r\n        }\r\n      ],\r\n      \/\/ B\u00e9ziers\r\n      \"34032\": [\r\n        {\r\n          n: \"AS B\u00e9ziers H\u00e9rault\",\r\n          siege: \"Stade de la M\u00e9diterran\u00e9e, rond-point Pierre Lacans, 34500 B\u00e9ziers\",\r\n          qpv: \"\",\r\n          qpvCode: \"\",\r\n          terr: \"Quartier prioritaire (QPV) \u00b7 Commune \u00b7 D\u00e9partement \u00b7 R\u00e9gion \u00b7 National\",\r\n          them: \"Jeunesse, \u00e9ducation et comp\u00e9tences\",\r\n          contact: \"CHOLLET Christophe\"\r\n        },\r\n        {\r\n          n: \"Ateliers Multiformes d'Accompagnement \u00e0 la Cr\u00e9ativit\u00e9\",\r\n          siege: \"7 rue du Chapeau Rouge, 34500 B\u00e9ziers\",\r\n          qpv: \"Centre Ville\",\r\n          qpvCode: \"QN03401M\",\r\n          terr: \"Quartier prioritaire (QPV) \u00b7 Commune\",\r\n          them: \"Coh\u00e9sion sociale et citoyennet\u00e9\",\r\n          contact: \"SOULAIROL Michel\"\r\n        },\r\n        {\r\n          n: \"Ecole taurine B\u00e9ziers M\u00e9diterran\u00e9e\",\r\n          siege: \"Ar\u00e8nes de B\u00e9ziers, avenue \u00c9mile Clapar\u00e8de, 34500 B\u00e9ziers\",\r\n          qpv: \"\",\r\n          qpvCode: \"\",\r\n          terr: \"Quartier prioritaire (QPV) \u00b7 Commune \u00b7 D\u00e9partement \u00b7 R\u00e9gion \u00b7 National \u00b7 Europ\u00e9en \u00b7 International\",\r\n          them: \"Culture et cr\u00e9ativit\u00e9 territoriale\",\r\n          contact: \"Monsarrat Philippe\"\r\n        },\r\n        {\r\n          n: \"M\u00e9moire Juive de B\u00e9ziers\",\r\n          siege: \"19 place Pierre Semard, 34500 B\u00e9ziers\",\r\n          qpv: \"Centre Ville\",\r\n          qpvCode: \"QN03401M\",\r\n          terr: \"Quartier prioritaire (QPV) \u00b7 Commune \u00b7 D\u00e9partement \u00b7 R\u00e9gion \u00b7 National \u00b7 Europ\u00e9en \u00b7 International\",\r\n          them: \"Culture et cr\u00e9ativit\u00e9 territoriale\",\r\n          contact: \"VIOTTE Chantal\"\r\n        }\r\n      ],\r\n      \/\/ Lod\u00e8ve\r\n      \"34142\": [\r\n        {\r\n          n: \"La Compagnie des jeux\",\r\n          siege: \"1 rue de la Sous-Pr\u00e9fecture, 34700 Lod\u00e8ve\",\r\n          qpv: \"\",\r\n          qpvCode: \"\",\r\n          terr: \"Quartier prioritaire (QPV) \u00b7 Commune \u00b7 D\u00e9partement\",\r\n          them: \"Coh\u00e9sion sociale et citoyennet\u00e9\",\r\n          contact: \"Bay Antoine\"\r\n        },\r\n        {\r\n          n: \"Terre en Partage\",\r\n          siege: \"Le Clap, 9 avenue Denfert, 34700 Lod\u00e8ve\",\r\n          qpv: \"Centre Ville\",\r\n          qpvCode: \"QN03420M\",\r\n          terr: \"Quartier prioritaire (QPV) \u00b7 Commune\",\r\n          them: \"Coh\u00e9sion sociale et citoyennet\u00e9\",\r\n          contact: \"Bodson Am\u00e9lie\"\r\n        }\r\n      ],\r\n      \/\/ Lunel\r\n      \"34145\": [\r\n        {\r\n          n: \"ASSOCIATION LA BOITE A MALICE\",\r\n          siege: \"416 rue de la Ferrade, 34400 Lunel\",\r\n          qpv: \"\",\r\n          qpvCode: \"\",\r\n          terr: \"Quartier prioritaire (QPV) \u00b7 Commune \u00b7 D\u00e9partement \u00b7 R\u00e9gion \u00b7 National\",\r\n          them: \"Culture et cr\u00e9ativit\u00e9 territoriale\",\r\n          contact: \"PRATLONG ALEXANDRE\"\r\n        },\r\n        {\r\n          n: \"UNION SPORTIVE LUNEL\",\r\n          siege: \"205 chemin des Cabanettes, 34400 Lunel\",\r\n          qpv: \"\",\r\n          qpvCode: \"\",\r\n          terr: \"Quartier prioritaire (QPV) \u00b7 Commune\",\r\n          them: \"Jeunesse, \u00e9ducation et comp\u00e9tences\",\r\n          contact: \"TAIBI\"\r\n        }\r\n      ],\r\n      \/\/ S\u00e8te\r\n      \"34301\": [\r\n        {\r\n          n: \"ASSOCIATION CONCERTHAU\",\r\n          siege: \"23 A place du Sardinal, 34200 S\u00e8te\",\r\n          qpv: \"Ile De Thau\",\r\n          qpvCode: \"QN03415I\",\r\n          terr: \"Quartier prioritaire (QPV)\",\r\n          them: \"Coh\u00e9sion sociale et citoyennet\u00e9\",\r\n          contact: \"ASSELIN\"\r\n        },\r\n        {\r\n          n: \"RENAISSANCE34\",\r\n          siege: \"53 rue Montmorency, 34200 S\u00e8te\",\r\n          qpv: \"Centre Ville - Ile Sud\",\r\n          qpvCode: \"QN03416M\",\r\n          terr: \"D\u00e9partement\",\r\n          them: \"Autre\",\r\n          contact: \"CHEVALIER CHRISTELLE\"\r\n        }\r\n      ],\r\n      \/\/ Agde\r\n      \"34003\": [\r\n        {\r\n          n: \"G\u00e9n\u00e9ration Music Centre de Ressources des Cultures Gitanes\",\r\n          siege: \"31 rue de la Paix, 34300 Agde\",\r\n          qpv: \"\",\r\n          qpvCode: \"\",\r\n          terr: \"Quartier prioritaire (QPV) \u00b7 Commune \u00b7 D\u00e9partement \u00b7 R\u00e9gion \u00b7 National \u00b7 Europ\u00e9en \u00b7 International\",\r\n          them: \"Autre\",\r\n          contact: \"Lemaitre R\u00e9mi\"\r\n        }\r\n      ],\r\n      \/\/ Capestang\r\n      \"34052\": [\r\n        {\r\n          n: \"ASSOCIATION INTERMEDIAIRE STEFI\",\r\n          siege: \"46 rue Louis Baisse, 34310 Capestang\",\r\n          qpv: \"\",\r\n          qpvCode: \"\",\r\n          terr: \"Quartier prioritaire (QPV) \u00b7 Commune\",\r\n          them: \"Autre\",\r\n          contact: \"CARVAJAL Lucie\"\r\n        }\r\n      ],\r\n      \/\/ Clermont-l'H\u00e9rault\r\n      \"34079\": [\r\n        {\r\n          n: \"Terre-Contact\",\r\n          siege: \"R\u00e9sidence Jean Moulin esc. 6\/7 apt 81, rue Jean Moulin, 34800 Clermont-l'H\u00e9rault\",\r\n          qpv: \"\",\r\n          qpvCode: \"\",\r\n          terr: \"Quartier prioritaire (QPV) \u00b7 Commune\",\r\n          them: \"Autre\",\r\n          contact: \"Ferreira de Oliveira St\u00e9phanie\"\r\n        }\r\n      ],\r\n      \/\/ P\u00e9rols\r\n      \"34198\": [\r\n        {\r\n          n: \"DANZART\",\r\n          siege: \"Impasse Papou-Mamou, 317 avenue Saint-Vincent, 34470 P\u00e9rols\",\r\n          qpv: \"\",\r\n          qpvCode: \"\",\r\n          terr: \"Quartier prioritaire (QPV) \u00b7 Commune \u00b7 D\u00e9partement \u00b7 R\u00e9gion\",\r\n          them: \"Autre\",\r\n          contact: \"TAULIER G\u00e9raldine et MARTIN Astrid\"\r\n        }\r\n      ]\r\n    };\r\n\r\n    \/* ===== Rendu neutre =====\r\n      341 communes, pas de d\u00e9coupage en territoires : les colorer au hasard\r\n      donnerait un damier qui ne dit rien. Le fond reste donc neutre, les 31\r\n      communes de la m\u00e9tropole simplement plus soutenues, et la couleur pleine\r\n      est r\u00e9serv\u00e9e aux communes qui portent une association. *\/\r\n    const MTP_INSEE  = \"34172\";\r\n    const MTP_EPCI   = \"243400017\";  \/\/ Montpellier M\u00e9diterran\u00e9e M\u00e9tropole\r\n    const C_BASE     = \"#e7edf3\";  \/\/ commune de l'H\u00e9rault\r\n    const C_METRO    = \"#c3d3e1\";  \/\/ commune de la m\u00e9tropole\r\n    const C_ACTIVE   = \"#24476d\";  \/\/ commune accompagn\u00e9e\r\n    const C_QPV      = \"#c7cfdd\";  \/\/ quartier prioritaire\r\n    const C_QPV_ON   = \"#6d4f84\";  \/\/ quartier accompagn\u00e9\r\n\r\n    \/* Contours des communes, par ordre de pr\u00e9f\u00e9rence. france-geojson est une\r\n      version simplifi\u00e9e : 115 Ko compress\u00e9s contre 3,1 Mo pour geo.api.gouv.fr\r\n      en g\u00e9om\u00e9trie pleine, soit vingt-sept fois moins \u00e0 t\u00e9l\u00e9charger. \u00c0 l'\u00e9chelle\r\n      du d\u00e9partement le trait est le m\u00eame \u00e0 l'\u0153il.\r\n        Ne pas remettre france-geojson.gregoiredavid.fr en t\u00eate de liste : ce\r\n      miroir ne renvoie aucun en-t\u00eate CORS, le navigateur refuse la r\u00e9ponse et on\r\n      paie une requ\u00eate perdue avant de basculer. geo.api.gouv.fr ferme la marche :\r\n      lourd, mais c'est la source officielle, et ses propri\u00e9t\u00e9s (nom, code) sont\r\n      celles que la suite attend. *\/\r\n    const COMMUNES_GEO = [\r\n      \"https:\/\/cdn.jsdelivr.net\/gh\/gregoiredavid\/france-geojson@master\/departements\/34-herault\/communes-34-herault.geojson\",\r\n      \"https:\/\/raw.githubusercontent.com\/gregoiredavid\/france-geojson\/master\/departements\/34-herault\/communes-34-herault.geojson\",\r\n      \"https:\/\/geo.api.gouv.fr\/departements\/34\/communes?format=geojson&geometry=contour&fields=nom,code\"\r\n    ];\r\n    \/* \u2026mais ce fichier tra\u00eene deux communes fusionn\u00e9es depuis (V\u00e9rargues,\r\n      Dio-et-Valqui\u00e8res). Cette liste-ci fait foi : elle donne les codes encore\r\n      valides, les noms \u00e0 jour, et l'EPCI de chaque commune \u2014 d'o\u00f9 sortent les 31\r\n      communes de la m\u00e9tropole, sans avoir \u00e0 les \u00e9crire en dur. 21 Ko. *\/\r\n    const COMMUNES_META = \"https:\/\/geo.api.gouv.fr\/departements\/34\/communes?fields=nom,code,codeEpci\";\r\n    \/\/ Quartiers prioritaires (source : ANCT, via le portail Opendatasoft public).\r\n    const QPV_URL = \"https:\/\/public.opendatasoft.com\/api\/explore\/v2.1\/catalog\/datasets\/\"\r\n      + \"quartiers-prioritaires-de-la-politique-de-la-ville-qpv\/records\"\r\n      + \"?where=nom_dep%3D%22H%C3%A9rault%22&select=code_qp,nom_qp,commune_qp,geo_shape\"\r\n      + \"&limit=100&order_by=nom_qp\";\r\n\r\n    const $ = id => document.getElementById(id);\r\n    const esc = s => String(s == null ? '' : s)\r\n      .replace(\/&\/g,'&amp;').replace(\/<\/g,'&lt;').replace(\/>\/g,'&gt;').replace(\/\"\/g,'&quot;');\r\n    const normT = s => (s||'').toLowerCase().normalize('NFD').replace(\/[\u0300-\u036f]\/g,'')\r\n      .replace(\/[^a-z0-9]+\/g,' ').trim();\r\n\r\n    \/\/ d3.geoPath attend l'anneau ext\u00e9rieur en sens horaire (et les trous en\r\n    \/\/ anti-horaire). Opendatasoft renvoie l'inverse -> on r\u00e9oriente, sinon d3\r\n    \/\/ dessine \"tout le globe sauf la zone\" (carte vide \/ bounds \u00e9normes).\r\n    function ringArea(r){let s=0;for(let i=0,n=r.length,j=n-1;i<n;j=i++)s+=r[j][0]*r[i][1]-r[i][0]*r[j][1];return s\/2;}\r\n    function rewind(geom){\r\n      const fix=poly=>poly.forEach((ring,i)=>{const a=ringArea(ring);if(i===0?a>0:a<0)ring.reverse();});\r\n      if(!geom) return geom;\r\n      if(geom.type==='Polygon') fix(geom.coordinates);\r\n      else if(geom.type==='MultiPolygon') geom.coordinates.forEach(fix);\r\n      return geom;\r\n    }\r\n\r\n    \/\/ Essaie les miroirs l'un apr\u00e8s l'autre, renvoie le premier qui r\u00e9pond.\r\n    function firstJson(urls){\r\n      return urls.reduce(\r\n        (chain,url)=>chain.catch(()=>d3.json(url)),\r\n        Promise.reject()\r\n      ).catch(()=>{ throw new Error(\"contours des communes indisponibles\"); });\r\n    }\r\n\r\n    function loadD3(){\r\n      return new Promise((res,rej)=>{\r\n        if(window.d3) return res();\r\n        const s=document.createElement('script');\r\n        s.src=\"https:\/\/d3js.org\/d3.v7.min.js\"; s.onload=res;\r\n        s.onerror=()=>rej(new Error(\"D3 indisponible\"));\r\n        document.head.appendChild(s);\r\n      });\r\n    }\r\n\r\n    function start(){\r\n      \/\/ Garde-fou : ne rien faire si le conteneur n'est pas sur la page.\r\n      if(!document.getElementById('mtp-map')) return;\r\n      loadD3().then(init).catch(e=>{ const l=$('mtp-loader'); if(l) l.textContent=\"Carte indisponible : \"+e.message; });\r\n    }\r\n    if(document.readyState==='loading') document.addEventListener('DOMContentLoaded',start);\r\n    else start();\r\n\r\n    function init(){\r\n      const stage=$('mtp-stage'), svgEl=$('mtp-svg'), tip=$('mtp-tip');\r\n      const svg=d3.select(svgEl);\r\n      let W=stage.clientWidth, H=stage.clientHeight;\r\n      svg.attr('viewBox',`0 0 ${W} ${H}`);\r\n\r\n      const gRoot=svg.append('g');\r\n      const gZones=gRoot.append('g');\r\n      const gCity=gRoot.append('g').style('display','none'); \/\/ contour de Montpellier en vue quartiers\r\n      const gQpv=gRoot.append('g').style('display','none');\r\n      const gAsso=gRoot.append('g');\r\n      const gQpvAsso=gRoot.append('g').style('display','none');\r\n      const gLabels=gRoot.append('g'); \/\/ au-dessus des bulles pour rester lisible\r\n\r\n      const proj=d3.geoMercator();\r\n      const path=d3.geoPath(proj);\r\n      let mtp=null, qpvFC=null, view='com';\r\n\r\n      const zoom=d3.zoom().scaleExtent([1,60]).on('zoom',ev=>{\r\n        gRoot.attr('transform',ev.transform);\r\n        rescale(ev.transform.k);\r\n      });\r\n      svg.call(zoom).on('dblclick.zoom',null);\r\n\r\n      function rescale(k){\r\n        gAsso.selectAll('.mtp-asso').attr('transform',d=>`translate(${d.xy}) scale(${1\/k})`);\r\n        gQpvAsso.selectAll('.mtp-qpv-bubble').attr('transform',d=>`translate(${d.xy}) scale(${1\/k})`);\r\n        gLabels.selectAll('text').style('font-size',d=>(d.fs\/k)+'px');\r\n        placeQpvLabels(k);\r\n      }\r\n\r\n      \/* Les 21 quartiers ne sont pas \u00e9galement espac\u00e9s : les dix de Montpellier\r\n        tiennent dans un mouchoir de poche \u00e0 l'\u00e9chelle du d\u00e9partement et leurs\r\n        noms se chevauchent \u2014 d'autant que certains sont longs (\u00ab Paul Val\u00e9ry -\r\n        Pas Du Loup - Val De Croze \u00bb). On n'\u00e9crit donc que les noms qui ont la\r\n        place, les quartiers les plus charg\u00e9s d'abord puisque ce sont eux qu'on\r\n        cherche, et le zoom en r\u00e9v\u00e8le d'autres \u00e0 mesure que les bulles\r\n        s'\u00e9cartent. Le survol nomme toujours le quartier, m\u00eame sans \u00e9tiquette. *\/\r\n      function placeQpvLabels(k){\r\n        const nodes=[];\r\n        gQpvAsso.selectAll('.mtp-qpv-bubble').each(function(d){nodes.push({el:this,d});});\r\n        if(!nodes.length) return;\r\n        \/\/ Les bulles gardent une taille fixe \u00e0 l'\u00e9cran : on raisonne donc en\r\n        \/\/ pixels \u00e9cran, soit la position projet\u00e9e multipli\u00e9e par le zoom.\r\n        nodes.forEach(n=>{ n.x=n.d.xy[0]*k; n.y=n.d.xy[1]*k;\r\n                           n.r=n.d.list.length?6+2.4*Math.sqrt(n.d.list.length):5;\r\n                           n.label=d3.select(n.el).select('.mtp-qpv-label'); });\r\n        \/\/ Les bulles elles-m\u00eames sont des obstacles : un nom qui barre une\r\n        \/\/ pastille voisine la rend illisible.\r\n        const blocked=nodes.map(n=>({x0:n.x-n.r-2,x1:n.x+n.r+2,y0:n.y-n.r-2,y1:n.y+n.r+2,own:n}));\r\n        const hit=(a,b)=>a.x0<b.x1 && a.x1>b.x0 && a.y0<b.y1 && a.y1>b.y0;\r\n        nodes.sort((a,b)=>b.d.list.length-a.d.list.length);\r\n        const kept=[];\r\n        nodes.forEach(n=>{\r\n          n.label.style('display',null);\r\n          \/\/ getBBox donne l'encombrement r\u00e9el du texte, position comprise : le\r\n          \/\/ groupe est contre-zoom\u00e9, ses unit\u00e9s locales sont donc d\u00e9j\u00e0 des\r\n          \/\/ pixels \u00e9cran, et il suffit de le replacer autour du centre de bulle.\r\n          let bb;\r\n          try{ bb=n.label.node().getBBox(); }\r\n          catch(e){ const w=n.d.nom.length*4.6; bb={x:-w\/2, y:-22, width:w, height:9}; }\r\n          const box={x0:n.x+bb.x-3, x1:n.x+bb.x+bb.width+3,\r\n                     y0:n.y+bb.y-2, y1:n.y+bb.y+bb.height+2};\r\n          const free=!kept.some(b=>hit(box,b))\r\n                  && !blocked.some(b=>b.own!==n && hit(box,b));\r\n          if(free) kept.push(box);\r\n          n.label.style('display',free?null:'none');\r\n        });\r\n      }\r\n\r\n      function showTip(html,ev){\r\n        tip.innerHTML=html;\r\n        const r=stage.getBoundingClientRect();\r\n        let x=ev.clientX-r.left+14, y=ev.clientY-r.top+14;\r\n        tip.style.left=Math.min(x,r.width-220)+'px';\r\n        tip.style.top=y+'px';\r\n        tip.classList.add('mtp-show');\r\n      }\r\n      const hideTip=()=>tip.classList.remove('mtp-show');\r\n\r\n      let _listCtx=null;\r\n\r\n      function showPanel(titre, eyebrow, list, color){\r\n        _listCtx={titre, eyebrow, list, color};\r\n        const panel=$('mtp-panel');\r\n        panel.style.setProperty('--pc', color);\r\n        $('mtp-panel-bar').style.background=color;\r\n        $('mtp-panel-eyebrow').textContent=eyebrow || 'Associations accompagn\u00e9es';\r\n        $('mtp-panel-title').textContent=titre;\r\n        const n=list.length;\r\n        $('mtp-panel-count').textContent = n\r\n          ? n+' association'+(n>1?'s':'')+' accompagn\u00e9e'+(n>1?'s':'')\r\n          : 'Aucune association renseign\u00e9e';\r\n        $('mtp-panel-hd').style.display='';\r\n        const closeBtn=$('mtp-panel-close');\r\n        closeBtn.textContent='\u2190 Retour';\r\n        closeBtn.onclick=hidePanel;\r\n        $('mtp-panel-list').innerHTML = n\r\n          ? list.map((a,i)=>\r\n            `<div class=\"mtp-pitem\" data-idx=\"${i}\">\r\n              <span class=\"mtp-pitem-chip\" style=\"background:${color}\"><\/span>\r\n              <span class=\"mtp-pitem-body\"><span class=\"mtp-pitem-name\">${esc(a.n)}<\/span><\/span>\r\n              <span class=\"mtp-pitem-arrow\">\u203a<\/span>\r\n            <\/div>`).join('')\r\n          : `<p class=\"mtp-empty\">Aucune association n'est encore rattach\u00e9e \u00e0 ce territoire.<\/p>`;\r\n        $('mtp-panel-list').querySelectorAll('.mtp-pitem').forEach((el,i)=>{\r\n          el.addEventListener('click',()=>showProfile(list[i], titre, eyebrow, color));\r\n        });\r\n        panel.classList.add('mtp-open');\r\n        panel.setAttribute('aria-hidden','false');\r\n      }\r\n\r\n      function showProfile(asso, titre, eyebrow, color){\r\n        $('mtp-panel-hd').style.display='none';\r\n\r\n        const svgPin=`<svg width=\"11\" height=\"11\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z\"\/><circle cx=\"12\" cy=\"10\" r=\"3\"\/><\/svg>`;\r\n        const metaParts=[];\r\n        if(titre)     metaParts.push(`<span class=\"mtp-profile-meta-zone\">${svgPin} ${esc(titre)}<\/span>`);\r\n        if(asso.qpv)  metaParts.push(`<span>QPV ${esc(asso.qpv)}<\/span>`);\r\n        else if(eyebrow) metaParts.push(`<span>${esc(eyebrow)}<\/span>`);\r\n        const metaHTML=metaParts.length\r\n          ? `<div class=\"mtp-profile-meta\">${metaParts.join('<span class=\"mtp-profile-meta-sep\">\u00b7<\/span>')}<\/div>` : '';\r\n        const domainBadge=asso.them ? `<div class=\"mtp-profile-domain\">${esc(asso.them)}<\/div>` : '';\r\n\r\n        const pillDefs=[\r\n          {label:'Publics accompagn\u00e9s', value:asso.publics},\r\n          {label:'R\u00e9f\u00e9rent\u00b7e',          value:asso.contact}\r\n        ].filter(p=>p.value);\r\n        const pillsHTML=pillDefs.length\r\n          ? `<div class=\"mtp-profile-pills\">${pillDefs.map(p=>\r\n              `<div class=\"mtp-profile-pill\">\r\n                <span class=\"mtp-profile-pill-label\">${esc(p.label)}<\/span>\r\n                <span class=\"mtp-profile-pill-value\">${esc(p.value)}<\/span>\r\n              <\/div>`).join('')}<\/div>` : '';\r\n\r\n        const section=(label,txt)=> txt\r\n          ? `<div>\r\n              <div class=\"mtp-profile-section-label\">${esc(label)}<\/div>\r\n              <p class=\"mtp-profile-desc\">${esc(txt)}<\/p>\r\n            <\/div>` : '';\r\n\r\n        const enjeux=(asso.enjeux||'').split(',').map(s=>s.trim()).filter(Boolean);\r\n        const enjeuxHTML=enjeux.length\r\n          ? `<div>\r\n              <div class=\"mtp-profile-section-label\">Enjeux de l'association<\/div>\r\n              <div class=\"mtp-profile-tags\">${enjeux.map(e=>`<span class=\"mtp-profile-tag\">${esc(e)}<\/span>`).join('')}<\/div>\r\n            <\/div>` : '';\r\n\r\n        const svgHome=`<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 10.5 12 3l9 7.5\"\/><path d=\"M5 9.5V21h14V9.5\"\/><\/svg>`;\r\n        const siegeHTML=asso.siege\r\n          ? `<div>\r\n              <div class=\"mtp-profile-section-label\">Si\u00e8ge<\/div>\r\n              <div class=\"mtp-profile-contacts\">\r\n                <div class=\"mtp-profile-contact-row\">\r\n                  <span class=\"mtp-profile-contact-icon\">${svgHome}<\/span>\r\n                  <span class=\"mtp-profile-contact-body\">\r\n                    <span class=\"mtp-profile-contact-label\">Adresse<\/span>\r\n                    <span class=\"mtp-profile-contact-value\" style=\"white-space:normal\">${esc(asso.siege)}<\/span>\r\n                  <\/span>\r\n                <\/div>\r\n              <\/div>\r\n            <\/div>` : '';\r\n\r\n        $('mtp-panel-list').innerHTML=`\r\n          <div class=\"mtp-profile\">\r\n            <div class=\"mtp-profile-hero\">\r\n              <button class=\"mtp-profile-back\" id=\"mtp-profile-back\">\u2190 ${esc(titre)}<\/button>\r\n              ${domainBadge}\r\n              <p class=\"mtp-profile-title\">${esc(asso.n)}<\/p>\r\n              ${metaHTML}\r\n            <\/div>\r\n            <div class=\"mtp-profile-body\">\r\n              ${pillsHTML}\r\n              ${section(\"Territoires d'intervention\", asso.terr)}\r\n              ${section('Objet social \/ mission', asso.objet)}\r\n              ${section('Projets \u00e0 financer', asso.proj)}\r\n              ${enjeuxHTML}\r\n              ${section(\"Motivations pour l'incubateur\", asso.motiv)}\r\n              ${siegeHTML}\r\n            <\/div>\r\n          <\/div>`;\r\n        $('mtp-profile-back').addEventListener('click',()=>{\r\n          if(_listCtx) showPanel(_listCtx.titre,_listCtx.eyebrow,_listCtx.list,_listCtx.color);\r\n        });\r\n        $('mtp-panel-list').scrollTop=0;\r\n      }\r\n\r\n      function hidePanel(){\r\n        $('mtp-panel').classList.remove('mtp-open');\r\n        $('mtp-panel').setAttribute('aria-hidden','true');\r\n        $('mtp-panel-hd').style.display='';\r\n        _listCtx=null;\r\n      }\r\n      $('mtp-panel-close').addEventListener('click',hidePanel);\r\n\r\n      function zoomTo(features,pad){\r\n        pad=pad||30;\r\n        const [[x0,y0],[x1,y1]]=path.bounds({type:\"FeatureCollection\",features});\r\n        const k=Math.min(60,0.92\/Math.max((x1-x0)\/W,(y1-y0)\/H));\r\n        const tx=W\/2-k*(x0+x1)\/2, ty=H\/2-k*(y0+y1)\/2;\r\n        svg.transition().duration(750).ease(d3.easeCubicInOut)\r\n          .call(zoom.transform,d3.zoomIdentity.translate(tx,ty).scale(k));\r\n      }\r\n      const resetZoom=()=>svg.transition().duration(750).ease(d3.easeCubicInOut)\r\n          .call(zoom.transform,d3.zoomIdentity);\r\n\r\n      const featOf  = code => mtp.features.find(x=>x.properties.code===code);\r\n      const assosOf = code => ASSOS[code] || [];\r\n      const inMetro = f => f.properties.epci===MTP_EPCI;\r\n      const metroFeatures = () => mtp.features.filter(inMetro);\r\n      \/\/ Au survol et en en-t\u00eate de panneau : situer la commune dans le d\u00e9partement.\r\n      const zoneEyebrow = f => inMetro(f) ? 'Montpellier M\u00e9diterran\u00e9e M\u00e9tropole' : 'H\u00e9rault';\r\n\r\n      \/\/ Une commune ne se colore que si elle porte une association ; la\r\n      \/\/ m\u00e9tropole reste lisible d'un ton plus soutenu que le reste du d\u00e9partement.\r\n      const colorOf = f => assosOf(f.properties.code).length ? C_ACTIVE\r\n        : (inMetro(f) ? C_METRO : C_BASE);\r\n\r\n      Promise.all([firstJson(COMMUNES_GEO), d3.json(COMMUNES_META)]).then(([raw, meta])=>{\r\n        \/\/ La liste officielle fait foi : elle \u00e9carte les communes fusionn\u00e9es que\r\n        \/\/ le fichier de contours tra\u00eene encore, et fournit noms et EPCI \u00e0 jour.\r\n        const byCode=new Map(meta.map(c=>[c.code,c]));\r\n        mtp={type:\"FeatureCollection\",features:raw.features\r\n          .filter(f=>byCode.has(f.properties.code))\r\n          .map(f=>{\r\n            const m=byCode.get(f.properties.code);\r\n            return {...f, properties:{code:m.code, nom:m.nom, epci:m.codeEpci}};\r\n          })};\r\n        proj.fitSize([W,H],mtp);\r\n        drawZones();\r\n        drawAssos();\r\n        buildLegend();\r\n        $('mtp-loader').style.display='none';\r\n      }).catch(e=>{ $('mtp-loader').textContent=\"Carte indisponible : \"+e.message; });\r\n\r\n      function drawZones(){\r\n        gZones.selectAll('path').data(mtp.features).join('path')\r\n          .attr('class','mtp-zone')\r\n          .attr('d',path)\r\n          .attr('fill',colorOf)\r\n          .attr('fill-opacity',.92)\r\n          .on('mousemove',(ev,f)=>{\r\n            const n=assosOf(f.properties.code).length;\r\n            showTip(`<b>${esc(f.properties.nom)}<\/b><div class=\"mtp-tip-meta\">${esc(zoneEyebrow(f))}${n?' \u00b7 '+n+' asso'+(n>1?'s':''):''}<\/div>`,ev);\r\n          })\r\n          .on('mouseleave',hideTip)\r\n          .on('click',(ev,f)=>{\r\n            zoomTo([f],60);\r\n            showPanel(f.properties.nom, zoneEyebrow(f),\r\n                      assosOf(f.properties.code), colorOf(f));\r\n          });\r\n      }\r\n\r\n      function drawLabels(){\r\n        if(view==='qpv'){\r\n          gLabels.selectAll('text.mtp-zlabel').remove();\r\n          return;\r\n        }\r\n        gLabels.selectAll('text.mtp-alabel').remove();\r\n        \/\/ 341 communes : les nommer toutes rendrait la carte illisible. On\r\n        \/\/ n'\u00e9crit que celles qui portent une association \u2014 les seules que l'\u0153il\r\n        \/\/ cherche \u2014 le survol donnant le nom de toutes les autres.\r\n        const data=mtp.features.map(f=>{\r\n          if(!assosOf(f.properties.code).length) return null;\r\n          const c=path.centroid(f);\r\n          if(!isFinite(c[0])) return null;\r\n          return {x:c[0], y:c[1], label:f.properties.nom,\r\n                  fs:f.properties.code===MTP_INSEE?12:9.5, on:true};\r\n        }).filter(Boolean);\r\n        gLabels.selectAll('text.mtp-zlabel').data(data).join('text')\r\n          .attr('class',d=>d.on?'mtp-zlabel':'mtp-zlabel mtp-zlabel-off')\r\n          .attr('x',d=>d.x).attr('y',d=>d.y)\r\n          .style('font-size',d=>d.fs+'px').text(d=>d.label);\r\n      }\r\n\r\n      function drawAssos(){\r\n        const data=Object.keys(ASSOS).map(code=>{\r\n          const f=featOf(code);\r\n          if(!f) return null;\r\n          const list=ASSOS[code], n=list.length;\r\n          if(!n) return null;\r\n          return {code, list, n, xy:path.centroid(f), r:6+2.4*Math.sqrt(n),\r\n                  nom:f.properties.nom, eyebrow:zoneEyebrow(f), color:colorOf(f)};\r\n        }).filter(Boolean).sort((a,b)=>b.r-a.r);\r\n\r\n        const k0=d3.zoomTransform(svgEl).k||1;\r\n        const g=gAsso.selectAll('.mtp-asso').data(data).join('g')\r\n          .attr('class','mtp-asso').attr('transform',d=>`translate(${d.xy}) scale(${1\/k0})`);\r\n        g.selectAll('*').remove();\r\n        g.append('circle').attr('r',d=>d.r);\r\n        g.append('text').text(d=>d.n);\r\n        g.on('mousemove',(ev,d)=>{\r\n          const list=d.list.slice(0,8).map(x=>`<li>${esc(x.n)}<\/li>`).join('');\r\n          const more=d.n>8?`<li>\u2026 +${d.n-8}<\/li>`:'';\r\n          showTip(`<b>${esc(d.nom)}<\/b><div class=\"mtp-tip-meta\">${d.n} association${d.n>1?'s':''} accompagn\u00e9e${d.n>1?'s':''}<\/div><ul>${list}${more}<\/ul>`,ev);\r\n        }).on('mouseleave',hideTip)\r\n          .on('click',(ev,d)=>{ev.stopPropagation();\r\n            const f=featOf(d.code);\r\n            if(f) zoomTo([f],60);\r\n            showPanel(d.nom,d.eyebrow,d.list,d.color);\r\n          });\r\n        drawLabels();\r\n      }\r\n\r\n      \/* ===== Quartiers prioritaires =====\r\n        L'\u00e9quivalent des arrondissements de Marseille sur la carte m\u00e9tropole :\r\n        une seconde lecture, plus fine, l\u00e0 o\u00f9 se concentre l'action. Les contours\r\n        viennent de l'open data ANCT. Le rattachement se fait sur le code ANCT\r\n        (champ \u00ab qpvCode \u00bb) et non sur le nom : cinq quartiers du d\u00e9partement\r\n        s'appellent \u00ab Centre Ville \u00bb. Le nom ne reste accept\u00e9 que pour les fiches\r\n        anciennes, qui n'ont pas de code. *\/\r\n      const qpvName = f => f.properties.nom || '';\r\n      const qpvCommune = f => f.properties.commune || '';\r\n      const qpvEyebrow = f => 'Quartier prioritaire' + (qpvCommune(f) ? ' \u00b7 '+qpvCommune(f) : '');\r\n      const qpvColor = f => assosOfQpv(f).length ? C_QPV_ON : C_QPV;\r\n      const allAssos = () => Object.values(ASSOS).flat();\r\n      const assosOfQpv = f => {\r\n        const code=f.properties.code, key=normT(qpvName(f));\r\n        return allAssos().filter(a => a.qpvCode ? a.qpvCode===code\r\n                                                : (!!a.qpv && normT(a.qpv)===key));\r\n      };\r\n\r\n      function showQpv(){\r\n        const draw=()=>{\r\n          gQpv.selectAll('path').data(qpvFC.features).join('path')\r\n            .attr('class','mtp-qpv').attr('d',path)\r\n            .attr('fill',qpvColor).attr('fill-opacity',.92)\r\n            .on('mousemove',(ev,f)=>{\r\n              const n=assosOfQpv(f).length;\r\n              showTip(`<b>${esc(qpvName(f))}<\/b><div class=\"mtp-tip-meta\">${esc(qpvEyebrow(f))}${n?' \u00b7 '+n+' asso'+(n>1?'s':''):''}<\/div>`,ev);\r\n            })\r\n            .on('mouseleave',hideTip)\r\n            .on('click',(ev,f)=>{\r\n              showPanel(qpvName(f), qpvEyebrow(f), assosOfQpv(f), qpvColor(f));\r\n            });\r\n\r\n          const bub=qpvFC.features.map(f=>({\r\n            nom:qpvName(f), eyebrow:qpvEyebrow(f), list:assosOfQpv(f),\r\n            xy:path.centroid(f), color:qpvColor(f)\r\n          })).filter(d=>isFinite(d.xy[0]));\r\n          const k0=d3.zoomTransform(svgEl).k||1;\r\n          const bg=gQpvAsso.selectAll('.mtp-qpv-bubble').data(bub).join('g')\r\n            .attr('class','mtp-qpv-bubble').attr('transform',d=>`translate(${d.xy}) scale(${1\/k0})`);\r\n          bg.selectAll('*').remove();\r\n          bg.append('circle').attr('r',d=>d.list.length?6+2.4*Math.sqrt(d.list.length):5)\r\n            .attr('fill',d=>d.list.length?'var(--marker)':'rgba(255,255,255,.7)')\r\n            .attr('stroke','#fff').attr('stroke-width',1.6);\r\n          bg.filter(d=>d.list.length).append('text').text(d=>d.list.length);\r\n          bg.append('text').attr('class','mtp-qpv-label').attr('y',-13)\r\n            .text(d=>d.nom);\r\n          placeQpvLabels(d3.zoomTransform(svgEl).k||1);\r\n          bg.on('mousemove',(ev,d)=>{\r\n            const list=d.list.slice(0,8).map(x=>`<li>${esc(x.n)}<\/li>`).join('');\r\n            showTip(`<b>${esc(d.nom)}<\/b><div class=\"mtp-tip-meta\">${esc(d.eyebrow)}${d.list.length?' \u00b7 '+d.list.length+' asso'+(d.list.length>1?'s':''):''}<\/div>${d.list.length?'<ul>'+list+'<\/ul>':''}`,ev);\r\n          }).on('mouseleave',hideTip)\r\n            .on('click',(ev,d)=>{ev.stopPropagation();\r\n              showPanel(d.nom,d.eyebrow,d.list,d.color);\r\n            });\r\n\r\n          gLabels.selectAll('text.mtp-zlabel').remove();\r\n\r\n          \/\/ Contour des sept villes qui portent un quartier prioritaire : sans\r\n          \/\/ ce rep\u00e8re, les 21 quartiers flottent sans attache dans le d\u00e9partement.\r\n          const hostNames=new Set(qpvFC.features.map(f=>normT(qpvCommune(f))));\r\n          const hosts=mtp.features.filter(f=>hostNames.has(normT(f.properties.nom)));\r\n          gCity.selectAll('path').data(hosts).join('path')\r\n            .attr('class','mtp-city').attr('d',path);\r\n\r\n          gAsso.style('display','none');\r\n          gCity.style('display',null);\r\n          gQpv.style('display',null);\r\n          gQpvAsso.style('display',null);\r\n          \/\/ On garde les communes en fond, estomp\u00e9es : sans elles, les dix\r\n          \/\/ quartiers flottent sans rep\u00e8re dans la ville.\r\n          gZones.selectAll('path').attr('fill-opacity',.18);\r\n          zoomTo(qpvFC.features,40);\r\n        };\r\n        if(qpvFC) return draw();\r\n        $('mtp-loader').style.display='flex';\r\n        $('mtp-loader').textContent=\"Chargement des quartiers prioritaires\u2026\";\r\n        d3.json(QPV_URL).then(resp=>{\r\n          const results=(resp&&resp.results)||[];\r\n          const feats=results.filter(r=>r.geo_shape&&r.geo_shape.geometry).map(r=>({\r\n            type:\"Feature\",\r\n            properties:{code:r.code_qp, nom:r.nom_qp,\r\n                        commune:[].concat(r.commune_qp||[]).join(', ')},\r\n            geometry:rewind(r.geo_shape.geometry)\r\n          }));\r\n          if(!feats.length) throw new Error(\"aucun quartier re\u00e7u\");\r\n          qpvFC={type:\"FeatureCollection\",features:feats};\r\n          $('mtp-loader').style.display='none';\r\n          draw();\r\n        }).catch(e=>{\r\n          console.error('[carte] QPV',e);\r\n          $('mtp-loader').style.display='flex';\r\n          $('mtp-loader').textContent=\"Quartiers prioritaires indisponibles : \"+e.message;\r\n        });\r\n      }\r\n\r\n      function showCommunes(keepZoom){\r\n        gQpv.style('display','none');\r\n        gQpvAsso.style('display','none');\r\n        gCity.style('display','none');\r\n        gLabels.selectAll('text.mtp-alabel').remove();\r\n        gAsso.style('display',null);\r\n        gZones.selectAll('path').attr('fill-opacity',.92);\r\n        drawLabels();\r\n        \/\/ keepZoom : l'appelant va lui-m\u00eame cadrer (raccourci \u00ab M\u00e9tropole \u00bb),\r\n        \/\/ inutile de d\u00e9zoomer d'abord pour rezoomer aussit\u00f4t.\r\n        if(!keepZoom) resetZoom();\r\n      }\r\n\r\n      \/* ===== contr\u00f4les ===== *\/\r\n      document.querySelectorAll('#mtp-map .mtp-chip').forEach(btn=>{\r\n        btn.addEventListener('click',()=>{\r\n          const v=btn.dataset.view;\r\n          if(v==='reset'){\r\n            hidePanel();\r\n            if(view!=='com'){ view='com'; syncChips('com'); showCommunes(); }\r\n            else resetZoom();\r\n            return;\r\n          }\r\n          \/\/ \u00ab M\u00e9tropole \u00bb n'est pas une vue mais un cadrage : on reste sur les\r\n          \/\/ communes et on s'approche des 31 de Montpellier M\u00e9diterran\u00e9e.\r\n          if(v==='metro'){\r\n            hidePanel();\r\n            if(view!=='com'){ view='com'; showCommunes(true); }\r\n            syncChips('com');\r\n            if(mtp) zoomTo(metroFeatures(),40);\r\n            return;\r\n          }\r\n          if(v===view) return;\r\n          view=v; syncChips(v);\r\n          if(v==='qpv') showQpv(); else showCommunes();\r\n        });\r\n      });\r\n      function syncChips(v){\r\n        document.querySelectorAll('#mtp-map .mtp-chip').forEach(b=>{\r\n          if(b.dataset.view==='reset') return;\r\n          b.classList.toggle('mtp-on',b.dataset.view===v);\r\n        });\r\n      }\r\n\r\n      function buildLegend(){\r\n        const total=Object.values(ASSOS).reduce((s,a)=>s+a.length,0);\r\n        const communes=Object.keys(ASSOS).filter(c=>featOf(c) && ASSOS[c].length).length;\r\n        const metro=Object.keys(ASSOS)\r\n          .filter(c=>{const f=featOf(c);return f && inMetro(f) && ASSOS[c].length;})\r\n          .reduce((s,c)=>s+ASSOS[c].length,0);\r\n        $('mtp-stat').innerHTML = total\r\n          ? `<span class=\"mtp-stat-num\">${total}<\/span>`\r\n            +`<span class=\"mtp-stat-lab\">associations accompagn\u00e9es<\/span>`\r\n            +`<span class=\"mtp-stat-sub\">r\u00e9parties sur ${communes} commune${communes>1?'s':''}`\r\n            +(metro?`, dont ${metro} dans la m\u00e9tropole`:``)+`<\/span>`\r\n          : `<span class=\"mtp-stat-num\">341<\/span>`\r\n            +`<span class=\"mtp-stat-lab\">communes de l'H\u00e9rault<\/span>`\r\n            +`<span class=\"mtp-stat-sub\">liste des associations \u00e0 venir<\/span>`;\r\n        $('mtp-legend').innerHTML=\r\n          `<span><i style=\"background:${C_BASE}\"><\/i>Communes de l'H\u00e9rault<\/span>`\r\n          +`<span><i style=\"background:${C_METRO}\"><\/i>M\u00e9tropole de Montpellier<\/span>`\r\n          +`<span><i style=\"background:${C_ACTIVE}\"><\/i>Commune accompagn\u00e9e<\/span>`\r\n          +`<span><i class=\"mtp-dot\" style=\"background:var(--marker)\"><\/i>Associations accompagn\u00e9es<\/span>`\r\n          +`<span class=\"mtp-legend-note\">Contours : france-geojson &amp; geo.api.gouv.fr \u00b7 quartiers prioritaires : ANCT<\/span>`;\r\n      }\r\n\r\n      \/\/ redimensionnement\r\n      let rt;\r\n      window.addEventListener('resize',()=>{\r\n        clearTimeout(rt); rt=setTimeout(()=>{\r\n          W=stage.clientWidth; H=stage.clientHeight;\r\n          svg.attr('viewBox',`0 0 ${W} ${H}`);\r\n          if(!mtp) return;\r\n          proj.fitSize([W,H],mtp);\r\n          gZones.selectAll('path').attr('d',path);\r\n          if(qpvFC) gQpv.selectAll('path').attr('d',path);\r\n          drawAssos();\r\n          svg.call(zoom.transform,d3.zoomIdentity);\r\n          if(view==='qpv') showQpv();\r\n        },200);\r\n      });\r\n    }\r\n  })();\r\n  <\/script>","protected":false},"excerpt":{"rendered":"","protected":false},"author":64,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_et_pb_use_builder":"off","_et_pb_old_content":"","_et_gb_content_width":"","footnotes":""},"class_list":["post-23814165","page","type-page","status-publish","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.7 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>carte herault - Social Hackers Lab<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/socialhackerslab.com\/en\/carte-herault\/\" \/>\n<meta property=\"og:locale\" content=\"en_GB\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"carte herault - Social Hackers Lab\" \/>\n<meta property=\"og:url\" content=\"https:\/\/socialhackerslab.com\/en\/carte-herault\/\" \/>\n<meta property=\"og:site_name\" content=\"Social Hackers Lab\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/profile.php?id=61561806211570\" \/>\n<meta property=\"article:modified_time\" content=\"2026-09-21T17:59:29+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/socialhackerslab.com\/wp-content\/uploads\/2024\/10\/Social-Hacker-Lab-1.png\" \/>\n\t<meta property=\"og:image:width\" content=\"250\" \/>\n\t<meta property=\"og:image:height\" content=\"140\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/socialhackerslab.com\\\/carte-herault\\\/\",\"url\":\"https:\\\/\\\/socialhackerslab.com\\\/carte-herault\\\/\",\"name\":\"carte herault - Social Hackers Lab\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/socialhackerslab.com\\\/#website\"},\"datePublished\":\"2026-09-21T17:59:28+00:00\",\"dateModified\":\"2026-09-21T17:59:29+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/socialhackerslab.com\\\/carte-herault\\\/#breadcrumb\"},\"inLanguage\":\"en-GB\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/socialhackerslab.com\\\/carte-herault\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/socialhackerslab.com\\\/carte-herault\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Accueil\",\"item\":\"https:\\\/\\\/socialhackerslab.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"carte herault\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/socialhackerslab.com\\\/#website\",\"url\":\"https:\\\/\\\/socialhackerslab.com\\\/\",\"name\":\"SocialHackersLab\",\"description\":\"Hacking Social pour une soci\u00e9t\u00e9 \u00e9galitaire\",\"publisher\":{\"@id\":\"https:\\\/\\\/socialhackerslab.com\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/socialhackerslab.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-GB\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/socialhackerslab.com\\\/#organization\",\"name\":\"Social Hackers Lab\",\"url\":\"https:\\\/\\\/socialhackerslab.com\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-GB\",\"@id\":\"https:\\\/\\\/socialhackerslab.com\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/socialhackerslab.com\\\/wp-content\\\/uploads\\\/2024\\\/10\\\/BW-SHL.png\",\"contentUrl\":\"https:\\\/\\\/socialhackerslab.com\\\/wp-content\\\/uploads\\\/2024\\\/10\\\/BW-SHL.png\",\"width\":3431,\"height\":3431,\"caption\":\"Social Hackers Lab\"},\"image\":{\"@id\":\"https:\\\/\\\/socialhackerslab.com\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/profile.php?id=61561806211570\",\"https:\\\/\\\/www.linkedin.com\\\/company\\\/socialhackerslab\\\/\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"carte herault - Social Hackers Lab","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/socialhackerslab.com\/en\/carte-herault\/","og_locale":"en_GB","og_type":"article","og_title":"carte herault - Social Hackers Lab","og_url":"https:\/\/socialhackerslab.com\/en\/carte-herault\/","og_site_name":"Social Hackers Lab","article_publisher":"https:\/\/www.facebook.com\/profile.php?id=61561806211570","article_modified_time":"2026-09-21T17:59:29+00:00","og_image":[{"width":250,"height":140,"url":"https:\/\/socialhackerslab.com\/wp-content\/uploads\/2024\/10\/Social-Hacker-Lab-1.png","type":"image\/png"}],"twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/socialhackerslab.com\/carte-herault\/","url":"https:\/\/socialhackerslab.com\/carte-herault\/","name":"carte herault - Social Hackers Lab","isPartOf":{"@id":"https:\/\/socialhackerslab.com\/#website"},"datePublished":"2026-09-21T17:59:28+00:00","dateModified":"2026-09-21T17:59:29+00:00","breadcrumb":{"@id":"https:\/\/socialhackerslab.com\/carte-herault\/#breadcrumb"},"inLanguage":"en-GB","potentialAction":[{"@type":"ReadAction","target":["https:\/\/socialhackerslab.com\/carte-herault\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/socialhackerslab.com\/carte-herault\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Accueil","item":"https:\/\/socialhackerslab.com\/"},{"@type":"ListItem","position":2,"name":"carte herault"}]},{"@type":"WebSite","@id":"https:\/\/socialhackerslab.com\/#website","url":"https:\/\/socialhackerslab.com\/","name":"SocialHackersLab","description":"Hacking Social pour une soci\u00e9t\u00e9 \u00e9galitaire","publisher":{"@id":"https:\/\/socialhackerslab.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/socialhackerslab.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-GB"},{"@type":"Organization","@id":"https:\/\/socialhackerslab.com\/#organization","name":"Social Hackers Lab","url":"https:\/\/socialhackerslab.com\/","logo":{"@type":"ImageObject","inLanguage":"en-GB","@id":"https:\/\/socialhackerslab.com\/#\/schema\/logo\/image\/","url":"https:\/\/socialhackerslab.com\/wp-content\/uploads\/2024\/10\/BW-SHL.png","contentUrl":"https:\/\/socialhackerslab.com\/wp-content\/uploads\/2024\/10\/BW-SHL.png","width":3431,"height":3431,"caption":"Social Hackers Lab"},"image":{"@id":"https:\/\/socialhackerslab.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/profile.php?id=61561806211570","https:\/\/www.linkedin.com\/company\/socialhackerslab\/"]}]}},"_links":{"self":[{"href":"https:\/\/socialhackerslab.com\/en\/wp-json\/wp\/v2\/pages\/23814165","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/socialhackerslab.com\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/socialhackerslab.com\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/socialhackerslab.com\/en\/wp-json\/wp\/v2\/users\/64"}],"replies":[{"embeddable":true,"href":"https:\/\/socialhackerslab.com\/en\/wp-json\/wp\/v2\/comments?post=23814165"}],"version-history":[{"count":1,"href":"https:\/\/socialhackerslab.com\/en\/wp-json\/wp\/v2\/pages\/23814165\/revisions"}],"predecessor-version":[{"id":23814166,"href":"https:\/\/socialhackerslab.com\/en\/wp-json\/wp\/v2\/pages\/23814165\/revisions\/23814166"}],"wp:attachment":[{"href":"https:\/\/socialhackerslab.com\/en\/wp-json\/wp\/v2\/media?parent=23814165"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}