/* ============================================================
   Customize modals — Add Metrics, Available Charts, Date Range.
   Light-theme counterparts to the dark reference screenshots.
   ============================================================ */

/* ---------- Shared modal shell ---------- */
const ModalShell = ({ open, onClose, title, subtitle, width = 860, children, footer }) => {
  React.useEffect(() => {
    if (!open) return;
    const h = (e) => { if (e.key === 'Escape') onClose(); };
    document.addEventListener('keydown', h);
    return () => document.removeEventListener('keydown', h);
  }, [open, onClose]);
  if (!open) return null;
  return (
    <div onClick={onClose} style={{
      position: 'fixed', inset: 0, zIndex: 200,
      background: 'rgba(15,23,42,0.45)', backdropFilter: 'blur(2px)',
      display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 20,
    }}>
      <div onClick={e => e.stopPropagation()} style={{
        width, maxWidth: '100%', maxHeight: '88vh',
        background: 'var(--rc-card)', borderRadius: 14,
        border: '1px solid var(--rc-border)',
        boxShadow: '0 30px 60px -15px rgba(15,23,42,0.35)',
        overflow: 'hidden', display: 'flex', flexDirection: 'column',
      }}>
        <div style={{
          padding: '16px 22px', borderBottom: '1px solid var(--rc-border)',
          display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 12,
          flexShrink: 0,
        }}>
          <div>
            <div style={{ fontFamily: 'Poppins', fontSize: 17, fontWeight: 700, letterSpacing: '-0.01em', color: 'var(--rc-text)' }}>
              {title}
            </div>
            {subtitle && (
              <div style={{ fontSize: 12, color: 'var(--rc-text-sub)', marginTop: 3, fontVariantNumeric: 'tabular-nums' }}>
                {subtitle}
              </div>
            )}
          </div>
          <IconBtn name="x" title="Close" onClick={onClose} />
        </div>

        <div style={{ flex: 1, overflow: 'auto', padding: 22 }}>
          {children}
        </div>

        {footer && (
          <div style={{
            padding: '14px 22px', borderTop: '1px solid var(--rc-border)',
            background: 'var(--rc-subtle)',
            display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12,
            flexShrink: 0,
          }}>{footer}</div>
        )}
      </div>
    </div>
  );
};

/* ---------- Category chip palette ---------- */
const categoryChip = (cat) => {
  const palette = {
    sales:       { bg: 'color-mix(in srgb, #2563eb 10%, transparent)', fg: '#2563eb', br: 'color-mix(in srgb, #2563eb 28%, transparent)' },
    advertising: { bg: 'color-mix(in srgb, #16a34a 12%, transparent)', fg: 'var(--rc-green-ink)', br: 'color-mix(in srgb, #16a34a 32%, transparent)' },
    efficiency:  { bg: 'color-mix(in srgb, #8b5cf6 12%, transparent)', fg: '#7c3aed', br: 'color-mix(in srgb, #8b5cf6 32%, transparent)' },
    traffic:     { bg: 'color-mix(in srgb, #f59e0b 12%, transparent)', fg: '#b45309', br: 'color-mix(in srgb, #f59e0b 32%, transparent)' },
    other:       { bg: 'var(--rc-chip-bg)', fg: 'var(--rc-text-sub)', br: 'var(--rc-border)' },
  };
  return palette[cat] || palette.other;
};

const CategoryTag = ({ cat }) => {
  const c = categoryChip(cat);
  return (
    <span style={{
      padding: '2px 9px', borderRadius: 9999, fontSize: 10, fontWeight: 600,
      background: c.bg, color: c.fg, border: `1px solid ${c.br}`,
      letterSpacing: 0.2, lineHeight: 1.5, whiteSpace: 'nowrap',
    }}>{cat}</span>
  );
};

/* ---------- Checkbox ---------- */
const CustomCheck = ({ checked, onChange }) => (
  <button onClick={(e) => { e.stopPropagation(); onChange(!checked); }} style={{
    width: 18, height: 18, borderRadius: 4, flexShrink: 0,
    border: '1.5px solid ' + (checked ? 'var(--rc-green)' : 'var(--rc-border)'),
    background: checked ? 'var(--rc-green)' : 'var(--rc-card)',
    display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
    cursor: 'pointer', padding: 0,
  }}>
    {checked && <Icon name="check" size={11} color="#fff" strokeWidth={3} />}
  </button>
);

/* ============================================================
   1. Add Metrics modal — grid of metric preview cards
   ============================================================ */
const METRIC_CATEGORIES = {
  // Sales & units
  ops: 'sales', adSales: 'advertising', adSalesPct: 'advertising',
  totOrdItems: 'sales', aovByOrdItem: 'sales', unitsOrdered: 'sales',
  asp: 'sales', unitsPerOrdItem: 'sales', aov: 'sales', orders: 'sales', units: 'sales',
  // Advertising investment / return
  spend: 'advertising', acos: 'efficiency', tacos: 'efficiency', roas: 'advertising',
  adOrders: 'advertising',
  // Traffic
  impressions: 'traffic', clicks: 'traffic', sessions: 'traffic', pageViews: 'traffic',
  // Efficiency
  ctr: 'efficiency', cpc: 'efficiency', cpa: 'efficiency', convR: 'efficiency',
  adConvR: 'efficiency', unitSessPct: 'efficiency',
  // Coverage
  buyBoxPct: 'traffic', avgOfrCount: 'traffic',
  // Rollup / other
  asins: 'other', campaigns: 'other',
};

