// ════════════════════════════════════════════════════════════════
// PEXXIE · design core — tokens, icons, primitives
// Aesthetic: warm-neutral, Preline-clean. White cards, crisp hairlines,
// generous radius, restrained shadow. Cormorant for display accents only.
// ════════════════════════════════════════════════════════════════

window.PX_LIGHT = {
  // Canvas / surfaces
  canvas:   '#FAF8F3',   // app background (warm off-white)
  sidebar:  '#F4F1EA',   // rails
  card:     '#FFFFFF',   // cards / panels
  sunk:     '#F6F3EC',   // inset wells, inputs
  hover:    '#F1EDE4',

  // Borders
  line:     '#ECE7DD',   // hairline
  lineStrong:'#DED7C9',  // emphasized divider

  // Ink
  ink:      '#1B1915',   // primary
  ink2:     '#56524A',   // secondary
  ink3:     '#8A8478',   // tertiary / labels
  ink4:     '#B6B0A2',   // quiet / placeholder

  // Brand — Spark orange
  spark:    '#E5662A',
  sparkDim: '#C4521B',
  sparkTint:'#FBE7D7',
  sparkWash:'#FDF4EC',

  // Semantic
  sage:     '#3E7A50',  sageTint: '#E4EFE5',  sageWash: '#F1F7F1',
  sky:      '#3B6CA0',  skyTint:  '#E1EAF3',  skyWash:  '#F0F5F9',
  amber:    '#B07F1C',  amberTint:'#F6ECCE',  amberWash:'#FBF6E7',
  rose:     '#B0463A',  roseTint: '#F6E0DA',  roseWash: '#FCEFEB',
  plum:     '#6B4A86',  plumTint: '#EBE2F1',

  // Radius / shadow tokens (strings)
  rCard: '14px', rCtl: '10px', rSm: '8px',
  shSm: '0 1px 2px rgba(28,22,12,0.05)',
  shMd: '0 2px 4px rgba(28,22,12,0.04), 0 8px 24px rgba(28,22,12,0.06)',
  shLg: '0 8px 20px rgba(28,22,12,0.08), 0 24px 60px rgba(28,22,12,0.10)',
  shPop:'0 12px 32px rgba(28,22,12,0.14), 0 40px 80px rgba(28,22,12,0.16)',

  // Type (set by app.html after load)
  sans:  "'Inter', -apple-system, system-ui, sans-serif",
  serif: "'Cormorant Garamond', Georgia, serif",
  mono:  "'JetBrains Mono', ui-monospace, monospace",
};

// Dark scheme — same warm-neutral character, inverted. Same key shape as
// PX_LIGHT so every screen (which just reads `const P = window.PX`) needs
// zero changes to support it.
window.PX_DARK = {
  canvas:   '#17130E',
  sidebar:  '#1C1712',
  card:     '#211B14',
  sunk:     '#241E16',
  hover:    '#2C2419',

  line:     'rgba(255,247,232,0.08)',
  lineStrong:'rgba(255,247,232,0.16)',

  ink:      '#F3EDE0',
  ink2:     '#C9BFAC',
  ink3:     '#948A76',
  ink4:     '#615A49',

  spark:    '#F0894A',
  sparkDim: '#E5662A',
  sparkTint:'#3D2415',
  sparkWash:'#271B12',

  sage:     '#6FA57E',  sageTint: '#1E2B20',  sageWash: '#182219',
  sky:      '#6FA0D6',  skyTint:  '#1B2734',  skyWash:  '#16202B',
  amber:    '#D6A94A',  amberTint:'#332A15',  amberWash:'#28210F',
  rose:     '#DD7C6C',  roseTint: '#3A211C',  roseWash: '#2C1915',
  plum:     '#B18FCB',  plumTint: '#2B2036',

  rCard: '14px', rCtl: '10px', rSm: '8px',
  shSm: '0 1px 2px rgba(0,0,0,0.28)',
  shMd: '0 2px 4px rgba(0,0,0,0.24), 0 8px 24px rgba(0,0,0,0.30)',
  shLg: '0 8px 20px rgba(0,0,0,0.32), 0 24px 60px rgba(0,0,0,0.40)',
  shPop:'0 12px 32px rgba(0,0,0,0.40), 0 40px 80px rgba(0,0,0,0.50)',

  sans:  "'Inter', -apple-system, system-ui, sans-serif",
  serif: "'Cormorant Garamond', Georgia, serif",
  mono:  "'JetBrains Mono', ui-monospace, monospace",
};

// The live, mutable token object every screen reads. We mutate its
// properties in place (never reassign the reference) so a mode/brand
// change just needs a re-render — no screen-level plumbing.
window.PX = Object.assign({}, window.PX_LIGHT);
window.PX_MODE = 'light';

window.setPXMode = function (mode) {
  window.PX_MODE = mode === 'dark' ? 'dark' : 'light';
  Object.assign(window.PX, window.PX_MODE === 'dark' ? window.PX_DARK : window.PX_LIGHT);
  document.body.style.background = window.PX.canvas;
};

