// ============================================================
// WASIED · Primitives (extends Beggin DS)
// ============================================================

const { useState, useEffect, useRef, useMemo, useCallback, createContext, useContext } = React;

// ---------- Icon set (Lucide-style, 1.5px stroke) ----------
const ICONS = {
  // base set from Beggin
  close: '<path d="M18 6 6 18"/><path d="m6 6 12 12"/>',
  search: '<circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/>',
  chevron: '<path d="m9 18 6-6-6-6"/>',
  chevronDown: '<path d="m6 9 6 6 6-6"/>',
  chevronUp: '<path d="m18 15-6-6-6 6"/>',
  chevronLeft: '<path d="m15 18-6-6 6-6"/>',
  arrow: '<path d="M5 12h14"/><path d="m12 5 7 7-7 7"/>',
  arrowDown: '<path d="M12 5v14"/><path d="m19 12-7 7-7-7"/>',
  arrowUp: '<path d="M12 19V5"/><path d="m5 12 7-7 7 7"/>',
  arrowLeft: '<path d="M19 12H5"/><path d="m12 19-7-7 7-7"/>',
  // nav
  dashboard: '<rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/>',
  server: '<rect x="2" y="3" width="20" height="8" rx="2"/><rect x="2" y="13" width="20" height="8" rx="2"/><line x1="6" y1="7" x2="6.01" y2="7"/><line x1="6" y1="17" x2="6.01" y2="17"/>',
  package: '<line x1="16.5" y1="9.4" x2="7.5" y2="4.21"/><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/>',
  graduation: '<path d="M22 10v6M2 10l10-5 10 5-10 5z"/><path d="M6 12v5c3 3 9 3 12 0v-5"/>',
  award: '<circle cx="12" cy="8" r="6"/><path d="M15.477 12.89 17 22l-5-3-5 3 1.523-9.11"/>',
  cube: '<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/>',
  shoppingBag: '<path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/><line x1="3" y1="6" x2="21" y2="6"/><path d="M16 10a4 4 0 0 1-8 0"/>',
  users: '<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>',
  user: '<circle cx="12" cy="8" r="5"/><path d="M20 21a8 8 0 0 0-16 0"/>',
  terminal: '<polyline points="4 17 10 11 4 5"/><line x1="12" x2="20" y1="19" y2="19"/>',
  code: '<polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/>',
  key: '<circle cx="7.5" cy="15.5" r="5.5"/><path d="m21 2-9.6 9.6"/><path d="m15.5 7.5 3 3L22 7l-3-3"/>',
  logs: '<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="9" x2="15" y1="13" y2="13"/><line x1="9" x2="15" y1="17" y2="17"/>',
  shield: '<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>',
  shieldCheck: '<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><path d="m9 12 2 2 4-4"/>',
  eye: '<path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7z"/><circle cx="12" cy="12" r="3"/>',
  edit: '<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>',
  play: '<polygon points="6 4 20 12 6 20 6 4"/>',
  pause: '<rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/>',
  stop: '<rect x="5" y="5" width="14" height="14" rx="2"/>',
  refresh: '<path d="M3 12a9 9 0 0 1 15-6.7L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-15 6.7L3 16"/><path d="M3 21v-5h5"/>',
  download: '<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" x2="12" y1="15" y2="3"/>',
  upload: '<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" x2="12" y1="3" y2="15"/>',
  check: '<polyline points="20 6 9 17 4 12"/>',
  checkCircle: '<circle cx="12" cy="12" r="10"/><polyline points="9 11 12 14 17 9"/>',
  x: '<path d="M18 6 6 18"/><path d="m6 6 12 12"/>',
  plus: '<line x1="12" x2="12" y1="5" y2="19"/><line x1="5" x2="19" y1="12" y2="12"/>',
  minus: '<line x1="5" x2="19" y1="12" y2="12"/>',
  alert: '<path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" x2="12" y1="9" y2="13"/><line x1="12" x2="12.01" y1="17" y2="17"/>',
  info: '<circle cx="12" cy="12" r="10"/><line x1="12" x2="12" y1="16" y2="12"/><line x1="12" x2="12.01" y1="8" y2="8"/>',
  zap: '<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>',
  spark: '<path d="M12 3v3"/><path d="M12 18v3"/><path d="m5.6 5.6 2.1 2.1"/><path d="m16.3 16.3 2.1 2.1"/><path d="M3 12h3"/><path d="M18 12h3"/><path d="m5.6 18.4 2.1-2.1"/><path d="m16.3 7.7 2.1-2.1"/>',
  globe: '<circle cx="12" cy="12" r="10"/><line x1="2" x2="22" y1="12" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/>',
  link: '<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>',
  external: '<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" x2="21" y1="14" y2="3"/>',
  copy: '<rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>',
  bell: '<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/>',
  star: '<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>',
  heart: '<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/>',
  flame: '<path d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z"/>',
  message: '<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>',
  chat: '<path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/>',
  ticket: '<path d="M3 7v2a3 3 0 0 1 0 6v2a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-2a3 3 0 0 1 0-6V7a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2"/><path d="M13 5v2"/><path d="M13 17v2"/><path d="M13 11v2"/>',
  credit: '<rect x="2" y="5" width="20" height="14" rx="2"/><line x1="2" y1="10" x2="22" y2="10"/>',
  euro: '<path d="M4 10h12"/><path d="M4 14h9"/><path d="M19 6a7.7 7.7 0 0 0-5.2-2A7.9 7.9 0 0 0 6 12c0 4.4 3.5 8 7.8 8 2 0 3.8-.7 5.2-2"/>',
  receipt: '<path d="M4 2v20l2-1 2 1 2-1 2 1 2-1 2 1 2-1 2 1V2l-2 1-2-1-2 1-2-1-2 1-2-1-2 1Z"/><path d="M16 8h-6a2 2 0 1 0 0 4h4a2 2 0 1 1 0 4H8"/><path d="M12 17.5v-11"/>',
  clock: '<circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>',
  calendar: '<rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" x2="16" y1="2" y2="6"/><line x1="8" x2="8" y1="2" y2="6"/><line x1="3" x2="21" y1="10" y2="10"/>',
  filter: '<polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/>',
  sort: '<path d="m3 16 4 4 4-4"/><path d="M7 20V4"/><path d="m21 8-4-4-4 4"/><path d="M17 4v16"/>',
  layers: '<polygon points="12 2 2 7 12 12 22 7 12 2"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/>',
  trending: '<polyline points="22 7 13.5 15.5 8.5 10.5 2 17"/><polyline points="16 7 22 7 22 13"/>',
  chart: '<line x1="18" x2="18" y1="20" y2="10"/><line x1="12" x2="12" y1="20" y2="4"/><line x1="6" x2="6" y1="20" y2="14"/>',
  pie: '<path d="M21.21 15.89A10 10 0 1 1 8 2.83"/><path d="M22 12A10 10 0 0 0 12 2v10z"/>',
  cpu: '<rect x="4" y="4" width="16" height="16" rx="2"/><rect x="9" y="9" width="6" height="6"/><line x1="9" y1="2" x2="9" y2="4"/><line x1="15" y1="2" x2="15" y2="4"/><line x1="9" y1="20" x2="9" y2="22"/><line x1="15" y1="20" x2="15" y2="22"/><line x1="20" y1="9" x2="22" y2="9"/><line x1="20" y1="15" x2="22" y2="15"/><line x1="2" y1="9" x2="4" y2="9"/><line x1="2" y1="15" x2="4" y2="15"/>',
  hard: '<line x1="22" y1="12" x2="2" y2="12"/><path d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"/><line x1="6" y1="16" x2="6.01" y2="16"/><line x1="10" y1="16" x2="10.01" y2="16"/>',
  database: '<ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M3 5v14a9 3 0 0 0 18 0V5"/><path d="M3 12a9 3 0 0 0 18 0"/>',
  cloud: '<path d="M17.5 19a4.5 4.5 0 1 0 0-9 5.5 5.5 0 0 0-10.7-1.6A4 4 0 0 0 7 19h10.5z"/>',
  settings: '<circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/>',
  menu: '<line x1="3" x2="21" y1="6" y2="6"/><line x1="3" x2="21" y1="12" y2="12"/><line x1="3" x2="21" y1="18" y2="18"/>',
  more: '<circle cx="5" cy="12" r="1"/><circle cx="12" cy="12" r="1"/><circle cx="19" cy="12" r="1"/>',
  logout: '<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" x2="9" y1="12" y2="12"/>',
  discord: '<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028 14.09 14.09 0 0 0 1.226-1.994.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.331c-1.183 0-2.157-1.085-2.157-2.42 0-1.333.956-2.418 2.157-2.418 1.21 0 2.176 1.094 2.157 2.42 0 1.334-.956 2.418-2.157 2.418zm7.974 0c-1.183 0-2.157-1.085-2.157-2.42 0-1.333.956-2.418 2.157-2.418 1.21 0 2.176 1.094 2.157 2.42 0 1.334-.946 2.418-2.157 2.418z"/>',
  github: '<path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/>',
  twitter: '<path d="M22 4s-.7 2.1-2 3.4c1.6 10-9.4 17.3-18 11.6 2.2.1 4.4-.6 6-2C3 15.5.5 9.6 3 5c2.2 2.6 5.6 4.1 9 4-.9-4.2 4-6.6 7-3.8 1.1 0 3-1.2 3-1.2z"/>',
  youtube: '<path d="M22.54 6.42a2.78 2.78 0 0 0-1.94-2C18.88 4 12 4 12 4s-6.88 0-8.6.46a2.78 2.78 0 0 0-1.94 2A29 29 0 0 0 1 11.75a29 29 0 0 0 .46 5.33A2.78 2.78 0 0 0 3.4 19c1.72.46 8.6.46 8.6.46s6.88 0 8.6-.46a2.78 2.78 0 0 0 1.94-2 29 29 0 0 0 .46-5.25 29 29 0 0 0-.46-5.33z"/><polygon points="9.75 15.02 15.5 11.75 9.75 8.48 9.75 15.02"/>',
  rss: '<path d="M4 11a9 9 0 0 1 9 9"/><path d="M4 4a16 16 0 0 1 16 16"/><circle cx="5" cy="19" r="1"/>',
  mail: '<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/>',
  trash: '<polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>',
  briefcase: '<rect x="2" y="7" width="20" height="14" rx="2"/><path d="M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16"/>',
  rocket: '<path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z"/><path d="M12 15l-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z"/><path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0"/><path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5"/>',
  book: '<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/>',
  music: '<path d="M9 18V5l12-2v13"/><circle cx="6" cy="18" r="3"/><circle cx="18" cy="16" r="3"/>',
  video: '<polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/>',
  map: '<polygon points="1 6 1 22 8 18 16 22 23 18 23 2 16 6 8 2 1 6"/><line x1="8" x2="8" y1="2" y2="18"/><line x1="16" x2="16" y1="6" y2="22"/>',
  flag: '<path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z"/><line x1="4" y1="22" x2="4" y2="15"/>',
  trophy: '<path d="M6 9H4.5a2.5 2.5 0 0 1 0-5H6"/><path d="M18 9h1.5a2.5 2.5 0 0 0 0-5H18"/><path d="M4 22h16"/><path d="M10 14.66V17c0 .55.47.98.97 1.21C12.15 18.75 13 20.24 13 22"/><path d="M14 14.66V17c0 .55-.47.98-.97 1.21C11.85 18.75 11 20.24 11 22"/><path d="M18 2H6v7a6 6 0 0 0 12 0V2Z"/>',
  hashtag: '<line x1="4" x2="20" y1="9" y2="9"/><line x1="4" x2="20" y1="15" y2="15"/><line x1="10" x2="8" y1="3" y2="21"/><line x1="16" x2="14" y1="3" y2="21"/>',
  send: '<line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/>',
  paperclip: '<path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"/>',
  fileText: '<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" x2="8" y1="13" y2="13"/><line x1="16" x2="8" y1="17" y2="17"/><polyline points="10 9 9 9 8 9"/>',
  folder: '<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/>',
  lock: '<rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>',
  unlock: '<rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 9.9-1"/>',
  helpCircle: '<circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/>',
  sun: '<circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>',
  moon: '<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>',
  history: '<path d="M3 12a9 9 0 1 0 9-9 9.74 9.74 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/><path d="M12 7v5l4 2"/>',
  webhook: '<path d="M18 16.98h-5.99c-1.1 0-1.95.94-2.48 1.9A4 4 0 0 1 2 17c.01-.7.2-1.4.57-2"/><path d="m6 17 3.13-5.78c.53-.97.1-2.18-.5-3.1a4 4 0 1 1 6.89-4.06"/><path d="m12 6 3.13 5.73C15.66 12.7 16.9 13 18 13a4 4 0 0 1 0 8"/>',
  api: '<rect x="3" y="3" width="7" height="9" rx="1"/><rect x="14" y="3" width="7" height="5" rx="1"/><rect x="14" y="12" width="7" height="9" rx="1"/><rect x="3" y="16" width="7" height="5" rx="1"/>',
  pulse: '<polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/>',
};

