/* cursor.js : Curseur custom (desktop uniquement)
YBCursor.init();
Au repos : point accent. Sur un lien / bouton : le point s'étire en index et la main apparaît autour
(le bout du doigt reste sur la position de la souris). data-cursor-label="VOIR LE PROJET" = label contextuel.
La main change selon la fête quand c'est cohérent (html[data-season]), relue à chaque événement yb:theme.
Dans la roue des projets et articles (.cp-wheel) : le point devient une loupe, elle aussi accordée à la fête. */
window.YBCursor = (function () {
let wrap, dot, hand, loupe, lbl, curSeason = null, override = null;
const m = { x: -100, y: -100, cx: -100, cy: -100 };
/* Silhouette commune : main qui pointe, bout de l'index en (11.5, 1) */
const SHAPE = 'M11.5 1C13.2 1 14.5 2.3 14.5 4v9.2c.5-.7 1.4-1.1 2.4-1.1 1.3 0 2.4.8 2.7 1.9.5-.5 1.3-.8 2.2-.8 1.5 0 2.7 1.1 2.8 2.5.5-.4 1.2-.6 1.9-.6 1.5 0 2.7 1.2 2.7 2.7V25c0 5.5-4 9.5-9.5 9.5h-2.3c-3.4 0-5.8-1.3-7.8-3.9l-5.4-7.1c-.9-1.2-.7-2.9.5-3.8 1.2-.9 2.8-.7 3.7.4l.1.1V4c0-1.7 1.3-3 3-3z';
const CREASES = 'M14.5 13.2V19M19.6 14v5.5M24.6 15.7V20';
const base = (fill, stroke, extra, crease) =>
'' +
'' + (extra || '');
const HANDS = {
'': () => base('var(--lime)', 'var(--bg)'),
// gant squelette : os blancs sur gant noir
halloween: () => base('#15101a', 'var(--lime)',
'', '#efe6d2'),
// gant rouge et revers en fourrure blanche
noel: () => base('#d61f35', '#5a0b16',
'', '#8e1222'),
// gant vert, trèfle doré sur le dos de la main
'st-patrick': () => base('#1b8f4c', '#0b3d21',
''),
// gant blanc de cérémonie, revers doré, étincelle au bout du doigt
'nouvel-an': () => base('#ffffff', '#16141c',
'', '#9aa0b0'),
// gant de fête : turquoise, petite bague dorée
anniversaire: () => base('var(--lime)', 'var(--bg)',
'')
};
/* Loupe : centre de la lentille en (14, 14) = position de la souris ; manche dessiné à plat puis tourné à 45° */
const lens = (ring, glass, handle, extra, inside) =>
'' + (extra || '') + '' +
'' + (inside || '') +
'' +
'';
const stripes = (cols, w) => cols.map((c, i) => '').join('');
const LOUPES = {
'': () => lens('var(--lime)', 'rgba(255,255,255,.08)', 'var(--ink)'),
// toile d'araignée dans le verre, manche en bois sombre
halloween: () => lens('var(--lime)', 'rgba(255,123,28,.1)', '#9a6a3a', '',
''),
// manche en sucre d'orge, cerclage doré
noel: () => lens('#f2c14e', 'rgba(255,255,255,.1)', '#fdfbf6', stripes(['#d61f35', 'transparent', '#d61f35', 'transparent', '#d61f35', 'transparent', '#d61f35'], 2)),
'st-patrick': () => lens('#f2c14e', 'rgba(61,220,132,.12)', '#1b8f4c', '', ''),
'nouvel-an': () => lens('#f2c14e', 'rgba(242,193,78,.1)', '#d9dde8', stripes(['#f2c14e', '#f2c14e'], 2), ''),
juillet: () => lens('#ffffff', 'rgba(255,255,255,.08)', '#ffffff', stripes(['#1f3fa8', '#1f3fa8', '#ffffff', '#ffffff', '#c8102e', '#c8102e', '#c8102e'], 2)),
'black-friday': () => lens('#ffd400', 'rgba(255,212,0,.1)', '#0b0b0b', stripes(['#ffd400', '#0b0b0b', '#ffd400', '#0b0b0b', '#ffd400', '#0b0b0b', '#ffd400'], 2)),
anniversaire: () => lens('var(--lime)', 'rgba(46,230,197,.1)', '#ffffff', stripes(['#2ee6c5', '#ffffff', '#ff6b81', '#ffffff', '#ffcf4a', '#ffffff', '#2ee6c5'], 2)),
poisson: () => lens('#4fd1ff', 'rgba(79,209,255,.12)', '#ffb347', '', '')
};
function paint() {
const s = override !== null ? override : document.documentElement.getAttribute('data-season') || '';
const k = HANDS[s] ? s : '';
// aperçu (roue des thèmes) : la main prend aussi l'accent de la fête survolée
const acc = override !== null && override ? (((window.YB_SEASONS || {}).list || {})[override] || {}).accent || '' : '';
[hand, loupe].forEach(e => acc ? e.style.setProperty('--lime', acc) : e.style.removeProperty('--lime'));
const key = s + '|' + k;
if (key === curSeason) return;
curSeason = key;
hand.innerHTML = '';
loupe.innerHTML = '';
}
function setState(label, link, inWheel) {
lbl.textContent = label || '';
lbl.style.opacity = label ? '1' : '0';
lbl.style.transform = label ? 'scale(1)' : 'scale(.6)';
wrap.classList.toggle('is-loupe', !!inWheel);
wrap.classList.toggle('is-hand', !!link && !inWheel);
}
function loop() {
m.cx += (m.x - m.cx) * 0.25; m.cy += (m.y - m.cy) * 0.25;
wrap.style.transform = 'translate(' + m.cx + 'px,' + m.cy + 'px)';
requestAnimationFrame(loop);
}
return {
/* Montre la main d'une fête sans l'activer ('' = main normale, null = revenir à la fête affichée) */
preview(key) { if (!hand) return; override = key; curSeason = null; paint(); },
init() {
// Animations inconditionnelles : seul ?ybstatic=1 (html.yb-static) coupe le curseur
if (matchMedia('(pointer: coarse)').matches || document.documentElement.classList.contains('yb-static')) return;
document.documentElement.classList.add('has-custom-cursor'); // CSS : .has-custom-cursor, .has-custom-cursor * { cursor:none }
wrap = document.createElement('div'); wrap.className = 'yb-cursor';
dot = document.createElement('div'); dot.className = 'yb-cursor__dot';
hand = document.createElement('div'); hand.className = 'yb-cursor__hand';
loupe = document.createElement('div'); loupe.className = 'yb-cursor__loupe';
lbl = document.createElement('div'); lbl.className = 'yb-cursor__label';
wrap.append(dot, hand, loupe, lbl); document.body.appendChild(wrap);
paint(); addEventListener('yb:theme', paint);
addEventListener('mousemove', e => { m.x = e.clientX; m.y = e.clientY; });
document.addEventListener('mouseover', e => {
const w = e.target.closest('.cp-wheel');
const t = e.target.closest('[data-cursor-label], a, button, [data-cursor]');
if (!t) return setState('', false, w);
setState(t.getAttribute('data-cursor-label') || '', true, w);
});
requestAnimationFrame(loop);
}
};
})();