/* JewelLink EDU — shared primitives for the Parent App UI kit Exposes: Icon, Avatar, Pill, color tokens. */ function jlPascal(name){ return name.split(/[-_ ]/).map(s => s.charAt(0).toUpperCase()+s.slice(1)).join(''); } /* Lucide icon → inline SVG, built imperatively so React never fights it. */ function Icon({ name, size = 20, color = 'currentColor', stroke = 2, fill = 'none', style }) { const ref = React.useRef(null); React.useEffect(() => { const node = window.lucide && window.lucide.icons && window.lucide.icons[jlPascal(name)]; const host = ref.current; if (!host || !node) return; const ns = 'http://www.w3.org/2000/svg'; const svg = document.createElementNS(ns, 'svg'); svg.setAttribute('viewBox', '0 0 24 24'); svg.setAttribute('width', size); svg.setAttribute('height', size); svg.setAttribute('fill', fill); svg.setAttribute('stroke', color); svg.setAttribute('stroke-width', stroke); svg.setAttribute('stroke-linecap', 'round'); svg.setAttribute('stroke-linejoin', 'round'); node.forEach(([tag, attrs]) => { const child = document.createElementNS(ns, tag); for (const k in attrs) child.setAttribute(k, attrs[k]); svg.appendChild(child); }); host.innerHTML = ''; host.appendChild(svg); }, [name, size, color, stroke, fill]); return ; } /* Initials avatar — avoids shipping real faces, fully reusable. */ const JL_AV_COLORS = { blue:'var(--blue-500)', navy:'var(--navy-600)', gold:'var(--gold-500)', green:'var(--success-500)', red:'var(--danger-500)', cyan:'var(--cyan-500)', }; function Avatar({ initials, color = 'blue', size = 42, ring = false }) { const bg = JL_AV_COLORS[color] || color; return (