${c.nom}Dép. ${c.dep}
`
).join('');
dropdown.querySelectorAll('.d-item').forEach(el=>{
el.addEventListener('click',()=>{
const c = communesIndex[el.dataset.key];
searchInput.value = c.nom;
dropdown.classList.remove('open');
afficherCommune(c);
});
});
dropdown.classList.add('open');
}, 200);
});
document.addEventListener('click', e=>{ if(!e.target.closest('.search-wrap')) dropdown.classList.remove('open'); });
/* ===== Affichage commune ===== */
let listes=[], siegesTotal=0, tauxPrime=0.5, hasOfficialT2=false;
function afficherCommune(commune) {
[communeSection,t1Section,t2Section,resultsSection].forEach(el=>el.style.display='none');
listes=[];
// Données déjà agrégées depuis le JSON
const inscrits = commune.ins || 0;
const exprimes = commune.exp || 0;
const participation = inscrits>0 ? Math.round(exprimes/inscrits*100) : 0;
// Sièges
// Utiliser le nombre de sièges officiel du JSON (colonne 'Sièges au CM' du Ministère)
// Fallback sur le tableau L2121-2 si absent (Paris/Lyon/Marseille fichier séparé)
let nSieges = commune.sieges && commune.sieges > 0
? commune.sieges
: siegesLegaux(Math.round(inscrits * 1.55));
// Cas spéciaux PLM non présents dans le fichier BV
if (!commune.sieges || commune.sieges === 0) {
if (commune.dep === '75') nSieges = 163;
else if (commune.dep === '69' && commune.code === '69123') nSieges = 73;
else if (commune.dep === '13' && commune.code === '13055') nSieges = 101;
}
siegesTotal = nSieges;
// Cas PLM : Paris, Lyon, Marseille ont un scrutin distinct
// leurs listes sont dans un fichier séparé et peuvent être absentes du JSON principal
const isPLM = ['75056','69123','13055'].includes(commune.dep + commune.code) ||
(commune.dep === '75') ||
(commune.dep === '69' && commune.code === '69123') ||
(commune.dep === '13' && commune.code === '13055');
const hasListes = commune.listes && commune.listes.length > 0;
// Commune card
communeSection.innerHTML=`
${commune.nom}
${nSieges}
sièges
${msgPLM}
`;
t1s.style.display = 'block';
}
if (isParis) {
listes = assignerCouleurs([
{id:0, nom:'Paris est à vous! — Grégoire', nuance:'LUG', pct2:'', color:'', pctT1:37.98},
{id:1, nom:'Changer Paris — Dati', nuance:'LUD', pct2:'', color:'', pctT1:25.46},
{id:2, nom:'Nouveau Paris Populaire — Chikirou', nuance:'LFI', pct2:'', color:'', pctT1:11.72},
{id:3, nom:'Paris Apaisé — Bournazel', nuance:'LUC', pct2:'', color:'', pctT1:11.34},
{id:4, nom:'Knafo pour Paris', nuance:'LEXD', pct2:'', color:'', pctT1:10.40},
]);
tauxPrime = 0.25; // Conseil de Paris : prime de 25%
} else {
listes = [
{id:0, nom:'Liste 1', nuance:'', pct2:'', color:'#1B4FD8', pctT1:undefined},
{id:1, nom:'Liste 2', nuance:'', pct2:'', color:'#DC2626', pctT1:undefined},
];
tauxPrime = 0.5;
}
afficherSaisieT2(nSieges);
return;
}
// Listes T1 — depuis le JSON pré-agrégé
const listesT1 = (commune.listes || []).map((l,i)=>{
const voix = l.voix || 0;
const pct = exprimes>0 ? voix/exprimes*100 : 0;
const nomL = l.nom || `Liste ${i+1}`;
const nuance = l.nua || '';
const eluRaw = (l.elu || '').toLowerCase();
const elu = eluRaw.includes('élu')||eluRaw.includes('elu')||eluRaw==='1'||eluRaw==='true';
return {id:i, nom:nomL, nuance, pct, voix, elu, color:nuanceBaseColor(nuance) || PALETTE_FALLBACK[i % PALETTE_FALLBACK.length], pctT1:pct};
}).sort((a,b)=>b.pct-a.pct);
listesT1.splice(0, listesT1.length, ...assignerCouleurs(listesT1));
// Élu au 1er tour ?
// Élu si flag elu présent, OU si une liste dépasse 50% des suffrages exprimés
const elusT1 = listesT1.filter(l => (l.elu || l.pct > 50) && l.pct >= 50);
if(elusT1.length){
// Afficher tableau T1 + calcul direct des sièges
const listesPourCalc = listesT1.map(l => ({...l, pct2: l.pct}));
const resT1 = calculerSieges(listesPourCalc.map(l => ({...l, pct: l.pct})), nSieges, tauxPrime);
const winT1 = resT1.reduce((a,b) => a.total>b.total?a:b);
const nMT1 = Math.ceil(nSieges/2), nPT1 = nSieges-nMT1;
const nonQT1 = resT1.filter(r=>!r.q&&r.pct>0);
t1Section.innerHTML = `
✓ Liste élue dès le 1er tour — Majorité absolue obtenue, pas de 2e tour.
Résultats officiels — 1er tour (15 mars 2026)
| Liste | Voix | % exprimés |
|---|---|---|
| ${l.nom}${l.nuance?`${l.nuance}`:''} | ${l.voix?l.voix.toLocaleString('fr-FR'):'—'} | ${l.pct.toFixed(2)} % |
Répartition des ${nSieges} sièges
${nonQT1.length?`Liste(s) sous 5 % : ${nonQT1.map(l=>`${l.nom}`).join(', ')} — non participante(s).
`:''}
${nMT1}
Prime majorit.
${nPT1}
Sièges prop.
${resT1.filter(r=>r.q).length}
Listes qualifiées
| Liste | Sièges |
|---|---|
| ${r.nom} ${r.id===winT1.id?'▲ Élu':''} | ${r.total} ${r.maj>0?`${r.maj} maj.`:''}${r.maj>0&&r.prop>0?' + ':''}${r.prop>0?`${r.prop} prop.`:''} |
% des voix
Répartition des sièges
Résultats officiels — 1er tour (15 mars 2026)
| Liste | Voix | % exprimés | T2 |
|---|---|---|---|
| ${l.nom}${l.nuance?`${l.nuance}`:''} | ${l.voix?l.voix.toLocaleString('fr-FR'):'—'} | ${l.pct.toFixed(2)} % | ${q?'✓ Qualifiée':f?'Fusion possible':'—'} |
Qualifiées pour le 2e tour : listes avec ≥ 10 % des suffrages exprimés. Peuvent fusionner : listes avec ≥ 5 % des suffrages exprimés (art. L. 262 et L. 264 du Code électoral).
Scores du 2e tour — 22 mars 2026
Entrez vos estimations de scores. En cas de fusion ou désistement, supprimez la liste concernée avec ×. Le total doit être proche de 100 %.







































