/* global React */
const { useState } = React;

// ============================================================================
// Icon — unicode glyphs only. The whole icon system.
// ============================================================================
function Icon({ glyph, size = 16, style }) {
  return (
    <span
      aria-hidden="true"
      style={{
        fontFamily: 'var(--font-sketch)',
        fontSize: size,
        lineHeight: 1,
        display: 'inline-block',
        width: size,
        textAlign: 'center',
        ...style,
      }}
    >
      {glyph}
    </span>
  );
}

// ============================================================================
// Button — Redwood: primary (filled red), secondary (outlined), ghost, danger
// ============================================================================
function Button({ variant = 'secondary', size = 'md', children, onClick, disabled, style }) {
  const base = {
    fontFamily: 'var(--font-sketch)',
    fontWeight: 500,
    borderRadius: 6,
    cursor: disabled ? 'not-allowed' : 'pointer',
    opacity: disabled ? 0.45 : 1,
    transition: 'background 120ms ease, border-color 120ms ease, color 120ms ease, box-shadow 120ms ease',
    display: 'inline-flex',
    alignItems: 'center',
    gap: 6,
    whiteSpace: 'nowrap',
    letterSpacing: 0.01,
  };
  const sizes = {
    sm: { fontSize: 12, padding: '4px 10px' },
    md: { fontSize: 13, padding: '6px 14px' },
    lg: { fontSize: 15, padding: '9px 20px' },
  };
  const variants = {
    primary:   { background: 'var(--marker)',  color: '#ffffff',         border: '1px solid var(--marker)'  },
    secondary: { background: 'var(--surface)', color: 'var(--ink)',      border: '1px solid var(--border)'  },
    ghost:     { background: 'transparent',    color: 'var(--ink-2)',    border: '1px solid transparent'    },
    danger:    { background: 'var(--surface)', color: 'var(--marker)',   border: '1px solid var(--marker)'  },
  };
  const hoverFor = {
    primary:   { background: 'var(--marker-2)', borderColor: 'var(--marker-2)' },
    secondary: { background: 'var(--paper-dim)', borderColor: 'var(--border-strong)' },
    ghost:     { background: 'var(--paper-dim)', borderColor: 'transparent' },
    danger:    { background: '#f7e9e5', borderColor: 'var(--marker)' },
  };
  return (
    <button
      onClick={disabled ? undefined : onClick}
      style={{ ...base, ...sizes[size], ...variants[variant], ...style }}
      onMouseEnter={(e) => {
        if (disabled) return;
        const h = hoverFor[variant];
        if (h.background) e.currentTarget.style.background = h.background;
        if (h.borderColor) e.currentTarget.style.borderColor = h.borderColor;
      }}
      onMouseLeave={(e) => {
        if (disabled) return;
        const v = variants[variant];
        e.currentTarget.style.background  = v.background;
        e.currentTarget.style.borderColor = v.border.split(' ')[2] || 'var(--border)';
      }}
    >
      {children}
    </button>
  );
}

// ============================================================================
// Field — labeled input (Redwood)
// ============================================================================
function Field({ label, value, onChange, placeholder, helper, error, type = 'text', readOnly, multiline, options }) {
  const inputStyle = {
    fontFamily: 'var(--font-sketch)',
    fontSize: 14,
    color: 'var(--ink)',
    background: readOnly ? 'transparent' : 'var(--surface)',
    border: readOnly ? 'none' : '1px solid var(--border)',
    borderBottom: readOnly ? '1px solid var(--line)' : '1px solid var(--border)',
    borderRadius: readOnly ? 0 : 6,
    padding: readOnly ? '6px 0' : '8px 12px',
    outline: 'none',
    width: '100%',
    boxSizing: 'border-box',
    resize: 'none',
    transition: 'border-color 120ms ease, box-shadow 120ms ease',
  };
  return (
    <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
      <span style={{ fontFamily: 'var(--font-sketch)', fontSize: 12, fontWeight: 500, color: 'var(--ink-3)', letterSpacing: 0.02 }}>{label}</span>
      {options ? (
        <select value={value} onChange={(e) => onChange?.(e.target.value)} style={inputStyle} disabled={readOnly}>
          {options.map((o) => <option key={o} value={o}>{o}</option>)}
        </select>
      ) : multiline ? (
        <textarea value={value || ''} onChange={(e) => onChange?.(e.target.value)} placeholder={placeholder} rows={3} readOnly={readOnly} style={inputStyle}/>
      ) : (
        <input type={type} value={value || ''} onChange={(e) => onChange?.(e.target.value)} placeholder={placeholder} readOnly={readOnly} style={inputStyle}/>
      )}
      {(helper || error) && (
        <span style={{
          fontFamily: 'var(--font-sketch)',
          fontSize: 12,
          color: error ? 'var(--redline)' : 'var(--ink-3)',
        }}>{error || helper}</span>
      )}
    </label>
  );
}