// ── Design-system customization (Settings · Brand & theme) ────────────
// Simplified brand presets — not the real Brand Compiler's contrast math
// (that lives in src/theme/compile.ts), just enough to prove the same
// "swap a preset, zero component edits" idea at the prototype layer.
const ACCENT_PRESETS = {
  orange: { light: { spark:'#E5662A', sparkDim:'#C4521B', sparkTint:'#FBE7D7', sparkWash:'#FDF4EC' }, dark: { spark:'#F0894A', sparkDim:'#E5662A', sparkTint:'#3D2415', sparkWash:'#271B12' } },
  sky:    { light: { spark:'#3B6CA0', sparkDim:'#2E547E', sparkTint:'#E1EAF3', sparkWash:'#F0F5F9' }, dark: { spark:'#6FA0D6', sparkDim:'#3B6CA0', sparkTint:'#1B2734', sparkWash:'#16202B' } },
  sage:   { light: { spark:'#3E7A50', sparkDim:'#2F5F3E', sparkTint:'#E4EFE5', sparkWash:'#F1F7F1' }, dark: { spark:'#6FA57E', sparkDim:'#3E7A50', sparkTint:'#1E2B20', sparkWash:'#182219' } },
  plum:   { light: { spark:'#6B4A86', sparkDim:'#54396A', sparkTint:'#EBE2F1', sparkWash:'#F4EFF7' }, dark: { spark:'#B18FCB', sparkDim:'#6B4A86', sparkTint:'#2B2036', sparkWash:'#211829' } },
  rose:   { light: { spark:'#B0463A', sparkDim:'#8C372D', sparkTint:'#F6E0DA', sparkWash:'#FCEFEB' }, dark: { spark:'#DD7C6C', sparkDim:'#B0463A', sparkTint:'#3A211C', sparkWash:'#2C1915' } },
};
const RADIUS_PRESETS = {
  sharp: { rCard: '6px',  rCtl: '4px',  rSm: '3px'  },
  soft:  { rCard: '14px', rCtl: '10px', rSm: '8px'  },
  round: { rCard: '22px', rCtl: '16px', rSm: '12px' },
};
const FONT_PRESETS = {
  modern:    { sans: "'Inter', -apple-system, system-ui, sans-serif",      serif: "'Cormorant Garamond', Georgia, serif" },
  geometric: { sans: "'Poppins', 'Inter', system-ui, sans-serif",          serif: "'Fraunces', Georgia, serif" },
  mono:      { sans: "'JetBrains Mono', ui-monospace, monospace",          serif: "'JetBrains Mono', ui-monospace, monospace" },
};
window.PX_ACCENT_KEYS = Object.keys(ACCENT_PRESETS);
window.PX_RADIUS_KEYS = Object.keys(RADIUS_PRESETS);
window.PX_FONT_KEYS = Object.keys(FONT_PRESETS);
window.PX_ACCENT = 'orange';
window.PX_RADIUS = 'soft';
window.PX_FONT = 'modern';

function applyBoth(patch) {
  Object.assign(window.PX_LIGHT, patch);
  Object.assign(window.PX_DARK, patch);
  Object.assign(window.PX, patch);
}
window.setPXAccent = function (key) {
  const p = ACCENT_PRESETS[key]; if (!p) return;
  window.PX_ACCENT = key;
  Object.assign(window.PX_LIGHT, p.light);
  Object.assign(window.PX_DARK, p.dark);
  Object.assign(window.PX, window.PX_MODE === 'dark' ? p.dark : p.light);
};
window.setPXRadius = function (key) {
  const p = RADIUS_PRESETS[key]; if (!p) return;
  window.PX_RADIUS = key;
  applyBoth(p);
};
window.setPXFont = function (key) {
  const p = FONT_PRESETS[key]; if (!p) return;
  window.PX_FONT = key;
  applyBoth(p);
};

