// Shared components for Solution Order Management
const { useState, useMemo, useRef, useEffect } = React;

// ---------- Formatters ----------
const fmtBaht = (n, opts = {}) => {
  const { compact = false, sign = false } = opts;
  const x = parseFloat(n) || 0;
  if (compact && Math.abs(x) >= 1_000_000) return (sign && x > 0 ? '+' : '') + '฿' + (x / 1_000_000).toFixed(1) + 'M';
  if (compact && Math.abs(x) >= 1_000) return (sign && x > 0 ? '+' : '') + '฿' + (x / 1_000).toFixed(0) + 'K';
  return (sign && x > 0 ? '+' : '') + '฿' + x.toLocaleString('en-US', { maximumFractionDigits: 0 });
};
const fmtInt = n => Math.round(parseFloat(n) || 0).toLocaleString('en-US');
const fmtDate = (d) => {
  if (!d) return '—';
  const x = (d instanceof Date) ? d : new Date(d);
  return x.toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' });
};
const fmtDateShort = (d) => {
  if (!d) return '—';
  const x = (d instanceof Date) ? d : new Date(d);
  return x.toLocaleDateString('en-GB', { day: '2-digit', month: 'short' });
};
const relTime = (d) => {
  const x = (d instanceof Date) ? d : new Date(d);
  const days = Math.floor((Date.now() - x.getTime()) / 86400000);
  if (days === 0) return 'today';
  if (days === 1) return 'yesterday';
  if (days < 7) return days + 'd ago';
  if (days < 30) return Math.floor(days / 7) + 'w ago';
  return Math.floor(days / 30) + 'mo ago';
};