// ============================================================================
// Card — Redwood surface with subtle shadow
// ============================================================================
function Card({ children, dashed, hatch, tilt = 0, style, onClick }) {
  return (
    <div
      onClick={onClick}
      style={{
        border: '1px solid var(--border)',
        borderRadius: 8,
        background: hatch ? 'var(--paper-dim)' : 'var(--surface)',
        boxShadow: 'var(--shadow-1)',
        padding: 16,
        cursor: onClick ? 'pointer' : 'default',
        ...style,
      }}
    >
      {children}
    </div>
  );
}

// ============================================================================
// Badge / chip — Redwood
// ============================================================================
function Badge({ children, filled, dashed, tone = 'ink' }) {
  const tones = {
    ink: { borderColor: 'var(--border)',  bg: filled ? 'var(--ink)' : 'var(--surface)',    fg: filled ? '#ffffff' : 'var(--ink)' },
    red: { borderColor: 'var(--marker)',  bg: filled ? 'var(--marker)' : '#fbece8',         fg: filled ? '#ffffff' : 'var(--marker)' },
    hl:  { borderColor: 'var(--accent)',  bg: 'var(--highlight)',                            fg: 'var(--accent-2)' },
  };
  const t = tones[tone];
  return (
    <span style={{
      fontFamily: 'var(--font-sketch)', fontSize: 11, fontWeight: 500,
      color: t.fg, background: t.bg,
      border: `1px solid ${t.borderColor}`,
      borderRadius: 999, padding: '2px 9px', lineHeight: 1.4, whiteSpace: 'nowrap',
      letterSpacing: 0.02,
    }}>{children}</span>
  );
}

// ============================================================================
// Annotation overlay — soft highlight behind text (used for selection emphasis)
// ============================================================================
function Scribble({ children }) {
  return (
    <span style={{
      backgroundImage: 'linear-gradient(transparent 65%, var(--highlight) 65%)',
      padding: '0 2px',
    }}>{children}</span>
  );
}

// ============================================================================
// Placeholder — "TBD" surface; Redwood subdued
// ============================================================================
function Placeholder({ children, height = 80, style }) {
  return (
    <div style={{
      border: '1px dashed var(--border)',
      borderRadius: 6,
      background: 'var(--paper-dim)',
      minHeight: height,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      fontFamily: 'var(--font-sketch)', fontSize: 13, color: 'var(--ink-3)',
      padding: 16, textAlign: 'center',
      ...style,
    }}>{children}</div>
  );
}

// ============================================================================
// DeptPill — department chip
// ============================================================================
function DeptPill({ dept, source, size = 'md', style }) {
  const d = dept || (source ? (window.deptForSource && window.deptForSource(source)) : null);
  if (!d) return null;
  const fontSize = size === 'sm' ? 10 : 11;
  const padding  = size === 'sm' ? '2px 7px' : '3px 10px';
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 5,
      padding, borderRadius: 999,
      border: '1px solid rgba(255,255,255,0.2)',
      background: d.color || '#5a5246',
      color: '#ffffff',
      fontFamily: 'var(--font-sketch)', fontSize, fontWeight: 500,
      letterSpacing: 0.02,
      whiteSpace: 'nowrap',
      ...style,
    }}>
      <span style={{
        width: size === 'sm' ? 14 : 16, height: size === 'sm' ? 14 : 16, borderRadius: '50%',
        background: 'rgba(255,255,255,0.22)',
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
        fontSize: size === 'sm' ? 9 : 10, fontWeight: 600,
      }}>{d.short[0]}</span>
      {d.short}
    </span>
  );
}