// ────────────────────────────────────────────────────────────────
// ICONS — Lucide-style stroke set. <Icon name size stroke color/>
// ────────────────────────────────────────────────────────────────
const ICONS = {
  home:        'M3 11.5 12 4l9 7.5M5 10v9a1 1 0 0 0 1 1h4v-6h4v6h4a1 1 0 0 0 1-1v-9',
  sparkles:    'M12 3l1.6 4.4L18 9l-4.4 1.6L12 15l-1.6-4.4L6 9l4.4-1.6zM18 14l.8 2.2L21 17l-2.2.8L18 20l-.8-2.2L15 17l2.2-.8z',
  users:       'M16 19v-1.5a3.5 3.5 0 0 0-3.5-3.5h-5A3.5 3.5 0 0 0 4 17.5V19M10 11a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7zM20 19v-1.5a3.5 3.5 0 0 0-2.6-3.4M15 4.2a3.5 3.5 0 0 1 0 6.6',
  userPlus:    'M14 19v-1.5a3.5 3.5 0 0 0-3.5-3.5h-4A3.5 3.5 0 0 0 3 17.5V19M8.5 11a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7zM18 8v6M21 11h-6',
  dollar:      'M12 2v20M16.5 6.5C16.5 4.8 14.5 4 12 4S7.5 5 7.5 7s2 2.6 4.5 3 4.5 1.3 4.5 3.3-2 3-4.5 3-4.5-.9-4.5-2.6',
  globe:       'M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18zM3 12h18M12 3c2.5 2.4 3.9 5.6 4 9-.1 3.4-1.5 6.6-4 9-2.5-2.4-3.9-5.6-4-9 .1-3.4 1.5-6.6 4-9z',
  mail:        'M3.5 6.5h17v11h-17zM4 7l8 5.5L20 7',
  folder:      'M3.5 7.5a1 1 0 0 1 1-1H9l2 2h8.5a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1h-15a1 1 0 0 1-1-1z',
  calendar:    'M4.5 6.5h15v13h-15zM4.5 10h15M8 4v4M16 4v4',
  message:     'M4 5.5h16v10H9l-4 3.5v-3.5H4z',
  briefcase:   'M3.5 8.5h17v10h-17zM8.5 8.5V6a1.5 1.5 0 0 1 1.5-1.5h4A1.5 1.5 0 0 1 15.5 6v2.5M3.5 13h17',
  grid:        'M4 4.5h6v6H4zM14 4.5h6v6h-6zM4 13.5h6v6H4zM14 13.5h6v6h-6z',
  barchart:    'M4 20V10M10 20V4M16 20v-7M22 20H2',
  settings:    'M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM19.4 13a1.6 1.6 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.6 1.6 0 0 0-2.7 1.1V21a2 2 0 0 1-4 0v-.2a1.6 1.6 0 0 0-2.7-1.1l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.6 1.6 0 0 0-1.1-2.7H3a2 2 0 0 1 0-4h.2a1.6 1.6 0 0 0 1.1-2.7l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.6 1.6 0 0 0 1.8.3H9a1.6 1.6 0 0 0 1-1.5V3a2 2 0 0 1 4 0v.2a1.6 1.6 0 0 0 2.7 1.1l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.6 1.6 0 0 0-.3 1.8V9a1.6 1.6 0 0 0 1.5 1H21a2 2 0 0 1 0 4h-.2a1.6 1.6 0 0 0-1.4 1z',
  search:      'M11 18a7 7 0 1 0 0-14 7 7 0 0 0 0 14zM20 20l-4-4',
  bell:        'M18 8a6 6 0 1 0-12 0c0 7-3 9-3 9h18s-3-2-3-9M10.5 21a2 2 0 0 0 3 0',
  plus:        'M12 5v14M5 12h14',
  chevR:       'M9 6l6 6-6 6',
  chevD:       'M6 9l6 6 6-6',
  chevL:       'M15 6l-6 6 6 6',
  chevUp:      'M6 15l6-6 6 6',
  check:       'M5 12.5l4.5 4.5L19 7',
  x:           'M6 6l12 12M18 6L6 18',
  arrowUp:     'M12 19V5M6 11l6-6 6 6',
  arrowDown:   'M12 5v14M6 13l6 6 6-6',
  arrowRight:  'M5 12h14M13 6l6 6-6 6',
  panelLeft:   'M4 5.5h16v13H4zM9.5 5.5v13',
  dots:        'M6 12h.01M12 12h.01M18 12h.01',
  dotsV:       'M12 6h.01M12 12h.01M12 18h.01',
  filter:      'M3 5h18l-7 8v6l-4-2v-4z',
  download:    'M12 4v11M7 11l5 5 5-5M5 20h14',
  upload:      'M12 16V5M7 9l5-5 5 5M5 20h14',
  pencil:      'M16.5 4.5l3 3L8 19l-4 1 1-4zM14.5 6.5l3 3',
  trash:       'M4 7h16M9 7V5h6v2M6 7l1 13h10l1-13',
  eye:         'M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7zM12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6z',
  clock:       'M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18zM12 7v5l3.5 2',
  card:        'M3 6.5h18v11H3zM3 10h18',
  fileText:    'M6 3.5h8l4 4V20.5H6zM14 3.5V8h4M8.5 12.5h7M8.5 15.5h7',
  receipt:     'M6 3.5h12V21l-2-1.3-2 1.3-2-1.3-2 1.3-2-1.3V3.5zM9 8h6M9 11.5h6M9 15h3',
  send:        'M21 4L3 11l7 2.5L13 21l8-17zM10 13.5L21 4',
  phone:       'M5 4h3l1.5 4-2 1.5a11 11 0 0 0 5 5l1.5-2 4 1.5v3a2 2 0 0 1-2 2A16 16 0 0 1 3 6a2 2 0 0 1 2-2z',
  video:       'M3.5 7.5h11v9h-11zM14.5 11l5-3v8l-5-3z',
  link:        'M9.5 14.5l5-5M8 11l-2 2a3 3 0 0 0 4 4l2-2M16 13l2-2a3 3 0 0 0-4-4l-2 2',
  paperclip:   'M19 11l-7.5 7.5a4 4 0 0 1-6-6L13 5a2.7 2.7 0 0 1 4 4l-7.5 7.5a1.3 1.3 0 0 1-2-2L15 7',
  image:       'M3.5 5.5h17v13h-17zM8 11a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3zM20 15l-4.5-4.5L6 20',
  layout:      'M3.5 4.5h17v15h-17zM3.5 9.5h17M9 9.5v10',
  penTool:     'M12 2l3 6 5 2-5 2-3 6-3-6-5-2 5-2zM12 14v8',
  bag:         'M5 8h14l-1 12H6zM8.5 8V6a3.5 3.5 0 0 1 7 0v2',
  tag:         'M3 12V4.5h7.5L21 15l-7.5 7.5zM7.5 8h.01',
  trendUp:     'M3 17l6-6 4 4 8-8M15 7h6v6',
  trendDown:   'M3 7l6 6 4-4 8 8M15 17h6v-6',
  alert:       'M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18zM12 8v5M12 16h.01',
  info:        'M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18zM12 11v5M12 8h.01',
  lock:        'M5.5 11h13v9h-13zM8 11V8a4 4 0 0 1 8 0v3',
  shield:      'M12 3l8 3v6c0 4.5-3.2 7.8-8 9-4.8-1.2-8-4.5-8-9V6z',
  zap:         'M13 2L4 14h7l-1 8 9-12h-7z',
  refresh:     'M21 12a9 9 0 1 1-3-6.7M21 4v4h-4',
  ext:         'M14 4h6v6M20 4l-9 9M18 14v5H5V6h5',
  star:        'M12 3l2.6 5.6L21 9.5l-4.5 4.3L17.6 21 12 17.8 6.4 21l1.1-7.2L3 9.5l6.4-.9z',
  heart:       'M12 20S4 14.5 4 8.8A4.3 4.3 0 0 1 12 6a4.3 4.3 0 0 1 8 2.8C20 14.5 12 20 12 20z',
  building:    'M5 21V4.5h9V21M14 9.5h5V21M8 8h3M8 12h3M8 16h3M16.5 13h.5M16.5 17h.5M3 21h18',
  mapPin:      'M12 21s7-6 7-11a7 7 0 1 0-14 0c0 5 7 11 7 11zM12 12a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5z',
  hash:        'M5 9h14M5 15h14M9 4l-1.5 16M16.5 4L15 20',
  at:          'M12 16a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM16 12v1.5a2.5 2.5 0 0 0 5 0V12a9 9 0 1 0-3.5 7.1',
  sliders:     'M4 7h10M18 7h2M4 12h2M10 12h10M4 17h7M15 17h5M14 5v4M6 10v4M11 15v4',
  palette:     'M12 21a9 9 0 1 1 0-18c5 0 9 3.6 9 8 0 2.8-2.5 3.5-4 3.5h-2a2 2 0 0 0-1.4 3.4A1.6 1.6 0 0 1 12 21zM7.5 12a1 1 0 1 0 0-2 1 1 0 0 0 0 2zM10 8a1 1 0 1 0 0-2 1 1 0 0 0 0 2zM15 8a1 1 0 1 0 0-2 1 1 0 0 0 0 2z',
  type:        'M5 6V4.5h14V6M12 4.5V20M9 20h6',
  code:        'M9 8l-4 4 4 4M15 8l4 4-4 4',
  play:        'M7 5l11 7-11 7z',
  inbox:       'M3.5 13.5L6 5h12l2.5 8.5v5h-17zM3.5 13.5H9a3 3 0 0 0 6 0h5.5',
  reply:       'M9 8L4 12l5 4M4 12h9a6 6 0 0 1 6 6v1',
  archive:     'M3.5 5.5h17v4h-17zM5 9.5h14V19H5zM10 13h4',
  bookmark:    'M6 4.5h12V20l-6-4-6 4z',
  sun:         'M12 16a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM12 2v2M12 20v2M4 12H2M22 12h-2M5 5l1.5 1.5M17.5 17.5L19 19M19 5l-1.5 1.5M6.5 17.5L5 19',
  moon:        'M20 14A8 8 0 1 1 10 4a6.5 6.5 0 0 0 10 10z',
  menu:        'M4 7h16M4 12h16M4 17h16',
  logout:      'M9 21H5V3h4M16 16l4-4-4-4M20 12H9',
  user:        'M12 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM5 20a7 7 0 0 1 14 0',
  help:        'M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18zM9.5 9.5a2.5 2.5 0 1 1 3.5 2.3c-.8.4-1 .9-1 1.7M12 17h.01',
  gift:        'M4 11h16v9H4zM3 7.5h18V11H3zM12 7.5V20M12 7.5S11 4 8.5 4 6 7.5 12 7.5zM12 7.5S13 4 15.5 4 18 7.5 12 7.5z',
  package:     'M12 3l8 4.5v9L12 21l-8-4.5v-9zM4 7.5l8 4.5 8-4.5M12 12v9',
  truck:       'M3 6.5h11v9H3zM14 9.5h4l3 3v3h-7zM7 18.5a2 2 0 1 0 0-4 2 2 0 0 0 0 4zM18 18.5a2 2 0 1 0 0-4 2 2 0 0 0 0 4z',
  percent:     'M19 5L5 19M7.5 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3zM16.5 18a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z',
  pie:         'M12 3v9h9a9 9 0 1 1-9-9zM14 3.5A9 9 0 0 1 20.5 10H14z',
  activity:    'M22 12h-4l-3 8-6-16-3 8H2',
  database:    'M12 7c4.4 0 8-1.1 8-2.5S16.4 2 12 2 4 3.1 4 4.5 7.6 7 12 7zM4 4.5v15C4 20.9 7.6 22 12 22s8-1.1 8-2.5v-15M4 12c0 1.4 3.6 2.5 8 2.5s8-1.1 8-2.5',
  key:         'M14 8a4 4 0 1 1-5 4l-6 6v2h2l1-1h2v-2h2l1.5-1.5A4 4 0 0 1 14 8z',
  clipboard:   'M9 4.5h6V7H9zM7 5.5H5.5v15h13v-15H17M9 12h6M9 15.5h6',
  flag:        'M5 21V4M5 4h11l-2 4 2 4H5',
  megaphone:   'M4 9.5v5h3l8 4.5V5L7 9.5zM4 9.5H3v5h1M18 9a3 3 0 0 1 0 6',
  rocket:      'M5 15c-1 1-2 5-2 5s4-1 5-2M9 13l-3 .5L5 11l5-2M11 15l.5 3L14 19l2-5M14.5 14.5A12 12 0 0 0 19 4.5 12 12 0 0 0 9 9z',
  wand:        'M15 6l3 3M4 20l11-11 2 2L6 22zM18 3l.7 1.8L20.5 5.5l-1.8.7L18 8l-.7-1.8L15.5 5.5l1.8-.7zM5 5l.5 1.3L6.8 6.8 5.5 7.3 5 8.6l-.5-1.3L3.2 6.8l1.3-.5z',
  layers:      'M12 3l9 5-9 5-9-5zM3 13l9 5 9-5M3 16.5l9 5 9-5',
  bolt:        'M13 2L4 14h7l-1 8 9-12h-7z',
  copy:        'M9 9h11v11H9zM5 15H4V4h11v1',
  pin:         'M9 4h6l-1 6 4 3v2h-5v5l-1 2-1-2v-5H5v-2l4-3z',
  smile:       'M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18zM8.5 14a4 4 0 0 0 7 0M9 9.5h.01M15 9.5h.01',
};