// ---------- Icons (16x16 stroke 1.5) ----------
const Icon = ({ name, size = 14, color }) => {
  const props = { width: size, height: size, viewBox: '0 0 16 16', fill: 'none', stroke: color || 'currentColor', strokeWidth: 1.5, strokeLinecap: 'round', strokeLinejoin: 'round' };
  const paths = {
    grid: <><rect x="2" y="2" width="5" height="5"/><rect x="9" y="2" width="5" height="5"/><rect x="2" y="9" width="5" height="5"/><rect x="9" y="9" width="5" height="5"/></>,
    list: <><path d="M3 4h10M3 8h10M3 12h6"/></>,
    cart: <><path d="M2 3h2l2 8h7l1.5-6H5"/><circle cx="6.5" cy="13.5" r="1"/><circle cx="12" cy="13.5" r="1"/></>,
    package: <><path d="M8 2 14 5v6L8 14 2 11V5z"/><path d="M2 5l6 3 6-3M8 8v6"/></>,
    users: <><circle cx="6" cy="6" r="2.5"/><path d="M2 13c0-2.5 1.8-4 4-4s4 1.5 4 4"/><path d="M11 5.5a2 2 0 1 1 0 4"/><path d="M11 9.5c1.8 0 3 1.2 3 3"/></>,
    building: <><rect x="3" y="2" width="10" height="12"/><path d="M5.5 5h1M9.5 5h1M5.5 8h1M9.5 8h1M5.5 11h1M9.5 11h1"/></>,
    plus: <><path d="M8 3v10M3 8h10"/></>,
    search: <><circle cx="7" cy="7" r="4.5"/><path d="M14 14l-3.5-3.5"/></>,
    filter: <><path d="M2 3h12l-4.5 6V14L6.5 12V9z"/></>,
    download: <><path d="M8 2v8"/><path d="M4 7l4 3 4-3"/><path d="M2 13h12"/></>,
    upload: <><path d="M8 13V5"/><path d="M4 8l4-3 4 3"/><path d="M2 13h12"/></>,
    chevron: <path d="M5 3l5 5-5 5"/>,
    chevronDown: <path d="M3 5l5 5 5-5"/>,
    chevronLeft: <path d="M11 3 6 8l5 5"/>,
    close: <><path d="M3 3l10 10M13 3 3 13"/></>,
    check: <path d="M3 8l3.5 3.5L13 5"/>,
    bell: <><path d="M4 11V7a4 4 0 1 1 8 0v4l1 2H3z"/><path d="M6.5 14a1.5 1.5 0 0 0 3 0"/></>,
    cog: <><circle cx="8" cy="8" r="2"/><path d="M8 1v2M8 13v2M15 8h-2M3 8H1M13 3l-1.4 1.4M4.4 11.6 3 13M13 13l-1.4-1.4M4.4 4.4 3 3"/></>,
    info: <><circle cx="8" cy="8" r="6"/><path d="M8 7v4M8 5v.5"/></>,
    alert: <><path d="M8 1 15 14H1z"/><path d="M8 6v4M8 11.5v.5"/></>,
    file: <><path d="M3 2h6l4 4v8H3z"/><path d="M9 2v4h4"/></>,
    fileCheck: <><path d="M3 2h6l4 4v8H3z"/><path d="M9 2v4h4"/><path d="M5.5 10l1.5 1.5L11 8"/></>,
    clock: <><circle cx="8" cy="8" r="6"/><path d="M8 4.5V8l2.5 1.5"/></>,
    calendar: <><rect x="2" y="3" width="12" height="11"/><path d="M2 6h12M5 1.5V4M11 1.5V4"/></>,
    dot: <circle cx="8" cy="8" r="2.5" fill="currentColor" stroke="none"/>,
    arrowUp: <><path d="M8 13V3"/><path d="M4 7l4-4 4 4"/></>,
    arrowDown: <><path d="M8 3v10"/><path d="M4 9l4 4 4-4"/></>,
    arrowRight: <><path d="M3 8h10"/><path d="M9 4l4 4-4 4"/></>,
    edit: <><path d="M2 14h12"/><path d="M11 2 14 5 6 13 2 14 3 10z"/></>,
    more: <><circle cx="3" cy="8" r="1" fill="currentColor" stroke="none"/><circle cx="8" cy="8" r="1" fill="currentColor" stroke="none"/><circle cx="13" cy="8" r="1" fill="currentColor" stroke="none"/></>,
    phone: <><path d="M3 3h3l1 3-2 1c1 2 3 4 5 5l1-2 3 1v3c0 .5-.5 1-1 1C7 15 1 9 1 4c0-.5.5-1 1-1z"/></>,
    mail: <><rect x="2" y="3" width="12" height="10"/><path d="M2 4l6 5 6-5"/></>,
    shield: <><path d="M8 1 14 3v5c0 4-3 6-6 7-3-1-6-3-6-7V3z"/></>,
    pipeline: <><circle cx="8" cy="8" r="6"/><polyline points="8,5 8,8 10.5,9.5"/></>,
    sparkles: <><path d="M8 2v3M8 11v3M2 8h3M11 8h3M4 4l2 2M12 12l-2-2M4 12l2-2M12 4l-2 2"/></>,
    grid3: <><rect x="2" y="2" width="3.5" height="3.5"/><rect x="6.5" y="2" width="3.5" height="3.5"/><rect x="11" y="2" width="3.5" height="3.5"/><rect x="2" y="6.5" width="3.5" height="3.5"/><rect x="6.5" y="6.5" width="3.5" height="3.5"/><rect x="11" y="6.5" width="3.5" height="3.5"/><rect x="2" y="11" width="3.5" height="3.5"/><rect x="6.5" y="11" width="3.5" height="3.5"/><rect x="11" y="11" width="3.5" height="3.5"/></>,
    kanban: <><rect x="2" y="2" width="3" height="9"/><rect x="6.5" y="2" width="3" height="12"/><rect x="11" y="2" width="3" height="6"/></>,
    lock: <><rect x="4" y="7" width="8" height="7" rx="1"/><path d="M5 7V5a3 3 0 0 1 6 0v2"/></>,
    lockOpen: <><rect x="4" y="7" width="8" height="7" rx="1"/><path d="M5 7V5a3 3 0 0 1 6 0v2M9 7V5"/></>,
    eye: <><path d="M1 8s2.5-5 7-5 7 5 7 5-2.5 5-7 5-7-5-7-5z"/><circle cx="8" cy="8" r="2"/></>,
    eyeOff: <><path d="M13.5 6.5C14.4 7.2 15 8 15 8s-2.5 5-7 5c-.9 0-1.7-.2-2.5-.5M6.1 6.1C6.7 5.5 7.3 5 8 5c4.5 0 7 5 7 5s-.6.8-1.5 1.5M2 2l12 12"/><path d="M8.9 3.1C8.6 3 8.3 3 8 3 3.5 3 1 8 1 8s.7 1.3 2 2.5"/></>,
    logOut: <><path d="M9 3H5a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h4M11 11l3-3-3-3M14 8H6"/></>,
  };
  return <svg {...props}>{paths[name] || null}</svg>;
};