function Icon({ name, size = 18, color = 'currentColor', style, className }) {
  const lu = (typeof window !== 'undefined') && window.lucide && window.lucide.icons;
  const pascal = LUCIDE_MAP[name];
  const node = pascal && lu && lu[pascal];
  if (node && Array.isArray(node[2])) {
    return (
      <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" className={className} style={{ flexShrink: 0, ...style }}>
        {node[2].map((child, i) => React.createElement(child[0], { key: i, ...child[1] }))}
      </svg>
    );
  }
  // fallback to local path set (discord, or anything unmapped)
  const svg = ICONS[name] || ICONS.info;
  const isFill = name === 'discord';
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill={isFill ? 'currentColor' : 'none'} stroke={isFill ? 'none' : color} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" className={className} style={{ flexShrink: 0, ...style }} dangerouslySetInnerHTML={{ __html: svg }}/>
  );
}

// Map internal icon names → Lucide PascalCase (v0.460)
const LUCIDE_MAP = {
  close: 'X', search: 'Search', chevron: 'ChevronRight', chevronDown: 'ChevronDown',
  chevronUp: 'ChevronUp', chevronLeft: 'ChevronLeft', arrow: 'ArrowRight', arrowDown: 'ArrowDown',
  arrowUp: 'ArrowUp', arrowLeft: 'ArrowLeft', dashboard: 'LayoutGrid', server: 'Server',
  package: 'Package', graduation: 'GraduationCap', award: 'Award', cube: 'Box',
  shoppingBag: 'ShoppingBag', users: 'Users', user: 'User', terminal: 'SquareTerminal',
  code: 'Code', key: 'Key', logs: 'ScrollText', shield: 'Shield', shieldCheck: 'ShieldCheck',
  eye: 'Eye', edit: 'SquarePen', play: 'Play', pause: 'Pause', stop: 'Square',
  refresh: 'RefreshCw', download: 'Download', upload: 'Upload', check: 'Check',
  checkCircle: 'CircleCheck', x: 'X', plus: 'Plus', minus: 'Minus', alert: 'TriangleAlert',
  info: 'Info', zap: 'Zap', spark: 'Sparkles', globe: 'Globe', link: 'Link',
  external: 'ExternalLink', copy: 'Copy', bell: 'Bell', star: 'Star', heart: 'Heart',
  flame: 'Flame', message: 'MessageSquare', chat: 'MessageCircle', ticket: 'Ticket',
  credit: 'CreditCard', euro: 'Euro', receipt: 'Receipt', clock: 'Clock', calendar: 'Calendar',
  filter: 'ListFilter', sort: 'ArrowUpDown', layers: 'Layers', trending: 'TrendingUp',
  chart: 'ChartColumn', pie: 'ChartPie', cpu: 'Cpu', hard: 'HardDrive', database: 'Database',
  cloud: 'Cloud', settings: 'Settings', menu: 'Menu', more: 'Ellipsis', logout: 'LogOut',
  github: 'Github', twitter: 'Twitter', youtube: 'Youtube', rss: 'Rss', mail: 'Mail',
  trash: 'Trash2', briefcase: 'Briefcase', rocket: 'Rocket', book: 'BookOpen', music: 'Music',
  video: 'Video', map: 'Map', flag: 'Flag', trophy: 'Trophy', hashtag: 'Hash', send: 'Send',
  paperclip: 'Paperclip', fileText: 'FileText', folder: 'Folder', lock: 'Lock', unlock: 'LockOpen',
  helpCircle: 'CircleHelp', sun: 'Sun', moon: 'Moon', history: 'History', webhook: 'Webhook',
  api: 'Braces', pulse: 'Activity',
  // discord intentionally omitted → uses local fill path
};