window.Icon = ({ name, size = 18, stroke = 1.75, color = 'currentColor', fill = 'none', style = {} }) => {
  const d = ICONS[name];
  if (!d) return <span style={{ width: size, height: size, display: 'inline-block' }}/>;
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill={fill} stroke={color}
      strokeWidth={stroke} strokeLinecap="round" strokeLinejoin="round"
      style={{ display: 'inline-block', flexShrink: 0, ...style }}>
      {d.split('M').filter(Boolean).map((seg, i) => <path key={i} d={'M' + seg}/>)}
    </svg>
  );
};

// ────────────────────────────────────────────────────────────────
// Spark glyph (filled brand mark) + Pex avatar
// ────────────────────────────────────────────────────────────────
window.Spark = ({ size = 14, color }) => (
  <svg width={size} height={size} viewBox="0 0 24 24" style={{ display: 'inline-block', verticalAlign: '-2px', flexShrink: 0 }}>
    <path d="M13 2 L4 14 H11 L10 22 L20 9 H13 Z" fill={color || window.PX.spark}/>
  </svg>
);

window.Pex = ({ size = 34, glow = false }) => {
  const P = window.PX;
  return (
    <div style={{ width: size, height: size, borderRadius: size * 0.34, position: 'relative',
      background: `radial-gradient(circle at 32% 28%, #FFC58A, ${P.spark} 58%, ${P.sparkDim})`,
      display: 'inline-flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0,
      boxShadow: glow ? `0 0 0 4px ${P.sparkWash}, 0 4px 14px ${P.sparkTint}` : `0 2px 6px ${P.sparkTint}` }}>
      <svg width={size * 0.5} height={size * 0.5} viewBox="0 0 24 24">
        <path d="M13 2 L4 14 H11 L10 22 L20 9 H13 Z" fill="#FFFFFF"/>
      </svg>
    </div>
  );
};