// ---------- Product icons (named SVG paths, 16×16 viewBox) ----------
const PRODUCT_ICONS = {
  phone:       { label: 'Phone',       path: <path d="M3 3h3l1 3-2 1c1 2 3 4 5 5l1-2 3 1v3c0 .5-.5 1-1 1C7 15 1 9 1 4c0-.5.5-1 1-1z"/> },
  headset:     { label: 'Headset',     path: <><path d="M3 9V8a5 5 0 0 1 10 0v1M3 9h2v4H3zM11 9h2v4h-2zM5 13c0 1.5 1 2 3 2"/></> },
  cloud:       { label: 'Cloud',       path: <path d="M4.5 13a3.5 3.5 0 0 1 0-7 4.5 4.5 0 0 1 8.7-1 3 3 0 0 1 .3 8z"/> },
  cloud_phone: { label: 'Cloud+Phone', path: <><path d="M5 13a3 3 0 0 1 0-6 4 4 0 0 1 7.8-1 3 3 0 0 1 .2 7H5z"/><path d="M7.5 10.5h.01M10 10.5h.01"/></> },
  server:      { label: 'Server',      path: <><rect x="2" y="3" width="12" height="4" rx="1"/><rect x="2" y="9" width="12" height="4" rx="1"/><circle cx="13" cy="5" r=".6" fill="currentColor" stroke="none"/><circle cx="13" cy="11" r=".6" fill="currentColor" stroke="none"/></> },
  database:    { label: 'Database',    path: <><ellipse cx="8" cy="4.5" rx="5.5" ry="2"/><path d="M2.5 4.5v3c0 1.1 2.5 2 5.5 2s5.5-.9 5.5-2v-3"/><path d="M2.5 7.5v3c0 1.1 2.5 2 5.5 2s5.5-.9 5.5-2v-3"/></> },
  globe:       { label: 'Globe',       path: <><circle cx="8" cy="8" r="6"/><path d="M2 8h12"/><path d="M8 2c-2 2-3 4-3 6s1 4 3 6c2-2 3-4 3-6s-1-4-3-6z"/></> },
  grid:        { label: 'Grid',        path: <><rect x="2" y="2" width="5.5" height="5.5"/><rect x="8.5" y="2" width="5.5" height="5.5"/><rect x="2" y="8.5" width="5.5" height="5.5"/><rect x="8.5" y="8.5" width="5.5" height="5.5"/></> },
  mail:        { label: 'Mail',        path: <><rect x="2" y="4" width="12" height="9" rx="1"/><path d="M2 5l6 4.5L14 5"/></> },
  message:     { label: 'Message',     path: <path d="M2 3h12v9H9.5l-3 3v-3H2z"/> },
  monitor:     { label: 'Monitor',     path: <><rect x="1" y="2" width="14" height="10" rx="1"/><path d="M5 14h6M8 12v2"/></> },
  mobile:      { label: 'Mobile',      path: <><rect x="5" y="1" width="6" height="14" rx="1"/><circle cx="8" cy="12.5" r=".6" fill="currentColor" stroke="none"/></> },
  video:       { label: 'Video',       path: <><rect x="1" y="5" width="9" height="7" rx="1"/><path d="M10 7.5 14 5v7l-4-2.5z"/></> },
  wifi:        { label: 'WiFi',        path: <><path d="M1.5 6.5a9 9 0 0 1 13 0M4 9a6 6 0 0 1 8 0M6.5 11.5a3 3 0 0 1 3 0"/><circle cx="8" cy="14" r=".7" fill="currentColor" stroke="none"/></> },
  shield:      { label: 'Shield',      path: <path d="M8 1 14 3.5v4.5c0 4-3 6-6 7-3-1-6-3-6-7V3.5z"/> },
  key:         { label: 'Key',         path: <><circle cx="5.5" cy="8" r="3.5"/><path d="M8.5 8h6M12.5 6.5V8M10.5 6.5v2"/></> },
  zap:         { label: 'Zap',         path: <path d="M9.5 1.5 4 9h4l-1.5 5.5 6-8H8.5z"/> },
  chart:       { label: 'Bar chart',   path: <><rect x="2" y="9" width="3" height="5"/><rect x="6.5" y="5.5" width="3" height="8.5"/><rect x="11" y="2.5" width="3" height="11.5"/></> },
  layers:      { label: 'Layers',      path: <><path d="M8 1.5 14.5 5 8 8.5 1.5 5z"/><path d="M1.5 9l6.5 3.5L14.5 9"/><path d="M1.5 12l6.5 3.5 6.5-3.5"/></> },
  cpu:         { label: 'CPU',         path: <><rect x="4" y="4" width="8" height="8"/><path d="M6.5 2v2M9.5 2v2M6.5 12v2M9.5 12v2M2 6.5h2M2 9.5h2M12 6.5h2M12 9.5h2"/></> },
  broadcast:   { label: 'Broadcast',   path: <><path d="M4.5 4.5A5 5 0 0 0 4.5 11.5M11.5 4.5a5 5 0 0 1 0 7M2.5 2.5A8 8 0 0 0 2.5 13.5M13.5 2.5a8 8 0 0 1 0 11"/><circle cx="8" cy="8" r="1.5" fill="currentColor" stroke="none"/></> },
  pos:         { label: 'POS',         path: <><rect x="3" y="2" width="10" height="12" rx="1"/><path d="M6 5.5h4M6 8h4M6 10.5h2"/></> },
  printer:     { label: 'Printer',     path: <><path d="M4 6V3h8v3"/><rect x="2" y="6" width="12" height="6" rx="1"/><rect x="4" y="9" width="8" height="5"/><circle cx="12" cy="9" r=".6" fill="currentColor" stroke="none"/></> },
  link:        { label: 'Link',        path: <><path d="M6.5 9.5a3 3 0 0 0 4.24 0l2-2a3 3 0 0 0-4.24-4.24l-1 1M9.5 6.5a3 3 0 0 0-4.24 0l-2 2a3 3 0 0 0 4.24 4.24l1-1"/></> },
  box:         { label: 'Box',         path: <><path d="M8 2 14 5v6L8 14 2 11V5z"/><path d="M2 5l6 3 6-3M8 8v6"/></> },
};