// ---------- Button ----------
function Button(allProps) {
  const variant = allProps.variant || 'ghost';
  const size = allProps.size || 'md';
  const children = allProps.children;
  const onClick = allProps.onClick;
  const style = allProps.style;
  const icon = allProps.icon;
  const iconRight = allProps.iconRight;
  const disabled = allProps.disabled;
  const href = allProps.href;
  const type = allProps.type;
  const fullWidth = allProps.fullWidth;
  const variants = {
    primary:   { bg: 'var(--fg-1)', fg: 'var(--fg-inv)', bd: 'var(--fg-1)', hbg: '#fff' },
    accent:    { bg: 'var(--accent)', fg: 'var(--fg-inv)', bd: 'var(--accent)', hbg: 'var(--blue-300)' },
    secondary: { bg: 'var(--surface-2)', fg: 'var(--fg-1)', bd: 'var(--border-2)', hbg: 'var(--surface-3)' },
    ghost:     { bg: 'transparent', fg: 'var(--fg-2)', bd: 'var(--border-1)', hbg: 'var(--surface-2)' },
    bare:      { bg: 'transparent', fg: 'var(--fg-2)', bd: 'transparent', hbg: 'var(--surface-2)' },
    danger:    { bg: 'var(--hp)', fg: '#fff', bd: 'var(--hp)', hbg: 'var(--hp)' },
    success:   { bg: 'var(--cash)', fg: 'var(--fg-inv)', bd: 'var(--cash)', hbg: 'var(--cash)' },
    discord:   { bg: '#5865F2', fg: '#fff', bd: '#5865F2', hbg: '#4752C4' },
  };
  const sizes = {
    xs: { p: '4px 8px', fs: 11, h: 22 },
    sm: { p: '6px 10px', fs: 12, h: 28 },
    md: { p: '8px 14px', fs: 13, h: 34 },
    lg: { p: '11px 20px', fs: 14, h: 42 },
    xl: { p: '14px 26px', fs: 15, h: 50 },
  };
  const v = variants[variant];
  const sz = sizes[size];
  const [hover, setHover] = useState(false);
  const tag = href ? 'a' : 'button';
  const props = {
    onClick: (e) => { if (!disabled && onClick) onClick(e); },
    onMouseEnter: () => setHover(true),
    onMouseLeave: () => setHover(false),
    disabled,
    type,
    ...(href ? { href } : {}),
    style: {
      background: hover && !disabled ? v.hbg : v.bg,
      color: v.fg, border: `1px solid ${v.bd}`,
      padding: sz.p, fontFamily: 'var(--font-body)', fontWeight: 500, fontSize: sz.fs,
      cursor: disabled ? 'not-allowed' : 'pointer', opacity: disabled ? 0.4 : 1,
      borderRadius: 8, transition: 'all 120ms ease',
      display: fullWidth ? 'flex' : 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 6,
      width: fullWidth ? '100%' : 'auto',
      whiteSpace: 'nowrap',
      textDecoration: 'none',
      ...style,
    },
  };
  return React.createElement(tag, props,
    icon && <Icon name={icon} size={sz.fs + 2}/>,
    children,
    iconRight && <Icon name={iconRight} size={sz.fs + 2}/>
  );
}