const AddMetricsModal = ({ open, onClose, allMetrics, selectedIds, onSave, comparisonLabel, currency = 'USD' }) => {
  const [sel, setSel] = React.useState(new Set(selectedIds));
  const [search, setSearch] = React.useState('');
  // Display order — selected first (in saved order), then the rest in allMetrics order.
  // Users can drag to rearrange; this order is what the scorecard grid renders.
  const [order, setOrder] = React.useState(() => {
    const byId = new Set(allMetrics);
    const selIds = selectedIds.filter(id => byId.has(id));
    const rest = allMetrics.filter(id => !selIds.includes(id));
    return [...selIds, ...rest];
  });
  const [dragIdx, setDragIdx] = React.useState(null);
  const [hoverIdx, setHoverIdx] = React.useState(null);

  React.useEffect(() => {
    setSel(new Set(selectedIds));
    const byId = new Set(allMetrics);
    const selIds = selectedIds.filter(id => byId.has(id));
    const rest = allMetrics.filter(id => !selIds.includes(id));
    setOrder([...selIds, ...rest]);
  }, [selectedIds, allMetrics, open]);

  const toggle = (id) => {
    setSel(prev => {
      const next = new Set(prev);
      if (next.has(id)) next.delete(id); else next.add(id);
      return next;
    });
  };

  const handleReorderDrop = (to) => {
    if (dragIdx == null || dragIdx === to) { setDragIdx(null); setHoverIdx(null); return; }
    setOrder(prev => {
      const next = [...prev];
      const [moved] = next.splice(dragIdx, 1);
      next.splice(to, 0, moved);
      return next;
    });
    setDragIdx(null); setHoverIdx(null);
  };

  // Render in user-controlled order, filtered by search. Preserve original
  // index-in-order so DnD operates on the canonical order array.
  const displayRows = React.useMemo(() => {
    const q = search.trim().toLowerCase();
    const rows = order.map((id, origIdx) => ({ id, origIdx }));
    if (!q) return rows;
    return rows.filter(r =>
      r.id.toLowerCase().includes(q) ||
      (METRIC_LABELS[r.id] || '').toLowerCase().includes(q)
    );
  }, [order, search]);
  const canReorder = !search;

  // Save in order — only selected, in the order the user set.
  const saveIds = order.filter(id => sel.has(id));

  return (
    <ModalShell open={open} onClose={onClose}
      title="Add metrics"
      subtitle={comparisonLabel}
      width={900}
      footer={
        <>
          <div style={{ fontSize: 12, color: 'var(--rc-text-sub)' }}>
            <strong style={{ color: 'var(--rc-text)' }}>{sel.size}</strong> metric{sel.size === 1 ? '' : 's'} selected
          </div>
          <div style={{ display: 'flex', gap: 8 }}>
            <button onClick={onClose} style={secondaryBtn}>Cancel</button>
            <button onClick={() => { onSave(saveIds); onClose(); }} style={primaryBtn}>
              <Icon name="plus" size={12} strokeWidth={2.4} />
              Add selected metrics
            </button>
          </div>
        </>
      }
    >
      {/* Search */}
      <div style={{ position: 'relative', marginBottom: 16 }}>
        <span style={{ position: 'absolute', left: 12, top: '50%', transform: 'translateY(-50%)', color: 'var(--rc-text-mute)' }}>
          <Icon name="search" size={14} />
        </span>
        <input value={search} onChange={e => setSearch(e.target.value)}
          placeholder="Search metrics…"
          style={{
            width: '100%', padding: '10px 12px 10px 36px', borderRadius: 9,
            border: '1px solid var(--rc-border)', background: 'var(--rc-card)',
            color: 'var(--rc-text)', fontSize: 13, fontFamily: 'inherit', outline: 'none',
          }} />
      </div>

      {/* Grid */}
      <div style={{
        display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(190px, 1fr))',
        gap: 10,
      }}>
        {displayRows.map(({ id, origIdx }) => {
          const checked = sel.has(id);
          const cat = METRIC_CATEGORIES[id] || 'other';
          const label = METRIC_LABELS[id] || id;
          const val = window.BASELINES?.[id] ?? (window.series?.(id, 30)?.[0] ?? 0);
          const priorVal = val * 0.72;
          const ch = window.formatChange?.(id, val, priorVal, 'pct');
          const netCh = window.formatChange?.(id, val, priorVal, 'net');
          const isDragging = dragIdx === origIdx;
          const isTarget = hoverIdx === origIdx && dragIdx != null && dragIdx !== origIdx;
          const insertBefore = isTarget && dragIdx > origIdx;
          const insertAfter = isTarget && dragIdx < origIdx;
          return (
            <div key={id} style={{ position: 'relative' }}>
              {insertBefore && (
                <div style={{
                  position: 'absolute', left: -6, top: 4, bottom: 4, width: 3,
                  background: 'var(--rc-chart-1)', borderRadius: 2, zIndex: 2,
                  boxShadow: '0 0 0 4px color-mix(in srgb, var(--rc-chart-1) 18%, transparent)',
                }} />
              )}
              {insertAfter && (
                <div style={{
                  position: 'absolute', right: -6, top: 4, bottom: 4, width: 3,
                  background: 'var(--rc-chart-1)', borderRadius: 2, zIndex: 2,
                  boxShadow: '0 0 0 4px color-mix(in srgb, var(--rc-chart-1) 18%, transparent)',
                }} />
              )}
              <div
                draggable={canReorder}
                onDragStart={(e) => {
                  if (!canReorder) return;
                  setDragIdx(origIdx);
                  e.dataTransfer.effectAllowed = 'move';
                  try {
                    const rect = e.currentTarget.getBoundingClientRect();
                    const ghost = e.currentTarget.cloneNode(true);
                    ghost.style.opacity = '0.75';
                    ghost.style.position = 'absolute';
                    ghost.style.top = '-2000px';
                    ghost.style.width = rect.width + 'px';
                    ghost.style.transform = 'rotate(-0.5deg) scale(0.99)';
                    ghost.style.boxShadow = '0 14px 36px -10px rgba(15,23,42,0.35)';
                    document.body.appendChild(ghost);
                    e.dataTransfer.setDragImage(ghost, rect.width / 2, rect.height / 2);
                    setTimeout(() => ghost.remove(), 0);
                  } catch (err) {}
                }}
                onDragOver={(e) => { if (dragIdx == null) return; e.preventDefault(); setHoverIdx(origIdx); }}
                onDragLeave={() => { if (hoverIdx === origIdx) setHoverIdx(null); }}
                onDrop={(e) => { e.preventDefault(); handleReorderDrop(origIdx); }}
                onDragEnd={() => { setDragIdx(null); setHoverIdx(null); }}
                onClick={() => toggle(id)}
                style={{
                  position: 'relative',
                  display: 'flex', flexDirection: 'column', gap: 6,
                  padding: '12px 12px 10px',
                  border: '1.5px solid ' + (checked ? 'var(--rc-green)' : 'var(--rc-border)'),
                  borderRadius: 10,
                  background: checked ? 'color-mix(in srgb, var(--rc-green) 5%, var(--rc-card))' : 'var(--rc-card)',
                  textAlign: 'left',
                  cursor: canReorder ? (isDragging ? 'grabbing' : 'grab') : 'pointer',
                  fontFamily: 'inherit',
                  opacity: isDragging ? 0.4 : 1,
                  transform: isDragging ? 'scale(0.99)' : 'scale(1)',
                  transition: 'border-color 0.15s, background 0.15s, opacity 0.12s, transform 0.12s',
                  userSelect: 'none',
                }}
                onMouseEnter={e => { if (!checked && !isDragging) e.currentTarget.style.borderColor = 'var(--rc-text-mute)'; }}
                onMouseLeave={e => { if (!checked) e.currentTarget.style.borderColor = 'var(--rc-border)'; }}>
                {canReorder && (
                  <span
                    title="Drag to reorder"
                    onClick={(e) => e.stopPropagation()}
                    style={{
                      position: 'absolute', top: 10, right: 10,
                      display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                      color: 'var(--rc-text-mute)', cursor: 'grab', lineHeight: 0,
                      opacity: 0.55,
                    }}>
                    <svg width="10" height="14" viewBox="0 0 10 14" fill="currentColor" aria-hidden="true">
                      <circle cx="2" cy="2" r="1.3" />
                      <circle cx="8" cy="2" r="1.3" />
                      <circle cx="2" cy="7" r="1.3" />
                      <circle cx="8" cy="7" r="1.3" />
                      <circle cx="2" cy="12" r="1.3" />
                      <circle cx="8" cy="12" r="1.3" />
                    </svg>
                  </span>
                )}
                {/* Top row: checkbox + category */}
                <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', paddingRight: canReorder ? 18 : 0 }}>
                  <CustomCheck checked={checked} onChange={() => toggle(id)} />
                  <CategoryTag cat={cat} />
                </div>
                <div style={{ fontSize: 11, color: 'var(--rc-text-sub)', fontWeight: 500, marginTop: 2 }}>
                  {label}
                </div>
                <div style={{
                  fontSize: 20, fontWeight: 700, fontFamily: 'Poppins',
                  color: 'var(--rc-text)', letterSpacing: '-0.02em',
                  fontVariantNumeric: 'tabular-nums', lineHeight: 1.1,
                }}>
                  {window.formatValue?.(id, val, { currency }) ?? val}
                </div>
                <div style={{ display: 'flex', gap: 8, fontSize: 10, fontVariantNumeric: 'tabular-nums', marginTop: 2 }}>
                  <span style={{ color: 'var(--rc-green-ink)', fontWeight: 600 }}>{netCh || '+—'}</span>
                  <span style={{ color: 'var(--rc-green-ink)', fontWeight: 600 }}>{ch || '+—%'}</span>
                </div>
              </div>
            </div>
          );
        })}
        {displayRows.length === 0 && (
          <div style={{ gridColumn: '1 / -1', padding: 32, textAlign: 'center', fontSize: 12, color: 'var(--rc-text-sub)' }}>
            No metrics match "{search}".
          </div>
        )}
      </div>
    </ModalShell>
  );
};

