Skip to content

Nos partenaires communautaires

Découvrez nos partenaires communautaires partout au Canada : Passez la souris ou appuyez sur n’importe quelle province.

/* ========================================================= ICSN Canada Community Partners Map ========================================================= */ :root { --bg-panel: #242428; --province: #C4141B; --province-hover: #000000; --province-sel: #C9A419; --province-null: #cfd2d7; --accent-red: #C4141B; --accent-gold: #C9A419; --text-dark: #1C1C1F; --text-soft: #E7E7E7; --text-muted: #8F8F8F; --border-light: #E7E7E7; } .icsn-map-wrap * , .icsn-map-wrap *::before, .icsn-map-wrap *::after { box-sizing: border-box; } .icsn-map-wrap { background: #ffffff; font-family: 'Open Sans', system-ui, sans-serif; color: var(--text-dark); padding: 16px 0; position: relative; } .icsn-container { position: relative; max-width: 1180px; margin: 0 auto; } /* ── MAP LAYOUT ──────────────────────────────── */ .icsn-map-stage { position: relative; width: 100%; max-width: 1100px; margin: 0 auto; min-height: 240px; } #icsn-svg { width: 100%; height: auto; display: block; } @media (max-width: 620px) { .icsn-map-stage { width: 100vw !important; max-width: 100vw !important; margin-left: calc(50% - 50vw) !important; margin-right: calc(50% - 50vw) !important; padding: 0 8px !important; } } path.prov { fill: var(--province); stroke: #ffffff; stroke-width: 1; cursor: pointer; transition: fill .22s, filter .22s; } path.prov:hover { fill: var(--province-hover); filter: drop-shadow(0 0 10px rgba(0,0,0,.4)); } path.prov.is-selected { fill: var(--province-sel); filter: drop-shadow(0 0 10px rgba(201,164,25,.55)); } path.prov.is-empty { cursor: default; } /* ── LOADING / ERROR ─────────────────────────── */ .icsn-state { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px; background: #ffffff; color: var(--text-muted); font-size: .85rem; font-family: 'Open Sans', sans-serif; font-weight: 500; min-height: 240px; } .icsn-spinner { width: 32px; height: 32px; border: 2px solid var(--border-light); border-top-color: var(--accent-red); border-radius: 50%; animation: icsn-spin 0.9s linear infinite; } @keyframes icsn-spin { to { transform: rotate(360deg); } } .icsn-state.error { color: var(--accent-red); } /* ── TOOLTIP ─────────────────────────────────── */ #icsn-tooltip { position: absolute; opacity: 0; pointer-events: none; transform: translate(-50%, calc(-100% - 16px)); transition: opacity .18s ease, transform .18s ease; background: linear-gradient(150deg, #1f2940, var(--bg-panel)); border: 1px solid rgba(201,164,25,.4); border-radius: 12px; padding: 16px 20px 14px; min-width: 180px; max-width: 260px; box-shadow: 0 18px 40px rgba(28,28,31,.28); z-index: 100; text-align: center; } #icsn-tooltip.show { opacity: 1; transform: translate(-50%, calc(-100% - 12px)); } #icsn-tooltip.is-touch { pointer-events: auto; } #icsn-tooltip::after { content: ""; position: absolute; bottom: -6px; left: 50%; transform: translateX(-50%) rotate(45deg); width: 11px; height: 11px; background: var(--bg-panel); border-right: 1px solid rgba(201,164,25,.4); border-bottom: 1px solid rgba(201,164,25,.4); } .tt-close { position: absolute; top: 6px; right: 6px; width: 26px; height: 26px; border-radius: 50%; border: 0; background: rgba(196,20,27,.9); color: #fff; font-size: 14px; font-weight: 700; line-height: 1; cursor: pointer; display: none; align-items: center; justify-content: center; padding: 0; -webkit-tap-highlight-color: transparent; font-family: 'Open Sans', sans-serif; } #icsn-tooltip.is-touch .tt-close { display: flex; } .tt-close:active { transform: scale(.9); } .tt-name { font-family: 'Ubuntu', sans-serif; font-weight: 500; font-size: 1.05rem; letter-spacing: .5px; color: #ffffff; line-height: 1.1; margin-bottom: 10px; padding: 0 22px; } .tt-count { font-family: 'Ubuntu', sans-serif; font-weight: 700; font-size: 2.6rem; color: var(--accent-red); line-height: 1; } .tt-label { font-family: 'Open Sans', sans-serif; font-size: .72rem; font-weight: 600; color: var(--text-soft); opacity: .75; margin-top: 6px; } .tt-cta { font-family: 'Open Sans', sans-serif; font-size: .78rem; font-weight: 700; color: var(--accent-gold); margin-top: 12px; padding-top: 10px; border-top: 1px solid rgba(231,231,231,.12); cursor: pointer; } .tt-cta.is-hidden { display: none; } /* ── ACTION BUTTON (INTERNATIONAL) ────────────── */ .icsn-actions-row { width: 100%; max-width: 1100px; margin: 28px auto 0; display: flex; justify-content: center; gap: 16px; } @media (max-width: 620px) { .icsn-actions-row { padding: 0 16px; } } .icsn-action-btn { display: inline-flex; align-items: center; gap: 18px; padding: 16px 28px 16px 22px; background: #ffffff; border: 1px solid var(--border-light); border-radius: 14px; cursor: pointer; transition: transform .25s, border-color .25s, box-shadow .25s; font-family: inherit; color: var(--text-dark); flex: 0 1 auto; } @media (max-width: 620px) { .icsn-action-btn { width: 100%; } } .icsn-action-btn:hover { transform: translateY(-2px); border-color: var(--accent-red); box-shadow: 0 12px 28px rgba(196,20,27,.18); } .icsn-action-btn:disabled { cursor: default; opacity: .45; } .icsn-action-btn:disabled:hover { transform: none; border-color: var(--border-light); box-shadow: none; } .action-icon { width: 52px; height: 52px; color: var(--accent-red); flex-shrink: 0; } .action-info { display: flex; flex-direction: column; align-items: flex-start; text-align: left; line-height: 1.1; } .action-name { font-family: 'Ubuntu', sans-serif; font-weight: 500; font-size: 1.15rem; color: var(--text-dark); margin-bottom: 6px; } .action-count-row { display: flex; align-items: baseline; gap: 8px; } .action-count { font-family: 'Ubuntu', sans-serif; font-weight: 700; font-size: 1.9rem; color: var(--accent-red); line-height: 1; } .action-label { font-family: 'Open Sans', sans-serif; font-size: .78rem; font-weight: 600; color: var(--text-muted); } /* ── POPUP OVERLAY (portaled to body) ────────── */ #icsn-overlay { display: none; position: fixed !important; top: 0 !important; right: 0 !important; bottom: 0 !important; left: 0 !important; width: 100vw !important; height: 100vh !important; height: 100dvh !important; background: rgba(28, 28, 31, .85); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); z-index: 2147483647 !important; align-items: center; justify-content: center; padding: 20px; overscroll-behavior: contain; font-family: 'Open Sans', system-ui, sans-serif; } #icsn-overlay.open { display: flex !important; } .popup-card { background: linear-gradient(155deg, #1f2940 0%, var(--bg-panel) 100%); border: 1px solid rgba(201,164,25,.22); border-radius: 18px; padding: 38px 36px 32px; width: 100%; max-width: 780px; max-height: calc(100vh - 40px); max-height: calc(100dvh - 40px); display: flex; flex-direction: column; position: relative; box-shadow: 0 32px 80px rgba(0,0,0,.7), 0 0 0 1px rgba(231,231,231,.04); animation: pop-in .28s cubic-bezier(.34,1.56,.64,1); color: #ffffff; font-family: 'Open Sans', sans-serif; overflow: hidden; } @keyframes pop-in { from { transform: scale(.9) translateY(20px); opacity: 0; } to { transform: scale(1) translateY(0); opacity: 1; } } @media (max-width: 620px) { #icsn-overlay { padding: 20px 10px 10px; } .popup-card { padding: 56px 18px 18px; border-radius: 14px; max-height: calc(100vh - 40px); max-height: calc(100dvh - 40px); } .popup-close { top: 14px !important; right: 14px !important; } } .popup-close { position: absolute !important; top: 12px !important; right: 12px !important; width: 44px !important; height: 44px !important; min-width: 44px !important; min-height: 44px !important; padding: 0 !important; margin: 0 !important; border-radius: 50% !important; border: 0 !important; background: #C4141B !important; color: #ffffff !important; font-size: 22px !important; line-height: 1 !important; font-weight: 700 !important; text-align: center !important; cursor: pointer !important; display: flex !important; align-items: center !important; justify-content: center !important; visibility: visible !important; opacity: 1 !important; z-index: 10 !important; box-shadow: 0 4px 14px rgba(0,0,0,.5) !important; -webkit-tap-highlight-color: transparent; font-family: 'Open Sans', sans-serif !important; } .popup-close:hover { background: #8f0d13 !important; transform: scale(1.08); } .popup-close:active { transform: scale(.95); } .popup-head { flex: 0 0 auto; margin-bottom: 18px; padding-right: 40px; } .popup-eyebrow { display: inline-block; font-family: 'Open Sans', sans-serif; font-size: .72rem; font-weight: 700; letter-spacing: 1px; color: var(--accent-gold); border: 1px solid var(--accent-gold); border-radius: 3px; padding: 3px 9px; margin-bottom: 10px; } .popup-title { font-family: 'Ubuntu', sans-serif; font-weight: 500; font-size: clamp(1.6rem, 4vw, 2.4rem); letter-spacing: .5px; color: #ffffff; line-height: 1.05; margin: 0 0 8px; } .popup-counter { font-family: 'Ubuntu', sans-serif; font-weight: 400; font-size: .95rem; color: var(--text-soft); opacity: .75; } .popup-counter strong { font-weight: 700; color: var(--accent-red); font-size: 1.3rem; margin-right: 4px; opacity: 1; } .popup-divider { flex: 0 0 auto; height: 1px; background: linear-gradient(90deg, rgba(201,164,25,.5), transparent); margin: 0 0 18px; } .popup-grid { flex: 1 1 auto; min-height: 0; display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; overflow-y: auto; overflow-x: hidden; padding-right: 6px; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; } @media (max-width: 820px) { .popup-grid { grid-template-columns: repeat(3, 1fr); } } @media (max-width: 520px) { .popup-grid { grid-template-columns: repeat(2, 1fr); gap: 10px; } } .popup-grid::-webkit-scrollbar { width: 6px; } .popup-grid::-webkit-scrollbar-track { background: transparent; } .popup-grid::-webkit-scrollbar-thumb { background: rgba(231,231,231,.25); border-radius: 3px; } .logo-tile { background: #fff; border-radius: 10px; position: relative; display: block; width: 100%; padding-bottom: 100%; text-decoration: none; transition: transform .22s, box-shadow .22s; overflow: hidden; } .logo-tile:hover { transform: translateY(-3px) scale(1.03); box-shadow: 0 12px 28px rgba(201,164,25,.35); } .logo-tile img { position: absolute !important; top: 10% !important; left: 10% !important; width: 80% !important; height: 80% !important; max-width: 80% !important; max-height: 80% !important; object-fit: contain !important; display: block !important; } .logo-tile .logo-name { position: absolute; left: 0; right: 0; bottom: 0; background: linear-gradient(transparent, rgba(0,0,0,.88)); color: #fff; font-family: 'Open Sans', sans-serif; font-size: .72rem; font-weight: 600; padding: 14px 8px 6px; text-align: center; opacity: 0; transition: opacity .22s; pointer-events: none; z-index: 2; } .logo-tile:hover .logo-name { opacity: 1; } body.icsn-popup-open { overflow: hidden; }
Loading map…
(function () { 'use strict'; const IS_TOUCH = (('ontouchstart' in window) || navigator.maxTouchPoints > 0); // ========================================================= // LANGUAGE DETECTION // ========================================================= // Cascade fallback so it never breaks the widget: // 1) attribute // 2) URL path starts with /fr/ // 3) WPML cookie // 4) Default: 'en' // ========================================================= function detectLanguage() { try { var htmlLang = document.documentElement.getAttribute('lang') || ''; if (htmlLang.toLowerCase().indexOf('fr') === 0) return 'fr'; } catch (e) {} try { var path = window.location.pathname || ''; if (path === '/fr' || path.indexOf('/fr/') === 0) return 'fr'; } catch (e) {} try { var cookies = (document.cookie || '').split(';'); for (var i = 0; i v.slug).filter((v,i,a)=>a.indexOf(v)===i); const $ = (id) => document.getElementById(id); const state = { byProvince: {}, international: [], animatedProvinces: new Set(), currentSelectedSlug: null, savedScrollY: 0, geoFeatures: [], }; let touchTooltipSlug = null; // Now that $ is defined, apply static labels applyStaticLabels(); async function loadGeoJSON() { try { const cached = sessionStorage.getItem(CONFIG.CACHE_KEY); if (cached) return JSON.parse(cached); } catch (e) {} const res = await fetch(CONFIG.GEOJSON_URL); if (!res.ok) throw new Error('GeoJSON HTTP ' + res.status); const data = await res.json(); try { sessionStorage.setItem(CONFIG.CACHE_KEY, JSON.stringify(data)); } catch(e) {} return data; } async function loadProvinces() { const res = await fetch(CONFIG.WP_BASE + '/province?per_page=20'); if (!res.ok) throw new Error('Provinces HTTP ' + res.status); const terms = await res.json(); const idToSlug = {}; terms.forEach(t => { idToSlug[t.id] = t.slug; }); return idToSlug; } async function loadAllPartners() { const out = []; for (let page = 1; page 1) break; throw new Error('Partners HTTP ' + res.status); } const batch = await res.json(); if (!Array.isArray(batch) || batch.length === 0) break; out.push(...batch); if (batch.length { byProvince[s] = []; }); partners.forEach(p => { const acf = p.acf_fields; if (!acf || typeof acf !== 'object' || Array.isArray(acf)) return; const logoObj = acf.logo; if (!logoObj || typeof logoObj !== 'object' || !logoObj.url) return; const record = { id: p.id, name: decodeEntities(p.title && p.title.rendered ? p.title.rendered : ''), logo: logoObj.medium || logoObj.large || logoObj.url, alt: (logoObj.alt && logoObj.alt.trim()) || decodeEntities(p.title.rendered || 'Partner'), link: acf.permalink || null, }; if (!record.name) return; const provIds = Array.isArray(p.province) ? p.province : []; const slugs = provIds.map(id => idToSlug[id]).filter(Boolean); if (slugs.length === 0) return; if (slugs.includes('international')) { pushUnique(international, record); } slugs.forEach(s => { if (s === 'international') return; if (byProvince[s]) pushUnique(byProvince[s], record); }); }); return { byProvince, international }; } function pushUnique(arr, rec) { if (!arr.some(x => x.id === rec.id)) arr.push(rec); } function decodeEntities(str) { if (!str) return ''; const el = document.createElement('textarea'); el.innerHTML = str; return el.value.trim(); } const PROJ_W = 960, PROJ_H = 620; const projection = d3.geoAzimuthalEqualArea() .rotate([96, -62]) .center([0, 0]) .scale(720) .translate([PROJ_W / 2, PROJ_H / 2]); const geoPath = d3.geoPath().projection(projection); function slugForFeature(f) { const entry = PROVINCE_MAP[f.properties.name]; return entry ? entry.slug : null; } function labelForFeature(f) { const entry = PROVINCE_MAP[f.properties.name]; if (!entry) return f.properties.name; // Return FR label when in French mode, EN otherwise return IS_FR ? (entry.labelFr || entry.label) : entry.label; } function buildMap(geo) { const svg = d3.select('#icsn-svg'); svg.selectAll('path').remove(); svg.selectAll('path.prov') .data(geo.features) .enter().append('path') .attr('d', geoPath) .attr('class', d => { const slug = slugForFeature(d); const hasData = slug && state.byProvince[slug] && state.byProvince[slug].length > 0; return 'prov' + (hasData ? '' : ' is-empty'); }) .attr('data-slug', d => slugForFeature(d) || '') .on('mouseenter', onProvMouseEnter) .on('mousemove', onProvMouseMove) .on('mouseleave', onProvMouseLeave) .on('click', onProvClick); $('icsn-loading').style.display = 'none'; $('icsn-svg').style.display = 'block'; state.geoFeatures = geo.features; } // Helpers to set tooltip mode function setTooltipFull(d, list) { $('tt-name').textContent = labelForFeature(d); $('tt-label').textContent = partnersLabel(list.length); $('tt-cta').textContent = t('readMore'); $('tt-cta').classList.remove('is-hidden'); $('tt-cta').style.cursor = 'pointer'; const slug = slugForFeature(d); const countEl = $('tt-count'); if (!state.animatedProvinces.has(slug)) { state.animatedProvinces.add(slug); animateCount(countEl, list.length, 900); } else { countEl.textContent = list.length; } } function setTooltipEmpty(d) { $('tt-name').textContent = labelForFeature(d); $('tt-count').textContent = '0'; $('tt-label').textContent = t('communityPartners'); $('tt-cta').classList.add('is-hidden'); } function onProvMouseEnter(event, d) { if (IS_TOUCH) return; const slug = slugForFeature(d); if (!slug) return; const list = state.byProvince[slug] || []; if (list.length === 0) { setTooltipEmpty(d); } else { setTooltipFull(d, list); } positionTooltip(d); $('icsn-tooltip').classList.add('show'); } function onProvMouseMove(event, d) { if (IS_TOUCH) return; if (!slugForFeature(d)) return; positionTooltip(d); } function onProvMouseLeave() { if (IS_TOUCH) return; $('icsn-tooltip').classList.remove('show'); } function showTooltipTouch(d) { const slug = slugForFeature(d); if (!slug) return; const list = state.byProvince[slug] || []; const tip = $('icsn-tooltip'); tip.classList.add('is-touch'); if (list.length === 0) { setTooltipEmpty(d); } else { setTooltipFull(d, list); } positionTooltip(d); tip.classList.add('show'); touchTooltipSlug = slug; } function hideTooltipTouch() { $('icsn-tooltip').classList.remove('show'); touchTooltipSlug = null; } function positionTooltip(d) { const svgEl = $('icsn-svg'); const stage = $('icsn-stage'); const rect = svgEl.getBoundingClientRect(); const stageRect = stage.getBoundingClientRect(); const centroid = geoPath.centroid(d); if (isNaN(centroid[0])) return; const VB_X = 90, VB_Y = 120, VB_W = 800, VB_H = 440; const scaleX = rect.width / VB_W; const scaleY = rect.height / VB_H; const x = (centroid[0] - VB_X) * scaleX + (rect.left - stageRect.left); const y = (centroid[1] - VB_Y) * scaleY + (rect.top - stageRect.top); const tip = $('icsn-tooltip'); tip.style.left = x + 'px'; tip.style.top = y + 'px'; } function onProvClick(event, d) { const slug = slugForFeature(d); if (!slug) return; const list = state.byProvince[slug] || []; // Empty province: only show tooltip with message, never open the big popup if (list.length === 0) { if (IS_TOUCH) { if (touchTooltipSlug === slug) { hideTooltipTouch(); } else { showTooltipTouch(d); } } return; } // Province with partners: normal behaviour if (!IS_TOUCH) { openPopup({ slug, eyebrow: t('provinceEyebrow'), name: labelForFeature(d), list }); return; } if (touchTooltipSlug === slug) { hideTooltipTouch(); openPopup({ slug, eyebrow: t('provinceEyebrow'), name: labelForFeature(d), list }); } else { showTooltipTouch(d); } } function lockBodyScroll() { state.savedScrollY = window.scrollY || window.pageYOffset || 0; document.body.classList.add('icsn-popup-open'); document.body.style.position = 'fixed'; document.body.style.top = '-' + state.savedScrollY + 'px'; document.body.style.left = '0'; document.body.style.right = '0'; document.body.style.width = '100%'; } function unlockBodyScroll() { document.body.classList.remove('icsn-popup-open'); document.body.style.position = ''; document.body.style.top = ''; document.body.style.left = ''; document.body.style.right = ''; document.body.style.width = ''; window.scrollTo(0, state.savedScrollY); } function openPopup(opts) { hideTooltipTouch(); $('icsn-tooltip').classList.remove('show'); if (state.currentSelectedSlug) { d3.select('[data-slug="' + state.currentSelectedSlug + '"]').classed('is-selected', false); } if (opts.slug && opts.slug !== 'international') { d3.select('[data-slug="' + opts.slug + '"]').classed('is-selected', true); state.currentSelectedSlug = opts.slug; } else { state.currentSelectedSlug = null; } $('pop-eyebrow').textContent = opts.eyebrow; $('pop-name').textContent = opts.name; $('pop-count').textContent = opts.list.length; $('pop-label').textContent = partnersLabel(opts.list.length); const grid = $('pop-grid'); grid.innerHTML = ''; grid.scrollTop = 0; const sorted = opts.list.slice().sort((a,b) => a.name.localeCompare(b.name)); sorted.forEach(p => grid.appendChild(buildLogoTile(p))); $('icsn-overlay').classList.add('open'); lockBodyScroll(); } function buildLogoTile(p) { // Localize the permalink so /fr/ visitors land on the FR partner page const localizedLink = p.link ? localizePermalink(p.link) : null; const tile = document.createElement(localizedLink ? 'a' : 'div'); tile.className = 'logo-tile'; if (localizedLink) { tile.href = localizedLink; tile.target = '_blank'; tile.rel = 'noopener noreferrer'; } tile.title = p.name; const img = document.createElement('img'); img.src = p.logo; img.alt = p.alt || p.name; img.loading = 'lazy'; img.onerror = function() { console.warn('[ICSN Map] Logo failed to load:', p.name, p.logo); }; tile.appendChild(img); const label = document.createElement('span'); label.className = 'logo-name'; label.textContent = p.name; tile.appendChild(label); return tile; } function closePopup() { $('icsn-overlay').classList.remove('open'); unlockBodyScroll(); if (state.currentSelectedSlug) { d3.select('[data-slug="' + state.currentSelectedSlug + '"]').classed('is-selected', false); state.currentSelectedSlug = null; } } function setupActionButton(btnId, countId, list, popupOpts) { const btn = $(btnId); const countEl = $(countId); const count = list.length; if (count === 0) { btn.disabled = true; countEl.textContent = '0'; return; } btn.disabled = false; animateCount(countEl, count, 1100); btn.addEventListener('click', () => { openPopup(Object.assign({ list }, popupOpts)); }); } function animateCount(el, target, duration) { if (target { const p = Math.min((now - start) / duration, 1); const eased = 1 - Math.pow(1 - p, 3); el.textContent = Math.floor(eased * target); if (p { if (e.target === $('icsn-overlay')) closePopup(); }); $('tt-close').addEventListener('click', function(e) { e.stopPropagation(); hideTooltipTouch(); }); $('tt-cta').addEventListener('click', function(e) { if (!IS_TOUCH) return; e.stopPropagation(); if (!touchTooltipSlug) return; const feat = state.geoFeatures.find(f => slugForFeature(f) === touchTooltipSlug); if (feat) { const slug = touchTooltipSlug; const list = state.byProvince[slug] || []; if (list.length === 0) return; hideTooltipTouch(); openPopup({ slug, eyebrow: t('provinceEyebrow'), name: labelForFeature(feat), list, }); } }); document.addEventListener('click', function(e) { if (!IS_TOUCH) return; const tip = $('icsn-tooltip'); if (!tip.classList.contains('show')) return; if (tip.contains(e.target)) return; if (e.target.closest && e.target.closest('path.prov')) return; hideTooltipTouch(); }); } document.addEventListener('keydown', e => { if ($('icsn-overlay').classList.contains('open')) { if (e.key === 'Escape') closePopup(); } else if ($('icsn-tooltip').classList.contains('show')) { if (e.key === 'Escape') hideTooltipTouch(); } }); if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', function() { bindPopupEvents(); init(); }); } else { bindPopupEvents(); init(); } })();

Dernière mise à jour : juin 2026