// ---------- Panel ----------
function Panel({ children, style, onClick, onMouseEnter, onMouseLeave, hoverable, className }) {
  const [hover, setHover] = useState(false);
  return (
    <div className={className}
      onClick={onClick}
      onMouseEnter={(e) => { setHover(true); onMouseEnter && onMouseEnter(e); }}
      onMouseLeave={(e) => { setHover(false); onMouseLeave && onMouseLeave(e); }}
      style={{
        background: 'var(--surface-1)',
        border: `1px solid ${hover && hoverable ? 'var(--border-2)' : 'var(--border-1)'}`,
        borderRadius: 10,
        transition: 'border-color 200ms ease',
        ...style,
      }}>{children}</div>
  );
}

// ---------- Badge ----------
function Badge({ tone = 'default', children, dot, icon, style, size = 'md' }) {
  const tones = {
    default: { bg: 'var(--surface-2)', fg: 'var(--fg-2)' },
    subtle:  { bg: 'rgba(255,255,255,0.04)', fg: 'var(--fg-3)' },
    brand:   { bg: 'rgba(143,168,255,0.10)', fg: 'var(--accent)' },
    hosting: { bg: 'rgba(103,232,249,0.10)', fg: 'var(--service-hosting)' },
    orders:  { bg: 'rgba(110,231,167,0.10)', fg: 'var(--service-orders)' },
    formations: { bg: 'rgba(196,181,253,0.10)', fg: 'var(--service-formations)' },
    addons:  { bg: 'rgba(251,191,36,0.10)', fg: 'var(--service-addons)' },
    cash:    { bg: 'rgba(110,231,167,0.08)', fg: 'var(--cash)' },
    hp:      { bg: 'rgba(251,113,133,0.08)', fg: 'var(--hp)' },
    xp:      { bg: 'rgba(251,191,36,0.08)', fg: 'var(--xp)' },
    vip:     { bg: 'rgba(196,181,253,0.08)', fg: 'var(--vip)' },
  };
  const t = tones[tone] || tones.default;
  const sizes = {
    sm: { p: '1px 6px', fs: 10, gap: 4 },
    md: { p: '2px 8px', fs: 11, gap: 5 },
    lg: { p: '4px 10px', fs: 12, gap: 6 },
  };
  const s = sizes[size];
  return <span style={{
    display: 'inline-flex', alignItems: 'center', gap: s.gap, padding: s.p,
    background: t.bg, color: t.fg, borderRadius: 4,
    fontFamily: 'var(--font-body)', fontWeight: 500, fontSize: s.fs,
    whiteSpace: 'nowrap', letterSpacing: 0,
    ...style,
  }}>
    {dot && <span className={dot === 'live' ? 'live-dot' : ''} style={{ width: 5, height: 5, background: t.fg, borderRadius: '50%', color: t.fg }}/>}
    {icon && <Icon name={icon} size={s.fs}/>}
    {children}
  </span>;
}