// Backward-compat map for products that never had an icon field
const LEGACY_GLYPHS = {
  cloud_pbx: 'cloud_phone',
  ccc:       'headset',
  one_call:  'phone',
  gws:       'globe',
  m365:      'grid',
};

// ---------- Product glyph (visual identity for each product) ----------
const ProductGlyph = ({ productId, size = 28 }) => {
  const product = (window.PRODUCTS || PRODUCTS).find(p => p.id === productId);
  if (!product) return null;
  const iconKey = product.icon || LEGACY_GLYPHS[productId] || 'box';
  const iconDef = PRODUCT_ICONS[iconKey] || PRODUCT_ICONS.box;
  return (
    <div style={{
      width: size, height: size, borderRadius: 4,
      background: product.color + '14',
      color: product.color,
      display: 'grid', placeItems: 'center', flexShrink: 0,
    }}>
      <svg width={size * 0.55} height={size * 0.55} viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
        {iconDef.path}
      </svg>
    </div>
  );
};

// ---------- Status chip ----------
const StatusChip = ({ status, dense = false }) => {
  if (!status) return null;
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 5,
      padding: dense ? '2px 6px' : '3px 8px',
      background: status.color + '14',
      color: status.color,
      borderRadius: 2,
      fontSize: dense ? 10.5 : 11,
      fontWeight: 500,
      whiteSpace: 'nowrap',
    }}>
      <span style={{ width: 5, height: 5, borderRadius: '50%', background: status.color, flexShrink: 0 }}/>
      {status.label}
    </span>
  );
};

// ---------- Avatar (initials, deterministic color) ----------
const initials = (name) => {
  if (!name) return '?';
  // Strip honorific prefixes
  const clean = name.replace(/^(คุณ|Khun|Mr\.?|Mrs\.?|Ms\.?)\s*/i, '').trim();
  const parts = clean.split(/\s+/);
  if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
  return (parts[0][0] + parts[1][0]).toUpperCase();
};
const AVATAR_COLORS = ['#2d3a8c','#d97b2e','#1f7a4d','#6b4a8a','#3a6b8a','#a8553a'];
const avatarColor = (s) => {
  let h = 0; for (let i = 0; i < (s || '').length; i++) h = (h * 31 + s.charCodeAt(i)) >>> 0;
  return AVATAR_COLORS[h % AVATAR_COLORS.length];
};
const Avatar = ({ name, size = 26, square = false }) => (
  <div style={{
    width: size, height: size,
    borderRadius: square ? 3 : '50%',
    background: avatarColor(name),
    color: '#fff',
    display: 'grid', placeItems: 'center',
    fontSize: Math.max(9, size * 0.4),
    fontWeight: 600,
    fontFamily: 'Kanit, sans-serif',
    flexShrink: 0,
    letterSpacing: 0,
  }}>{initials(name)}</div>
);