// ============================================================================
// ActionSelect — split-button (primary action + dropdown of alternatives)
// ============================================================================
function ActionSelect({ value, onChange, onFire, options, disabled, size = 'md' }) {
  const [open, setOpen] = useState(false);
  const current = options.find(o => o.id === value) || options[0];
  const padY = size === 'md' ? 7 : 5;
  const padX = size === 'md' ? 14 : 10;
  const fontSize = size === 'md' ? 13 : 12;
  return (
    <div style={{ position: 'relative', display: 'inline-flex' }}>
      <button onClick={() => onFire && onFire(current.id)} disabled={disabled} style={{
        padding: `${padY}px ${padX}px`,
        background: disabled ? 'var(--ink-5)' : 'var(--marker)', color: '#ffffff',
        border: '1px solid ' + (disabled ? 'var(--border)' : 'var(--marker)'),
        borderRadius: '6px 0 0 6px',
        fontFamily: 'var(--font-sketch)', fontWeight: 500, fontSize,
        cursor: disabled ? 'not-allowed' : 'pointer',
        display: 'inline-flex', alignItems: 'center', gap: 8,
      }}>
        {current.glyph && <span style={{ fontFamily: 'var(--font-sketch)', fontSize: fontSize + 1, lineHeight: 1 }}>{current.glyph}</span>}
        {current.label}
      </button>
      <button onClick={() => setOpen(!open)} disabled={disabled} title="other actions" style={{
        padding: `${padY}px 10px`,
        background: disabled ? 'var(--ink-5)' : 'var(--marker-2)', color: '#ffffff',
        border: '1px solid ' + (disabled ? 'var(--border)' : 'var(--marker-2)'),
        borderLeft: '1px solid rgba(255,255,255,0.25)',
        borderRadius: '0 6px 6px 0',
        fontFamily: 'var(--font-sketch)', fontSize: fontSize + 2, lineHeight: 1,
        cursor: disabled ? 'not-allowed' : 'pointer',
      }}>▾</button>
      {open && (
        <>
          <div onClick={() => setOpen(false)} style={{ position: 'fixed', inset: 0, zIndex: 60 }}/>
          <div style={{
            position: 'absolute', top: 'calc(100% + 6px)', right: 0,
            minWidth: 280, zIndex: 70,
            background: 'var(--surface)',
            border: '1px solid var(--border)', borderRadius: 8,
            boxShadow: 'var(--shadow-2)',
            padding: 6,
          }}>
            {options.map(opt => (
              <div key={opt.id} onClick={() => { onChange && onChange(opt.id); setOpen(false); }} style={{
                padding: '8px 10px', borderRadius: 5,
                cursor: 'pointer',
                background: opt.id === value ? 'var(--highlight)' : 'transparent',
                display: 'flex', alignItems: 'flex-start', gap: 10,
              }}
              onMouseEnter={(e) => { if (opt.id !== value) e.currentTarget.style.background = 'var(--paper-dim)'; }}
              onMouseLeave={(e) => { if (opt.id !== value) e.currentTarget.style.background = 'transparent'; }}>
                {opt.glyph && <span style={{ fontFamily: 'var(--font-sketch)', fontSize: 16, lineHeight: 1, marginTop: 1 }}>{opt.glyph}</span>}
                <div style={{ flex: 1 }}>
                  <div style={{ fontFamily: 'var(--font-sketch)', fontSize: 13, fontWeight: 500 }}>{opt.label}</div>
                  {opt.hint && <div style={{ fontFamily: 'var(--font-sketch)', fontSize: 11, color: 'var(--ink-3)', marginTop: 2 }}>{opt.hint}</div>}
                </div>
                {opt.id === value && <span style={{ fontFamily: 'var(--font-sketch)', fontSize: 14, color: 'var(--accent)' }}>✓</span>}
              </div>
            ))}
          </div>
        </>
      )}
    </div>
  );
}