/* ============================================================
   2. Available Charts modal — list of preset chart definitions
   ============================================================ */
const CHART_PRESETS = [
  // IDs aligned with ALL_CHART_PANELS in pages_1_2.jsx.
  { id: 'salesmix',    name: 'Sales Mix',              cat: 'sales',       desc: 'Ad Sales, OPS, and Ad % of Total Sales over time.' },
  { id: 'invest',      name: 'Advertising Investment', cat: 'advertising', desc: 'Ad Spend, ACoS, TACoS efficiency trends.' },
  { id: 'return',      name: 'Advertising Return',     cat: 'advertising', desc: 'CPA, AOV, and ROAS.' },
  { id: 'efficiency',  name: 'Efficiency',             cat: 'efficiency',  desc: 'Ad ConvR and Ord. Item Sess.%.' },
  { id: 'sales',       name: 'Sales',                  cat: 'sales',       desc: 'OPS, ordered items, AOV by item.' },
  { id: 'units',       name: 'Units',                  cat: 'sales',       desc: 'Units ordered, ASP, units per item.' },
  { id: 'traffic',     name: 'Traffic',                cat: 'traffic',     desc: 'Sessions, page views, session %.' },
  { id: 'coverage',    name: 'Coverage',               cat: 'traffic',     desc: 'Buy Box % and avg. offer count.' },
  { id: 'perf',        name: 'Performance',            cat: 'advertising', desc: 'Ad Spend, Ad Sales, ACoS.' },
  { id: 'reach',       name: 'Awareness',              cat: 'traffic',     desc: 'Impressions, clicks, CTR.' },
  { id: 'acq',         name: 'Acquisitions',           cat: 'advertising', desc: 'CPA, AOV, Ad Orders.' },
  { id: 'eff',         name: 'Efficiency',             cat: 'efficiency',  desc: 'CPC and ConvR.' },
  { id: 'trafficMix',  name: 'Traffic Mix',            cat: 'traffic',     desc: 'Sessions, page views, clicks.' },
  { id: 'convFunnel',  name: 'Conversion Funnel',      cat: 'efficiency',  desc: 'Impression → click → conv. funnel.' },
  { id: 'salesad',     name: 'Sales vs. Ad Investment',cat: 'sales',       desc: 'OPS against ad spend.' },
];

