{"id":7,"date":"2026-02-27T16:00:31","date_gmt":"2026-02-27T16:00:31","guid":{"rendered":"https:\/\/nomad-s-land.eu\/?page_id=7"},"modified":"2026-02-27T16:00:31","modified_gmt":"2026-02-27T16:00:31","slug":"7-2","status":"publish","type":"page","link":"https:\/\/nomad-s-land.eu\/index.php\/7-2\/","title":{"rendered":""},"content":{"rendered":"<p><!DOCTYPE html><br \/>\n<html lang=\"fr\"><br \/>\n<head><br \/>\n<meta charset=\"UTF-8\"><br \/>\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><br \/>\n<title>R\u00e9sultats \u00c9lectoraux \u2014 Visualisation<\/title>\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Recursive:wght@300;400;500;600;700;900&#038;display=swap\" rel=\"stylesheet\">\n<style>\n:root {\n  --bg:        #fffbdc;\n  --bg2:       #fff8cc;\n  --bg3:       #f5f0b0;\n  --surface:   #ffffff;\n  --border:    rgba(149,27,129,0.18);\n  --primary:   #951b81;\n  --primary-l: #b5239e;\n  --teal:      #00a19a;\n  --teal-l:    #007f79;\n  --cream:     #1a0f18;\n  --cream-dim: rgba(26,15,24,0.6);\n  --cream-faint: rgba(149,27,129,0.07);\n  --glow:      rgba(149,27,129,0.15);\n  --glow-t:    rgba(0,161,154,0.15);\n  --text:      #1a0f18;\n  --text-dim:  rgba(26,15,24,0.5);\n  --text-faint:rgba(26,15,24,0.25);\n  --rouge:     #c0392b;\n  --or:        #b8860b;<\/p>\n<p>  \/* Couleurs listes \u2014 index 0 = Nouveau CAP *\/\n  --c0: #951b81;\n  --c1: #00a19a;\n  --c2: #e05a20;\n  --c3: #3a7bd5;\n  --c4: #c4870a;\n  --c5: #27ae60;\n  --c6: #c0392b;\n  --c7: #7d3c98;\n}<\/p>\n<p>* { box-sizing: border-box; margin: 0; padding: 0; }<\/p>\n<p>html, body {\n  width: 100%; height: 100%;\n  background: var(--bg);\n  color: var(--text);\n  font-family: 'Recursive', sans-serif;\n  font-variation-settings: 'MONO' 0, 'CASL' 0.1;\n  overflow: hidden;\n}<\/p>\n<p>\/* \u2500\u2500 LAYOUT \u2500\u2500 *\/\n.layout {\n  display: grid;\n  grid-template-rows: 64px 1fr 1fr;\n  height: 100vh;\n  width: 100vw;\n  gap: 0;\n}<\/p>\n<p>\/* \u2500\u2500 HEADER \u2500\u2500 *\/\nheader {\n  background: var(--surface);\n  border-bottom: 1px solid var(--border);\n  box-shadow: 0 2px 12px rgba(149,27,129,0.08);\n  display: flex;\n  align-items: center;\n  padding: 0 32px;\n  gap: 24px;\n  position: relative;\n  z-index: 10;\n}<\/p>\n<p>header::after {\n  content: '';\n  position: absolute;\n  bottom: -1px; left: 0; right: 0;\n  height: 1px;\n  background: linear-gradient(90deg, transparent, var(--primary), var(--teal), transparent);\n  opacity: 0.6;\n}<\/p>\n<p>.header-title {\n  font-size: 1.1rem;\n  font-weight: 700;\n  letter-spacing: 0.08em;\n  text-transform: uppercase;\n  background: linear-gradient(90deg, var(--primary), var(--teal));\n  -webkit-background-clip: text;\n  -webkit-text-fill-color: transparent;\n  background-clip: text;\n}<\/p>\n<p>.header-meta {\n  display: flex;\n  gap: 16px;\n  margin-left: auto;\n  align-items: center;\n}<\/p>\n<p>.pill {\n  padding: 4px 14px;\n  border-radius: 20px;\n  font-size: 0.75rem;\n  font-weight: 600;\n  letter-spacing: 0.06em;\n  border: 1px solid;\n}<\/p>\n<p>.pill-primary { background: rgba(149,27,129,0.1); border-color: var(--primary); color: var(--primary); }\n.pill-teal    { background: rgba(0,161,154,0.12);  border-color: var(--teal);    color: var(--teal-l); }\n.pill-dim     { background: rgba(26,15,24,0.05);    border-color: var(--border);  color: var(--text-dim); }<\/p>\n<p>.live-dot {\n  width: 8px; height: 8px;\n  border-radius: 50%;\n  background: var(--teal);\n  box-shadow: 0 0 8px rgba(0,161,154,0.5);\n  animation: pulse 2s ease-in-out infinite;\n}<\/p>\n<p>@keyframes pulse {\n  0%, 100% { opacity: 1; transform: scale(1); }\n  50%       { opacity: 0.5; transform: scale(0.7); }\n}<\/p>\n<p>.tour-selector {\n  display: flex;\n  gap: 6px;\n}<\/p>\n<p>.tour-btn {\n  padding: 5px 14px;\n  border-radius: 6px;\n  border: 1px solid var(--border);\n  background: var(--bg3);\n  color: var(--text-dim);\n  border-bottom: 1px solid var(--border);\n  font-family: 'Recursive', sans-serif;\n  font-size: 0.78rem;\n  font-weight: 600;\n  cursor: pointer;\n  transition: all 0.2s;\n}<\/p>\n<p>.tour-btn.active {\n  background: var(--primary);\n  border-color: var(--primary);\n  color: #fffbdc;\n}<\/p>\n<p>.tour-btn:hover:not(.active) {\n  border-color: var(--primary);\n  color: var(--primary);\n}<\/p>\n<p>.refresh-btn {\n  width: 32px; height: 32px;\n  border-radius: 8px;\n  border: 1px solid var(--border);\n  background: var(--bg3);\n  color: var(--text-dim);\n  border-bottom: 1px solid var(--border);\n  cursor: pointer;\n  display: flex; align-items: center; justify-content: center;\n  font-size: 0.9rem;\n  transition: all 0.2s;\n}<\/p>\n<p>.refresh-btn:hover { border-color: var(--teal); color: var(--teal); }\n.refresh-btn.spinning { animation: spin 0.8s linear infinite; }\n@keyframes spin { to { transform: rotate(360deg); } }<\/p>\n<p>\/* \u2500\u2500 ROW 1 : BARRES CANDIDATS \u2500\u2500 *\/\n.row-listes {\n  background: var(--surface);\n  border-bottom: 1px solid var(--border);\n  box-shadow: 0 2px 8px rgba(149,27,129,0.06);\n  padding: 16px 32px;\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  gap: 10px;\n  overflow: hidden;\n}<\/p>\n<p>.row-listes-title {\n  font-size: 0.65rem;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.15em;\n  color: var(--text-dim);\n  margin-bottom: 2px;\n}<\/p>\n<p>.listes-grid {\n  display: flex;\n  flex-direction: column;\n  gap: 7px;\n}<\/p>\n<p>.liste-bar-row {\n  display: grid;\n  grid-template-columns: 220px 1fr 72px 1fr 72px 80px;\n  align-items: center;\n  gap: 10px;\n}<\/p>\n<p>.bar-label {\n  font-size: 0.58rem;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.1em;\n  color: var(--text-faint);\n  text-align: center;\n  margin-bottom: 2px;\n}<\/p>\n<p>.bar-col {\n  display: flex;\n  flex-direction: column;\n  gap: 2px;\n}<\/p>\n<p>.bar-cp-track {\n  height: 22px;\n  background: rgba(0,161,154,0.08);\n  border-radius: 4px;\n  overflow: hidden;\n  position: relative;\n}<\/p>\n<p>.bar-cp-fill {\n  height: 100%;\n  border-radius: 4px;\n  transition: width 1.2s cubic-bezier(0.16, 1, 0.3, 1);\n  position: relative;\n  display: flex;\n  align-items: center;\n  padding-left: 10px;\n  opacity: 0.65;\n}<\/p>\n<p>.bar-cp-fill::after {\n  content: '';\n  position: absolute;\n  inset: 0;\n  background: repeating-linear-gradient(\n    45deg,\n    transparent,\n    transparent 4px,\n    rgba(255,255,255,0.07) 4px,\n    rgba(255,255,255,0.07) 8px\n  );\n}<\/p>\n<p>.bar-pct-cp {\n  font-size: 1.1rem;\n  font-weight: 700;\n  text-align: right;\n  font-variation-settings: 'MONO' 1;\n  opacity: 0.7;\n}<\/p>\n<p>.liste-bar-name {\n  font-size: 0.78rem;\n  font-weight: 600;\n  white-space: nowrap;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}<\/p>\n<p>.liste-color-dot {\n  width: 10px; height: 10px;\n  border-radius: 50%;\n  flex-shrink: 0;\n}<\/p>\n<p>.bar-track {\n  height: 22px;\n  background: rgba(149,27,129,0.08);\n  border-radius: 4px;\n  overflow: hidden;\n  position: relative;\n}<\/p>\n<p>.bar-fill {\n  height: 100%;\n  border-radius: 4px;\n  transition: width 1.2s cubic-bezier(0.16, 1, 0.3, 1);\n  position: relative;\n  display: flex;\n  align-items: center;\n  padding-left: 10px;\n}<\/p>\n<p>.bar-fill::after {\n  content: '';\n  position: absolute;\n  inset: 0;\n  background: linear-gradient(90deg, transparent 60%, rgba(255,255,255,0.15));\n  border-radius: 4px;\n}<\/p>\n<p>.bar-voix {\n  font-size: 0.72rem;\n  font-weight: 600;\n  color: white;\n  position: relative;\n  z-index: 1;\n  white-space: nowrap;\n}<\/p>\n<p>.bar-pct {\n  font-size: 1.1rem;\n  font-weight: 700;\n  text-align: right;\n  font-variation-settings: 'MONO' 1;\n}<\/p>\n<p>.bar-bureaux {\n  font-size: 0.68rem;\n  color: var(--text-dim);\n  text-align: right;\n  font-variation-settings: 'MONO' 1;\n}<\/p>\n<p>\/* \u2500\u2500 ROW 2 : MATRICE \u2500\u2500 *\/\n.row-matrice {\n  background: var(--bg2);\n  \/* bg = #fffbdc *\/\n  padding: 16px 32px;\n  overflow: auto;\n  position: relative;\n}<\/p>\n<p>.matrice-title {\n  font-size: 0.65rem;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.15em;\n  color: var(--text-dim);\n  margin-bottom: 12px;\n}<\/p>\n<p>.matrice-wrap {\n  overflow: auto;\n  height: calc(100% - 28px);\n}<\/p>\n<p>\/* Table matrice *\/\n.matrice-table {\n  border-collapse: collapse;\n  width: 100%;\n  font-size: 0.72rem;\n  font-variation-settings: 'MONO' 0.8;\n}<\/p>\n<p>.matrice-table th {\n  background: #f0ecb8;\n  color: var(--text-dim);\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  padding: 7px 10px;\n  white-space: nowrap;\n  position: sticky;\n  top: 0;\n  z-index: 5;\n  border-bottom: 1px solid var(--border);\n}<\/p>\n<p>.matrice-table th.th-bureau {\n  left: 0;\n  z-index: 6;\n  background: #f0ecb8;\n  border-right: 1px solid var(--border);\n  text-align: left;\n  min-width: 160px;\n}<\/p>\n<p>.matrice-table td {\n  padding: 6px 10px;\n  border-bottom: 1px solid rgba(149,27,129,0.07);\n  white-space: nowrap;\n  text-align: right;\n  vertical-align: middle;\n}<\/p>\n<p>.matrice-table td.td-bureau {\n  position: sticky;\n  left: 0;\n  background: #fff8cc;\n  border-right: 1px solid var(--border);\n  text-align: left;\n  font-weight: 600;\n  color: var(--text);\n  font-size: 0.75rem;\n  z-index: 3;\n}<\/p>\n<p>.matrice-table tr:hover td { background: rgba(149,27,129,0.06); }\n.matrice-table tr:hover td.td-bureau { background: rgba(149,27,129,0.12); }<\/p>\n<p>.matrice-table tr.tr-total td {\n  background: #f0ecb8;\n  color: var(--text);\n  font-weight: 700;\n  border-top: 1px solid var(--primary);\n  position: sticky;\n  bottom: 0;\n  z-index: 2;\n}<\/p>\n<p>.matrice-table tr.tr-total td.td-bureau {\n  background: #f0ecb8;\n  z-index: 4;\n}<\/p>\n<p>\/* Groupe de colonnes *\/\n.th-group {\n  font-size: 0.6rem;\n  text-align: center;\n  padding: 4px 8px 3px;\n  letter-spacing: 0.12em;\n}<\/p>\n<p>.th-group-inscrits  { color: var(--cream-dim); }\n.th-group-votants   { color: var(--teal-l); }\n.th-group-exprimes  { color: var(--primary-l); }\n.th-group-abstention{ color: var(--rouge); }\n.th-group-blancs    { color: var(--or); }<\/p>\n<p>.val-neutral { color: var(--text-dim); }\n.val-teal    { color: var(--teal); }\n.val-primary { color: var(--primary); }\n.val-rouge   { color: var(--rouge); }\n.val-or      { color: var(--or); }\n.val-dim     { color: var(--text-faint); }<\/p>\n<p>.pct-badge {\n  display: inline-block;\n  font-size: 0.65rem;\n  padding: 1px 5px;\n  border-radius: 3px;\n  margin-left: 4px;\n  font-variation-settings: 'MONO' 1;\n}<\/p>\n<p>.pct-teal    { background: rgba(0,161,154,0.12); color: var(--teal); font-weight:700; }\n.pct-primary { background: rgba(149,27,129,0.1); color: var(--primary); font-weight:700; }\n.pct-rouge   { background: rgba(192,57,43,0.1);   color: var(--rouge); font-weight:700; }\n.pct-or      { background: rgba(176,125,0,0.1);   color: var(--or); font-weight:700; }<\/p>\n<p>\/* \u2500\u2500 LOADING \/ ERROR \u2500\u2500 *\/\n.overlay {\n  position: fixed; inset: 0;\n  background: rgba(255,251,220,0.92);\n  display: flex; align-items: center; justify-content: center;\n  flex-direction: column; gap: 16px;\n  z-index: 100;\n  transition: opacity 0.5s;\n}<\/p>\n<p>.overlay.hidden { opacity: 0; pointer-events: none; }<\/p>\n<p>.loader-ring {\n  width: 48px; height: 48px;\n  border: 3px solid rgba(149,27,129,0.15);\n  border-top-color: var(--primary);\n  border-right-color: var(--teal);\n  border-radius: 50%;\n  animation: spin 1s linear infinite;\n}<\/p>\n<p>.loader-text {\n  font-size: 0.85rem;\n  color: var(--text-dim);\n  letter-spacing: 0.1em;\n}<\/p>\n<p>\/* Config modale *\/\n.config-modal {\n  position: fixed; inset: 0;\n  background: rgba(255,251,220,0.85);\n  display: flex; align-items: center; justify-content: center;\n  z-index: 200;\n  backdrop-filter: blur(4px);\n}<\/p>\n<p>.config-modal.hidden { display: none; }<\/p>\n<p>.config-box {\n  background: #ffffff;\n  border: 1px solid var(--border);\n  border-radius: 16px;\n  padding: 32px;\n  width: 480px;\n  box-shadow: 0 8px 40px rgba(149,27,129,0.15), 0 0 0 1px rgba(149,27,129,0.12);\n}<\/p>\n<p>.config-box h2 {\n  font-size: 1rem;\n  font-weight: 700;\n  margin-bottom: 20px;\n  background: linear-gradient(90deg, var(--primary), var(--teal));\n  -webkit-background-clip: text;\n  -webkit-text-fill-color: transparent;\n  background-clip: text;\n}<\/p>\n<p>.config-box label {\n  display: block;\n  font-size: 0.72rem;\n  font-weight: 600;\n  text-transform: uppercase;\n  letter-spacing: 0.1em;\n  color: var(--text-dim);\n  margin-bottom: 6px;\n  margin-top: 14px;\n}<\/p>\n<p>.config-box input {\n  width: 100%;\n  padding: 10px 14px;\n  background: var(--bg2);\n  border: 1px solid var(--border);\n  border-radius: 8px;\n  color: var(--text);\n  font-family: 'Recursive', sans-serif;\n  font-size: 0.85rem;\n  font-variation-settings: 'MONO' 0.8;\n  outline: none;\n  transition: border-color 0.2s;\n}<\/p>\n<p>.config-box input:focus { border-color: var(--primary); }<\/p>\n<p>.config-box button {\n  margin-top: 20px;\n  width: 100%;\n  padding: 12px;\n  background: var(--primary);\n  border: none;\n  border-radius: 8px;\n  color: white;\n  font-family: 'Recursive', sans-serif;\n  font-weight: 700;\n  font-size: 0.9rem;\n  cursor: pointer;\n  transition: background 0.2s;\n}<\/p>\n<p>.config-box button:hover { background: var(--primary-l); }<\/p>\n<p>\/* \u2500\u2500 GRID SEPARATOR \u2500\u2500 *\/\n.col-sep { border-left: 1px solid rgba(255,251,220,0.08) !important; }<\/p>\n<p>\/* \u2500\u2500 BACK LINK \u2500\u2500 *\/\n.back-link {\n  color: var(--primary);\n  text-decoration: none;\n  font-size: 0.75rem;\n  display: flex; align-items: center; gap: 6px;\n  transition: color 0.2s;\n}\n.back-link:hover { color: var(--teal); }<\/p>\n<p>\/* empty state *\/\n.empty { color: var(--text-faint); font-size: 0.8rem; padding: 24px; text-align: center; }\n<\/style>\n<p><\/head><br \/>\n<body><\/p>\n<p><!-- CONFIG MODAL --><\/p>\n<div class=\"config-modal\" id=\"configModal\">\n<div class=\"config-box\">\n<h2>\u2699\ufe0f Configuration Azure Table Storage<\/h2>\n<p>    <label>Nom du compte de stockage<\/label><br \/>\n    <input type=\"text\" id=\"cfgAccount\" placeholder=\"corlouhome\" value=\"corlouhome\"><br \/>\n    <label>Jeton SAS<\/label><br \/>\n    <input type=\"text\" id=\"cfgSas\" placeholder=\"?sv=2024-11-04&#038;ss=t&#038;srt=o&#038;sp=...\"><br \/>\n    <button onclick=\"applyConfig()\">Connecter &#038; Charger \u2192<\/button>\n  <\/div>\n<\/div>\n<p><!-- LOADING OVERLAY --><\/p>\n<div class=\"overlay\" id=\"overlay\">\n<div class=\"loader-ring\"><\/div>\n<div class=\"loader-text\">Chargement des r\u00e9sultats\u2026<\/div>\n<\/div>\n<p><!-- MAIN LAYOUT --><\/p>\n<div class=\"layout\">\n<p>  <!-- HEADER --><\/p>\n<header>\n    <a class=\"back-link\" href=\"index.html\">\u2190 Saisie<\/a><\/p>\n<div style=\"width:1px; height:28px; background:var(--border);\"><\/div>\n<div class=\"header-title\">\ud83d\uddf3\ufe0f R\u00e9sultats \u00c9lectoraux Municipaux<\/div>\n<div class=\"header-meta\">\n<div class=\"tour-selector\">\n        <button class=\"tour-btn active\" id=\"t1btn\" onclick=\"setTour(1)\">Tour 1<\/button><br \/>\n        <button class=\"tour-btn\" id=\"t2btn\" onclick=\"setTour(2)\">Tour 2<\/button>\n      <\/div>\n<p>      <span class=\"pill pill-primary\" id=\"statBureaux\">\u2014 bureaux<\/span><br \/>\n      <span class=\"pill pill-teal\"    id=\"statInscrits\">\u2014 inscrits<\/span><br \/>\n      <span class=\"pill pill-dim\"     id=\"statParticipation\">\u2014 participation<\/span><\/p>\n<div class=\"live-dot\" title=\"Actualisation auto\"><\/div>\n<p>      <button class=\"refresh-btn\" id=\"refreshBtn\" onclick=\"loadAll()\" title=\"Actualiser\">\u21bb<\/button><br \/>\n      <button class=\"refresh-btn\" onclick=\"showConfig()\" title=\"Param\u00e8tres\">\u2699<\/button>\n    <\/div>\n<\/header>\n<p>  <!-- ROW 1 : LISTES --><\/p>\n<div class=\"row-listes\">\n<div class=\"row-listes-title\">Voix exprim\u00e9es par liste \u2014 cumul\u00e9 tous bureaux &nbsp;\u00b7&nbsp; <span style=\"opacity:0.55;\">Hachur\u00e9 = 100 premiers bulletins (CP)<\/span><\/div>\n<div class=\"listes-grid\" id=\"listesGrid\">\n<div class=\"empty\">Chargement\u2026<\/div>\n<\/p><\/div>\n<\/p><\/div>\n<p>  <!-- ROW 2 : MATRICE --><\/p>\n<div class=\"row-matrice\">\n<div class=\"matrice-title\">D\u00e9tail par bureau de vote<\/div>\n<div class=\"matrice-wrap\">\n<table class=\"matrice-table\" id=\"matriceTable\">\n<tbody>\n<tr>\n<td class=\"empty\">Chargement\u2026<\/td>\n<\/tr>\n<\/tbody>\n<\/table><\/div>\n<\/p><\/div>\n<\/div>\n<p><script>\n\/\/ \u2500\u2500 CONFIG \u2500\u2500\nconst AZ_API_VERSION = '2021-06-08';\nlet cfg = {\n  account: localStorage.getItem('azAccount') || 'corlouhome',\n  sas: localStorage.getItem('azSas') || ''\n};\nlet currentTour = 1;\nlet autoRefreshTimer = null;<\/p>\n<p>\/\/ Couleurs par index de liste (RowKey num\u00e9rique tri\u00e9)\n\/\/ RowKey \"1\" = premi\u00e8re liste dans l'ordre \u2192 re\u00e7oit --c0 = #951b81 si c'est Nouveau CAP\nconst LIST_COLORS = ['#951b81','#00a19a','#e05a20','#3a7bd5','#d4a017','#2ecc71','#e74c3c','#9b59b6','#1abc9c','#e67e22'];\n\/\/ La liste \"Nouveau CAP\" re\u00e7oit toujours #951b81 \u2014 on la d\u00e9tecte par son nom\nconst NOUVEAU_CAP_COLOR = '#951b81';<\/p>\n<p>\/\/ \u2500\u2500 AZURE \u2500\u2500\nfunction azFetch(table, filter = '') {\n  const sas = cfg.sas.replace(\/^\\?\/, '');\n  let path = `\/${table}()`;\n  if (filter) path += `?$filter=${encodeURIComponent(filter)}`;\n  const sep = filter ? '&' : '?';\n  const url = `https:\/\/${cfg.account}.table.core.windows.net${path}${sep}${sas}`;\n  return fetch(url, {\n    headers: { 'Accept': 'application\/json;odata=nometadata', 'x-ms-version': AZ_API_VERSION }\n  }).then(r => {\n    if (!r.ok) throw new Error(`HTTP ${r.status}`);\n    return r.json();\n  }).then(d => d.value || []);\n}<\/p>\n<p>\/\/ \u2500\u2500 DATA \u2500\u2500\nlet data = { bureaux: [], listes: [], voteGlobal: [], voteListe: [] };<\/p>\n<p>async function loadAll() {\n  const btn = document.getElementById('refreshBtn');\n  btn.classList.add('spinning');\n  try {\n    const [bureaux, listes, vg, vl] = await Promise.all([\n      azFetch('ListeBureaux'),\n      azFetch('Listes'),\n      azFetch('VoteGlobal', `PartitionKey eq '${currentTour}'`),\n      azFetch('VoteListe',  `PartitionKey ge '${currentTour}_' and PartitionKey lt '${currentTour}\\`'`)\n    ]);\n    data.bureaux    = bureaux.sort((a,b) => (parseInt(a.RowKey)||0) - (parseInt(b.RowKey)||0));\n    data.listes     = listes.sort((a,b)  => (parseInt(a.RowKey)||0) - (parseInt(b.RowKey)||0));\n    data.voteGlobal = vg;\n    data.voteListe  = vl;\n    render();\n    updateHeader();\n  } catch(e) {\n    console.error(e);\n  }\n  btn.classList.remove('spinning');\n  document.getElementById('overlay').classList.add('hidden');\n}<\/p>\n<p>\/\/ \u2500\u2500 COULEUR PAR LISTE \u2500\u2500\nfunction getListeColor(liste, idx) {\n  const nom = (liste.NomListe || liste.nomListe || '').toLowerCase();\n  if (nom.includes('nouveau cap') || nom.includes('nouveau_cap')) return NOUVEAU_CAP_COLOR;\n  return LIST_COLORS[idx % LIST_COLORS.length];\n}<\/p>\n<p>\/\/ \u2500\u2500 RENDER LISTES \u2500\u2500\nfunction render() {\n  renderListesBars();\n  renderMatrice();\n}<\/p>\n<p>function isNouveauCap(liste) {\n  const nom = (liste.NomListe || '').toLowerCase();\n  return nom.includes('nouveau cap') || nom.includes('nouveau_cap');\n}<\/p>\n<p>function renderListesBars() {\n  const grid = document.getElementById('listesGrid');\n  if (!data.listes.length) { grid.innerHTML = '<\/p>\n<div class=\"empty\">Aucune liste<\/div>\n<p>'; return; }<\/p>\n<p>  \/\/ Calculer total NbVoixExp et CPVoixExp par liste (tous bureaux)\n  const totalNb = {}, totalCp = {};\n  data.listes.forEach(l => { totalNb[l.RowKey] = 0; totalCp[l.RowKey] = 0; });\n  data.voteListe.forEach(vl => {\n    const rk = vl.RowKey;\n    if (totalNb[rk] !== undefined) {\n      totalNb[rk] += parseInt(vl.NbVoixExp) || 0;\n      totalCp[rk] += parseInt(vl.CPVoixExp) || 0;\n    }\n  });<\/p>\n<p>  const totalExprimes = Object.values(totalNb).reduce((a,b) => a+b, 0);\n  const totalCpAll    = Object.values(totalCp).reduce((a,b) => a+b, 0);\n  const maxNb = Math.max(...Object.values(totalNb), 1);\n  const maxCp = Math.max(...Object.values(totalCp), 1);<\/p>\n<p>  \/\/ Trier : Nouveau CAP toujours en premier, reste inchang\u00e9\n  const sorted = [\n    ...data.listes.filter(l => isNouveauCap(l)),\n    ...data.listes.filter(l => !isNouveauCap(l))\n  ];<\/p>\n<p>  const rows = sorted.map((l, idx) => {\n    const isNC  = isNouveauCap(l);\n    const voix  = totalNb[l.RowKey] || 0;\n    const cp    = totalCp[l.RowKey] || 0;\n    const pct   = totalExprimes > 0 ? (voix \/ totalExprimes * 100) : 0;\n    const pctCp = totalCpAll    > 0 ? (cp   \/ totalCpAll    * 100) : 0;\n    const barW  = (voix \/ maxNb * 100);\n    const barCpW= (cp   \/ maxCp * 100);\n    const color = getListeColor(l, isNC ? 0 : idx + 1);\n    const nom   = l.NomListe || l.RowKey;\n    const bureauCount = new Set(\n      data.voteListe.filter(v => v.RowKey === l.RowKey).map(v => v.PartitionKey)\n    ).size;<\/p>\n<p>    \/\/ Nouveau CAP : barre plus haute, nom plus grand, s\u00e9parateur en dessous\n    const barH      = isNC ? '34px' : '22px';\n    const nomSize   = isNC ? '1rem' : '0.8rem';\n    const nomWeight = isNC ? '800'  : '600';\n    const rowGlow   = isNC ? `box-shadow: 0 0 20px ${color}22; border-radius:8px; padding: 4px 6px; margin: 0 -6px 4px; border: 1px solid ${color}33; background: ${color}0a;` : '';\n    const sepAfter  = isNC ? `<\/p>\n<div style=\"height:1px; background:linear-gradient(90deg, ${color}80, transparent); margin: 4px 0 6px;\"><\/div>\n<p>` : '';<\/p>\n<p>    return `<\/p>\n<div class=\"liste-bar-row\" style=\"${rowGlow}\">\n<div class=\"liste-bar-name\">\n<div class=\"liste-color-dot\" style=\"width:${isNC?14:10}px; height:${isNC?14:10}px; background:${color}; box-shadow:0 0 ${isNC?10:6}px ${color}${isNC?'cc':'80'};\"><\/div>\n<p>        <span style=\"color:${color}; font-size:${nomSize}; font-weight:${nomWeight}; letter-spacing:${isNC?'0.04em':'0'};\">${nom}<\/span>\n      <\/div>\n<div class=\"bar-col\">\n<div class=\"bar-track\" style=\"height:${barH};\">\n<div class=\"bar-fill\" style=\"height:100%; width:${Math.max(barW,0.3)}%; background:linear-gradient(90deg, ${color}bb, ${color}); ${isNC ? `box-shadow: inset 0 -2px 0 rgba(0,0,0,0.2);` : ''}\">\n            <span class=\"bar-voix\" style=\"font-size:${isNC?'0.85rem':'0.72rem'}; font-weight:${isNC?700:600};\">${voix.toLocaleString('fr-FR')}<\/span>\n          <\/div>\n<\/p><\/div>\n<\/p><\/div>\n<div class=\"bar-pct\" style=\"color:${color}; font-size:${isNC?'1.4rem':'1.1rem'}; font-weight:${isNC?800:700};\">${pct.toFixed(1)}%<\/div>\n<div class=\"bar-col\">\n<div class=\"bar-cp-track\" style=\"height:${barH};\">\n<div class=\"bar-cp-fill\" style=\"height:100%; width:${Math.max(barCpW,0.3)}%; background:linear-gradient(90deg, ${color}88, ${color}aa);\">\n            <span class=\"bar-voix\" style=\"font-size:${isNC?'0.85rem':'0.72rem'};\">${cp.toLocaleString('fr-FR')}<\/span>\n          <\/div>\n<\/p><\/div>\n<\/p><\/div>\n<div class=\"bar-pct-cp\" style=\"color:${color}; font-size:${isNC?'1.4rem':'1.1rem'}; font-weight:${isNC?800:700}; opacity:${isNC?0.9:0.7};\">${pctCp.toFixed(1)}%<\/div>\n<div class=\"bar-bureaux\" style=\"font-size:${isNC?'0.75rem':'0.68rem'};\">${bureauCount} bur.<\/div>\n<\/p><\/div>\n<p>${sepAfter}`;\n  }).join('');<\/p>\n<p>  const header = `<\/p>\n<div class=\"liste-bar-row\" style=\"margin-bottom:4px;\">\n<div><\/div>\n<div class=\"bar-label\">Total voix exprim\u00e9es<\/div>\n<div class=\"bar-label\">% exp.<\/div>\n<div class=\"bar-label\" style=\"color:var(--teal-l);\">100 premiers bulletins (CP)<\/div>\n<div class=\"bar-label\" style=\"color:var(--teal-l);\">% CP<\/div>\n<div class=\"bar-label\">Bureaux<\/div>\n<\/p><\/div>\n<p>`;\n  grid.innerHTML = header + rows;\n}<\/p>\n<p>\/\/ \u2500\u2500 RENDER MATRICE \u2500\u2500\nfunction renderMatrice() {\n  const table = document.getElementById('matriceTable');\n  if (!data.bureaux.length) {\n    table.innerHTML = '<\/p>\n<tbody>\n<tr>\n<td class=\"empty\">Aucun bureau<\/td>\n<\/tr>\n<\/tbody>\n<p>';\n    return;\n  }<\/p>\n<p>  \/\/ Header\n  const thead = `<\/p>\n<thead>\n<tr>\n<th class=\"th-bureau\" rowspan=\"2\">Bureau de vote<\/th>\n<th class=\"th-group th-group-inscrits col-sep\" colspan=\"1\">Inscrits<\/th>\n<th class=\"th-group th-group-votants col-sep\" colspan=\"2\">Votants<\/th>\n<th class=\"th-group th-group-exprimes col-sep\" colspan=\"2\">Exprim\u00e9s<\/th>\n<th class=\"th-group th-group-abstention col-sep\" colspan=\"2\">Abstention<\/th>\n<th class=\"th-group th-group-blancs col-sep\" colspan=\"2\">Blancs \/ Nuls<\/th>\n<\/tr>\n<tr>\n<th class=\"val-neutral col-sep\">N<\/th>\n<th class=\"val-teal col-sep\">N<\/th>\n<th class=\"val-teal\">%<\/th>\n<th class=\"val-primary col-sep\">N<\/th>\n<th class=\"val-primary\">%<\/th>\n<th class=\"val-rouge col-sep\">N<\/th>\n<th class=\"val-rouge\">%<\/th>\n<th class=\"val-or col-sep\">N<\/th>\n<th class=\"val-or\">%<\/th>\n<\/tr>\n<\/thead>\n<p>`;<\/p>\n<p>  \/\/ Totaux cumul\u00e9s\n  let tot = { inscrits:0, votants:0, exprimes:0 };<\/p>\n<p>  const rows = data.bureaux.map(b => {\n    const vg = data.voteGlobal.find(v => String(v.RowKey) === String(b.RowKey));\n    if (!vg) return `<\/p>\n<tr>\n<td class=\"td-bureau\">${b.BureauVote || b.RowKey}<\/td>\n<td class=\"val-dim col-sep\">\u2014<\/td>\n<td class=\"val-dim col-sep\">\u2014<\/td>\n<td class=\"val-dim\">\u2014<\/td>\n<td class=\"val-dim col-sep\">\u2014<\/td>\n<td class=\"val-dim\">\u2014<\/td>\n<td class=\"val-dim col-sep\">\u2014<\/td>\n<td class=\"val-dim\">\u2014<\/td>\n<td class=\"val-dim col-sep\">\u2014<\/td>\n<td class=\"val-dim\">\u2014<\/td>\n<\/tr>\n<p>`;<\/p>\n<p>    const ins = parseInt(vg.Inscrits)  || 0;\n    const vot = parseInt(vg.Votants)   || 0;\n    const exp = parseInt(vg.Exprimes)  || 0;\n    const abs = ins - vot;\n    const bln = vot - exp;<\/p>\n<p>    tot.inscrits  += ins;\n    tot.votants   += vot;\n    tot.exprimes  += exp;<\/p>\n<p>    const pVot = ins > 0 ? (vot\/ins*100).toFixed(1) : '\u2014';\n    const pExp = ins > 0 ? (exp\/ins*100).toFixed(1) : '\u2014';\n    const pAbs = ins > 0 ? (abs\/ins*100).toFixed(1) : '\u2014';\n    const pBln = ins > 0 ? (bln\/ins*100).toFixed(1) : '\u2014';<\/p>\n<p>    return `<\/p>\n<tr>\n<td class=\"td-bureau\">${b.BureauVote || b.RowKey}<\/td>\n<td class=\"val-neutral col-sep\">${ins.toLocaleString('fr-FR')}<\/td>\n<td class=\"val-teal col-sep\">${vot.toLocaleString('fr-FR')}<\/td>\n<td><span class=\"pct-badge pct-teal\">${pVot}%<\/span><\/td>\n<td class=\"val-primary col-sep\">${exp.toLocaleString('fr-FR')}<\/td>\n<td><span class=\"pct-badge pct-primary\">${pExp}%<\/span><\/td>\n<td class=\"val-rouge col-sep\">${abs.toLocaleString('fr-FR')}<\/td>\n<td><span class=\"pct-badge pct-rouge\">${pAbs}%<\/span><\/td>\n<td class=\"val-or col-sep\">${bln.toLocaleString('fr-FR')}<\/td>\n<td><span class=\"pct-badge pct-or\">${pBln}%<\/span><\/td>\n<\/tr>\n<p>`;\n  }).join('');<\/p>\n<p>  \/\/ Ligne total\n  const tAbs = tot.inscrits - tot.votants;\n  const tBln = tot.votants  - tot.exprimes;\n  const tpVot = tot.inscrits > 0 ? (tot.votants \/tot.inscrits*100).toFixed(1) : '\u2014';\n  const tpExp = tot.inscrits > 0 ? (tot.exprimes\/tot.inscrits*100).toFixed(1) : '\u2014';\n  const tpAbs = tot.inscrits > 0 ? (tAbs\/tot.inscrits*100).toFixed(1) : '\u2014';\n  const tpBln = tot.inscrits > 0 ? (tBln\/tot.inscrits*100).toFixed(1) : '\u2014';<\/p>\n<p>  const totalRow = `<\/p>\n<tr class=\"tr-total\">\n<td class=\"td-bureau\">TOTAL<\/td>\n<td class=\"val-neutral col-sep\">${tot.inscrits.toLocaleString('fr-FR')}<\/td>\n<td class=\"val-teal col-sep\">${tot.votants.toLocaleString('fr-FR')}<\/td>\n<td><span class=\"pct-badge pct-teal\">${tpVot}%<\/span><\/td>\n<td class=\"val-primary col-sep\">${tot.exprimes.toLocaleString('fr-FR')}<\/td>\n<td><span class=\"pct-badge pct-primary\">${tpExp}%<\/span><\/td>\n<td class=\"val-rouge col-sep\">${tAbs.toLocaleString('fr-FR')}<\/td>\n<td><span class=\"pct-badge pct-rouge\">${tpAbs}%<\/span><\/td>\n<td class=\"val-or col-sep\">${tBln.toLocaleString('fr-FR')}<\/td>\n<td><span class=\"pct-badge pct-or\">${tpBln}%<\/span><\/td>\n<\/tr>\n<p>`;<\/p>\n<p>  table.innerHTML = thead + `<\/p>\n<tbody>${rows}${totalRow}<\/tbody>\n<p>`;\n}<\/p>\n<p>\/\/ \u2500\u2500 HEADER STATS \u2500\u2500\nfunction updateHeader() {\n  const bureauDone = data.voteGlobal.length;\n  const totalIns   = data.voteGlobal.reduce((a,v) => a+(parseInt(v.Inscrits)||0), 0);\n  const totalVot   = data.voteGlobal.reduce((a,v) => a+(parseInt(v.Votants)||0),  0);\n  const pVot       = totalIns > 0 ? (totalVot\/totalIns*100).toFixed(1) : '\u2014';<\/p>\n<p>  document.getElementById('statBureaux').textContent       = `${bureauDone} \/ ${data.bureaux.length} bureaux`;\n  document.getElementById('statInscrits').textContent      = `${totalIns.toLocaleString('fr-FR')} inscrits`;\n  document.getElementById('statParticipation').textContent = `${pVot}% participation`;\n}<\/p>\n<p>\/\/ \u2500\u2500 TOUR \u2500\u2500\nfunction setTour(t) {\n  currentTour = t;\n  document.getElementById('t1btn').classList.toggle('active', t===1);\n  document.getElementById('t2btn').classList.toggle('active', t===2);\n  document.getElementById('overlay').classList.remove('hidden');\n  loadAll();\n}<\/p>\n<p>\/\/ \u2500\u2500 CONFIG \u2500\u2500\nfunction showConfig() {\n  document.getElementById('cfgAccount').value = cfg.account;\n  document.getElementById('cfgSas').value     = cfg.sas;\n  document.getElementById('configModal').classList.remove('hidden');\n}<\/p>\n<p>function applyConfig() {\n  cfg.account = document.getElementById('cfgAccount').value.trim() || 'corlouhome';\n  cfg.sas     = document.getElementById('cfgSas').value.trim();\n  localStorage.setItem('azAccount', cfg.account);\n  localStorage.setItem('azSas',     cfg.sas);\n  document.getElementById('configModal').classList.add('hidden');\n  document.getElementById('overlay').classList.remove('hidden');\n  loadAll();\n}<\/p>\n<p>\/\/ \u2500\u2500 AUTO REFRESH (60s) \u2500\u2500\nfunction startAutoRefresh() {\n  if (autoRefreshTimer) clearInterval(autoRefreshTimer);\n  autoRefreshTimer = setInterval(loadAll, 60000);\n}<\/p>\n<p>\/\/ \u2500\u2500 INIT \u2500\u2500\nwindow.onload = () => {\n  document.getElementById('cfgAccount').value = cfg.account;\n  document.getElementById('cfgSas').value     = cfg.sas;\n  if (cfg.sas) {\n    document.getElementById('configModal').classList.add('hidden');\n    loadAll();\n  } else {\n    document.getElementById('overlay').classList.add('hidden');\n    showConfig();\n  }\n  startAutoRefresh();\n};\n<\/script><br \/>\n<\/body><br \/>\n<\/html><\/p>\n","protected":false},"excerpt":{"rendered":"<p>R\u00e9sultats \u00c9lectoraux \u2014 Visualisation \u2699\ufe0f Configuration Azure Table Storage Nom du compte de stockage Jeton SAS Connecter &#038; Charger \u2192 Chargement des r\u00e9sultats\u2026 \u2190 Saisie \ud83d\uddf3\ufe0f R\u00e9sultats \u00c9lectoraux Municipaux Tour 1 Tour 2 \u2014 bureaux \u2014 inscrits \u2014 participation \u21bb \u2699 Voix exprim\u00e9es par liste \u2014 cumul\u00e9 tous bureaux &nbsp;\u00b7&nbsp; Hachur\u00e9 = 100 premiers bulletins [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-7","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/nomad-s-land.eu\/index.php\/wp-json\/wp\/v2\/pages\/7","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nomad-s-land.eu\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/nomad-s-land.eu\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/nomad-s-land.eu\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nomad-s-land.eu\/index.php\/wp-json\/wp\/v2\/comments?post=7"}],"version-history":[{"count":1,"href":"https:\/\/nomad-s-land.eu\/index.php\/wp-json\/wp\/v2\/pages\/7\/revisions"}],"predecessor-version":[{"id":9,"href":"https:\/\/nomad-s-land.eu\/index.php\/wp-json\/wp\/v2\/pages\/7\/revisions\/9"}],"wp:attachment":[{"href":"https:\/\/nomad-s-land.eu\/index.php\/wp-json\/wp\/v2\/media?parent=7"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}