window.PXLogo = ({ size = 26, tone, mark = true }) => {
  const P = window.PX;
  const c = tone || P.ink;
  return (
    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 9, fontFamily: P.serif, fontSize: size, color: c, letterSpacing: -0.5, lineHeight: 1, fontWeight: 600 }}>
      {mark && (
        <svg width={size * 0.92} height={size * 0.92} viewBox="0 0 40 40" style={{ flexShrink: 0 }}>
          <rect x="2" y="2" width="36" height="36" rx="11" fill={P.spark}/>
          <path d="M23 9 L11 23 H20 L18 32 L30 17 H22 Z" fill="#FFFFFF"/>
        </svg>
      )}
      <span style={{ fontStyle: 'italic' }}>pexxie</span>
    </span>
  );
};

// ────────────────────────────────────────────────────────────────
// Avatar — initials, deterministic warm palette
// ────────────────────────────────────────────────────────────────
window.Avatar = ({ name = '?', size = 32, src, ring }) => {
  const P = window.PX;
  const initials = name.split(' ').filter(Boolean).slice(0, 2).map(s => s[0]).join('').toUpperCase();
  const pal = [['#FBE7D7','#C4521B'],['#E4EFE5','#3E7A50'],['#E1EAF3','#3B6CA0'],['#F6ECCE','#B07F1C'],['#F6E0DA','#B0463A'],['#EBE2F1','#6B4A86']];
  const [bg, fg] = pal[(name.charCodeAt(0) + name.length) % pal.length];
  return (
    <div style={{ width: size, height: size, borderRadius: size, background: bg, color: fg,
      display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
      fontSize: size * 0.38, fontWeight: 600, fontFamily: P.sans, flexShrink: 0,
      boxShadow: ring ? `0 0 0 2px ${P.card}, 0 0 0 3px ${P.line}` : 'none' }}>{initials}</div>
  );
};