const ChartGlyph = ({ cat }) => {
  const c = categoryChip(cat);
  return (
    <span style={{
      width: 34, height: 34, borderRadius: 8, flexShrink: 0,
      background: c.bg, color: c.fg,
      display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
    }}>
      <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
        <polyline points="3 17 8 12 12 15 16 8 21 13" />
      </svg>
    </span>
  );
};

const AvailableChartsModal = ({ open, onClose, selectedIds, onSave }) => {
  const [sel, setSel] = React.useState(new Set(selectedIds));
  const [search, setSearch] = React.useState('');
  // Display order — selected first (in saved order), then the rest in preset order.
  // Users can drag to rearrange; this order is what PageOverTime renders.
  const [order, setOrder] = React.useState(() => {
    const byId = new Set(CHART_PRESETS.map(c => c.id));
    const selIds = selectedIds.filter(id => byId.has(id));
    const rest = CHART_PRESETS.map(c => c.id).filter(id => !selIds.includes(id));
    return [...selIds, ...rest];
  });
  const [dragIdx, setDragIdx] = React.useState(null);
  const [hoverIdx, setHoverIdx] = React.useState(null);

  React.useEffect(() => {
    setSel(new Set(selectedIds));
    const byId = new Set(CHART_PRESETS.map(c => c.id));
    const selIds = selectedIds.filter(id => byId.has(id));
    const rest = CHART_PRESETS.map(c => c.id).filter(id => !selIds.includes(id));
    setOrder([...selIds, ...rest]);
  }, [selectedIds, open]);

  const toggle = (id) => {
    setSel(prev => {
      const next = new Set(prev);
      if (next.has(id)) next.delete(id); else next.add(id);
      return next;
    });
  };

  const handleReorderDrop = (to) => {
    if (dragIdx == null || dragIdx === to) { setDragIdx(null); setHoverIdx(null); return; }
    setOrder(prev => {
      const next = [...prev];
      const [moved] = next.splice(dragIdx, 1);
      next.splice(to, 0, moved);
      return next;
    });
    setDragIdx(null); setHoverIdx(null);
  };

  // Render in user-controlled order, filtered by search. Preserve original
  // index-in-order so DnD operates on the canonical order array.
  const presetById = Object.fromEntries(CHART_PRESETS.map(c => [c.id, c]));
  const displayRows = order
    .map((id, origIdx) => ({ chart: presetById[id], origIdx }))
    .filter(row => row.chart)
    .filter(row => !search ||
      row.chart.name.toLowerCase().includes(search.toLowerCase()) ||
      row.chart.desc.toLowerCase().includes(search.toLowerCase()));
  const canReorder = !search;  // dragging while filtered would be confusing

  // Save in order — only selected, but in the order the user set.
  const saveIds = order.filter(id => sel.has(id));

  return (
    <ModalShell open={open} onClose={onClose}
      title="Available charts"
      width={620}
      footer={
        <>
          <div style={{ fontSize: 12, color: 'var(--rc-text-sub)' }}>
            <strong style={{ color: 'var(--rc-text)' }}>{sel.size}</strong> chart{sel.size === 1 ? '' : 's'} selected
          </div>
          <div style={{ display: 'flex', gap: 8 }}>
            <button onClick={onClose} style={secondaryBtn}>Cancel</button>
            <button onClick={() => { onSave(saveIds); onClose(); }} style={primaryBtn}>
              <Icon name="plus" size={12} strokeWidth={2.4} />
              Add selected charts
            </button>
          </div>
        </>
      }
    >
      <div style={{ position: 'relative', marginBottom: 14 }}>
        <span style={{ position: 'absolute', left: 12, top: '50%', transform: 'translateY(-50%)', color: 'var(--rc-text-mute)' }}>
          <Icon name="search" size={14} />
        </span>
        <input value={search} onChange={e => setSearch(e.target.value)}
          placeholder="Search charts…"
          style={{
            width: '100%', padding: '10px 12px 10px 36px', borderRadius: 9,
            border: '1px solid var(--rc-border)', background: 'var(--rc-card)',
            color: 'var(--rc-text)', fontSize: 13, fontFamily: 'inherit', outline: 'none',
          }} />
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
        {displayRows.map(({ chart, origIdx }) => {
          const checked = sel.has(chart.id);
          const isDragging = dragIdx === origIdx;
          const isTarget = hoverIdx === origIdx && dragIdx != null && dragIdx !== origIdx;
          const insertAbove = isTarget && dragIdx > origIdx;
          const insertBelow = isTarget && dragIdx < origIdx;
          return (
            <div key={chart.id} style={{ position: 'relative' }}>
              {insertAbove && (
                <div style={{
                  position: 'absolute', left: 0, right: 0, top: -5, height: 3,
                  background: 'var(--rc-chart-1)', borderRadius: 2, zIndex: 2,
                  boxShadow: '0 0 0 4px color-mix(in srgb, var(--rc-chart-1) 18%, transparent)',
                }} />
              )}
              {insertBelow && (
                <div style={{
                  position: 'absolute', left: 0, right: 0, bottom: -5, height: 3,
                  background: 'var(--rc-chart-1)', borderRadius: 2, zIndex: 2,
                  boxShadow: '0 0 0 4px color-mix(in srgb, var(--rc-chart-1) 18%, transparent)',
                }} />
              )}
              <div
                draggable={canReorder}
                onDragStart={(e) => {
                  if (!canReorder) return;
                  setDragIdx(origIdx);
                  e.dataTransfer.effectAllowed = 'move';
                  try {
                    const rect = e.currentTarget.getBoundingClientRect();
                    const ghost = e.currentTarget.cloneNode(true);
                    ghost.style.opacity = '0.75';
                    ghost.style.position = 'absolute';
                    ghost.style.top = '-2000px';
                    ghost.style.width = rect.width + 'px';
                    ghost.style.transform = 'rotate(-0.5deg) scale(0.99)';
                    ghost.style.boxShadow = '0 14px 36px -10px rgba(15,23,42,0.35)';
                    document.body.appendChild(ghost);
                    e.dataTransfer.setDragImage(ghost, 24, rect.height / 2);
                    setTimeout(() => ghost.remove(), 0);
                  } catch (err) {}
                }}
                onDragOver={(e) => { if (dragIdx == null) return; e.preventDefault(); setHoverIdx(origIdx); }}
                onDragLeave={() => { if (hoverIdx === origIdx) setHoverIdx(null); }}
                onDrop={(e) => { e.preventDefault(); handleReorderDrop(origIdx); }}
                onDragEnd={() => { setDragIdx(null); setHoverIdx(null); }}
                onClick={() => toggle(chart.id)}
                style={{
                  display: 'flex', alignItems: 'center', gap: 10,
                  padding: '12px 14px', borderRadius: 10,
                  border: '1.5px solid ' + (checked ? 'var(--rc-green)' : 'var(--rc-border)'),
                  background: checked ? 'color-mix(in srgb, var(--rc-green) 5%, var(--rc-card))' : 'var(--rc-card)',
                  textAlign: 'left', cursor: canReorder ? (isDragging ? 'grabbing' : 'grab') : 'pointer',
                  opacity: isDragging ? 0.4 : 1,
                  transform: isDragging ? 'scale(0.995)' : 'scale(1)',
                  transition: 'border-color 0.15s, background 0.15s, opacity 0.12s, transform 0.12s',
                  userSelect: 'none',
                }}>
                {canReorder && (
                  <span
                    title="Drag to reorder"
                    onClick={(e) => e.stopPropagation()}
                    style={{
                      display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                      width: 16, color: 'var(--rc-text-mute)', cursor: 'grab', flexShrink: 0,
                      lineHeight: 0,
                    }}>
                    <svg width="10" height="14" viewBox="0 0 10 14" fill="currentColor" aria-hidden="true">
                      <circle cx="2" cy="2" r="1.3" />
                      <circle cx="8" cy="2" r="1.3" />
                      <circle cx="2" cy="7" r="1.3" />
                      <circle cx="8" cy="7" r="1.3" />
                      <circle cx="2" cy="12" r="1.3" />
                      <circle cx="8" cy="12" r="1.3" />
                    </svg>
                  </span>
                )}
                <CustomCheck checked={checked} onChange={() => toggle(chart.id)} />
                <ChartGlyph cat={chart.cat} />
                <div style={{ flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', gap: 2 }}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                    <span style={{ fontSize: 13, fontWeight: 600, color: 'var(--rc-text)' }}>{chart.name}</span>
                    <CategoryTag cat={chart.cat} />
                  </div>
                  <span style={{ fontSize: 11, color: 'var(--rc-text-sub)' }}>{chart.desc}</span>
                </div>
              </div>
            </div>
          );
        })}
      </div>
    </ModalShell>
  );
};

/* ============================================================
   3. Date range picker — preset rail + two-month calendar
   ============================================================ */
const DATE_PICKER_PRESETS = [
  { value: 'yesterday',  label: 'Yesterday' },
  { value: 'l7',         label: 'Last 7 Days' },
  { value: 'thisWeek',   label: 'This Week' },
  { value: 'lastWeek',   label: 'Last Week' },
  { value: 'l30',        label: 'Last 30 Days' },
  { value: 'mtd',        label: 'This Month' },
  { value: 'lastMonth',  label: 'Last Month' },
  { value: 'ytd',        label: 'Year To Date' },
  { value: 'lifetime',   label: 'Lifetime' },
  { value: 'custom',     label: 'Custom range' },
];

// Build a calendar month grid (7 cols × 6 rows).
const buildMonth = (year, monthIdx) => {
  const firstOfMonth = new Date(year, monthIdx, 1);
  const lastOfMonth = new Date(year, monthIdx + 1, 0);
  const startDay = firstOfMonth.getDay();
  const days = [];
  // leading prev-month days
  for (let i = startDay - 1; i >= 0; i--) {
    const d = new Date(year, monthIdx, -i);
    days.push({ date: d, outside: true });
  }
  // this month
  for (let i = 1; i <= lastOfMonth.getDate(); i++) {
    days.push({ date: new Date(year, monthIdx, i), outside: false });
  }
  // trailing next-month days to 42 cells
  while (days.length < 42) {
    const last = days[days.length - 1].date;
    const d = new Date(last);
    d.setDate(d.getDate() + 1);
    days.push({ date: d, outside: true });
  }
  return days;
};

const sameDay = (a, b) => a && b &&
  a.getFullYear() === b.getFullYear() &&
  a.getMonth() === b.getMonth() &&
  a.getDate() === b.getDate();

const inRange = (d, start, end) => {
  if (!start || !end) return false;
  const t = d.getTime();
  const s = start.getTime();
  const e = end.getTime();
  return t >= Math.min(s, e) && t <= Math.max(s, e);
};

const fmtDate = (d) => {
  if (!d) return '—';
  const mm = String(d.getMonth() + 1).padStart(2, '0');
  const dd = String(d.getDate()).padStart(2, '0');
  return `${mm}/${dd}/${d.getFullYear()}`;
};

const MonthGrid = ({ year, monthIdx, rangeStart, rangeEnd, compareStart, compareEnd, today, onPick }) => {
  const days = buildMonth(year, monthIdx);
  const monthName = new Date(year, monthIdx, 1).toLocaleString('en-US', { month: 'long', year: 'numeric' });
  return (
    <div style={{ flex: 1, minWidth: 0 }}>
      <div style={{
        fontFamily: 'Poppins', fontSize: 13, fontWeight: 600,
        color: 'var(--rc-text)', textAlign: 'center', marginBottom: 10,
        letterSpacing: '-0.01em',
      }}>{monthName}</div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: 2, fontSize: 10, color: 'var(--rc-text-sub)', marginBottom: 4, fontWeight: 600, letterSpacing: 0.4 }}>
        {['Su','Mo','Tu','We','Th','Fr','Sa'].map(d => (
          <div key={d} style={{ textAlign: 'center', padding: '4px 0' }}>{d}</div>
        ))}
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: 2 }}>
        {days.map((cell, i) => {
          const d = cell.date;
          const isStart = sameDay(d, rangeStart);
          const isEnd = sameDay(d, rangeEnd);
          const isRange = inRange(d, rangeStart, rangeEnd);
          const isCmpRange = inRange(d, compareStart, compareEnd);
          const isCmpEdge = sameDay(d, compareStart) || sameDay(d, compareEnd);
          const isToday = sameDay(d, today);
          const isEdge = isStart || isEnd;

          return (
            <button key={i} onClick={() => !cell.outside && onPick(d)} disabled={cell.outside}
              style={{
                aspectRatio: '1 / 1',
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                fontSize: 11, fontWeight: isEdge ? 700 : 500,
                fontFamily: 'inherit', fontVariantNumeric: 'tabular-nums',
                cursor: cell.outside ? 'default' : 'pointer',
                padding: 0, borderRadius: 9999,
                color: cell.outside ? 'var(--rc-text-mute)'
                  : isEdge ? '#fff'
                  : isRange ? 'var(--rc-green-ink)'
                  : isCmpEdge ? 'var(--rc-text)'
                  : 'var(--rc-text)',
                background: isEdge ? 'var(--rc-green)'
                  : isRange ? 'color-mix(in srgb, var(--rc-green) 16%, transparent)'
                  : 'transparent',
                border: isCmpEdge && !isEdge ? '1.5px solid var(--rc-green)'
                  : isToday && !isEdge ? '1px solid var(--rc-border)' : '1.5px solid transparent',
                boxShadow: isEdge ? '0 1px 2px rgba(22,163,74,0.3)' : 'none',
                opacity: cell.outside ? 0.5 : 1,
                outline: isCmpRange && !isEdge && !isCmpEdge ? '1px dashed color-mix(in srgb, var(--rc-green) 35%, transparent)' : 'none',
                outlineOffset: -2,
                position: 'relative',
              }}>
              {d.getDate()}
            </button>
          );
        })}
      </div>
    </div>
  );
};