// ---------- Input ----------
function Input(allProps) {
  const { icon, style, containerStyle, label, hint, error } = allProps;
  const passthrough = {};
  for (const k in allProps) {
    if (!['icon', 'style', 'containerStyle', 'label', 'hint', 'error'].includes(k)) passthrough[k] = allProps[k];
  }
  return (
    <div style={{ width: '100%', ...containerStyle }}>
      {label && <div style={{ fontSize: 12, color: 'var(--fg-3)', marginBottom: 6, fontWeight: 500 }}>{label}</div>}
      <div style={{
        display: 'flex', alignItems: 'center', gap: 8,
        background: 'var(--surface-2)',
        border: `1px solid ${error ? 'var(--hp)' : 'var(--border-1)'}`,
        padding: '0 12px', borderRadius: 8,
      }}>
        {icon && <Icon name={icon} size={14} color="var(--fg-3)"/>}
        <input {...passthrough} style={{
          background: 'transparent', border: 'none', outline: 'none',
          color: 'var(--fg-1)', fontFamily: 'var(--font-body)', fontSize: 13,
          flex: 1, padding: '9px 0',
          ...style,
        }}/>
      </div>
      {hint && <div style={{ fontSize: 11, color: error ? 'var(--hp)' : 'var(--fg-3)', marginTop: 6 }}>{hint}</div>}
    </div>
  );
}