// ────────────────────────────────────────────────────────────────
// Card
// ────────────────────────────────────────────────────────────────
window.Card = ({ children, pad = 0, style = {}, hover = false, onClick }) => {
  const P = window.PX;
  return (
    <div onClick={onClick} style={{ background: P.card, border: `1px solid ${P.line}`, borderRadius: P.rCard,
      boxShadow: P.shSm, padding: pad, overflow: 'hidden', cursor: onClick ? 'pointer' : 'default',
      transition: 'box-shadow .15s, transform .15s', ...style }}
      onMouseEnter={hover ? e => { e.currentTarget.style.boxShadow = P.shMd; } : undefined}
      onMouseLeave={hover ? e => { e.currentTarget.style.boxShadow = P.shSm; } : undefined}>
      {children}
    </div>
  );
};

// Button — `loading` shows a spinner and blocks clicks (needs @keyframes pxspin in app.html)
window.Btn = ({ children, kind = 'default', size = 'md', icon, iconR, full, onClick, style = {}, disabled, loading }) => {
  const P = window.PX;
  const sizes = { sm: { p: '6px 11px', f: 12.5, g: 6 }, md: { p: '8px 14px', f: 13.5, g: 7 }, lg: { p: '11px 18px', f: 15, g: 8 } };
  const s = sizes[size];
  const kinds = {
    primary: { bg: P.spark, c: '#fff', b: P.spark },
    spark:   { bg: P.spark, c: '#fff', b: P.spark },
    default: { bg: P.card, c: P.ink2, b: P.lineStrong },
    soft:    { bg: P.sunk, c: P.ink2, b: 'transparent' },
    ghost:   { bg: 'transparent', c: P.ink2, b: 'transparent' },
    danger:  { bg: P.roseWash, c: P.rose, b: P.roseTint },
  };
  const k = kinds[kind] || kinds.default;
  const off = disabled || loading;
  return (
    <button onClick={off ? undefined : onClick} disabled={off} style={{ display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: s.g,
      padding: s.p, fontSize: s.f, fontWeight: 500, fontFamily: P.sans, lineHeight: 1.2,
      background: k.bg, color: k.c, border: `1px solid ${k.b}`, borderRadius: P.rCtl,
      cursor: off ? 'default' : 'pointer', opacity: disabled ? 0.5 : 1, width: full ? '100%' : 'auto',
      whiteSpace: 'nowrap', ...style }}>
      {loading
        ? <svg width={s.f + 1} height={s.f + 1} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" style={{ animation: 'pxspin .7s linear infinite' }}><path d="M12 3a9 9 0 1 1-9 9"/></svg>
        : icon && <window.Icon name={icon} size={s.f + 2} stroke={1.9}/>}
      {children}
      {!loading && iconR && <window.Icon name={iconR} size={s.f + 1} stroke={1.9}/>}
    </button>
  );
};