const DateRangePickerModal = ({ open, onClose, initialPreset, initialStart, initialEnd, compareMode, onSave }) => {
  const today = new Date(2026, 3, 22); // Apr 22, 2026 — matches fixture "today"
  const [preset, setPreset] = React.useState(initialPreset || 'mtd');
  const [start, setStart] = React.useState(initialStart || new Date(2026, 2, 1));
  const [end, setEnd] = React.useState(initialEnd || new Date(2026, 2, 31));
  const [compare, setCompare] = React.useState(compareMode || 'pp'); // 'pp' | 'py'
  const [anchor, setAnchor] = React.useState(new Date(2026, 2, 1)); // left-pane month
  const [picking, setPicking] = React.useState('start'); // 'start' | 'end'

  React.useEffect(() => { if (open) { setPreset(initialPreset || 'mtd'); setStart(initialStart || new Date(2026, 2, 1)); setEnd(initialEnd || new Date(2026, 2, 31)); setCompare(compareMode || 'pp'); }}, [open]);

  const applyPreset = (val) => {
    setPreset(val);
    const T = today;
    let s, e;
    switch (val) {
      case 'yesterday': s = new Date(T); s.setDate(T.getDate() - 1); e = new Date(s); break;
      case 'l7': e = new Date(T); e.setDate(T.getDate() - 1); s = new Date(e); s.setDate(e.getDate() - 6); break;
      case 'thisWeek': s = new Date(T); s.setDate(T.getDate() - T.getDay()); e = new Date(T); break;
      case 'lastWeek': e = new Date(T); e.setDate(T.getDate() - T.getDay() - 1); s = new Date(e); s.setDate(e.getDate() - 6); break;
      case 'l30': e = new Date(T); e.setDate(T.getDate() - 1); s = new Date(e); s.setDate(e.getDate() - 29); break;
      case 'mtd': s = new Date(T.getFullYear(), T.getMonth(), 1); e = new Date(T); break;
      case 'lastMonth': s = new Date(T.getFullYear(), T.getMonth() - 1, 1); e = new Date(T.getFullYear(), T.getMonth(), 0); break;
      case 'ytd': s = new Date(T.getFullYear(), 0, 1); e = new Date(T); break;
      case 'lifetime': s = new Date(2023, 0, 1); e = new Date(T); break;
      default: return;
    }
    setStart(s); setEnd(e); setAnchor(new Date(s.getFullYear(), s.getMonth(), 1));
  };

  const pickDay = (d) => {
    setPreset('custom');
    if (picking === 'start' || !start || (start && end && d < start)) {
      setStart(d); setEnd(null); setPicking('end');
    } else {
      if (d < start) { setStart(d); setEnd(start); }
      else setEnd(d);
      setPicking('start');
    }
  };

  // Compute compare range from primary range + compare mode.
  const compareRange = React.useMemo(() => {
    if (!start || !end) return { start: null, end: null };
    const days = Math.round((end - start) / 86400000);
    if (compare === 'pp') {
      const ce = new Date(start); ce.setDate(start.getDate() - 1);
      const cs = new Date(ce); cs.setDate(ce.getDate() - days);
      return { start: cs, end: ce };
    } else { // py
      const cs = new Date(start); cs.setFullYear(start.getFullYear() - 1);
      const ce = new Date(end); ce.setFullYear(end.getFullYear() - 1);
      return { start: cs, end: ce };
    }
  }, [start, end, compare]);

  // Left & right anchor months
  const leftMonth = anchor;
  const rightMonth = new Date(anchor.getFullYear(), anchor.getMonth() + 1, 1);

  const primaryLabel = `${fmtDate(start)} – ${fmtDate(end)}`;
  const compareLabel = `${fmtDate(compareRange.start)} – ${fmtDate(compareRange.end)}`;

  if (!open) return null;
  return (
    <div onClick={onClose} style={{
      position: 'fixed', inset: 0, zIndex: 200,
      background: 'rgba(15,23,42,0.45)', backdropFilter: 'blur(2px)',
      display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 20,
    }}>
      <div onClick={e => e.stopPropagation()} style={{
        width: 820, maxWidth: '100%',
        background: 'var(--rc-card)', borderRadius: 14,
        border: '1px solid var(--rc-border)',
        boxShadow: '0 30px 60px -15px rgba(15,23,42,0.35)',
        overflow: 'hidden', display: 'flex', flexDirection: 'column',
      }}>
        <div style={{ display: 'flex', minHeight: 360 }}>
          {/* Preset rail */}
          <div style={{
            width: 180, flexShrink: 0, padding: '14px 10px',
            borderRight: '1px solid var(--rc-border)',
            background: 'var(--rc-subtle)',
            display: 'flex', flexDirection: 'column', gap: 3,
          }}>
            {DATE_PICKER_PRESETS.map(p => {
              const on = preset === p.value;
              return (
                <button key={p.value} onClick={() => applyPreset(p.value)} style={{
                  padding: '8px 12px', textAlign: 'left', fontSize: 12, fontWeight: on ? 600 : 500,
                  borderRadius: 9999, fontFamily: 'inherit', cursor: 'pointer',
                  border: on ? '1.5px solid var(--rc-green)' : '1.5px solid transparent',
                  background: on && p.value === 'custom' ? 'var(--rc-green)'
                    : on ? 'var(--rc-card)' : 'transparent',
                  color: on && p.value === 'custom' ? '#fff'
                    : on ? 'var(--rc-text)' : 'var(--rc-text-sub)',
                }}>{p.label}</button>
              );
            })}
          </div>

          {/* Calendar pane */}
          <div style={{ flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0 }}>
            {/* Compare toggle row */}
            <div style={{
              padding: '14px 18px 10px', borderBottom: '1px solid var(--rc-border)',
              display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap',
            }}>
              <div style={{
                display: 'inline-flex', background: 'var(--rc-chip-bg)',
                border: '1px solid var(--rc-border)', borderRadius: 9999, padding: 2,
              }}>
                {[{v:'pp',l:'Prior Period'},{v:'py',l:'Prior Year'}].map(opt => {
                  const on = compare === opt.v;
                  return (
                    <button key={opt.v} onClick={() => setCompare(opt.v)} style={{
                      padding: '5px 14px', borderRadius: 9999, border: 0,
                      background: on ? 'var(--rc-green)' : 'transparent',
                      color: on ? '#fff' : 'var(--rc-text-sub)',
                      fontSize: 11, fontWeight: 600, fontFamily: 'inherit', cursor: 'pointer',
                    }}>{opt.l}</button>
                  );
                })}
              </div>
              <div style={{ fontSize: 11, color: 'var(--rc-text-sub)', fontVariantNumeric: 'tabular-nums' }}>
                {primaryLabel} <span style={{ color: 'var(--rc-text-mute)' }}>vs</span> {compareLabel}
              </div>
            </div>

            {/* Month navigation */}
            <div style={{
              padding: '10px 18px 4px', display: 'flex', alignItems: 'center', justifyContent: 'space-between',
            }}>
              <button onClick={() => setAnchor(new Date(anchor.getFullYear(), anchor.getMonth() - 1, 1))}
                style={navBtnStyle} aria-label="Previous month">
                <Icon name="chevronLeft" size={13} />
              </button>
              <button onClick={() => setAnchor(new Date(anchor.getFullYear(), anchor.getMonth() + 1, 1))}
                style={navBtnStyle} aria-label="Next month">
                <Icon name="chevronRight" size={13} />
              </button>
            </div>

            <div style={{ flex: 1, padding: '4px 18px 14px', display: 'flex', gap: 22 }}>
              <MonthGrid
                year={leftMonth.getFullYear()} monthIdx={leftMonth.getMonth()}
                rangeStart={start} rangeEnd={end}
                compareStart={compareRange.start} compareEnd={compareRange.end}
                today={today} onPick={pickDay} />
              <div style={{ width: 1, background: 'var(--rc-border-soft)' }} />
              <MonthGrid
                year={rightMonth.getFullYear()} monthIdx={rightMonth.getMonth()}
                rangeStart={start} rangeEnd={end}
                compareStart={compareRange.start} compareEnd={compareRange.end}
                today={today} onPick={pickDay} />
            </div>
          </div>
        </div>

        <div style={{
          padding: '12px 18px', borderTop: '1px solid var(--rc-border)',
          background: 'var(--rc-subtle)',
          display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12,
        }}>
          <div style={{ fontSize: 11, color: 'var(--rc-text-sub)', display: 'flex', alignItems: 'center', gap: 14 }}>
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
              <span style={{ width: 10, height: 10, borderRadius: 9999, background: 'var(--rc-green)' }} /> Primary
            </span>
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
              <span style={{ width: 10, height: 10, borderRadius: 9999, border: '1.5px dashed var(--rc-green)' }} /> Compare
            </span>
          </div>
          <div style={{ display: 'flex', gap: 8 }}>
            <button onClick={onClose} style={secondaryBtn}>Cancel</button>
            <button onClick={() => { onSave({ preset, start, end, compare }); onClose(); }} style={primaryBtn}>
              Save
            </button>
          </div>
        </div>
      </div>
    </div>
  );
};

const navBtnStyle = {
  width: 28, height: 28, borderRadius: 9999,
  display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
  border: '1px solid var(--rc-border)', background: 'var(--rc-card)',
  color: 'var(--rc-text-sub)', cursor: 'pointer', fontFamily: 'inherit', padding: 0,
};

const primaryBtn = {
  display: 'inline-flex', alignItems: 'center', gap: 6,
  padding: '8px 16px', borderRadius: 9999,
  border: 0, background: 'var(--rc-green)', color: '#fff',
  fontSize: 12, fontWeight: 600, cursor: 'pointer', fontFamily: 'inherit',
  boxShadow: '0 1px 2px rgba(22,163,74,0.25)',
};

const secondaryBtn = {
  padding: '8px 14px', borderRadius: 9999, fontSize: 12, fontWeight: 500,
  border: '1px solid var(--rc-border)', background: 'var(--rc-card)',
  color: 'var(--rc-text)', cursor: 'pointer', fontFamily: 'inherit',
};

Object.assign(window, { AddMetricsModal, AvailableChartsModal, DateRangePickerModal, CHART_PRESETS, METRIC_CATEGORIES });