// ---------- Segmented control ----------
const Segmented = ({ options, value, onChange, dense = false }) => (
  <div style={{
    display: 'inline-flex', background: 'var(--bg-3)', padding: 2, borderRadius: 3, fontSize: 12,
  }}>
    {options.map(o => {
      const v = typeof o === 'string' ? o : o.value;
      const lbl = typeof o === 'string' ? o : o.label;
      const icon = typeof o === 'string' ? null : o.icon;
      const disabled = typeof o === 'object' && o.disabled;
      const active = value === v;
      return (
        <button key={v} onClick={() => !disabled && onChange(v)} disabled={disabled} style={{
          padding: dense ? '3px 9px' : '5px 12px',
          background: active ? 'var(--panel)' : 'transparent',
          color: active ? 'var(--ink)' : 'var(--ink-3)',
          fontWeight: active ? 500 : 400,
          borderRadius: 2,
          boxShadow: active ? 'var(--shadow-segment)' : 'none',
          transition: 'all 120ms',
          border: 'none', cursor: disabled ? 'not-allowed' : 'pointer',
          opacity: disabled ? 0.4 : 1,
          fontFamily: 'Kanit, sans-serif', fontSize: 12,
          display: 'inline-flex', alignItems: 'center', gap: 6,
        }}>
          {icon && <Icon name={icon} size={12}/>}
          {lbl}
        </button>
      );
    })}
  </div>
);

// ---------- Buttons ----------
const Button = ({ children, variant = 'secondary', size = 'md', icon, iconRight, onClick, disabled, type = 'button', style = {} }) => {
  const base = {
    display: 'inline-flex', alignItems: 'center', gap: 6,
    fontFamily: 'Kanit, sans-serif', fontSize: size === 'sm' ? 11.5 : 12, fontWeight: 500,
    borderRadius: 3, cursor: disabled ? 'not-allowed' : 'pointer',
    padding: size === 'sm' ? '5px 10px' : '7px 14px',
    border: '1px solid transparent',
    transition: 'all 120ms',
    whiteSpace: 'nowrap',
  };
  const variants = {
    primary:   { background: 'var(--ink)', color: '#fff' },
    accent:    { background: 'var(--accent)', color: '#fff' },
    secondary: { background: 'var(--panel)', color: 'var(--ink)', borderColor: 'var(--ink)' },
    ghost:     { background: 'transparent', color: 'var(--ink-2)', borderColor: 'var(--line)' },
    text:      { background: 'transparent', color: 'var(--ink-2)', padding: size === 'sm' ? '4px 6px' : '6px 8px' },
    danger:    { background: 'var(--panel)', color: 'var(--negative)', borderColor: 'var(--negative)' },
  };
  const disabledStyle = disabled ? { background: 'var(--bg-3)', color: 'var(--ink-4)', borderColor: 'transparent' } : {};
  return (
    <button type={type} onClick={disabled ? undefined : onClick} disabled={disabled}
      style={{ ...base, ...variants[variant], ...disabledStyle, ...style }}>
      {icon && <Icon name={icon} size={size === 'sm' ? 11 : 12}/>}
      {children}
      {iconRight && <Icon name={iconRight} size={size === 'sm' ? 10 : 11}/>}
    </button>
  );
};

// ---------- Empty state ----------
const Empty = ({ icon = 'file', title, hint }) => (
  <div style={{ padding: '48px 24px', textAlign: 'center', color: 'var(--ink-3)' }}>
    <div style={{ width: 40, height: 40, borderRadius: 4, background: 'var(--bg-2)', margin: '0 auto 12px', display: 'grid', placeItems: 'center' }}>
      <Icon name={icon} size={18}/>
    </div>
    <div style={{ fontSize: 13, color: 'var(--ink-2)', fontWeight: 500 }}>{title}</div>
    {hint && <div style={{ fontSize: 11.5, marginTop: 4 }}>{hint}</div>}
  </div>
);

