MezzounaGouvernorat de Sidi Bouzid · centre-ouest de la Tunisie. Lycée endeuillé le 14 avril 2025.
');
// cercle de contexte autour de Mezzouna
L.circle(MEZZOUNA, {
radius: 12000, color:'#A63D3D', weight:1.4,
fillColor:'#A63D3D', fillOpacity:.08, dashArray:'4 5'
}).addTo(map);
// boutons de pilotage
document.getElementById('mezzZoom').addEventListener('click', ()=>{
map.flyTo(MEZZOUNA, 13, {duration:1.6});
setTimeout(()=>marker.openPopup(), 1700);
});
document.getElementById('mezzWide').addEventListener('click', ()=>{
map.closePopup();
map.flyTo(TUNISIA_CENTER, 7, {duration:1.4});
});
// zoom automatique de présentation lors du premier affichage
setTimeout(()=>{
map.flyTo(MEZZOUNA, 11, {duration:2.2});
setTimeout(()=>{ map.invalidateSize(); marker.openPopup(); }, 2300);
}, 600);
}
// initialisation paresseuse quand la carte entre dans le viewport
const mObs = new IntersectionObserver((es)=>{
es.forEach(e=>{ if(e.isIntersecting){ initMap(); setTimeout(()=>map && map.invalidateSize(), 200); mObs.disconnect(); }});
},{threshold:.2});
mObs.observe(el);
})();
/* ============================================================
VIZ 01 — ROTATION DES PM (desktop SVG)
============================================================ */
const MANDATES = [
{label:'Avant 2021', months:34, color:C.sky, note:'Gouvernements de coalition issus de la transition'},
{label:'Mandat A', months:11, color:C.wheatDeep, note:'Exécutant administratif'},
{label:'Mandat B', months:9, color:C.wheat, note:'Remaniement'},
{label:'Mandat C', months:14, color:C.olive, note:'Orientations définies au sommet'},
{label:'Mandat D', months:8, color:C.rust, note:'Rotation rapide'},
{label:'Mandat E', months:6, color:C.earth, note:'Mobilisation permanente'},
{label:'Mandat F', months:5, color:C.skySoft, note:'Arbitrage ponctuel'}
];
(function(){
const svg = d3.select('#ssqRota');
if(svg.empty()) return;
const W=1400, H=420, m={t:40,r:50,b:64,l:60};
const x = d3.scaleBand().domain(MANDATES.map(d=>d.label)).range([m.l, W-m.r]).padding(0.32);
const y = d3.scaleLinear().domain([0, 36]).range([H-m.b, m.t]);
svg.append('g').attr('transform',`translate(${m.l},0)`)
.call(d3.axisLeft(y).ticks(4).tickFormat(d=>d+' mois'))
.selectAll('text').attr('font-family','JetBrains Mono, monospace').attr('font-size',12).attr('fill',C.mute);
svg.selectAll('.tick line, .domain').attr('stroke',C.ink).attr('stroke-opacity',.15);
y.ticks(4).forEach(t=>{
svg.append('line').attr('x1',m.l).attr('x2',W-m.r).attr('y1',y(t)).attr('y2',y(t)).attr('stroke',C.ink).attr('stroke-opacity',.06);
});
svg.append('line').attr('x1',m.l).attr('x2',W-m.r).attr('y1',y(24)).attr('y2',y(24))
.attr('stroke',C.rust).attr('stroke-dasharray','6 5').attr('stroke-width',1.4).attr('opacity',.6);
svg.append('text').attr('x',W-m.r).attr('y',y(24)-8).attr('text-anchor','end')
.attr('font-family','JetBrains Mono, monospace').attr('font-size',11).attr('fill',C.rust)
.text('Seuil de continuité stratégique');
const bars = svg.append('g').selectAll('rect').data(MANDATES).enter().append('rect')
.attr('x',d=>x(d.label)).attr('width',x.bandwidth())
.attr('y',y(0)).attr('height',0)
.attr('fill',d=>d.color).attr('fill-opacity',.85)
.attr('stroke',C.paper).attr('stroke-width',2).attr('rx',2);
svg.append('g').selectAll('text.lbl').data(MANDATES).enter().append('text')
.attr('x',d=>x(d.label)+x.bandwidth()/2).attr('y',H-m.b+24).attr('text-anchor','middle')
.attr('font-family','Cormorant Garamond, serif').attr('font-style','italic')
.attr('font-size',15).attr('fill',C.ink).text(d=>d.label);
const vals = svg.append('g').selectAll('text.val').data(MANDATES).enter().append('text')
.attr('x',d=>x(d.label)+x.bandwidth()/2).attr('y',y(0)).attr('text-anchor','middle')
.attr('font-family','JetBrains Mono, monospace').attr('font-size',14).attr('font-weight',700)
.attr('fill',C.ink).attr('opacity',0).text(d=>d.months+' m');
bars.append('title').text(d=>d.note);
ScrollTrigger.create({
trigger:'#ssqRota', start:'top 80%',
onEnter:()=>{
bars.transition().duration(900).delay((d,i)=>i*120)
.attr('y',d=>y(d.months)).attr('height',d=>y(0)-y(d.months));
vals.transition().duration(500).delay((d,i)=>i*120+700)
.attr('y',d=>y(d.months)-10).attr('opacity',1);
}
});
})();
/* VIZ 01 — MOBILE (barres horizontales HTML) */
(function(){
const wrap = document.getElementById('ssqRotaMobile');
if(!wrap) return;
const max = 36;
let html = '';
MANDATES.forEach(d=>{
html += `${d.label}
${d.months} m
— — Seuil de continuité stratégique : 24 mois — —
`;
wrap.innerHTML = html;
const obs = new IntersectionObserver((es)=>{
es.forEach(e=>{ if(e.isIntersecting){
wrap.querySelectorAll('.vizm-rota-fill').forEach((f,i)=>{
setTimeout(()=>{ f.style.width = f.dataset.w+'%'; }, i*90);
});
obs.disconnect();
}});
},{threshold:.3});
obs.observe(wrap);
})();
/* ============================================================
VIZ 02 — DOUBLE TUNISIE
============================================================ */
const DIMS = [
{key:'Part du PIB national', littoral:85, interieur:15, unitL:'≈ 85 %', unitI:'≈ 15 %',
descL:'Le littoral (Grand Tunis, Sfax, Sousse) concentre environ 85 % du PIB du pays.',
descI:'Les régions intérieures ne pèsent qu\'une fraction du PIB national.',
src:'Banque mondiale, Tunisia Urbanization Review'},
{key:'Entreprises industrielles', littoral:92, interieur:8, unitL:'≈ 92 %', unitI:'≈ 8 %',
descL:'≈ 92 % des entreprises industrielles sont situées à une heure de Tunis, Sfax ou Sousse.',
descI:'Très faible densité industrielle dans l\'intérieur du pays.',
src:'Banque mondiale, La révolution inachevée (chap. 10)'},
{key:'Taux de chômage', littoral:35, interieur:80, unitL:'7-11 %', unitI:'20-23 %',
descL:'Chômage de 7 à 11 % dans les gouvernorats côtiers.',
descI:'Chômage de 20 à 23 % dans les gouvernorats de l\'intérieur (Kef, Jendouba, Kasserine, Gafsa).',
src:'INS / Banque mondiale'},
{key:'Accès au réseau d\'eau', littoral:97, interieur:62, unitL:'97 %', unitI:'61-66 %',
descL:'97 % des ménages du Grand Tunis disposaient de l\'eau courante.',
descI:'61 à 66 % seulement dans le Nord-Ouest et le Centre-Ouest.',
src:'Banque mondiale / INS'}
];
(function(){
const svg = d3.select('#ssqSplit');
if(svg.empty()) return;
const W=760, H=540, mid=W/2, padTop=46, rowH=98, barMax=230;
const x = d3.scaleLinear().domain([0,100]).range([0, barMax]);
svg.append('text').attr('x',mid-14).attr('y',26).attr('text-anchor','end')
.attr('font-family','Cormorant Garamond, serif').attr('font-style','italic').attr('font-weight',700)
.attr('font-size',20).attr('fill',C.sky).text('Le littoral');
svg.append('text').attr('x',mid+14).attr('y',26).attr('text-anchor','start')
.attr('font-family','Cormorant Garamond, serif').attr('font-style','italic').attr('font-weight',700)
.attr('font-size',20).attr('fill',C.rust).text('Les régions intérieures');
const info = document.getElementById('splitInfo');
DIMS.forEach((d,i)=>{
const yy = padTop + i*rowH + 28;
svg.append('text').attr('x',mid).attr('y',yy-14).attr('text-anchor','middle')
.attr('font-family','Inter, sans-serif').attr('font-size',12).attr('fill',C.mute)
.attr('letter-spacing','.03em').text(d.key);
const bl = svg.append('rect').attr('class','vs-bar')
.attr('x',mid-7).attr('y',yy).attr('height',24).attr('width',0)
.attr('fill',C.sky).attr('fill-opacity',.85).attr('rx',3).datum({side:'littoral', d});
const bi = svg.append('rect').attr('class','vs-bar')
.attr('x',mid+7).attr('y',yy).attr('height',24).attr('width',0)
.attr('fill',C.rust).attr('fill-opacity',.85).attr('rx',3).datum({side:'interieur', d});
bl.targetW = x(d.littoral); bi.targetW = x(d.interieur);
d._bl = bl; d._bi = bi;
function hov(side){
info.innerHTML = `
${d.key}
${side==='littoral'?'Le littoral':'Les régions intérieures'}
${side==='littoral'?d.unitL:d.unitI}
${side==='littoral'?d.descL:d.descI}
Source : ${d.src}
`;
}
bl.on('mouseenter',()=>hov('littoral')).on('click',()=>hov('littoral'));
bi.on('mouseenter',()=>hov('interieur')).on('click',()=>hov('interieur'));
});
ScrollTrigger.create({
trigger:'#ssqSplit', start:'top 78%',
onEnter:()=>{
DIMS.forEach((d,i)=>{
d._bl.transition().duration(1000).delay(i*100)
.attr('x',()=>mid-7-d._bl.targetW).attr('width',d._bl.targetW);
d._bi.transition().duration(1000).delay(i*100).attr('width',d._bi.targetW);
});
}
});
})();
/* VIZ 02 — MOBILE (cartes empilées avec barres) */
(function(){
const wrap = document.getElementById('ssqSplitMobile');
if(!wrap) return;
let html='';
DIMS.forEach(d=>{
html += `
${d.key}
`;
});
wrap.innerHTML = html;
const obs = new IntersectionObserver((es)=>{
es.forEach(e=>{ if(e.isIntersecting){
wrap.querySelectorAll('.vizm-fill').forEach((f,i)=>{
setTimeout(()=>{ f.style.width = f.dataset.w+'%'; }, i*120);
});
obs.disconnect();
}});
},{threshold:.25});
obs.observe(wrap);
})();
/* ============================================================
VIZ 03 — TROIS PILIERS + ICÔNES ANIMÉES
============================================================ */
const PILLARS = [
{name:'Tourisme', val:34, color:C.sky, icon:'tourism', note:'Recettes touristiques, exposées aux chocs extérieurs.'},
{name:'Sous-traitance industrielle', val:38, color:C.olive, icon:'factory', note:'Destinée au marché européen, fondée sur le faible coût du travail.'},
{name:'Transferts de la diaspora', val:28, color:C.earth, icon:'diaspora', note:'≈ 6,5 % du PIB en 2024 ; une soupape économique.'}
];
/* ============================================================
Icônes médaillon — finition « ultra-premium » : dégradés,
glow, reflets, profondeur. Tout est clippé dans le disque.
============================================================ */
let _iconSeq = 0;
function buildPillarIcon(parent, type, x, y, size, color){
const s = size, R = s*0.5;
const ID = 'ic'+(_iconSeq++);
const g = parent.append('g').attr('transform',`translate(${x},${y})`);
const defs = g.append('defs');
// clip principal
defs.append('clipPath').attr('id',ID+'clip').append('circle').attr('r',R);
// fond médaillon : dégradé radial profond + vignettage
const bg = defs.append('radialGradient').attr('id',ID+'bg').attr('cx','36%').attr('cy','30%').attr('r','80%');
bg.append('stop').attr('offset','0%').attr('stop-color','#21375c');
bg.append('stop').attr('offset','55%').attr('stop-color','#142844');
bg.append('stop').attr('offset','100%').attr('stop-color','#0a1626');
// glow doux réutilisable
const fg = defs.append('filter').attr('id',ID+'glow').attr('x','-60%').attr('y','-60%').attr('width','220%').attr('height','220%');
fg.append('feGaussianBlur').attr('stdDeviation',R*0.05).attr('result','b');
const fm = fg.append('feMerge'); fm.append('feMergeNode').attr('in','b'); fm.append('feMergeNode').attr('in','SourceGraphic');
// dégradé "or" pour accents
const gold = defs.append('linearGradient').attr('id',ID+'gold').attr('x1','0').attr('y1','0').attr('x2','1').attr('y2','1');
gold.append('stop').attr('offset','0%').attr('stop-color','#F4E3A8');
gold.append('stop').attr('offset','50%').attr('stop-color','#E8C969');
gold.append('stop').attr('offset','100%').attr('stop-color','#A8862A');
// dégradé "acier" pour le métal
const steel = defs.append('linearGradient').attr('id',ID+'steel').attr('x1','0').attr('y1','0').attr('x2','0').attr('y2','1');
steel.append('stop').attr('offset','0%').attr('stop-color','#ffffff');
steel.append('stop').attr('offset','45%').attr('stop-color','#e6edf5');
steel.append('stop').attr('offset','100%').attr('stop-color','#aab8c8');
g.append('circle').attr('r',R).attr('fill',`url(#${ID}bg)`);
// vignettage interne
g.append('circle').attr('r',R).attr('fill','none')
.attr('stroke','rgba(0,0,0,0.45)').attr('stroke-width',R*0.16).attr('opacity',.5);
// anneau or + segment lumineux qui orbite
const ring = g.append('g').attr('clip-path',`url(#${ID}clip)`);
ring.append('circle').attr('r',R-2.5).attr('fill','none').attr('stroke',`url(#${ID}gold)`).attr('stroke-width',1.6).attr('opacity',.55);
const spark = ring.append('circle').attr('r',R-2.5).attr('fill','none')
.attr('stroke','#FFF6DD').attr('stroke-width',2.2).attr('stroke-linecap','round')
.attr('stroke-dasharray',`${(R-2.5)*0.5} ${(R-2.5)*5.5}`).attr('filter',`url(#${ID}glow)`);
gsap.to(spark.node(),{rotation:360, transformOrigin:'0px 0px', duration:6, repeat:-1, ease:'none'});
// reflet de verre en haut (statique, donne la profondeur)
const gloss = defs.append('linearGradient').attr('id',ID+'gloss').attr('x1','0').attr('y1','0').attr('x2','0').attr('y2','1');
gloss.append('stop').attr('offset','0%').attr('stop-color','rgba(255,255,255,0.30)');
gloss.append('stop').attr('offset','100%').attr('stop-color','rgba(255,255,255,0)');
// calque contenu
const ic = g.append('g').attr('clip-path',`url(#${ID}clip)`)
.attr('fill','none').attr('stroke','#fff').attr('stroke-width',2.2)
.attr('stroke-linecap','round').attr('stroke-linejoin','round');
/* ---------------- TOURISME ---------------- */
if(type==='tourism'){
// ciel dégradé bas + horizon courbe
ic.append('circle').attr('cx',0).attr('cy',R*1.0).attr('r',R*0.96)
.attr('fill','rgba(232,201,105,0.07)').attr('stroke','rgba(255,255,255,0.22)').attr('stroke-width',1.2);
// soleil avec glow
const sun = ic.append('circle').attr('cx',R*0.46).attr('cy',-R*0.46).attr('r',R*0.15)
.attr('fill',`url(#${ID}gold)`).attr('stroke','none').attr('filter',`url(#${ID}glow)`);
gsap.to(sun.node(),{attr:{r:R*0.18}, duration:2.2, repeat:-1, yoyo:true, ease:'sine.inOut'});
const pathDef = `M ${-R*0.66} ${R*0.38} Q ${-R*0.05} ${-R*0.82} ${R*0.7} ${-R*0.4}`;
const trail = ic.append('path').attr('d',pathDef)
.attr('stroke',`url(#${ID}gold)`).attr('stroke-width',1.8).attr('stroke-dasharray','1.5 5').attr('filter',`url(#${ID}glow)`);
const fp = ic.append('path').attr('d',pathDef).attr('stroke','none').node();
const fpLen = fp.getTotalLength();
// avion stylisé (corps + ailes)
const plane = ic.append('g');
plane.append('path').attr('d',`M ${R*0.22} 0 L ${-R*0.14} ${R*0.07} L ${-R*0.04} 0 L ${-R*0.14} ${-R*0.07} Z`)
.attr('fill',`url(#${ID}steel)`).attr('stroke','none');
plane.append('path').attr('d',`M 0 0 L ${-R*0.1} ${R*0.13} M 0 0 L ${-R*0.1} ${-R*0.13}`)
.attr('stroke','#fff').attr('stroke-width',1.6);
const st={p:0};
gsap.to(st,{p:1, duration:3.6, repeat:-1, ease:'power1.inOut', onUpdate:()=>{
const len=st.p*fpLen, pt=fp.getPointAtLength(Math.min(len,fpLen-.5)),
pt2=fp.getPointAtLength(Math.min(len+1,fpLen));
const ang=Math.atan2(pt2.y-pt.y,pt2.x-pt.x)*180/Math.PI;
plane.attr('transform',`translate(${pt.x},${pt.y}) rotate(${ang}) scale(.78)`);
}});
gsap.set(trail.node(),{attr:{'stroke-dasharray':`${fpLen} ${fpLen}`,'stroke-dashoffset':fpLen}});
gsap.to(trail.node(),{attr:{'stroke-dashoffset':0}, duration:3.6, repeat:-1, ease:'power1.inOut'});
}
/* ---------------- INDUSTRIE ---------------- */
else if(type==='factory'){
function gear(cx,cy,rOut,rIn,teeth,dur,dir,phase){
const gg = ic.append('g').attr('transform',`translate(${cx},${cy}) rotate(${phase||0})`);
let p='';
for(let i=0;i{
const cr=R*0.18, px=big.x+Math.cos(pst.a)*cr, py=big.y+Math.sin(pst.a)*cr,
hx=R*0.48, hy=-R*0.32;
rod.attr('x1',px).attr('y1',py).attr('x2',hx).attr('y2',hy);
head.attr('x',hx-R*0.14).attr('y',hy-R*0.065);
}});
// étincelles discrètes à l'engrènement
const sp = ic.append('circle').attr('r',R*0.03).attr('fill','#FFF0C0').attr('stroke','none').attr('opacity',0).attr('filter',`url(#${ID}glow)`);
gsap.to(sp.node(),{opacity:.9, duration:0.15, repeat:-1, repeatDelay:1.1, yoyo:true, ease:'none',
onRepeat:()=>{ sp.attr('cx',R*0.08+(Math.random()-.5)*R*0.1).attr('cy',R*0.08+(Math.random()-.5)*R*0.1); }});
}
/* ---------------- DIASPORA ---------------- */
else if(type==='diaspora'){
// globe avec léger glow
const globe = ic.append('g');
globe.append('circle').attr('r',R*0.42).attr('stroke','#fff').attr('stroke-width',2.2);
globe.append('circle').attr('r',R*0.42).attr('fill','rgba(107,137,184,0.10)').attr('stroke','none');
[-0.22,0,0.22].forEach(k=>{
const hw=Math.sqrt(Math.max(0,(R*0.42)**2-(R*0.42*k*1.9)**2));
globe.append('line').attr('x1',-hw).attr('y1',R*0.42*k*1.9).attr('x2',hw).attr('y2',R*0.42*k*1.9)
.attr('stroke','rgba(255,255,255,0.4)').attr('stroke-width',1.2);
});
const lonG = globe.append('g').attr('clip-path',`url(#${ID}clip)`);
const lons=[1,0.5,0].map(o=>lonG.append('ellipse').attr('rx',R*0.42*o).attr('ry',R*0.42)
.attr('stroke','rgba(255,255,255,0.5)').attr('stroke-width',1.2).attr('fill','none'));
const rot={t:0};
gsap.to(rot,{t:1, duration:5, repeat:-1, ease:'none', onUpdate:()=>{
lons.forEach((e,i)=>{ const k=Math.cos(((rot.t+i/3)%1)*Math.PI*2);
e.attr('rx',R*0.42*Math.abs(k)).attr('stroke','rgba(255,255,255,'+(0.25+0.4*Math.abs(k))+')'); });
}});
// flux de valeur convergents (or, avec glow), traînée
const flux = ic.append('g').attr('filter',`url(#${ID}glow)`);
for(let i=0;i<5;i++){
const a=i/5*Math.PI*2, dot=flux.append('circle').attr('r',R*0.045).attr('fill',`url(#${ID}gold)`).attr('stroke','none');
const st={p:0};
gsap.to(st,{p:1, duration:2.6, repeat:-1, delay:i*0.5, ease:'power2.in', onUpdate:()=>{
const rr=R*0.8*(1-st.p);
dot.attr('cx',Math.cos(a)*rr).attr('cy',Math.sin(a)*rr)
.attr('opacity', st.p<0.1?st.p/0.1 : (st.p>0.9?(1-st.p)/0.1:1));
}});
}
const core = ic.append('circle').attr('r',R*0.1).attr('fill','#FFF6DD').attr('stroke','none').attr('filter',`url(#${ID}glow)`);
gsap.to(core.node(),{attr:{r:R*0.15}, opacity:.75, duration:0.7, repeat:-1, yoyo:true, ease:'sine.inOut'});
}
// reflet de verre par-dessus tout (profondeur)
g.append('path').attr('clip-path',`url(#${ID}clip)`)
.attr('d',`M ${-R} ${-R*0.2} A ${R} ${R} 0 0 1 ${R} ${-R*0.2} Q 0 ${-R*0.05} ${-R} ${-R*0.2} Z`)
.attr('fill',`url(#${ID}gloss)`).attr('opacity',.5);
return g;
}
(function(){
const svg = d3.select('#ssqPillars');
if(svg.empty()) return;
const W=1000, H=460;
const defs = svg.append('defs');
function grad(id,c1,c2){
const g=defs.append('linearGradient').attr('id',id).attr('x1','0').attr('y1','0').attr('x2','0').attr('y2','1');
g.append('stop').attr('offset','0%').attr('stop-color',c2);
g.append('stop').attr('offset','100%').attr('stop-color',c1);
}
grad('pg0', C.sky, C.skySoft); grad('pg1', C.olive, C.oliveSoft); grad('pg2', C.earth, C.earthSoft);
const f=defs.append('filter').attr('id','pSoft').attr('x','-30%').attr('y','-30%').attr('width','160%').attr('height','160%');
f.append('feDropShadow').attr('dx',0).attr('dy',6).attr('stdDeviation',8).attr('flood-color','rgba(21,25,31,.18)');
const fills=['url(#pg0)','url(#pg1)','url(#pg2)'];
const cx = [W*0.2, W*0.5, W*0.8];
const baseY = 360, colW = 150, maxH = 250;
const x = d3.scaleLinear().domain([0,40]).range([0,maxH]);
svg.append('rect').attr('x',60).attr('y',baseY).attr('width',W-120).attr('height',12).attr('fill',C.paper2).attr('stroke',C.line);
svg.append('rect').attr('x',40).attr('y',baseY+12).attr('width',W-80).attr('height',12).attr('fill',C.sand).attr('stroke',C.line);
svg.append('text').attr('x',W/2).attr('y',baseY+48).attr('text-anchor','middle')
.attr('font-family','Cormorant Garamond, serif').attr('font-style','italic')
.attr('font-size',16).attr('fill',C.mute)
.text('Un socle de stabilité macroéconomique sans transformation productive');
PILLARS.forEach((p,i)=>{
const colX = cx[i]-colW/2;
const col = svg.append('rect').attr('filter','url(#pSoft)')
.attr('x',colX).attr('width',colW).attr('y',baseY).attr('height',0)
.attr('fill',fills[i]).attr('stroke',C.paper).attr('stroke-width',2).attr('rx',3);
col.append('title').text(p.note);
const cap = svg.append('rect').attr('x',colX-10).attr('width',colW+20).attr('height',12)
.attr('y',baseY).attr('fill',p.color).attr('opacity',0).attr('rx',2);
svg.append('text').attr('x',cx[i]).attr('y',baseY+72).attr('text-anchor','middle')
.attr('font-family','Cormorant Garamond, serif').attr('font-style','italic').attr('font-weight',600)
.attr('font-size',16).attr('fill',C.ink)
.each(function(){
const words=p.name.split(' '); const sel=d3.select(this);
if(words.length>1){ sel.append('tspan').attr('x',cx[i]).attr('dy',0).text(words[0]);
sel.append('tspan').attr('x',cx[i]).attr('dy','1.1em').text(words.slice(1).join(' ')); }
else sel.text(p.name);
});
p._col=col; p._cap=cap; p._cx=cx[i];
});
let iconsBuilt=false;
ScrollTrigger.create({
trigger:'#ssqPillars', start:'top 80%',
onEnter:()=>{
PILLARS.forEach((p,i)=>{
const h=x(p.val);
p._col.transition().duration(1000).delay(i*150+100).attr('y',baseY-h).attr('height',h);
p._cap.transition().duration(600).delay(i*150+900).attr('y',baseY-h-12).attr('opacity',.95);
// icône animée placée en haut de la colonne
setTimeout(()=>{
if(p._iconG) return;
// icône médaillon clippée, posée au sommet de la colonne
p._iconG = buildPillarIcon(svg, p.icon, p._cx, baseY-h+46, 78, p.color)
.attr('opacity',0);
p._iconG.transition().duration(500).attr('opacity',1);
}, i*150+1100);
});
}
});
})();
/* VIZ 03 — MOBILE (barres horizontales + mini-icônes animées) */
function buildMiniIcon(type, color){
// petites icônes SVG animées via CSS/SMIL inline
if(type==='tourism'){
return ``;
}
if(type==='factory'){
return ``;
}
// diaspora
return ``;
}
(function(){
const wrap = document.getElementById('ssqPillarsMobile');
if(!wrap) return;
const max=40;
let html='';
PILLARS.forEach(p=>{
html += `Littoral
${d.unitL}
Intérieur
${d.unitI}
pilier
${v.label}
${v.desc}
↳ alimente : ${feeds}
${i+1}
${s.name}
${s.leaves.map(l=>`${l}`).join('')}