// ESIGN PLATFORM — event bus, mutable store, extended enterprise data
// Mirrors DocStore pattern. Emits arbiter:envelope.* etc. for cross-platform coordination.
(function () {
  const T_es = window.ArbiterTokens;

  // ── EVENT BUS ──────────────────────────────────────────────────────────────
  const EsBus = {
    _subs: new Map(),
    on(topic, fn) {
      if (!this._subs.has(topic)) this._subs.set(topic, new Set());
      this._subs.get(topic).add(fn);
      return () => { const s = this._subs.get(topic); if (s) s.delete(fn); };
    },
    emit(topic, data) {
      (this._subs.get(topic) || new Set()).forEach(fn => { try { fn(data); } catch (e) { console.error(e); } });
      (this._subs.get('*') || new Set()).forEach(fn => { try { fn({ topic, data }); } catch (e) {} });
      try { window.dispatchEvent(new CustomEvent('arbiter:' + topic, { detail: data })); } catch (e) {}
    },
  };
  window.EsBus = EsBus;

  // ── EXTENDED DATA (improvements #2, #6, #11, #12, #16) ─────────────────────

  // Field placements per envelope (for signer canvas preview — improvement #2)
  const ES_FIELD_PLACEMENTS = {
    'ENV-001': [
      { id: 'F1', page: 17, x: 0.55, y: 0.80, w: 0.30, h: 0.05, type: 'signature', recipient: 'R-1', required: true, label: 'Signature' },
      { id: 'F2', page: 17, x: 0.55, y: 0.86, w: 0.12, h: 0.03, type: 'date',      recipient: 'R-1', required: true, label: 'Date' },
      { id: 'F3', page: 17, x: 0.10, y: 0.80, w: 0.30, h: 0.05, type: 'signature', recipient: 'R-2', required: true, label: 'Signature' },
      { id: 'F4', page: 17, x: 0.10, y: 0.86, w: 0.12, h: 0.03, type: 'date',      recipient: 'R-2', required: true, label: 'Date' },
      { id: 'F5', page:  1, x: 0.80, y: 0.94, w: 0.08, h: 0.03, type: 'initials',  recipient: 'R-1', required: true, label: 'Initials' },
      { id: 'F6', page:  1, x: 0.88, y: 0.94, w: 0.08, h: 0.03, type: 'initials',  recipient: 'R-2', required: true, label: 'Initials' },
    ],
    'ENV-002': [
      { id: 'F1', page: 5, x: 0.50, y: 0.78, w: 0.30, h: 0.05, type: 'signature', recipient: 'R-4', required: true, label: 'Signature' },
      { id: 'F2', page: 5, x: 0.50, y: 0.85, w: 0.12, h: 0.03, type: 'date',      recipient: 'R-4', required: true, label: 'Date' },
      { id: 'F3', page: 5, x: 0.10, y: 0.78, w: 0.30, h: 0.05, type: 'signature', recipient: 'R-5', required: true, label: 'Signature' },
      { id: 'F4', page: 5, x: 0.10, y: 0.85, w: 0.12, h: 0.03, type: 'date',      recipient: 'R-5', required: true, label: 'Date' },
    ],
    'ENV-003': [
      { id: 'F1', page: 3, x: 0.55, y: 0.80, w: 0.30, h: 0.05, type: 'signature', recipient: 'R-6', required: true, label: 'Signature' },
      { id: 'F2', page: 3, x: 0.10, y: 0.80, w: 0.30, h: 0.05, type: 'signature', recipient: 'R-7', required: true, label: 'Signature' },
      { id: 'F3', page: 3, x: 0.55, y: 0.87, w: 0.12, h: 0.03, type: 'date',      recipient: 'R-6', required: true, label: 'Date' },
      { id: 'F4', page: 3, x: 0.10, y: 0.87, w: 0.12, h: 0.03, type: 'date',      recipient: 'R-7', required: true, label: 'Date' },
    ],
  };

  // Clause versions (improvement #6)
  const ES_CLAUSE_VERSIONS = {
    'CL-004': [
      { version: '1.0', date: '2024-08-10', approvedBy: 'M. Kirkland', text: 'This Agreement shall automatically renew for successive one-year terms unless either party provides ninety (90) days written notice of non-renewal.' },
      { version: '2.0', date: '2025-06-22', approvedBy: 'S. Chen',     text: 'This Agreement shall automatically renew for successive one-year terms unless either party provides sixty (60) days written notice of non-renewal.' },
      { version: '2.1', date: '2026-03-04', approvedBy: 'S. Chen',     text: 'This Agreement shall automatically renew for successive one-year terms unless either party provides thirty (30) days written notice of non-renewal. Renewal fee increases capped at CPI.' },
    ],
    'CL-007': [
      { version: '2.4', date: '2025-09-10', approvedBy: 'M. Kirkland', text: 'Neither party shall be liable for delays caused by acts of God, war, or government action.' },
      { version: '3.0', date: '2026-01-14', approvedBy: 'A. Williams', text: 'Neither party shall be liable for delays caused by force majeure, defined as acts of God, war, terrorism, pandemics, cyber-attacks, or government action.' },
      { version: '3.5-draft', date: '2026-04-12', approvedBy: 'A. Williams (proposed)', text: 'Neither party shall be liable for delays caused by force majeure, defined broadly as any event beyond reasonable control including but not limited to acts of God, war, terrorism, pandemics, supply chain disruption, cyber-attacks, sanctions, or government action. Affected party must notify within 10 business days.' },
    ],
  };

  // Per-envelope hash chain (improvement #11)
  const ES_HASH_CHAINS = {};
  // (generated on the fly — fallback reads envelope.auditTrail)

  // Recipient-level funnel per envelope (improvement #16)
  const ES_SIGNER_FUNNEL = {
    'ENV-001': { sent: 2, opened: 2, started: 2, completed: 2, declined: 0, avgTimeToOpen: '4h 43m', avgTimeToComplete: '1d 5h' },
    'ENV-002': { sent: 2, opened: 2, started: 2, completed: 1, declined: 0, avgTimeToOpen: '3h 42m', avgTimeToComplete: '5h 14m' },
    'ENV-003': { sent: 2, opened: 0, started: 0, completed: 0, declined: 0, avgTimeToOpen: null,     avgTimeToComplete: null },
    'ENV-004': { sent: 1, opened: 1, started: 1, completed: 1, declined: 0, avgTimeToOpen: '1h 40m', avgTimeToComplete: '1h 45m' },
    'ENV-005': { sent: 1, opened: 1, started: 0, completed: 0, declined: 1, avgTimeToOpen: '1h 30m', avgTimeToComplete: null },
    'ENV-007': { sent: 3, opened: 3, started: 3, completed: 3, declined: 0, avgTimeToOpen: '6h 00m', avgTimeToComplete: '2d 9h' },
    'ENV-008': { sent: 1, opened: 0, started: 0, completed: 0, declined: 0, avgTimeToOpen: null,     avgTimeToComplete: null },
  };

  // Per-field drop-off analytics (improvement #16)
  const ES_FIELD_DROPOFF = [
    { field: 'Signature',    completion: 96.2, avgTime: '42s',  dropOffRate: 3.8 },
    { field: 'Date',         completion: 98.7, avgTime: '18s',  dropOffRate: 1.3 },
    { field: 'Initials',     completion: 91.4, avgTime: '1m 14s', dropOffRate: 8.6 },
    { field: 'Text (name)',  completion: 88.2, avgTime: '2m 38s', dropOffRate: 11.8 },
    { field: 'Text (SSN)',   completion: 72.1, avgTime: '4m 22s', dropOffRate: 27.9 },
    { field: 'Checkbox',     completion: 94.8, avgTime: '22s',  dropOffRate: 5.2 },
  ];

  // Risk score trend 6mo (improvement #17)
  const ES_RISK_TREND = [
    { month: 'Nov', avgRisk: 28, high: 2, medium: 6, low: 16 },
    { month: 'Dec', avgRisk: 24, high: 1, medium: 4, low: 13 },
    { month: 'Jan', avgRisk: 31, high: 3, medium: 8, low: 20 },
    { month: 'Feb', avgRisk: 22, high: 1, medium: 5, low: 21 },
    { month: 'Mar', avgRisk: 26, high: 2, medium: 7, low: 33 },
    { month: 'Apr', avgRisk: 29, high: 2, medium: 6, low: 21 },
  ];

  // Workflow runtime state (improvement #8) — envelope → workflow node
  const ES_WF_RUNTIME = [
    { envelopeId: 'ENV-003', workflowId: 'WF-001', currentNode: 'n3', startedAt: '2026-04-20T14:05:00Z', status: 'In Progress' },
    { envelopeId: 'ENV-INB-1', workflowId: 'WF-002', currentNode: 'n3', startedAt: '2026-04-20T16:00:00Z', status: 'At Risk' },
    { envelopeId: 'ENV-006', workflowId: 'WF-001', currentNode: 'n3', startedAt: '2026-04-19T09:00:00Z', status: 'In Progress' },
    { envelopeId: 'ENV-INB-2', workflowId: 'WF-002', currentNode: 'n2', startedAt: '2026-04-21T08:00:00Z', status: 'Breached' },
  ];

  // Notary journal entries (improvement #12)
  const ES_NOTARY_JOURNAL = [
    { id: 'NJ-00341', sessionId: 'NOT-002', date: '2026-04-05', notary: 'David Moreno',   state: 'NY', act: 'Acknowledgement',    signers: 2, witnesses: 2, fee: 25.00, method: 'IPEN+Witness' },
    { id: 'NJ-00340', sessionId: null,      date: '2026-04-03', notary: 'Rita Calloway',  state: 'CA', act: 'Jurat',              signers: 1, witnesses: 0, fee: 15.00, method: 'RON' },
    { id: 'NJ-00339', sessionId: null,      date: '2026-03-28', notary: 'Rita Calloway',  state: 'CA', act: 'Acknowledgement',    signers: 2, witnesses: 0, fee: 30.00, method: 'RON' },
    { id: 'NJ-00338', sessionId: null,      date: '2026-03-22', notary: 'David Moreno',   state: 'NY', act: 'Oath/Affirmation',   signers: 1, witnesses: 0, fee: 10.00, method: 'IPEN' },
    { id: 'NJ-00337', sessionId: null,      date: '2026-03-15', notary: 'Rita Calloway',  state: 'CA', act: 'Acknowledgement',    signers: 1, witnesses: 0, fee: 15.00, method: 'RON' },
    { id: 'NJ-00336', sessionId: null,      date: '2026-03-12', notary: 'Maria Santos',   state: 'TX', act: 'Acknowledgement',    signers: 2, witnesses: 0, fee: 20.00, method: 'RON' },
    { id: 'NJ-00335', sessionId: null,      date: '2026-03-08', notary: 'David Moreno',   state: 'NY', act: 'Copy Certification', signers: 0, witnesses: 0, fee: 10.00, method: 'In-Person' },
  ];

  // Signing flow events for audit-chain simulator (improvement #1)
  const ES_SIGN_FLOW_DEMO = {
    steps: ['Consent (ESIGN)', 'Identity Check', 'Review Document', 'Sign Fields', 'Confirm & Submit'],
    kba: { questions: 5, passedAt: null },
  };

  // Commission expiry tracking (improvement #12)
  const ES_COMMISSIONS = [
    { id: 'CM-1', notary: 'Rita Calloway',  state: 'CA', number: 'NNA #284117', expires: '2028-03-14', daysLeft: 691 },
    { id: 'CM-2', notary: 'David Moreno',   state: 'NY', number: 'NNA #198402', expires: '2027-11-30', daysLeft: 587 },
    { id: 'CM-3', notary: 'Maria Santos',   state: 'TX', number: 'NNA #301208', expires: '2026-07-12', daysLeft: 81  },
    { id: 'CM-4', notary: 'Jonathan Reese', state: 'FL', number: 'NNA #412909', expires: '2026-05-20', daysLeft: 28  },
    { id: 'CM-5', notary: 'Elena Petrova',  state: 'IL', number: 'NNA #502101', expires: '2027-02-08', daysLeft: 292 },
  ];

  // ── CENTRAL STORE ──────────────────────────────────────────────────────────
  const src = window.ESIGN_DATA;
  const state = {
    envelopes:          [...src.envelopes],
    templates:          [...src.templates],
    inbox:              [...src.inbox],
    bulkJobs:           [...src.bulkJobs],
    contacts:           [...src.contacts],
    analytics:          { ...src.analytics },
    kpis:               { ...src.kpis },
    notarySessions:     [...src.notarySessions],
    notaryStats:        { ...src.notaryStats },
    workflows:          [...src.workflows],
    workflowSlaMonitors:[...src.workflowSlaMonitors],
    clauseLibrary:      [...src.clauseLibrary],
    riskScans:          [...src.riskScans],
    aiFieldTraining:    { ...src.aiFieldTraining },
    webhooks:           [...src.webhooks],
    sdkKeys:            [...src.sdkKeys],
    connectedApps:      [...src.connectedApps],
    payments:           [...src.payments],
    compliancePosture:  [...src.compliancePosture],
    vaultSettings:      { ...src.vaultSettings },
    delegations:        [...src.delegations],
    redlines:           [...src.redlines],
    templateGovernance: [...src.templateGovernance],
    nudgeLadders:       [...src.nudgeLadders],
    courtCertificates:  { ...src.courtCertificates },

    // Extended
    fieldPlacements: { ...ES_FIELD_PLACEMENTS },
    clauseVersions:  { ...ES_CLAUSE_VERSIONS },
    signerFunnel:    { ...ES_SIGNER_FUNNEL },
    fieldDropoff:    [...ES_FIELD_DROPOFF],
    riskTrend:       [...ES_RISK_TREND],
    wfRuntime:       [...ES_WF_RUNTIME],
    notaryJournal:   [...ES_NOTARY_JOURNAL],
    commissions:     [...ES_COMMISSIONS],

    // Draft compose working state (improvement #5)
    composeDraft: { subject: '', template: null, recipients: [], clauses: [], fields: [] },
  };

  const mkId = (p) => p + '-' + Math.random().toString(36).slice(2, 8).toUpperCase();
  const nowIso = () => new Date().toISOString();

  // Build a synthetic hash chain from an envelope's audit trail
  function buildHashChain(env) {
    if (!env.auditTrail || env.auditTrail.length === 0) return [];
    return env.auditTrail.map((e, i) => ({
      seq: i + 1,
      ts: e.ts,
      event: e.event,
      actor: e.actor,
      detail: e.detail,
      hash: Array.from(e.event + e.ts + i).reduce((h, c) => ((h << 5) - h + c.charCodeAt(0)) | 0, 0).toString(16).padStart(8, '0') + '...',
      prevHash: i === 0 ? '00000000' : null,
    }));
  }

  // Generate a court cert summary
  function generateCourtCert(env) {
    const chain = buildHashChain(env);
    return {
      id: 'CERT-' + env.id,
      generatedAt: nowIso(),
      sha256: Array.from(env.id + (env.completedAt || nowIso())).reduce((h, c) => ((h << 5) - h + c.charCodeAt(0)) | 0, 0).toString(16).padStart(8, '0').repeat(8).slice(0, 64),
      signingKeyFingerprint: 'ARBITER-2026-C2-QES · RSA-4096 · FIPS 140-3 L3',
      tlsCipher: 'TLS_AES_256_GCM_SHA384',
      totpEvents: env.recipients.filter(r => r.status === 'Signed').length,
      hashChain: chain,
      evidentiary: {
        admissibility: 'Federal Rules of Evidence 901(b)(9) · compliant',
        retention: '7 years · matter-bound',
        witnessedBy: env.witnessed ? 'Yes' : null,
        notarizedBy: env.notarized ? 'Yes' : null,
      },
    };
  }

  const EsStore = {
    get: () => state,

    // ── ENVELOPES ────────────────────────────────────────────────────────────
    createEnvelope(draft, user = 'M. Kirkland') {
      const id = mkId('ENV');
      const env = {
        id, subject: draft.subject || 'Untitled Envelope',
        matter: draft.matter || 'Unassigned', matterId: draft.matterId || null,
        status: 'Draft', sentAt: null, completedAt: null, expiresAt: null,
        template: draft.template || null, sender: user,
        turnaround: null, pages: draft.pages || 1, fields: (draft.fields || []).length,
        recipients: (draft.recipients || []).map((r, i) => ({ id: mkId('R'), ...r, order: i + 1, status: 'Pending', signedAt: null, ip: null, device: null })),
        auditTrail: [{ ts: nowIso(), event: 'Envelope created', actor: user, detail: 'Draft created' }],
        privilege: 'None', ethicalWall: false, paymentLinked: false, hasRedlines: false,
        esignConsent: false, notarized: false, witnessed: false,
      };
      state.envelopes.unshift(env);
      if (draft.fields && draft.fields.length) state.fieldPlacements[id] = draft.fields;
      EsBus.emit('envelope.created', { envelope: env });
      return env;
    },

    sendEnvelope(envelopeId, user = 'M. Kirkland') {
      const env = state.envelopes.find(e => e.id === envelopeId);
      if (!env) return;
      env.status = 'Sent';
      env.sentAt = nowIso();
      env.expiresAt = new Date(Date.now() + 30 * 86400000).toISOString();
      env.esignConsent = true;
      env.recipients.forEach(r => { if (r.status === 'Pending') r.status = 'Sent'; });
      env.auditTrail.push({ ts: nowIso(), event: 'Sent', actor: user, detail: `Sent to ${env.recipients.length} recipient(s)` });
      state.kpis.pendingOthers++;
      EsBus.emit('envelope.sent', { envelope: env });
    },

    extendExpiry(envelopeId, days = 14, reason = '', user = 'M. Kirkland') {
      const env = state.envelopes.find(e => e.id === envelopeId);
      if (!env) return;
      const base = env.expiresAt ? new Date(env.expiresAt) : new Date();
      const next = new Date(Math.max(base.getTime(), Date.now()) + days * 86400000).toISOString();
      env.expiresAt = next;
      if (env.status === 'Expired') env.status = env.recipients.some(r => r.status === 'Signed') ? 'Waiting' : 'Sent';
      env.auditTrail.push({ ts: nowIso(), event: 'Extended', actor: user, detail: `Extended ${days}d · Reason: ${reason || 'n/a'}` });
      EsBus.emit('envelope.extended', { envelopeId, days, reason });
    },

    voidEnvelope(envelopeId, reason = '', user = 'M. Kirkland') {
      const env = state.envelopes.find(e => e.id === envelopeId);
      if (!env) return;
      env.status = 'Voided';
      env.auditTrail.push({ ts: nowIso(), event: 'Voided', actor: user, detail: reason || 'Voided by sender' });
      EsBus.emit('envelope.voided', { envelopeId, reason });
    },

    completeEnvelope(envelopeId, user = 'System') {
      const env = state.envelopes.find(e => e.id === envelopeId);
      if (!env) return;
      env.status = 'Completed';
      env.completedAt = nowIso();
      env.recipients.forEach(r => { if (r.role === 'Signer' && r.status !== 'Signed') { r.status = 'Signed'; r.signedAt = nowIso(); } });
      env.auditTrail.push({ ts: nowIso(), event: 'Completed', actor: user, detail: 'All signers complete · Certificate generated' });
      state.courtCertificates[envelopeId] = generateCourtCert(env);
      state.kpis.completedMonth++;
      EsBus.emit('envelope.completed', { envelopeId, envelope: env });
      // Cross-platform: tell docs platform
      EsBus.emit('signdesk.envelopeCompleted', { envelopeId });
    },

    declineEnvelope(envelopeId, reason = '', user = 'Signer') {
      const env = state.envelopes.find(e => e.id === envelopeId);
      if (!env) return;
      env.status = 'Declined';
      env.auditTrail.push({ ts: nowIso(), event: 'Declined', actor: user, detail: `Reason: ${reason || 'No reason provided'}` });
      EsBus.emit('envelope.declined', { envelopeId, reason });
    },

    resendToRecipient(envelopeId, recipientId, user = 'M. Kirkland') {
      const env = state.envelopes.find(e => e.id === envelopeId);
      if (!env) return;
      const r = env.recipients.find(x => x.id === recipientId);
      if (!r) return;
      env.auditTrail.push({ ts: nowIso(), event: 'Resent', actor: user, detail: `Resent to ${r.name}` });
      EsBus.emit('envelope.resent', { envelopeId, recipientId });
    },

    // ── COURT CERT (improvement #10, #11) ────────────────────────────────────
    getCert(envelopeId) {
      if (state.courtCertificates[envelopeId]) return state.courtCertificates[envelopeId];
      const env = state.envelopes.find(e => e.id === envelopeId);
      if (!env || env.status !== 'Completed') return null;
      const cert = generateCourtCert(env);
      state.courtCertificates[envelopeId] = cert;
      return cert;
    },

    exportEvidentiaryPackage(envelopeId, user = 'M. Kirkland') {
      const env = state.envelopes.find(e => e.id === envelopeId);
      if (!env) return;
      env.auditTrail.push({ ts: nowIso(), event: 'Evidentiary export', actor: user, detail: 'PDF + hash chain + audit JSON downloaded' });
      EsBus.emit('envelope.evidentiaryExported', { envelopeId });
    },

    // ── REDLINES (improvement #4) ────────────────────────────────────────────
    proposeRedline(envelopeId, proposer, clause, summary) {
      const rl = { id: mkId('RL'), envelopeId, proposer, clause, status: 'Pending Review', proposed: nowIso(), summary };
      state.redlines.unshift(rl);
      const env = state.envelopes.find(e => e.id === envelopeId);
      if (env) { env.hasRedlines = true; env.auditTrail.push({ ts: nowIso(), event: 'Redline proposed', actor: proposer, detail: `${clause} — ${summary}` }); }
      EsBus.emit('redline.proposed', { redline: rl });
    },

    decideRedline(redlineId, decision, user = 'M. Kirkland', counterText = null) {
      const rl = state.redlines.find(r => r.id === redlineId);
      if (!rl) return;
      rl.status = decision;
      rl.decidedBy = user;
      rl.decidedAt = nowIso();
      if (counterText) rl.counterText = counterText;
      const env = state.envelopes.find(e => e.id === rl.envelopeId);
      if (env) env.auditTrail.push({ ts: nowIso(), event: `Redline ${decision.toLowerCase()}`, actor: user, detail: `${rl.clause} — ${rl.summary}` });
      EsBus.emit('redline.decided', { redlineId, decision });
      if (decision === 'Accepted' && env) {
        // Bump envelope to a new version — in a real system this would branch; here we just note it
        env.auditTrail.push({ ts: nowIso(), event: 'Version bumped', actor: 'System', detail: 'New envelope version from accepted redline' });
      }
    },

    // ── CLAUSES (improvement #5, #6) ─────────────────────────────────────────
    insertClauseIntoDraft(clauseId) {
      const cl = state.clauseLibrary.find(c => c.id === clauseId);
      if (!cl) return;
      state.composeDraft.clauses = [...(state.composeDraft.clauses || []), { id: cl.id, name: cl.name, version: cl.version }];
      EsBus.emit('compose.clauseInserted', { clauseId });
    },
    resetDraft() {
      state.composeDraft = { subject: '', template: null, recipients: [], clauses: [], fields: [] };
      EsBus.emit('compose.reset', {});
    },
    updateDraft(patch) {
      state.composeDraft = { ...state.composeDraft, ...patch };
      EsBus.emit('compose.updated', {});
    },

    // ── NUDGES (improvement #7) ──────────────────────────────────────────────
    updateNudgeLadder(envelopeId, signer, patch) {
      const n = state.nudgeLadders.find(x => x.envelopeId === envelopeId && x.signer === signer);
      if (!n) return;
      Object.assign(n, patch);
      EsBus.emit('nudge.updated', { envelopeId, signer });
    },
    triggerNudge(envelopeId, signer, user = 'M. Kirkland') {
      const n = state.nudgeLadders.find(x => x.envelopeId === envelopeId && x.signer === signer);
      if (n) { n.lastNudge = nowIso(); n.attempts = (n.attempts || 0) + 1; }
      const env = state.envelopes.find(e => e.id === envelopeId);
      if (env) env.auditTrail.push({ ts: nowIso(), event: 'Nudge sent', actor: user, detail: `Nudged ${signer}` });
      EsBus.emit('nudge.triggered', { envelopeId, signer });
    },

    // ── WORKFLOWS (improvement #8) ───────────────────────────────────────────
    advanceWorkflow(envelopeId, user = 'System') {
      const rt = state.wfRuntime.find(r => r.envelopeId === envelopeId);
      if (!rt) return;
      const wf = state.workflows.find(w => w.id === rt.workflowId);
      if (!wf) return;
      const idx = wf.nodes.findIndex(n => n.id === rt.currentNode);
      const next = wf.nodes[idx + 1];
      if (next) { rt.currentNode = next.id; rt.status = 'In Progress'; EsBus.emit('workflow.advanced', { envelopeId, nodeId: next.id }); }
    },

    // ── NOTARY (improvement #12) ─────────────────────────────────────────────
    addJournalEntry(entry, user = 'System') {
      const e = { id: 'NJ-' + String(Math.floor(Math.random() * 100000)).padStart(5, '0'), date: new Date().toISOString().slice(0, 10), ...entry };
      state.notaryJournal.unshift(e);
      EsBus.emit('notary.journalAdded', { entry: e });
    },
    scheduleNotary(sessionId, scheduledAt, user = 'M. Kirkland') {
      const s = state.notarySessions.find(x => x.id === sessionId);
      if (!s) return;
      s.scheduledAt = scheduledAt;
      s.status = 'Scheduled';
      EsBus.emit('notary.scheduled', { sessionId, scheduledAt });
    },

    // ── DELEGATIONS (improvement #18) ────────────────────────────────────────
    revokeDelegation(delegationId, reason = '', user = 'M. Kirkland') {
      const d = state.delegations.find(x => x.id === delegationId);
      if (!d) return;
      d.status = 'Revoked';
      d.revokedAt = nowIso();
      d.revokedBy = user;
      d.revokeReason = reason;
      EsBus.emit('delegation.revoked', { delegationId, reason });
    },

    // ── RISK SCANS (improvement #17) ─────────────────────────────────────────
    runRiskScanBatch(envelopeIds, user = 'M. Kirkland') {
      const scanned = [];
      envelopeIds.forEach(id => {
        const env = state.envelopes.find(e => e.id === id);
        if (!env) return;
        const existing = state.riskScans.find(s => s.envelopeId === id);
        if (existing) { existing.scannedAt = nowIso(); scanned.push(existing); return; }
        const newScan = {
          id: mkId('SCAN'), envelopeId: id, subject: env.subject, scannedAt: nowIso(),
          overallRisk: ['Low', 'Medium', 'High'][Math.floor(Math.random() * 3)],
          findings: [{ severity: 'Low', category: 'Auto-scan', clause: '(multiple)', finding: 'Deviation analysis run against current clause library', recommendation: 'Review findings in detail view' }],
        };
        state.riskScans.unshift(newScan);
        scanned.push(newScan);
      });
      EsBus.emit('risk.batchScanned', { count: scanned.length });
      return scanned;
    },

    // ── PAYMENTS (improvement #13) ───────────────────────────────────────────
    capturePayment(paymentId, user = 'System') {
      const p = state.payments.find(x => x.id === paymentId);
      if (!p) return;
      p.status = 'Captured';
      p.capturedAt = nowIso();
      EsBus.emit('payment.captured', { paymentId, payment: p });
    },
  };

  window.EsStore = EsStore;

  // ── React hook ───────────────────────────────────────────────────────────
  window.useEsStore = function (topics = ['*']) {
    const [, force] = React.useState(0);
    React.useEffect(() => {
      const arr = Array.isArray(topics) ? topics : [topics];
      const unsubs = arr.map(t => EsBus.on(t, () => force(n => n + 1)));
      return () => unsubs.forEach(u => u());
    }, []);
    return EsStore.get();
  };

  // Expose extended data on window for any code that reads directly
  window.ES_FIELD_PLACEMENTS = ES_FIELD_PLACEMENTS;
  window.ES_CLAUSE_VERSIONS  = ES_CLAUSE_VERSIONS;
  window.ES_SIGNER_FUNNEL    = ES_SIGNER_FUNNEL;
  window.ES_FIELD_DROPOFF    = ES_FIELD_DROPOFF;
  window.ES_RISK_TREND       = ES_RISK_TREND;
  window.ES_WF_RUNTIME       = ES_WF_RUNTIME;
  window.ES_NOTARY_JOURNAL   = ES_NOTARY_JOURNAL;
  window.ES_COMMISSIONS      = ES_COMMISSIONS;

  // ── Cross-platform bridge ────────────────────────────────────────────────
  // Doc platform → when a doc requests signature
  window.addEventListener('arbiter:doc.requestSignature', (e) => {
    const { docId, docName, matter, matterId, signers } = e.detail || {};
    const env = EsStore.createEnvelope({ subject: docName || 'Doc-initiated envelope', matter, matterId, template: null, recipients: (signers || []).map(s => ({ name: s, email: s.toLowerCase().replace(/\s/g, '.') + '@counterparty.com', role: 'Signer' })) });
    env.sourceDocId = docId;
    EsBus.emit('envelope.createdFromDoc', { envelopeId: env.id, docId });
  });
  // Matters platform → matter closed
  window.addEventListener('arbiter:matter.closed', (e) => {
    const matterId = e.detail?.matterId;
    if (!matterId) return;
    const affected = state.envelopes.filter(env => env.matterId === matterId && (env.status === 'Sent' || env.status === 'Waiting' || env.status === 'Draft'));
    affected.forEach(env => { env.status = 'Voided'; env.auditTrail.push({ ts: nowIso(), event: 'Voided (matter closed)', actor: 'System', detail: `Matter ${matterId} closed — envelope auto-voided` }); });
    EsBus.emit('envelope.bulkVoidedByMatter', { matterId, count: affected.length });
  });

  // ── Broadcast completed envelope as doc sign event for the doc platform ──
  EsBus.on('envelope.completed', (data) => {
    try { window.dispatchEvent(new CustomEvent('arbiter:external:signdeskCompleted', { detail: { envelopeId: data.envelopeId } })); } catch (e) {}
  });
})();