// Pill / badge — pass onClick (+ optional `on` for a selected state) to use as an interactive chip
window.Pill = ({ children, tone = 'neutral', dot = false, icon, style = {}, onClick, on }) => {
  const P = window.PX;
  const tones = {
    neutral: { bg: P.sunk, c: P.ink2 }, spark: { bg: P.sparkTint, c: P.sparkDim },
    sage: { bg: P.sageTint, c: P.sage }, sky: { bg: P.skyTint, c: P.sky },
    amber: { bg: P.amberTint, c: P.amber }, rose: { bg: P.roseTint, c: P.rose },
    plum: { bg: P.plumTint, c: P.plum }, ink: { bg: P.ink, c: '#fff' },
    outline: { bg: 'transparent', c: P.ink3, bd: P.lineStrong },
  };
  const t = tones[on ? 'ink' : tone] || tones.neutral;
  return (
    <span onClick={onClick} style={{ display: 'inline-flex', alignItems: 'center', gap: 5, padding: '2px 8px', borderRadius: 999,
      background: t.bg, color: t.c, border: t.bd ? `1px solid ${t.bd}` : 'none', whiteSpace: 'nowrap',
      cursor: onClick ? 'pointer' : 'inherit', userSelect: onClick ? 'none' : 'auto',
      fontSize: 11, fontWeight: 600, fontFamily: P.sans, letterSpacing: 0.1, lineHeight: 1.5, ...style }}>
      {dot && <span style={{ width: 6, height: 6, borderRadius: 6, background: t.c }}/>}
      {icon && <window.Icon name={icon} size={11} stroke={2.2}/>}
      {children}
    </span>
  );
};

// Section label
window.Label = ({ children, style = {} }) => {
  const P = window.PX;
  return <div style={{ fontSize: 10.5, fontWeight: 600, color: P.ink3, letterSpacing: 0.7, textTransform: 'uppercase', ...style }}>{children}</div>;
};

// Display number (serif)
window.Num = ({ children, size = 30, color, style = {} }) => {
  const P = window.PX;
  return <div style={{ fontFamily: P.serif, fontStyle: 'italic', fontSize: size, color: color || P.ink, letterSpacing: -0.5, lineHeight: 1, ...style }}>{children}</div>;
};

// Toggle — real switch semantics: keyboard (Space/Enter), aria, disabled
window.Toggle = ({ on = false, onClick, size = 'md', disabled }) => {
  const P = window.PX;
  const w = size === 'sm' ? 30 : 36, h = size === 'sm' ? 17 : 20, k = h - 4;
  const fire = disabled ? undefined : onClick;
  return (
    <span role="switch" aria-checked={on} aria-disabled={disabled || undefined} tabIndex={disabled ? -1 : 0}
      onClick={fire} onKeyDown={fire ? (e) => { if (e.key === ' ' || e.key === 'Enter') { e.preventDefault(); fire(e); } } : undefined}
      style={{ width: w, height: h, borderRadius: h, background: on ? P.spark : P.lineStrong, position: 'relative',
        cursor: disabled ? 'default' : 'pointer', opacity: disabled ? 0.45 : 1, flexShrink: 0, transition: 'background .15s', display: 'inline-block', outline: 'none' }}>
      <span style={{ position: 'absolute', top: 2, left: on ? w - k - 2 : 2, width: k, height: k, borderRadius: k, background: '#fff', transition: 'left .15s', boxShadow: '0 1px 2px rgba(0,0,0,0.2)' }}/>
    </span>
  );
};

// Field — controlled when `onChange` is passed (called with the string value,
// same convention as the rest of the app); uncontrolled fallback otherwise so
// legacy call sites keep rendering. Supports type / disabled / error.
window.Field = ({ label, value, onChange, placeholder, icon, sub, suffix, mono, type = 'text', disabled, error, style = {} }) => {
  const P = window.PX;
  const inputProps = onChange ? { value: value ?? '', onChange: (e) => onChange(e.target.value) } : { defaultValue: value };
  return (
    <label style={{ display: 'block', opacity: disabled ? 0.55 : 1, ...style }}>
      {label && <div style={{ fontSize: 12, color: P.ink3, marginBottom: 5, fontWeight: 500 }}>{label}</div>}
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, background: P.card, border: `1px solid ${error ? P.rose : P.lineStrong}`, borderRadius: P.rCtl, padding: '9px 11px' }}>
        {icon && <window.Icon name={icon} size={15} color={P.ink3}/>}
        <input type={type} disabled={disabled} {...inputProps} placeholder={placeholder} style={{ flex: 1, border: 'none', background: 'transparent', outline: 'none', fontSize: 13.5, color: P.ink, fontFamily: mono ? P.mono : P.sans, minWidth: 0 }}/>
        {suffix && <span style={{ fontSize: 12, color: P.ink3 }}>{suffix}</span>}
      </div>
      {(error || sub) && <div style={{ fontSize: 11, color: error ? P.rose : P.ink3, marginTop: 4 }}>{error || sub}</div>}
    </label>
  );
};