// ============================================================================
// ApprovalChain — horizontal chain of approval stops
// ============================================================================
function ApprovalChain({ stops, style }) {
  return (
    <div style={{
      display: 'flex', alignItems: 'stretch', gap: 0,
      flexWrap: 'wrap',
      padding: '10px 6px',
      ...style,
    }}>
      {stops.map((s, i) => (
        <React.Fragment key={s.id || i}>
          <ChainStop stop={s}/>
          {i < stops.length - 1 && <ChainArrow nextState={stops[i + 1].state}/>}
        </React.Fragment>
      ))}
    </div>
  );
}

function ChainStop({ stop }) {
  const { label, actor, when, state = 'future', note } = stop;
  const isDone    = state === 'done';
  const isCurrent = state === 'current';
  const isSkipped = state === 'skipped';
  const bg = isDone    ? 'var(--success)'
           : isCurrent ? 'var(--highlight)'
           : isSkipped ? 'var(--paper-dim)'
                       : 'var(--surface)';
  const fg = isDone    ? '#ffffff' : 'var(--ink)';
  const borderColor = isCurrent ? 'var(--accent)' : isDone ? 'var(--success)' : 'var(--border)';
  return (
    <div style={{
      minWidth: 150, maxWidth: 220,
      padding: '8px 12px',
      border: `1px solid ${borderColor}`,
      borderRadius: 6,
      background: bg, color: fg,
      opacity: isSkipped ? 0.7 : 1,
      boxShadow: isCurrent ? 'var(--shadow-1)' : 'none',
      display: 'flex', flexDirection: 'column', gap: 2,
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{
          width: 18, height: 18, borderRadius: '50%',
          border: `1px solid ${isDone ? '#ffffff' : isCurrent ? 'var(--accent)' : 'var(--border-strong)'}`,
          background: isDone ? 'rgba(255,255,255,0.2)' : 'transparent',
          color: fg,
          fontFamily: 'var(--font-sketch)', fontSize: 11, fontWeight: 600,
          display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
          flexShrink: 0,
        }}>
          {isDone ? '✓' : isSkipped ? '—' : isCurrent ? '●' : ''}
        </span>
        <span style={{ fontFamily: 'var(--font-sketch)', fontSize: 12.5, fontWeight: 600, lineHeight: 1.2 }}>{label}</span>
      </div>
      {isDone && (actor || when) && (
        <div style={{ fontFamily: 'var(--font-mono)', fontSize: 10, color: fg, opacity: 0.9, marginLeft: 24, lineHeight: 1.25 }}>
          {actor && <div>{actor}</div>}
          {when && <div>{when}</div>}
        </div>
      )}
      {isCurrent && (
        <div style={{ fontFamily: 'var(--font-sketch)', fontSize: 11, color: 'var(--accent-2)', marginLeft: 24, fontWeight: 500 }}>
          {note || 'awaiting'}
        </div>
      )}
      {isSkipped && (
        <div style={{ fontFamily: 'var(--font-mono)', fontSize: 9.5, color: 'var(--ink-3)', marginLeft: 24, letterSpacing: 0.8 }}>
          NOT REQUIRED
        </div>
      )}
      {!isDone && !isCurrent && !isSkipped && (
        <div style={{ fontFamily: 'var(--font-mono)', fontSize: 9.5, color: 'var(--ink-3)', marginLeft: 24, letterSpacing: 0.8 }}>
          PENDING
        </div>
      )}
    </div>
  );
}

function ChainArrow({ nextState }) {
  const dimmed = nextState === 'skipped' || nextState === 'future';
  return (
    <div style={{
      alignSelf: 'center',
      fontFamily: 'var(--font-sketch)', fontSize: 18,
      color: dimmed ? 'var(--ink-4)' : 'var(--ink-3)',
      margin: '0 4px',
      userSelect: 'none',
    }}>→</div>
  );
}

// ============================================================================
// EditSlideout — right-side panel for quick-editing rows
// ============================================================================
function EditSlideout({
  title, subtitle, eyebrow,
  pills,
  onClose,
  onOpenFull,
  onSave,
  saveLabel = 'Save',
  saveDisabled,
  width = 520,
  children,
  footerLeft,
  tone = 'default',
}) {
  return (
    <div style={{ position: 'fixed', inset: 0, zIndex: 80 }}>
      <div onClick={onClose} style={{ position: 'absolute', inset: 0, background: 'rgba(22,21,19,0.28)' }}/>
      <div style={{
        position: 'absolute', top: 0, right: 0, bottom: 0,
        width, maxWidth: '92vw',
        background: 'var(--surface)',
        borderLeft: '1px solid var(--border)',
        boxShadow: 'var(--shadow-3)',
        display: 'flex', flexDirection: 'column',
        animation: 'slide-in-right 200ms cubic-bezier(.2,.8,.3,1)',
      }}>
        {/* HEADER */}
        <div style={{
          padding: '14px 18px',
          borderBottom: '1px solid var(--line)',
          background: tone === 'warn' ? 'var(--highlight)' : 'var(--paper-dim)',
        }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
            <div style={{ flex: 1, minWidth: 0 }}>
              {eyebrow && <div style={{ fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--ink-3)', letterSpacing: 1.2 }}>{eyebrow.toUpperCase()}</div>}
              <div style={{ fontFamily: 'var(--font-display)', fontSize: 20, fontWeight: 600, lineHeight: 1.2 }}>{title}</div>
              {subtitle && <div style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--ink-3)', letterSpacing: 0.6, marginTop: 2 }}>{subtitle}</div>}
              {pills && pills.length > 0 && (
                <div style={{ display: 'flex', gap: 8, alignItems: 'center', marginTop: 6, flexWrap: 'wrap' }}>
                  {pills.map((p, i) => <React.Fragment key={i}>{p}</React.Fragment>)}
                </div>
              )}
            </div>
            <button onClick={onClose} title="close" style={{
              background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 6,
              width: 30, height: 30, cursor: 'pointer',
              fontFamily: 'var(--font-sketch)', fontSize: 16, lineHeight: 1,
              color: 'var(--ink-2)',
            }}>×</button>
          </div>
        </div>

        {/* BODY */}
        <div style={{ flex: 1, overflowY: 'auto', padding: 18, display: 'flex', flexDirection: 'column', gap: 14, background: 'var(--surface)' }}>
          {children}
        </div>

        {/* FOOTER */}
        <div style={{
          padding: '12px 18px',
          borderTop: '1px solid var(--line)',
          background: 'var(--paper-dim)',
          display: 'flex', gap: 8, alignItems: 'center',
        }}>
          {footerLeft != null ? footerLeft : (
            onOpenFull ? <Button size="sm" variant="ghost" onClick={onOpenFull}>Open full record →</Button> : <span/>
          )}
          <span style={{ flex: 1 }}/>
          <Button size="md" variant="ghost" onClick={onClose}>Cancel</Button>
          {onSave && (
            <Button size="md" variant="primary" disabled={saveDisabled} onClick={onSave}>
              <Icon glyph="✓" size={12}/> {saveLabel}
            </Button>
          )}
        </div>
      </div>
      <style>{`@keyframes slide-in-right { from { transform: translateX(100%); } to { transform: translateX(0); } }`}</style>
    </div>
  );
}

// ============================================================================
// EditField — labeled input for use inside an EditSlideout
// ============================================================================
function EditField({ label, value, onChange, multiline, mono, type = 'text', prefix, placeholder, readOnly, hint }) {
  const sharedStyle = {
    fontFamily: mono ? 'var(--font-mono)' : 'var(--font-sketch)',
    fontSize: 13,
    border: '1px solid ' + (readOnly ? 'var(--line)' : 'var(--border)'),
    borderRadius: 6,
    padding: '7px 10px',
    background: readOnly ? 'var(--paper-dim)' : 'var(--surface)',
    color: readOnly ? 'var(--ink-3)' : 'var(--ink)',
    width: '100%', boxSizing: 'border-box',
    outline: 'none',
    transition: 'border-color 120ms ease',
  };
  return (
    <div>
      <div style={{ fontFamily: 'var(--font-mono)', fontSize: 9.5, color: 'var(--ink-3)', letterSpacing: 1.2, marginBottom: 4 }}>{label.toUpperCase()}</div>
      <div style={{ position: 'relative' }}>
        {prefix && <span style={{
          position: 'absolute', left: 10, top: '50%', transform: 'translateY(-50%)',
          fontFamily: 'var(--font-mono)', fontSize: 13, color: 'var(--ink-3)', zIndex: 1,
        }}>{prefix}</span>}
        {multiline
          ? <textarea value={value || ''} readOnly={readOnly} onChange={(e) => onChange && onChange(e.target.value)} placeholder={placeholder} rows={3} style={{ ...sharedStyle, resize: 'vertical' }}/>
          : <input value={value == null ? '' : value} readOnly={readOnly} onChange={(e) => onChange && onChange(e.target.value)} type={type} placeholder={placeholder} style={{ ...sharedStyle, paddingLeft: prefix ? 22 : 10 }}/>
        }
      </div>
      {hint && <div style={{ fontFamily: 'var(--font-sketch)', fontSize: 11.5, color: 'var(--ink-3)', marginTop: 3 }}>{hint}</div>}
    </div>
  );
}

// ============================================================================
// AckModal — universal acknowledgement modal. Same 3-affirmation gate.
// ============================================================================
function AckModal({
  title = 'Acknowledgement',
  subtitle,
  context,
  onClose,
  onAck,
  onRoute,
  questions,
}) {
  const qs = questions && questions.length === 3 ? questions : [
    'I validated the transactions in this batch',
    'I acknowledge the totals and GL combinations are correct',
    "I am authorized to acknowledge for this department / source",
  ];
  const [ticks, setTicks] = useState([false, false, false]);
  const [acknowledged, setAcknowledged] = useState(false);
  const [routed, setRouted] = useState(false);
  const allTicked = ticks.every(Boolean);

  const toggle = (i) => setTicks(prev => prev.map((v, j) => j === i ? !v : v));
  const commitAck = () => { setAcknowledged(true); onAck && onAck(); };
  const commitRoute = () => { setRouted(true); onRoute && onRoute(); };

  return (
    <div style={{
      position: 'fixed', inset: 0, zIndex: 100,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      padding: 20,
    }}>
      <div onClick={onClose} style={{ position: 'absolute', inset: 0, background: 'rgba(22,21,19,0.4)' }}/>

      <div style={{
        position: 'relative', zIndex: 1,
        width: 560, maxWidth: '100%', maxHeight: '92vh',
        display: 'flex', flexDirection: 'column',
        background: 'var(--surface)',
        border: '1px solid var(--border)', borderRadius: 10,
        boxShadow: 'var(--shadow-3)',
      }}>
        {/* HEADER */}
        <div style={{
          padding: '14px 18px',
          borderBottom: '1px solid var(--line)',
          background: acknowledged ? 'var(--highlight)' : 'var(--paper-dim)',
          display: 'flex', alignItems: 'center', gap: 10,
          borderRadius: '10px 10px 0 0',
        }}>
          <span style={{ fontFamily: 'var(--font-sketch)', fontSize: 22, color: acknowledged ? 'var(--success)' : 'var(--ink-3)' }}>✓</span>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--ink-3)', letterSpacing: 1.2 }}>
              {(subtitle || 'ACK ENGINE · 3 AFFIRMATIONS').toUpperCase()}
            </div>
            <div style={{ fontFamily: 'var(--font-display)', fontSize: 18, fontWeight: 600, lineHeight: 1.2 }}>{title}</div>
          </div>
          <button onClick={onClose} title="close" style={{
            background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 6,
            width: 30, height: 30, cursor: 'pointer',
            fontFamily: 'var(--font-sketch)', fontSize: 16, lineHeight: 1,
            color: 'var(--ink-2)',
          }}>×</button>
        </div>

        {/* BODY */}
        <div style={{ flex: 1, overflowY: 'auto', padding: 18, display: 'flex', flexDirection: 'column', gap: 14 }}>
          {context && (
            <div style={{
              border: '1px solid var(--line)', borderRadius: 6,
              padding: '10px 12px',
              background: 'var(--paper-dim)',
              display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '4px 16px',
              fontFamily: 'var(--font-sketch)', fontSize: 12.5,
            }}>
              {context.id     && <div><span style={{ color: 'var(--ink-3)' }}>id · </span><span style={{ fontFamily: 'var(--font-mono)' }}>{context.id}</span></div>}
              {context.source && <div><span style={{ color: 'var(--ink-3)' }}>source · </span><span style={{ fontFamily: 'var(--font-mono)' }}>{context.source}</span></div>}
              {context.count  != null && <div><span style={{ color: 'var(--ink-3)' }}>lines · </span><span style={{ fontFamily: 'var(--font-mono)' }}>{context.count}</span></div>}
              {context.total  != null && <div><span style={{ color: 'var(--ink-3)' }}>total · </span><span style={{ fontFamily: 'var(--font-mono)' }}>${context.total.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}</span></div>}
              {context.dept   && <div style={{ gridColumn: '1 / -1' }}><span style={{ color: 'var(--ink-3)' }}>dept · </span>{context.dept}</div>}
            </div>
          )}

          {/* 3 AFFIRMATIONS */}
          <div>
            <div style={{ fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--ink-3)', letterSpacing: 1.2, marginBottom: 6 }}>
              YOU MUST AFFIRM
            </div>
            <div style={{
              border: '1px solid var(--border)', borderRadius: 6,
              background: 'var(--surface)',
              padding: '6px 14px',
              boxShadow: 'var(--shadow-1)',
            }}>
              {qs.map((q, i) => (
                <label key={i} onClick={() => acknowledged ? null : toggle(i)} style={{
                  display: 'flex', alignItems: 'flex-start', gap: 10,
                  padding: '12px 0',
                  borderBottom: i < qs.length - 1 ? '1px solid var(--line)' : 'none',
                  cursor: acknowledged ? 'default' : 'pointer',
                  opacity: acknowledged ? 0.7 : 1,
                }}>
                  <span style={{
                    width: 20, height: 20, borderRadius: 4,
                    border: '1px solid ' + (ticks[i] ? 'var(--marker)' : 'var(--border-strong)'),
                    background: ticks[i] ? 'var(--marker)' : 'var(--surface)',
                    color: '#ffffff',
                    display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                    fontFamily: 'var(--font-sketch)', fontSize: 13, fontWeight: 600,
                    flexShrink: 0, marginTop: 1,
                  }}>{ticks[i] ? '✓' : ''}</span>
                  <span style={{
                    fontFamily: 'var(--font-sketch)', fontSize: 13.5, lineHeight: 1.4,
                    color: ticks[i] ? 'var(--ink)' : 'var(--ink-2)',
                    fontWeight: 400,
                  }}>{q}</span>
                </label>
              ))}
            </div>
          </div>

          {acknowledged && (
            <div style={{
              fontFamily: 'var(--font-sketch)', fontSize: 13, color: 'var(--ink)',
              border: '1px solid var(--success)', borderRadius: 6,
              padding: '10px 12px',
              background: '#eaf5ec',
              display: 'flex', alignItems: 'center', gap: 10,
            }}>
              <span style={{ fontFamily: 'var(--font-sketch)', fontSize: 16, color: 'var(--success)' }}>✓</span>
              <div>
                <div><b>Acknowledged</b> · signed as k.lee · {new Date().toISOString().slice(0,10)}</div>
                <div style={{ fontFamily: 'var(--font-mono)', fontSize: 10.5, color: 'var(--ink-3)', letterSpacing: 0.8, marginTop: 2 }}>
                  recorded · downstream notified · audit entry written
                </div>
              </div>
            </div>
          )}
          {routed && !acknowledged && (
            <div style={{
              fontFamily: 'var(--font-sketch)', fontSize: 13,
              border: '1px solid var(--border)', borderRadius: 6,
              padding: '10px 12px',
              background: 'var(--paper-dim)',
              display: 'flex', alignItems: 'center', gap: 10,
            }}>
              <span style={{ fontFamily: 'var(--font-sketch)', fontSize: 16 }}>↗</span>
              <div>
                <div><b>Routed for acknowledgement</b> · sent to next approver in queue</div>
                <div style={{ fontFamily: 'var(--font-mono)', fontSize: 10.5, color: 'var(--ink-3)', letterSpacing: 0.8, marginTop: 2 }}>
                  email sent · in-app badge raised
                </div>
              </div>
            </div>
          )}
        </div>

        {/* FOOTER */}
        <div style={{
          padding: '12px 18px',
          borderTop: '1px solid var(--line)',
          background: 'var(--paper-dim)',
          display: 'flex', gap: 8, alignItems: 'center',
          borderRadius: '0 0 10px 10px',
        }}>
          <span style={{ flex: 1, fontFamily: 'var(--font-sketch)', fontSize: 12, color: 'var(--ink-3)' }}>
            {acknowledged ? 'Ack recorded — close when ready'
              : routed ? 'Routed — you can still acknowledge if you affirm'
              : allTicked ? 'All 3 affirmed · ready to acknowledge'
              : `${ticks.filter(Boolean).length} of 3 affirmed`}
          </span>
          <Button size="md" variant="ghost" disabled={acknowledged || routed} onClick={commitRoute}>
            <Icon glyph="↗" size={12}/> Route
          </Button>
          <Button
            size="md"
            variant={acknowledged ? 'secondary' : 'primary'}
            disabled={acknowledged || !allTicked}
            onClick={commitAck}
          >
            <Icon glyph="✓" size={12}/> {acknowledged ? 'Acknowledged' : 'Acknowledge'}
          </Button>
        </div>
      </div>
    </div>
  );
}

// ============================================================================
// PipelineStrip — horizontal status pipeline (Redwood). Lifted from the
// apio-demo-showcase v2 PipelineStrip pattern and aligned to the gate
// sequence Deborah Wann named on 2026-05-08:
//   Dept Review → AP Review → Audit Review → In Fusion → Paid
// Optional pre-step "Draft" + post-step "Reconciled" can be passed.
// ============================================================================
function PipelineStrip({ stages, current, compact, style }) {
  const idx = stages.findIndex(s => (typeof s === 'string' ? s : s.id || s.label) === current);
  return (
    <div style={{ display: 'flex', alignItems: 'stretch', width: '100%', ...style }}>
      {stages.map((raw, i) => {
        const stage = typeof raw === 'string' ? { label: raw } : raw;
        const isDone = i < idx;
        const isCurrent = i === idx;
        const bg = isCurrent ? 'var(--accent)' : isDone ? '#e8efe8' : 'var(--paper-dim)';
        const fg = isCurrent ? '#ffffff' : isDone ? 'var(--success)' : 'var(--ink-3)';
        const borderColor = isCurrent ? 'var(--accent)' : isDone ? '#cae0ce' : 'var(--border)';
        return (
          <React.Fragment key={stage.id || stage.label}>
            <div style={{
              flex: 1,
              minWidth: 0,
              padding: compact ? '6px 10px' : '10px 14px',
              background: bg,
              color: fg,
              border: `1px solid ${borderColor}`,
              borderRadius: i === 0 ? '6px 0 0 6px' : i === stages.length - 1 ? '0 6px 6px 0' : 0,
              borderRight: i < stages.length - 1 ? 'none' : `1px solid ${borderColor}`,
              fontFamily: 'var(--font-sketch)',
              fontSize: compact ? 11 : 12.5,
              fontWeight: isCurrent ? 600 : 500,
              display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 6,
              whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
              transition: 'background 280ms ease, color 280ms ease',
            }}>
              <span style={{ fontFamily: 'var(--font-mono)', fontSize: compact ? 9 : 10, opacity: 0.7 }}>
                {String(i + 1).padStart(2, '0')}
              </span>
              <span>{stage.label}</span>
              {isCurrent && <span style={{ fontSize: 10 }}>●</span>}
              {isDone && <span style={{ fontSize: 10 }}>✓</span>}
            </div>
          </React.Fragment>
        );
      })}
    </div>
  );
}

Object.assign(window, { Icon, Button, Field, Card, Badge, Scribble, Placeholder, useState,
  DeptPill, ActionSelect, ApprovalChain, PipelineStrip,
  EditSlideout, EditField, AckModal });
