/* global React */ /* * LunaMascot — SVG mascot * props: * state: 'idle' | 'happy' | 'sleep' | 'think' | 'angry' | 'focus' * look: 'center' | 'left' | 'right' * size: number (px width) * talk: bool — adds a subtle scale pulse */ const { useEffect, useState, useRef } = React; function LunaMascot({ state = 'idle', look = 'center', size = 280, talk = false, idleAnim = true }) { // built-in idle look cycle (frontal → left → right) so the mascot feels alive const [autoLook, setAutoLook] = useState('center'); useEffect(() => { if (!idleAnim) return; const sequence = ['center', 'center', 'left', 'center', 'right', 'center']; let i = 0; const t = setInterval(() => { setAutoLook(sequence[i % sequence.length]); i += 1; }, 1600); return () => clearInterval(t); }, [idleAnim]); const lookDir = look === 'center' && idleAnim ? autoLook : look; const lookX = lookDir === 'left' ? -4 : lookDir === 'right' ? 4 : 0; const lookY = state === 'think' ? -3 : 0; // pupil scaling for blink/sleep const eyeOpen = state !== 'sleep'; // mouth path varies with state let mouth = null; if (state === 'happy') { mouth = ; } else if (state === 'angry') { mouth = ; } else if (state === 'think') { mouth = ; } else if (state === 'sleep') { mouth = ; } // eyebrows for emotional states let brows = null; if (state === 'angry') { brows = ( ); } else if (state === 'think') { brows = ( ); } // accessory above (zZ, ?, !) let accessory = null; if (state === 'sleep') { accessory = ( z Z ); } else if (state === 'think') { accessory = ( ? ? ? ); } else if (state === 'angry') { accessory = ( ! ! ! ); } else if (state === 'happy') { accessory = ( ); } const pulseClass = talk ? 'luna-talk' : ''; return ( {/* ears (back) */} {/* main head body */} {/* shine highlight */} {/* eyes */} {/* left eye */} {eyeOpen ? ( ) : ( )} {eyeOpen && } {/* right eye */} {eyeOpen ? ( ) : ( )} {eyeOpen && } {brows} {mouth} {/* bottom shading */} {accessory} ); } window.LunaMascot = LunaMascot;