// Select — controlled dropdown; options are strings or { v, label }
window.Select = ({ label, value, onChange, options = [], placeholder, disabled, style = {} }) => {
  const P = window.PX;
  return (
    <label style={{ display: 'block', opacity: disabled ? 0.55 : 1, ...style }}>
      {label && <div style={{ fontSize: 12, color: P.ink3, marginBottom: 5, fontWeight: 500 }}>{label}</div>}
      <select value={value ?? ''} disabled={disabled} onChange={(e) => onChange && onChange(e.target.value)}
        style={{ width: '100%', background: P.card, border: `1px solid ${P.lineStrong}`, borderRadius: P.rCtl, padding: '9px 11px', fontSize: 13.5, color: P.ink, fontFamily: P.sans, outline: 'none', cursor: disabled ? 'default' : 'pointer' }}>
        {placeholder != null && <option value="" disabled>{placeholder}</option>}
        {options.map((o) => {
          const v = typeof o === 'object' ? o.v : o;
          const l = typeof o === 'object' ? o.label : o;
          return <option key={v} value={v}>{l}</option>;
        })}
      </select>
    </label>
  );
};

// TextArea — controlled multiline sibling of Field
window.TextArea = ({ label, value, onChange, placeholder, rows = 4, sub, error, disabled, style = {} }) => {
  const P = window.PX;
  const inputProps = onChange ? { value: value ?? '', onChange: (e) => onChange(e.target.value) } : { defaultValue: value };
  return (
    <label style={{ display: 'block', opacity: disabled ? 0.55 : 1, ...style }}>
      {label && <div style={{ fontSize: 12, color: P.ink3, marginBottom: 5, fontWeight: 500 }}>{label}</div>}
      <textarea rows={rows} disabled={disabled} {...inputProps} placeholder={placeholder}
        style={{ width: '100%', resize: 'vertical', background: P.card, border: `1px solid ${error ? P.rose : P.lineStrong}`, borderRadius: P.rCtl, padding: '9px 11px', fontSize: 13.5, color: P.ink, fontFamily: P.sans, lineHeight: 1.5, outline: 'none' }}/>
      {(error || sub) && <div style={{ fontSize: 11, color: error ? P.rose : P.ink3, marginTop: 4 }}>{error || sub}</div>}
    </label>
  );
};

// Check — square checkbox (row select, task done, option lists)
window.Check = ({ on = false, onClick, size = 16, disabled }) => {
  const P = window.PX;
  const fire = disabled ? undefined : onClick;
  return (
    <span role="checkbox" aria-checked={on} tabIndex={disabled ? -1 : 0}
      onClick={fire} onKeyDown={fire ? (e) => { if (e.key === ' ' || e.key === 'Enter') { e.preventDefault(); fire(e); } } : undefined}
      style={{ width: size, height: size, borderRadius: 5, flexShrink: 0, display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
        background: on ? P.spark : P.card, border: `1.5px solid ${on ? P.spark : P.lineStrong}`,
        cursor: disabled ? 'default' : 'pointer', opacity: disabled ? 0.45 : 1, transition: 'background .12s', outline: 'none' }}>
      {on && <window.Icon name="check" size={size - 5} stroke={3} color="#fff"/>}
    </span>
  );
};

// Sparkline
window.Spark2 = ({ data = [], w = 60, h = 18, color, fill = false }) => {
  const P = window.PX; const c = color || P.ink3;
  if (!data || data.length < 2) return <svg width={w} height={h} viewBox={`0 0 ${w} ${h}`} style={{ display: 'block' }}/>;
  const max = Math.max(...data), min = Math.min(...data), rng = max - min || 1;
  const pts = data.map((v, i) => `${(i / (data.length - 1)) * w},${h - ((v - min) / rng) * (h - 2) - 1}`);
  return (
    <svg width={w} height={h} viewBox={`0 0 ${w} ${h}`} style={{ display: 'block' }}>
      {fill && <polygon points={`0,${h} ${pts.join(' ')} ${w},${h}`} fill={c} opacity="0.12"/>}
      <polyline points={pts.join(' ')} fill="none" stroke={c} strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/>
    </svg>
  );
};

// Tabs (controlled)
window.Tabs = ({ tabs, active, onChange, style = {} }) => {
  const P = window.PX;
  return (
    <div style={{ display: 'flex', gap: 2, borderBottom: `1px solid ${P.line}`, ...style }}>
      {tabs.map(t => {
        const key = typeof t === 'string' ? t : t.k;
        const lbl = typeof t === 'string' ? t : t.label;
        const on = active === key;
        return (
          <div key={key} onClick={() => onChange && onChange(key)} style={{ padding: '9px 13px', fontSize: 13, fontWeight: on ? 600 : 500,
            color: on ? P.ink : P.ink3, borderBottom: `2px solid ${on ? P.spark : 'transparent'}`, marginBottom: -1, cursor: 'pointer', whiteSpace: 'nowrap', display: 'flex', alignItems: 'center', gap: 6 }}>
            {lbl}{typeof t === 'object' && t.count != null && <span style={{ fontSize: 11, color: P.ink4, fontWeight: 500 }}>{t.count}</span>}
          </div>
        );
      })}
    </div>
  );
};

window.money = (n, cur = '$') => cur + n.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