function Textarea(allProps) {
  const { label, hint, error, style } = allProps;
  const passthrough = {};
  for (const k in allProps) {
    if (!['label', 'hint', 'error', 'style'].includes(k)) passthrough[k] = allProps[k];
  }
  return (
    <div style={{ width: '100%' }}>
      {label && <div style={{ fontSize: 12, color: 'var(--fg-3)', marginBottom: 6, fontWeight: 500 }}>{label}</div>}
      <textarea {...passthrough} style={{
        background: 'var(--surface-2)',
        border: `1px solid ${error ? 'var(--hp)' : 'var(--border-1)'}`,
        borderRadius: 8, padding: '10px 12px',
        color: 'var(--fg-1)', fontFamily: 'var(--font-body)', fontSize: 13,
        outline: 'none', width: '100%', minHeight: 100, resize: 'vertical',
        ...style,
      }}/>
      {hint && <div style={{ fontSize: 11, color: error ? 'var(--hp)' : 'var(--fg-3)', marginTop: 6 }}>{hint}</div>}
    </div>
  );
}

// ---------- Toggle ----------
function Toggle({ on, onChange, label, sub }) {
  return (
    <div onClick={() => onChange(!on)} style={{
      display: 'flex', alignItems: 'center', gap: 12, padding: '8px 0', cursor: 'pointer',
    }}>
      {label && <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontWeight: 500, fontSize: 13, color: 'var(--fg-1)' }}>{label}</div>
        {sub && <div style={{ fontSize: 12, color: 'var(--fg-3)', marginTop: 2 }}>{sub}</div>}
      </div>}
      <div style={{
        width: 34, height: 20, background: on ? 'var(--accent)' : 'var(--surface-3)',
        borderRadius: 999, position: 'relative', flexShrink: 0, transition: 'all 160ms ease',
      }}>
        <div style={{
          position: 'absolute', top: 2, left: on ? 16 : 2, width: 16, height: 16,
          background: '#fff', borderRadius: '50%', transition: 'all 160ms ease',
          boxShadow: '0 1px 2px rgba(0,0,0,0.3)',
        }}/>
      </div>
    </div>
  );
}