// ---------- Toast ----------
function ensureToastHost() {
  let host = document.getElementById('__toast_host');
  if (!host) {
    host = document.createElement('div');
    host.id = '__toast_host';
    Object.assign(host.style, {
      position: 'fixed', bottom: '24px', right: '24px', zIndex: 9999,
      display: 'flex', flexDirection: 'column', gap: '8px', pointerEvents: 'none',
    });
    document.body.appendChild(host);
    const style = document.createElement('style');
    style.textContent = `
      @keyframes __toastIn { from { transform: translateY(8px); opacity: 0; } to { transform: none; opacity: 1; } }
      @keyframes __toastOut { to { transform: translateY(-4px); opacity: 0; } }
    `;
    document.head.appendChild(style);
  }
  return host;
}
function showToast(message, opts = {}) {
  const host = ensureToastHost();
  const { variant = 'info', detail, duration = 3200 } = opts;
  const colors = {
    success: { bg: '#1f7a4d', dot: '#7ad6a3' },
    error:   { bg: '#b8492f', dot: '#f5b9a8' },
    info:    { bg: '#1a1d24', dot: '#d97b2e' },
  };
  const c = colors[variant] || colors.info;
  const toast = document.createElement('div');
  Object.assign(toast.style, {
    background: c.bg, color: '#fff',
    padding: '12px 16px', borderRadius: '4px',
    fontFamily: 'Kanit, sans-serif', fontSize: '12.5px',
    minWidth: '240px', maxWidth: '380px',
    boxShadow: '0 8px 24px rgba(0,0,0,0.18)',
    pointerEvents: 'auto', cursor: 'pointer',
    animation: '__toastIn 180ms ease-out',
    display: 'flex', gap: '10px', alignItems: 'flex-start',
  });
  toast.innerHTML = `
    <span style="width:8px;height:8px;border-radius:50%;background:${c.dot};margin-top:6px;flex-shrink:0"></span>
    <div style="flex:1;line-height:1.4">
      <div style="font-weight:500">${message}</div>
      ${detail ? `<div style="font-size:11px;color:rgba(255,255,255,0.7);margin-top:2px">${detail}</div>` : ''}
    </div>
  `;
  toast.onclick = () => { toast.style.animation = '__toastOut 160ms forwards'; setTimeout(() => toast.remove(), 160); };
  host.appendChild(toast);
  setTimeout(() => {
    if (toast.parentNode) {
      toast.style.animation = '__toastOut 200ms forwards';
      setTimeout(() => toast.remove(), 200);
    }
  }, duration);
}

// ---------- Modal ----------
const Modal = ({ open, title, subtitle, onClose, children, footer, width = 480 }) => {
  if (!open) return null;
  return (
    <div onClick={onClose} style={{
      position: 'fixed', inset: 0, background: 'rgba(20,22,28,0.45)',
      zIndex: 200, display: 'grid', placeItems: 'center',
      animation: '__toastIn 140ms ease-out',
    }}>
      <div onClick={e => e.stopPropagation()} style={{
        background: 'var(--panel)', width, maxWidth: '92vw', maxHeight: '88vh',
        display: 'flex', flexDirection: 'column',
        boxShadow: 'var(--shadow-modal)', borderRadius: 4,
      }}>
        <div style={{ padding: '20px 24px', borderBottom: '1px solid var(--line)', display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 16 }}>
          <div>
            <h2 style={{ fontSize: 18, fontWeight: 500, letterSpacing: '-0.01em', margin: 0 }}>{title}</h2>
            {subtitle && <div style={{ fontSize: 12, color: 'var(--ink-3)', marginTop: 4 }}>{subtitle}</div>}
          </div>
          <button onClick={onClose} style={{ color: 'var(--ink-3)', background: 'none', border: 'none', cursor: 'pointer', padding: 4 }}><Icon name="close" size={14}/></button>
        </div>
        <div style={{ padding: '20px 24px', overflowY: 'auto', flex: 1 }}>{children}</div>
        {footer && <div style={{ padding: '14px 24px', borderTop: '1px solid var(--line)', display: 'flex', justifyContent: 'flex-end', gap: 8 }}>{footer}</div>}
      </div>
    </div>
  );
};

// ---------- Inputs ----------
const Field = ({ label, hint, required, children, error }) => (
  <label style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
    <span style={{ fontSize: 11.5, color: 'var(--ink-2)', fontWeight: 500 }}>
      {label}
      {required && <span style={{ color: 'var(--negative)', marginLeft: 3 }}>*</span>}
    </span>
    {children}
    {hint && !error && <span style={{ fontSize: 10.5, color: 'var(--ink-3)' }}>{hint}</span>}
    {error && <span style={{ fontSize: 10.5, color: 'var(--negative)' }}>{error}</span>}
  </label>
);

const inputStyle = {
  fontFamily: 'Kanit, sans-serif',
  fontSize: 13,
  padding: '8px 10px',
  background: 'var(--panel)',
  border: '1px solid var(--line)',
  borderRadius: 3,
  color: 'var(--ink)',
  width: '100%',
  outline: 'none',
  transition: 'border-color 120ms',
};
const TextInput = (props) => <input {...props} style={{ ...inputStyle, ...(props.style || {}) }}
  onFocus={e => e.currentTarget.style.borderColor = 'var(--ink)'}
  onBlur={e => e.currentTarget.style.borderColor = 'var(--line)'}/>;
const Select = (props) => <select {...props} style={{ ...inputStyle, cursor: 'pointer', ...(props.style || {}) }}/>;
const Textarea = (props) => <textarea {...props} style={{ ...inputStyle, resize: 'vertical', minHeight: 64, fontFamily: 'Kanit, sans-serif', ...(props.style || {}) }}/>;

// ---------- Date picker (popover calendar) ----------
const DatePicker = ({ value, onChange, recommended, recommendedLabel, asapLabel = 'As soon as possible', minDate }) => {
  // value: 'asap' | 'YYYY-MM-DD'
  const [open, setOpen] = useState(false);
  const [cursor, setCursor] = useState(() => {
    const base = value && value !== 'asap' ? new Date(value) : (recommended || new Date());
    return new Date(base.getFullYear(), base.getMonth(), 1);
  });
  const ref = useRef(null);

  useEffect(() => {
    if (!open) return;
    const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', onDoc);
    return () => document.removeEventListener('mousedown', onDoc);
  }, [open]);

  const selectedDate = value && value !== 'asap' ? new Date(value) : null;
  const today = new Date(); today.setHours(0,0,0,0);
  const min = minDate ? new Date(minDate) : today;

  const sameDay = (a, b) => a && b && a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
  const toISO = (d) => `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;

  // Build calendar grid (6 weeks)
  const firstDow = new Date(cursor.getFullYear(), cursor.getMonth(), 1).getDay(); // 0=Sun
  const daysInMonth = new Date(cursor.getFullYear(), cursor.getMonth()+1, 0).getDate();
  const cells = [];
  // Lead with previous-month tail
  const prevDays = new Date(cursor.getFullYear(), cursor.getMonth(), 0).getDate();
  for (let i = firstDow - 1; i >= 0; i--) cells.push({ d: new Date(cursor.getFullYear(), cursor.getMonth()-1, prevDays - i), outside: true });
  for (let i = 1; i <= daysInMonth; i++) cells.push({ d: new Date(cursor.getFullYear(), cursor.getMonth(), i), outside: false });
  // Trail with next-month head to fill 42 cells
  let nextI = 1;
  while (cells.length < 42) { cells.push({ d: new Date(cursor.getFullYear(), cursor.getMonth()+1, nextI++), outside: true }); }

  const monthLabel = cursor.toLocaleDateString('en-US', { month: 'long', year: 'numeric' });
  const dows = ['Su','Mo','Tu','We','Th','Fr','Sa'];

  const displayValue = value === 'asap'
    ? <span>{asapLabel}{recommended && <span style={{ color: 'var(--ink-3)', marginLeft: 6 }}>({fmtDate(recommended)})</span>}</span>
    : selectedDate ? fmtDate(selectedDate) : 'Pick a date';

  const pickDate = (d) => {
    if (d < min) return;
    onChange(toISO(d));
    setOpen(false);
  };

  const pickAsap = () => { onChange('asap'); setOpen(false); };

  return (
    <div ref={ref} style={{ position: 'relative', display: 'inline-flex' }}>
      <button onClick={() => setOpen(o => !o)} style={{
        display: 'inline-flex', alignItems: 'center', gap: 7,
        padding: '5px 10px', background: 'var(--panel)', border: '1px solid var(--line)',
        borderRadius: 3, cursor: 'pointer', fontFamily: 'Kanit, sans-serif', fontSize: 12,
        color: 'var(--ink)', minWidth: 200,
      }}>
        <Icon name="calendar" size={12} color="var(--ink-3)"/>
        <span style={{ flex: 1, textAlign: 'left' }}>{displayValue}</span>
        <Icon name="chevronDown" size={10} color="var(--ink-3)"/>
      </button>
      {open && (
        <div style={{
          position: 'absolute', top: 'calc(100% + 6px)', right: 0,
          background: 'var(--panel)', border: '1px solid var(--line)', borderRadius: 4,
          boxShadow: 'var(--shadow-pop)', padding: 12, zIndex: 200, width: 280,
        }}>
          {/* Quick action: ASAP */}
          <button onClick={pickAsap} style={{
            width: '100%', padding: '8px 10px',
            background: value === 'asap' ? 'var(--bg-2)' : 'transparent',
            border: '1px solid var(--line)', borderRadius: 3, cursor: 'pointer',
            fontFamily: 'Kanit, sans-serif', fontSize: 12, color: 'var(--ink)',
            display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10, textAlign: 'left',
          }}>
            <Icon name="sparkles" size={12} color="var(--accent-2)"/>
            <div style={{ flex: 1 }}>
              <div style={{ fontWeight: 500 }}>{asapLabel}</div>
              {recommendedLabel && <div style={{ fontSize: 10.5, color: 'var(--ink-3)' }}>{recommendedLabel}</div>}
            </div>
            {value === 'asap' && <Icon name="check" size={12} color="var(--positive)"/>}
          </button>

          {/* Month nav */}
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 8 }}>
            <button onClick={() => setCursor(c => new Date(c.getFullYear(), c.getMonth()-1, 1))} style={navArrowBtn}>
              <Icon name="chevronLeft" size={11}/>
            </button>
            <div className="num" style={{ fontSize: 12.5, fontWeight: 500 }}>{monthLabel}</div>
            <button onClick={() => setCursor(c => new Date(c.getFullYear(), c.getMonth()+1, 1))} style={navArrowBtn}>
              <Icon name="chevron" size={11}/>
            </button>
          </div>

          {/* Day-of-week header */}
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: 1, marginBottom: 4 }}>
            {dows.map(d => (
              <div key={d} className="eyebrow" style={{ textAlign: 'center', fontSize: 9.5, padding: '4px 0' }}>{d}</div>
            ))}
          </div>

          {/* Day grid */}
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: 1 }}>
            {cells.map((c, i) => {
              const isSel = sameDay(c.d, selectedDate);
              const isToday = sameDay(c.d, today);
              const isRec = sameDay(c.d, recommended);
              const disabled = c.d < min;
              return (
                <button key={i} onClick={() => pickDate(c.d)} disabled={disabled}
                  style={{
                    aspectRatio: '1/1', padding: 0, border: 'none', cursor: disabled ? 'not-allowed' : 'pointer',
                    fontFamily: 'IBM Plex Mono', fontSize: 11.5,
                    background: isSel ? 'var(--ink)' : isRec && !c.outside ? 'var(--accent-2)' + '20' : 'transparent',
                    color: isSel ? '#fff'
                         : disabled ? 'var(--ink-4)'
                         : c.outside ? 'var(--ink-4)'
                         : isRec ? 'var(--accent-2)'
                         : 'var(--ink)',
                    fontWeight: isSel || isRec ? 600 : 400,
                    borderRadius: 3, position: 'relative',
                    transition: 'background 120ms',
                  }}
                  onMouseEnter={e => { if (!disabled && !isSel) e.currentTarget.style.background = 'var(--bg-hover)'; }}
                  onMouseLeave={e => { if (!disabled && !isSel) e.currentTarget.style.background = isRec && !c.outside ? 'var(--accent-2)' + '20' : 'transparent'; }}>
                  {c.d.getDate()}
                  {isToday && !isSel && (
                    <span style={{ position: 'absolute', bottom: 2, left: '50%', transform: 'translateX(-50%)', width: 3, height: 3, borderRadius: '50%', background: 'var(--accent-2)' }}/>
                  )}
                </button>
              );
            })}
          </div>

          {/* Footer legend */}
          {recommended && (
            <div style={{ marginTop: 10, paddingTop: 8, borderTop: '1px solid var(--line-2)', fontSize: 10.5, color: 'var(--ink-3)', display: 'flex', alignItems: 'center', gap: 6 }}>
              <span style={{ width: 8, height: 8, borderRadius: 2, background: 'var(--accent-2)' + '40', border: '1px solid var(--accent-2)' }}/>
              SLA recommended · <span className="num" style={{ color: 'var(--accent-2)', fontWeight: 500 }}>{fmtDate(recommended)}</span>
            </div>
          )}
        </div>
      )}
    </div>
  );
};

const navArrowBtn = {
  width: 24, height: 24, padding: 0,
  background: 'transparent', border: 'none', cursor: 'pointer',
  color: 'var(--ink-2)', borderRadius: 3,
  display: 'grid', placeItems: 'center',
};

Object.assign(window, {
  fmtBaht, fmtInt, fmtDate, fmtDateShort, relTime,
  Icon, ProductGlyph, PRODUCT_ICONS, StatusChip, Avatar,
  Segmented, Button, Empty, Modal,
  Field, TextInput, Select, Textarea, inputStyle, DatePicker,
  showToast, initials, avatarColor,
});