// ---------- Avatar (with optional image, fallback to initials) ----------
function Avatar({ name, src, size = 36, tone = 'default', ring }) {
  const tones = {
    default: '#334155', indigo: '#4F5DBE', cyan: '#0E7490', green: '#15803D',
    purple: '#7E22CE', amber: '#B45309', red: '#B91C1C', slate: '#475569',
  };
  const bg = tones[tone] || tones.default;
  const initials = (name || '??').split(/[_\s.]/).filter(Boolean).slice(0, 2).map(s => s[0]?.toUpperCase()).join('');
  return (
    <div style={{
      width: size, height: size, flexShrink: 0, background: src ? '#000' : bg,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      fontFamily: 'var(--font-body)', fontWeight: 600, fontSize: size * 0.38, color: '#fff',
      borderRadius: '50%', overflow: 'hidden',
      boxShadow: ring ? `0 0 0 2px ${ring}, 0 0 0 4px var(--surface-0)` : 'none',
    }}>
      {src ? <img src={src} alt={name} style={{ width: '100%', height: '100%', objectFit: 'cover' }}/> : initials}
    </div>
  );
}

// ---------- Sparkline ----------
function Sparkline({ data, color = 'var(--accent)', width = 160, height = 40, fill = true }) {
  if (!data || data.length < 2) return null;
  const max = Math.max(...data), min = Math.min(...data);
  const range = max - min || 1;
  const pts = data.map((v, i) => {
    const x = (i / (data.length - 1)) * width;
    const y = height - ((v - min) / range) * (height - 4) - 2;
    return [x, y];
  });
  const line = pts.map((p, i) => (i ? 'L' : 'M') + p[0].toFixed(1) + ' ' + p[1].toFixed(1)).join(' ');
  const area = line + ` L${width} ${height} L0 ${height} Z`;
  return (
    <svg width={width} height={height} style={{ display: 'block' }}>
      {fill && <path d={area} fill={color} opacity="0.12"/>}
      <path d={line} stroke={color} strokeWidth="1.5" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
    </svg>
  );
}

function BarChart({ data, color = 'var(--accent)', width = 280, height = 80 }) {
  const max = Math.max(...data);
  const bw = width / data.length - 3;
  return (
    <svg width={width} height={height} style={{ display: 'block' }}>
      {data.map((v, i) => {
        const h = (v / max) * (height - 4);
        const x = i * (bw + 3);
        const y = height - h - 2;
        return <rect key={i} x={x} y={y} width={bw} height={h} fill={color} opacity="0.75" rx="1"/>;
      })}
    </svg>
  );
}

// ---------- Logo mark (Wasied) ----------
function WLogo({ size = 22, withWord = false, opacity = 0.95 }) {
  return (
    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 9, opacity }}>
      <img src="assets/wasied-logo-white-red.png" alt="Wasied"
        width={size * 1.36} height={size}
        style={{ flexShrink: 0, height: size, width: 'auto', display: 'block', objectFit: 'contain' }}/>
      {withWord && <span style={{ fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: Math.max(14, Math.round(size * 0.7)), letterSpacing: '-0.01em', color: 'var(--fg-1)' }}>Wasied</span>}
    </span>
  );
}

// ---------- Service icon helpers ----------
const SERVICES = {
  hosting:    { label: 'Hébergement',  icon: 'server',     color: 'var(--service-hosting)',    path: '#/hosting' },
  orders:     { label: 'Commandes',     icon: 'briefcase',  color: 'var(--service-orders)',     path: '#/orders' },
  formations: { label: 'Formations',    icon: 'graduation', color: 'var(--service-formations)', path: '#/formations' },
  certifs:    { label: 'Certifications',icon: 'award',      color: 'var(--service-certifs)',    path: '#/certifications' },
  addons:     { label: 'Addons',        icon: 'package',    color: 'var(--service-addons)',     path: '#/addons' },
};

// ---------- Section component ----------
function Section({ children, eyebrow, title, sub, align = 'left', style, narrow }) {
  return (
    <section style={{
      padding: 'var(--pad-section, 110px) 0',
      ...style,
    }}>
      <div className="wcontainer" style={{ maxWidth: narrow ? 880 : undefined }}>
        {(eyebrow || title || sub) && (
          <div style={{ textAlign: align, marginBottom: 56, maxWidth: narrow ? '100%' : 760, marginLeft: align === 'center' ? 'auto' : 0, marginRight: align === 'center' ? 'auto' : 0 }}>
            {eyebrow && (
              <div style={{ display: 'inline-flex', alignItems: 'center', gap: 8, padding: '4px 10px', background: 'var(--surface-1)', border: '1px solid var(--border-1)', borderRadius: 999, fontSize: 11, fontWeight: 500, color: 'var(--fg-3)', marginBottom: 18 }}>
                <span style={{ width: 5, height: 5, background: 'var(--accent)', borderRadius: '50%' }}/>{eyebrow}
              </div>
            )}
            {title && <h2 className="h-display" style={{ fontSize: 'clamp(32px, 4.5vw, 56px)', margin: 0, marginBottom: sub ? 16 : 0 }}>{title}</h2>}
            {sub && <p style={{ fontSize: 17, color: 'var(--fg-2)', lineHeight: 1.55, margin: 0, maxWidth: 620 }}>{sub}</p>}
          </div>
        )}
        {children}
      </div>
    </section>
  );
}

Object.assign(window, {
  Icon, ICONS, Button, Panel, Badge, Input, Textarea, Toggle, Avatar,
  Sparkline, BarChart, WLogo, SERVICES, Section,
});
