// T2 Falcon Admin — Voice Service (CommChannels & Services → Voice Service)
// Voice account tab: client-scoped grid of voice accounts + a "Create Voice Account"
// wizard (name → SIP provider → phone numbers → review). Mirrors the Add Contact
// Group flow chrome (.ac-page / .ac-topbar / .ac-stepper) and the Contact Groups
// list chrome (.node-header / .table-panel / .users-table / status-badge / row-menu).

const { useState: useStateVS, useRef: useRefVS, useEffect: useEffectVS } = React;

// ===== Steps =====
const VS_STEPS = [
  { id: 'details', label: 'Account Details' },
  { id: 'review',  label: 'Review & Create' },
];

const vsProviderLabel = (p, t) =>
  p === 'byo' ? (t.vsProviderByo || 'Own SIP (BYO)') : (t.vsProviderT2 || 'T2 SIP Trunk');

const vsNowStamp = () => {
  const d = new Date();
  const pad = (n) => String(n).padStart(2, '0');
  let h = d.getHours(); const am = h < 12 ? 'am' : 'pm'; h = h % 12 || 12;
  return `${pad(d.getDate())}/${pad(d.getMonth() + 1)}/${d.getFullYear()} | ${pad(h)}:${pad(d.getMinutes())} ${am}`;
};

// Date stamps are stored "DD/MM/YYYY | HH:MM am". Display them as "DD-Mon-YYYY"
// (e.g. 12-Mar-2025) — the same day-month-year style as the Contracts module.
const VS_MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const vsParseStamp = (s) => {
  const str = String(s || '');
  const i = str.indexOf('|');
  const datePart = (i === -1 ? str : str.slice(0, i)).trim();
  const time = i === -1 ? '' : str.slice(i + 1).trim();
  const m = datePart.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/);
  const date = m ? `${m[1].padStart(2, '0')}-${VS_MONTHS[(+m[2] - 1)] || m[2]}-${m[3]}` : datePart;
  return { date, time };
};
// Inline "DD-Mon-YYYY · HH:MM am" for labelled detail fields.
const vsFmtStamp = (s) => { const { date, time } = vsParseStamp(s); return time ? `${date} · ${time}` : date; };
// Render a stamp as a two-line grid cell (date over time), using the DD-Mon-YYYY date format.
const VsDateCell = ({ stamp }) => {
  const { date, time } = vsParseStamp(stamp);
  return <div className="tpl-date"><span className="d">{date}</span>{time && <span className="t">{time}</span>}</div>;
};

// ===== Voice records helpers =====
const VR_STEPS = [
  { id: 'details', label: 'Record details' },
  { id: 'share',   label: 'Share record' },
];
const fmtDur = (sec) => { const s = Math.max(0, Math.round(sec || 0)); return `${Math.floor(s / 60)}:${String(s % 60).padStart(2, '0')}`; };
const vsSourceLabel = (src, t) =>
  src === 'tts' ? (t.vrSrcTts || 'Text-to-Speech')
  : src === 'record' ? (t.vrSrcRecord || 'Microphone')
  : (t.vrSrcUpload || 'Upload (MP3/WAV)');

// ===== Stepper (mirrors ACGStepBar) =====
const VSStepBar = ({ steps = VS_STEPS, current, onJump }) => {
  const pct = (current / (steps.length - 1)) * 100;
  return (
    <div className="ac-stepper">
      <div className="ac-stepper-track">
        <div className="ac-stepper-fill" style={{ width: `${pct}%` }} />
        {steps.map((s, i) => {
          const state = i < current ? 'done' : i === current ? 'active' : 'idle';
          return (
            <button
              key={s.id}
              className={`ac-stepper-dot ${state}`}
              style={{ left: `${(i / (steps.length - 1)) * 100}%` }}
              onClick={() => onJump && i <= current && onJump(i)}
              type="button"
              aria-label={s.label}
            >
              {state === 'done' ? (
                <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="3.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12" /></svg>
              ) : state === 'active' ? <span className="ac-stepper-dot-pulse" /> : null}
            </button>
          );
        })}
      </div>
      <div className="ac-stepper-labels">
        {steps.map((s, i) => (
          <span
            key={s.id}
            className={`ac-step-label ${i === current ? 'active' : ''} ${i < current ? 'done' : ''}`}
            style={{ left: `${(i / (steps.length - 1)) * 100}%` }}
          >
            {s.label}
          </span>
        ))}
      </div>
    </div>
  );
};

// ===== Status pill =====
const VoiceStatusPill = ({ status, t }) => {
  const active = status === 'active';
  return (
    <span className={`status-badge ${active ? 'active' : 'disabled'}`}>
      <span className="dot"></span>
      {active ? (t.vsStatusActive || 'Active') : (t.vsStatusDisabled || 'Disabled')}
    </span>
  );
};

// ===== Phone numbers cell (grid) =====
const VoiceNumbersCell = ({ numbers = [], t = {} }) => {
  const head = numbers.slice(0, 2);
  const rest = numbers.length - head.length;
  const wrapRef = useRefVS(null);
  const [tip, setTip] = useStateVS(null); // null | { top, right } — popup listing all numbers
  useEffectVS(() => {
    if (!tip) return;
    const onDoc = (e) => { if (wrapRef.current && !wrapRef.current.contains(e.target)) setTip(null); };
    const close = () => setTip(null);
    document.addEventListener('mousedown', onDoc);
    window.addEventListener('scroll', close, true);
    window.addEventListener('resize', close);
    return () => { document.removeEventListener('mousedown', onDoc); window.removeEventListener('scroll', close, true); window.removeEventListener('resize', close); };
  }, [tip]);
  // The "+N" badge opens a popup with the full list. position:fixed (from the badge's
  // rect) escapes the table's overflow clipping; flips up when there's no room below.
  const toggle = (e) => {
    e.stopPropagation();
    if (tip) { setTip(null); return; }
    const r = e.currentTarget.getBoundingClientRect();
    const estH = Math.min(300, 46 + numbers.length * 34);
    const below = window.innerHeight - r.bottom;
    const top = (below < estH + 12 && r.top > below) ? (r.top - estH - 6) : (r.bottom + 6);
    setTip({ top, right: window.innerWidth - r.right });
  };
  return (
    <div className="vs-num-cell" ref={wrapRef}>
      {head.map((n, i) => (
        <span key={i} className="vs-num-chip">
          {n.value}
          {n.status === 'pending' && <span className="vs-num-dot" title={t.vsPending || 'Pending approval'} />}
        </span>
      ))}
      {rest > 0 && (
        <button type="button" className={`vs-num-more ${tip ? 'is-open' : ''}`} onClick={toggle} title={t.vsShowAllNumbers || 'Show all numbers'} aria-label={t.vsShowAllNumbers || 'Show all numbers'}>+{rest}</button>
      )}
      {numbers.length === 0 && <span className="vs-num-empty">—</span>}
      {tip && (
        <div className="vs-num-pop" role="dialog" style={{ position: 'fixed', top: tip.top, right: tip.right }} onClick={(e) => e.stopPropagation()}>
          <div className="vs-num-pop-title">{t.vsColNumbers || 'Phone numbers'} <span className="vs-num-pop-count">{numbers.length}</span></div>
          <div className="vs-num-pop-items">
            {numbers.map((n, i) => (
              <div key={i} className="vs-num-pop-item">
                <IcPhone size={13} stroke={1.7} />
                <span className="vs-num-val">{n.value}</span>
                {n.status === 'pending' && <span className="vs-num-dot" title={t.vsPending || 'Pending approval'} />}
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
};

// ===== Row menu =====
const VoiceRowMenu = ({ open, row, onMore, onEdit, onToggle, t }) => {
  const ref = useRefVS(null);
  const [up, setUp] = useStateVS(false);
  // Flip the menu upward when opening it on a bottom row would clip it against the
  // table's scroll container (.tpl-table-wrap has overflow → it would cut the menu off).
  React.useLayoutEffect(() => {
    if (!open || !ref.current) { setUp(false); return; }
    const wrap = ref.current.closest('.tpl-table-wrap') || ref.current.closest('.table-scroll');
    if (!wrap) return;
    const mr = ref.current.getBoundingClientRect();
    const wr = wrap.getBoundingClientRect();
    setUp(mr.bottom > wr.bottom - 4 && (mr.top - wr.top) > mr.height);
  }, [open]);
  if (!open) return null;
  const active = row.status === 'active';
  return (
    <div className={`row-menu ${up ? 'up' : ''}`} ref={ref} onClick={(e) => e.stopPropagation()}>
      <button className="row-menu-item" onClick={onMore}>
        <IcInfo size={14} stroke={1.8} /> <span>{t.vsMoreDetails || 'More Details'}</span>
      </button>
      <button className="row-menu-item" onClick={onEdit}>
        <IcEdit size={14} stroke={1.8} /> <span>{t.vsEdit || 'Edit'}</span>
      </button>
      <button className="row-menu-item" onClick={onToggle}>
        {active ? <IcClose size={14} stroke={2} /> : <IcCheck size={14} stroke={2.4} />}
        <span>{active ? (t.vsDisable || 'Disable') : (t.vsActivate || 'Activate')}</span>
      </button>
    </div>
  );
};

// ===== Accounts grid =====
const VoiceAccountsList = ({ rows, t, onView, onEdit, onToggle, openMenuId, setOpenMenuId, page, pageSize, setPage, setPageSize }) => {
  const total = rows.length;
  const pageRows = rows.slice((page - 1) * pageSize, page * pageSize);
  return (
    <div className="table-panel tpl-table-panel vs-table-panel">
      <div className="table-head-bar tpl-head-bar"><div className="table-head-title">{t.vsTabAccount || 'Voice account'}</div></div>
      <div className="table-scroll tpl-table-wrap">
        <table className="tpl-table vs-accounts-table">
          <thead>
            <tr>
              <th>{t.vsColName || 'Account name'}</th>
              <th>{t.vsColProvider || 'Provider'}</th>
              <th>{t.vsColNumbers || 'Phone numbers'}</th>
              <th>{t.vsColStatus || 'Status'}</th>
              <th>{t.vsColDate || 'Creation Date'}</th>
              <th>{t.vsColCreatedBy || 'Created by'}</th>
              <th>{t.vsColModifiedDate || 'Modified Date'}</th>
              <th>{t.vsColModifiedBy || 'Modified by'}</th>
              <th className="col-actions">{t.vsColActions || 'Actions'}</th>
            </tr>
          </thead>
          <tbody>
            {pageRows.map((r) => {
              const isOpen = openMenuId === r.id;
              return (
                <tr key={r.id} onClick={() => onView(r)} style={{ cursor: 'pointer' }}>
                  <td className="vs-cell-name">{r.name}</td>
                  <td><span className={`vs-provider-tag ${r.provider}`}>{vsProviderLabel(r.provider, t)}</span></td>
                  <td><VoiceNumbersCell numbers={r.numbers} t={t} /></td>
                  <td><VoiceStatusPill status={r.status} t={t} /></td>
                  <td><VsDateCell stamp={r.createdAt} /></td>
                  <td className="vs-cell-who">{r.createdBy?.name || '—'}</td>
                  <td>{r.modifiedAt ? <VsDateCell stamp={r.modifiedAt} /> : <span className="muted">—</span>}</td>
                  <td className="vs-cell-who">{r.modifiedBy?.name || '—'}</td>
                  <td className="col-actions" style={{ position: 'relative' }} onClick={(e) => e.stopPropagation()}>
                    <button className={`row-action-btn ${isOpen ? 'open' : ''}`} onClick={() => setOpenMenuId(isOpen ? null : r.id)} aria-label="Actions">
                      <IcMore size={16} />
                    </button>
                    <VoiceRowMenu
                      open={isOpen} row={r} t={t}
                      onMore={() => { setOpenMenuId(null); onView(r); }}
                      onEdit={() => { setOpenMenuId(null); onEdit(r); }}
                      onToggle={() => { setOpenMenuId(null); onToggle(r); }}
                    />
                  </td>
                </tr>
              );
            })}
            {pageRows.length === 0 && (
              <tr><td colSpan={9} className="vs-empty-row">{t.vsEmpty || 'No voice accounts yet.'}</td></tr>
            )}
          </tbody>
        </table>
      </div>
      {window.TablePagination && (
        <window.TablePagination
          total={total} page={page} pageSize={pageSize}
          onPageChange={setPage}
          onPageSizeChange={(n) => { setPageSize(n); setPage(1); }}
          t={t}
        />
      )}
    </div>
  );
};

// ===== Account details (read-only) =====
const VoiceAccountDetails = ({ row, t }) => {
  if (!row) return null;
  return (
    <div className="tpl-details-card vs-details-card">
      <div className="hd">{t.vsDetailsTitle || 'Voice account details'}</div>
      <div className="tpl-details-grid">
        <div className="tpl-d-field"><span className="lab">{t.vsColName || 'Account name'}</span><span className="val">{row.name}</span></div>
        <div className="tpl-d-field"><span className="lab">{t.vsColProvider || 'Provider'}</span><span className="val">{vsProviderLabel(row.provider, t)}</span></div>
        <div className="tpl-d-field"><span className="lab">{t.vsColStatus || 'Status'}</span><div className="pill-row"><VoiceStatusPill status={row.status} t={t} /></div></div>
        <div className="tpl-d-field"><span className="lab">{t.vsColDate || 'Creation Date'}</span><span className="val">{vsFmtStamp(row.createdAt)}</span></div>
        <div className="tpl-d-field"><span className="lab">{t.vsColCreatedBy || 'Created by'}</span><span className="val">{row.createdBy?.name || '—'}</span></div>
        <div className="tpl-d-field"><span className="lab">{t.vsColModifiedDate || 'Modified Date'}</span><span className="val">{row.modifiedAt ? vsFmtStamp(row.modifiedAt) : '—'}</span></div>
        <div className="tpl-d-field"><span className="lab">{t.vsColModifiedBy || 'Modified by'}</span><span className="val">{row.modifiedBy?.name || '—'}</span></div>
        <div className="tpl-d-field full">
          <span className="lab">{t.vsColNumbers || 'Phone numbers'}</span>
          <div className="vs-num-list">
            {row.numbers.map((n, i) => (
              <span key={i} className="vs-num-chip lg">
                <IcPhone size={13} stroke={1.7} />
                <span className="vs-num-val">{n.value}</span>
                {n.status === 'pending' && <span className="vs-num-pending">{t.vsPending || 'Pending approval'}</span>}
              </span>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
};

// ===== Wizard Step 1 — Account details =====
// ===== Wizard Step 1 — Account Details (name + SIP provider + phone numbers) =====
const VSStepAccount = ({ form, update, errors, t }) => {
  const opts = [
    { id: 't2', tint: 'teal', icon: <T2Mark size={26} color="#0d7a72" />, title: t.vsProviderT2 || 'T2 SIP Trunk', desc: t.vsProviderT2Desc || 'All client voice calls use the T2 SIP trunk.' },
    { id: 'byo', tint: 'blue', icon: <IcGlobe size={26} stroke={1.7} />, title: t.vsProviderByo || 'Own SIP (BYO)', desc: t.vsProviderByoDesc || "All voice calls use the client's own SIP trunk — linked between Falcon and the client (T2 DevOps + client network/app teams)." },
  ];
  const pick = (id) => { if (form.provider !== id) { update('provider', id); update('numbers', []); } };
  const isT2 = form.provider === 't2';
  return (
    <div className="ac-step-pane vs-account-step">
      <div className="ac-field-grid ac-grid-4">
        <div className="ac-field wide">
          <label className="ac-label">{t.vsName || 'Account Name'} <span className="req">*</span></label>
          <input
            className={`ac-input ${errors.name ? 'error' : ''}`}
            placeholder={t.vsNamePh || 'e.g. Main Customer Line'}
            value={form.name}
            maxLength={50}
            onChange={(e) => update('name', e.target.value)}
          />
          {errors.name && <span className="ac-error-msg">{errors.name}</span>}
        </div>
      </div>

      <div className="vs-acc-block">
        <label className="ac-label">{t.vsProviderLabel || 'SIP provider'} <span className="req">*</span></label>
        <div className="vs-provider-grid">
          {opts.map((o) => (
            <button key={o.id} type="button" className={`vs-provider-card ${form.provider === o.id ? 'on' : ''}`} onClick={() => pick(o.id)}>
              <span className={`vs-provider-ic ${o.tint}`}>{o.icon}</span>
              <span className="vs-provider-ttl">{o.title}</span>
              <span className="vs-provider-desc">{o.desc}</span>
            </button>
          ))}
        </div>
        {errors.provider && <span className="ac-error-msg">{errors.provider}</span>}
      </div>

      {form.provider && (
        <div className="vs-acc-block">
          <div className="vs-num-head">
            <span className="vs-num-head-ttl">{t.vsNumbersTitle || 'Phone numbers (Sender IDs)'} <span className="req">*</span></span>
            <span className="acg-cols-hint">{isT2 ? (t.vsNumbersT2Hint || 'Purchasing T2 numbers needs approval — selected numbers stay "Pending approval".') : (t.vsNumbersByoHint || 'Enter the numbers supported by your linked SIP trunk (digits only).')}</span>
          </div>
          <VoiceNumbersEditor provider={form.provider} numbers={form.numbers} onChange={(v) => update('numbers', v)} t={t} />
          {errors.numbers && <span className="ac-error-msg">{errors.numbers}</span>}
        </div>
      )}
    </div>
  );
};

// ===== Wizard Step 2 — SIP provider =====
const VSStepProvider = ({ form, update, t }) => {
  const opts = [
    { id: 't2', icon: <T2Mark size={24} color="#0d3f44" />, title: t.vsProviderT2 || 'T2 SIP Trunk', desc: t.vsProviderT2Desc || 'All client voice calls use the T2 SIP trunk.' },
    { id: 'byo', icon: <IcGlobe size={24} stroke={1.6} />, title: t.vsProviderByo || 'Bring your own', desc: t.vsProviderByoDesc || "All voice calls use the client's own SIP trunk — linked between Falcon and the client (T2 DevOps + client network/app teams)." },
  ];
  const pick = (id) => { if (form.provider !== id) { update('provider', id); update('numbers', []); } };
  return (
    <div className="ac-step-pane">
      <div className="vs-provider-grid">
        {opts.map((o) => (
          <button key={o.id} type="button" className={`vs-provider-card ${form.provider === o.id ? 'on' : ''}`} onClick={() => pick(o.id)}>
            <span className="vs-provider-radio">{form.provider === o.id && <span className="vs-provider-radio-dot" />}</span>
            <span className="vs-provider-ic">{o.icon}</span>
            <span className="vs-provider-ttl">{o.title}</span>
            <span className="vs-provider-desc">{o.desc}</span>
          </button>
        ))}
      </div>
    </div>
  );
};

// ===== Provider dropdown (DDL) — used in the account edit screen =====
const VsProviderDropdown = ({ value, onChange, t }) => {
  const [open, setOpen] = useStateVS(false);
  const wrapRef = useRefVS(null);
  useEffectVS(() => {
    if (!open) return;
    const onDown = (e) => { if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', onDown);
    return () => document.removeEventListener('mousedown', onDown);
  }, [open]);
  const opts = [
    { id: 't2', label: t.vsProviderT2 || 'T2 SIP Trunk' },
    { id: 'byo', label: t.vsProviderByo || 'Own SIP (BYO)' },
  ];
  const cur = opts.find((o) => o.id === value) || opts[0];
  return (
    <div className={`vs-ddl vs-provider-ddl ${open ? 'open' : ''}`} ref={wrapRef}>
      <button type="button" className="vs-ddl-control" onClick={() => setOpen((o) => !o)}>
        <span>{cur.label}</span>
        <svg className="vs-ddl-caret" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><polyline points="6 9 12 15 18 9" /></svg>
      </button>
      {open && (
        <div className="vs-ddl-pop">
          {opts.map((o) => (
            <button type="button" key={o.id} className={`vs-ddl-item ${o.id === value ? 'on' : ''}`} onClick={() => { onChange(o.id); setOpen(false); }}>{o.label}</button>
          ))}
        </div>
      )}
    </div>
  );
};

// ===== T2 number picker — multi-select dropdown; selected numbers show as chips in the control =====
const T2NumberPicker = ({ pool, numbers, onChange, t }) => {
  const [open, setOpen] = useStateVS(false);
  const [dropUp, setDropUp] = useStateVS(false);
  const wrapRef = useRefVS(null);
  useEffectVS(() => {
    if (!open) return;
    const onDown = (e) => { if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', onDown);
    return () => document.removeEventListener('mousedown', onDown);
  }, [open]);
  // Open upward when there isn't enough room below (so the list isn't clipped by the wizard's scroll area).
  const toggleOpen = () => {
    if (!open) {
      const r = wrapRef.current && wrapRef.current.getBoundingClientRect();
      if (r) { const below = window.innerHeight - r.bottom; setDropUp(below < 300 && r.top > below); }
    }
    setOpen((o) => !o);
  };
  const has = (v) => numbers.some((n) => n.value === v);
  // Options = the T2 pool + any already-selected numbers not in the pool (so every
  // selected number is shown/uncheckable in the list, not just an orphan chip).
  const opts = [...pool.map((p) => p.value)];
  numbers.forEach((n) => { if (!opts.includes(n.value)) opts.push(n.value); });
  const toggle = (v) => (has(v) ? onChange(numbers.filter((n) => n.value !== v)) : onChange([...numbers, { value: v, status: 'pending' }]));
  const remove = (v) => onChange(numbers.filter((n) => n.value !== v));
  const allOn = opts.length > 0 && opts.every((v) => has(v));
  const toggleAll = () => onChange(allOn ? [] : opts.map((v) => { const ex = numbers.find((n) => n.value === v); return { value: v, status: ex ? ex.status : 'pending' }; }));
  const check = <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="3.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12" /></svg>;
  // Compact trigger shows a count summary (like the system's share combo); the
  // chosen numbers render as chips BELOW the control — the same area/markup the
  // BYO path uses — instead of cramming every chip inside the control.
  const count = numbers.length;
  const summary = count === 0
    ? (t.vsT2Pick || 'Select numbers from the list…')
    : allOn
      ? (t.vsT2AllSel || 'All numbers selected')
      : `${count} ${count === 1 ? (t.vsT2SelectedOne || 'number selected') : (t.vsT2Selected || 'numbers selected')}`;
  return (
    <div className="vs-num-t2">
      <div className={`vs-ddl vs-num-ddl ${open ? 'open' : ''} ${dropUp ? 'drop-up' : ''}`} ref={wrapRef}>
        <button type="button" className="vs-ddl-control vs-num-trigger" onClick={toggleOpen}>
          <span className={count ? 'vs-num-summary' : 'vs-ddl-ph'}>{summary}</span>
          <svg className="vs-ddl-caret" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><polyline points="6 9 12 15 18 9" /></svg>
        </button>
        {open && (
          <div className="vs-ddl-pop vs-num-pop">
            <label className={`acg-combo-row all ${allOn ? 'on' : ''}`}>
              <input type="checkbox" checked={allOn} onChange={toggleAll} />
              <span className="acg-cb-mark">{allOn && check}</span>
              <span className="acg-combo-row-name">{t.vsT2All || 'All numbers'}</span>
            </label>
            {opts.map((v) => {
              const on = has(v);
              return (
                <label key={v} className={`acg-combo-row ${on ? 'on' : ''}`}>
                  <input type="checkbox" checked={on} onChange={() => toggle(v)} />
                  <span className="acg-cb-mark">{on && check}</span>
                  <span className="vs-num-opt-val"><IcPhone size={14} stroke={1.7} /> {v}</span>
                </label>
              );
            })}
          </div>
        )}
      </div>
      <div className="vs-num-selected vs-num-t2-chips">
        {count === 0 ? (
          <span className="vs-num-empty">{t.vsT2NoneSel || 'No numbers selected yet.'}</span>
        ) : (
          numbers.map((n) => (
            <span key={n.value} className="vs-num-chip lg">
              <IcPhone size={13} stroke={1.7} />
              <span className="vs-num-val">{n.value}</span>
              <button type="button" className="vs-num-x" title={t.remove || 'Remove'} onClick={() => remove(n.value)}>
                <IcClose size={11} stroke={2} />
              </button>
            </span>
          ))
        )}
      </div>
    </div>
  );
};

// ===== Wizard Step 3 — Phone numbers (Sender IDs) =====
// Reusable phone-numbers editor (used by the create wizard + the account edit screen).
// T2 → choose from the available pool via a dropdown (no free entry). BYO → enter
// numbers manually with the country-code field.
const VoiceNumbersEditor = ({ provider, numbers, onChange, t }) => {
  const isT2 = provider === 't2';
  const pool = window.seedT2Numbers || [];
  const has = (v) => numbers.some((n) => n.value === v);
  const add = (v, status) => { const val = (v || '').trim(); if (!val || has(val)) return; onChange([...numbers, { value: val, status }]); };
  const remove = (v) => onChange(numbers.filter((n) => n.value !== v));
  // BYO draft = a country + number, entered with the shared PhoneInput (same as the user phone field).
  const [draft, setDraft] = useStateVS({ phoneCountry: 'SA', phone: '' });
  const PI = window.PhoneInput;
  const dialOf = (code) => ((window.COUNTRIES || []).find((c) => c.code === code) || {}).dial || '';
  const onAddDraft = () => {
    const raw = (draft.phone || '').replace(/[^\d\s]/g, '').replace(/\s+/g, ' ').trim();
    if (!/\d/.test(raw)) return; // need at least one digit
    add(`${dialOf(draft.phoneCountry)} ${raw}`.trim(), 'approved');
    setDraft((d) => ({ ...d, phone: '' }));
  };
  // T2 → multi-select dropdown (compact trigger + chips below).
  if (isT2) {
    return <T2NumberPicker pool={pool} numbers={numbers} onChange={onChange} t={t} />;
  }
  // BYO (Own SIP) → manual entry with a country code, like the user phone-number field.
  return (
    <>
      <div className="vs-num-add vs-num-add-cc">
        {PI ? (
          <PI data={draft} onChange={(k, v) => setDraft((d) => ({ ...d, [k]: v }))} showVerify={false} />
        ) : (
          <input
            className="ac-input"
            inputMode="numeric"
            placeholder={t.vsNumberPh || 'Enter digits only (e.g. 96650…)'}
            value={draft.phone || ''}
            onChange={(e) => setDraft((d) => ({ ...d, phone: e.target.value.replace(/\D/g, '') }))}
            onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); onAddDraft(); } }}
          />
        )}
        <button type="button" className="btn btn-primary" onClick={onAddDraft}>
          <IcPlus size={14} stroke={2} /> {t.vsAddNumber || 'Add'}
        </button>
      </div>
      <div className="vs-num-selected">
        {numbers.length === 0 ? (
          <span className="vs-num-empty">{t.vsNoNumbers || 'No numbers added yet.'}</span>
        ) : (
          numbers.map((n) => (
            <span key={n.value} className="vs-num-chip lg">
              <IcPhone size={13} stroke={1.7} />
              <span className="vs-num-val">{n.value}</span>
              {n.status === 'pending' && <span className="vs-num-pending">{t.vsPending || 'Pending approval'}</span>}
              <button type="button" className="vs-num-x" title="Remove" onClick={() => remove(n.value)}>
                <IcClose size={11} stroke={2} />
              </button>
            </span>
          ))
        )}
      </div>
    </>
  );
};

const VSStepNumbers = ({ form, update, t }) => {
  const isT2 = form.provider === 't2';
  return (
    <div className="ac-step-pane">
      <div className="vs-num-head">
        <span className="vs-num-head-ttl">{t.vsNumbersTitle || 'Phone numbers (Sender IDs)'}</span>
        <span className="acg-cols-hint">
          {isT2
            ? (t.vsNumbersT2Hint || 'Purchasing T2 numbers needs approval — selected numbers stay "Pending approval".')
            : (t.vsNumbersByoHint || 'Enter the numbers supported by your linked SIP trunk.')}
        </span>
      </div>
      <VoiceNumbersEditor provider={form.provider} numbers={form.numbers} onChange={(v) => update('numbers', v)} t={t} />
    </div>
  );
};

// ===== Edit Voice Account — same as "More Details" but the phone numbers are editable =====
const VoiceAccountEditForm = ({ row, name, onNameChange, provider, onProviderChange, numbers, onNumbersChange, t }) => {
  if (!row) return null;
  const isT2 = provider === 't2';
  return (
    <div className="tpl-details-card vs-details-card">
      <div className="hd">{t.vsEditTitle || 'Edit Voice Account'}</div>
      <div className="tpl-details-grid">
        <div className="tpl-d-field vs-edit-name-field"><span className="lab">{t.vsColName || 'Account name'}</span><input className="ac-input vs-edit-name-input" value={name} maxLength={50} placeholder={t.vsNamePh || 'e.g. Main Customer Line'} onChange={(e) => onNameChange(e.target.value)} /></div>
        <div className="tpl-d-field vs-edit-provider-field"><span className="lab">{t.vsColProvider || 'Provider'}</span><VsProviderDropdown value={provider} onChange={onProviderChange} t={t} /></div>
        <div className="tpl-d-field"><span className="lab">{t.vsColDate || 'Creation Date'}</span><span className="val">{vsFmtStamp(row.createdAt)}</span></div>
        <div className="tpl-d-field"><span className="lab">{t.vsColStatus || 'Status'}</span><div className="pill-row"><VoiceStatusPill status={row.status} t={t} /></div></div>
        <div className="tpl-d-field full vs-edit-numbers-field">
          <span className="lab">{t.vsColNumbers || 'Phone numbers'} <span className="vs-edit-hint">{isT2 ? (t.vsNumbersT2Hint || 'Purchasing T2 numbers needs approval — selected numbers stay "Pending approval".') : (t.vsNumbersByoHint || 'Enter the numbers supported by your linked SIP trunk.')}</span></span>
          <div className="vs-edit-numbers">
            <VoiceNumbersEditor provider={provider} numbers={numbers} onChange={onNumbersChange} t={t} />
            {numbers.length === 0 && <span className="ac-error-msg vs-edit-num-error">{t.vsNumbersReq || 'Add at least one phone number'}</span>}
          </div>
        </div>
      </div>
    </div>
  );
};

// ===== Wizard Step 4 — Review & create =====
const VSStepReview = ({ form, t }) => (
  <div className="ac-step-pane">
    <div className="vs-review">
      <div className="vs-review-grid">
        <div className="vs-review-field"><label>{t.vsColName || 'Account name'}</label><div className="val">{form.name || '—'}</div></div>
        <div className="vs-review-field"><label>{t.vsColProvider || 'Provider'}</label><div className="val">{vsProviderLabel(form.provider, t)}</div></div>
        <div className="vs-review-field"><label>{t.vsColStatus || 'Status'}</label><div className="val"><span className="status-badge active"><span className="dot"></span>{t.vsStatusActive || 'Active'}</span></div></div>
      </div>
      <div className="vs-review-numbers">
        <label>{t.vsColNumbers || 'Phone numbers'}</label>
        <div className="vs-num-list">
          {form.numbers.map((n, i) => (
            <span key={i} className="vs-num-chip lg">
              <IcPhone size={13} stroke={1.7} />
              <span className="vs-num-val">{n.value}</span>
              {n.status === 'pending' && <span className="vs-num-pending">{t.vsPending || 'Pending approval'}</span>}
            </span>
          ))}
        </div>
      </div>
    </div>
  </div>
);

// ===== Create / Edit Voice Account wizard (mirrors AddContactGroupFlow) =====
const CreateVoiceAccountFlow = ({ t, pushToast, scopeNode, onClose, onSave, initial, existingNames = [] }) => {
  const editing = !!initial;
  const [step, setStep] = useStateVS(0);
  const [direction, setDirection] = useStateVS(1);
  const [errors, setErrors] = useStateVS({});
  const [form, setForm] = useStateVS({
    name: initial?.name || '',
    provider: initial?.provider || '',
    numbers: initial ? initial.numbers.map((n) => ({ ...n })) : [],
  });
  const update = (k, v) => { setForm((f) => ({ ...f, [k]: v })); if (errors[k]) setErrors((e) => ({ ...e, [k]: undefined })); };

  const nameTrim = form.name.trim();
  const nameDuplicate = !!nameTrim && existingNames.some((n) => (n || '').trim().toLowerCase() === nameTrim.toLowerCase() && (!initial || n !== initial.name));
  // Live inline error for the name (shown as the user types, since Next is gated/disabled when invalid).
  const nameLiveError = !nameTrim ? null
    : nameTrim.length < 2 ? (t.vsNameMin || 'At least 2 characters')
    : nameTrim.length > 50 ? (t.vsNameMax || 'Max 50 characters')
    : nameDuplicate ? (t.vsNameUnique || 'An account with this name already exists')
    : null;

  const isLast = step === VS_STEPS.length - 1;
  const stepIsValid = (s) => {
    if (s === 0) {
      const nameOk = nameTrim.length >= 2 && nameTrim.length <= 50 && !nameDuplicate;
      const providerOk = form.provider === 't2' || form.provider === 'byo';
      return nameOk && providerOk && form.numbers.length > 0;
    }
    return true; // step 1 — review
  };
  const validateStep = (s) => {
    const errs = {};
    if (s === 0) {
      if (!nameTrim) errs.name = t.vsNameReq || '*Required';
      else if (nameTrim.length < 2) errs.name = t.vsNameMin || 'At least 2 characters';
      else if (nameTrim.length > 50) errs.name = t.vsNameMax || 'Max 50 characters';
      else if (nameDuplicate) errs.name = t.vsNameUnique || 'An account with this name already exists';
      if (!form.provider) errs.provider = t.vsProviderReq || 'Please select a SIP provider';
      if (form.numbers.length === 0) errs.numbers = t.vsNumbersReq || 'Add at least one phone number';
    }
    setErrors(errs);
    return Object.keys(errs).length === 0;
  };
  const nextDisabled = !stepIsValid(step);
  const handleNext = () => {
    if (!validateStep(step)) return;
    if (isLast) { onSave({ ...(initial || {}), name: nameTrim, provider: form.provider, numbers: form.numbers }); return; }
    setDirection(1); setStep((s) => s + 1);
  };
  const handlePrev = () => { if (step > 0) { setDirection(-1); setStep((s) => s - 1); } };

  return (
    <div className="ac-page vs-page">
      <div className="ac-topbar">
        <div className="ac-topbar-brand cw-topbar-brand">
          {window.CgScopeLogo
            ? <window.CgScopeLogo node={scopeNode} size={32} />
            : <BrandLogo brand={scopeNode?.brand} size={32} />}
          <span className="ac-brand-name cw-brand-name">{scopeNode?.name || 'Falcon'}</span>
        </div>
        <div className="ac-topbar-actions">
          <button className="btn btn-secondary" type="button" onClick={onClose}>Cancel</button>
          <button className="btn btn-secondary" type="button" onClick={handlePrev} disabled={step === 0}>Previous</button>
          <button className="btn btn-primary" type="button" onClick={handleNext} disabled={nextDisabled}>
            {isLast ? (editing ? 'Save Changes' : 'Create') : 'Next'}
          </button>
        </div>
      </div>

      <div className="ac-body">
        <div className="ac-card">
          <div className="ac-card-head">
            <div className="ac-card-title">{editing ? (t.vsEditTitle || 'Edit Voice Account') : (t.vsNewTitle || 'New Voice Account')}</div>
            <div className="ac-step-counter">step <strong>{step + 1}/{VS_STEPS.length}</strong></div>
          </div>

          <VSStepBar current={step} onJump={(i) => { setDirection(i > step ? 1 : -1); setStep(i); }} />

          <div className="ac-card-body">
            <div key={step} className={`ac-anim-pane ${direction > 0 ? 'in-right' : 'in-left'}`}>
              {step === 0 && <VSStepAccount form={form} update={update} errors={{ ...errors, name: errors.name || nameLiveError }} t={t} />}
              {step === 1 && <VSStepReview form={form} t={t} />}
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

// ===== Voice record audio preview (simulated playback — waveform that fills as it plays) =====
const VoiceRecordPlayer = ({ rec }) => {
  const [state, setState] = useStateVS('idle'); // idle | playing | paused
  const [elapsed, setElapsed] = useStateVS(0);
  const dur = rec.durationSec || 8;
  const timerRef = useRefVS(null);
  useEffectVS(() => {
    if (state !== 'playing') { clearInterval(timerRef.current); return; }
    timerRef.current = setInterval(() => setElapsed((e) => +(e + 0.1).toFixed(2)), 100);
    return () => clearInterval(timerRef.current);
  }, [state]);
  useEffectVS(() => { if (state === 'playing' && elapsed >= dur) { setState('idle'); setElapsed(0); } }, [elapsed, dur, state]);
  const toggle = (e) => { e.stopPropagation(); setState((s) => { if (s === 'playing') return 'paused'; if (elapsed >= dur) setElapsed(0); return 'playing'; }); };
  const progress = dur ? Math.min(1, elapsed / dur) : 0;
  const shown = state === 'idle' && elapsed === 0 ? dur : elapsed;   // idle shows total length; playing counts up
  return (
    <span className={`vr-player ${state === 'playing' ? 'on' : ''}`} onClick={toggle} title={state === 'playing' ? 'Pause' : 'Play'}>
      <span className="vr-player-btn">
        {state === 'playing'
          ? <svg width="11" height="11" viewBox="0 0 24 24" fill="currentColor"><rect x="6" y="5" width="4" height="14" rx="1" /><rect x="14" y="5" width="4" height="14" rx="1" /></svg>
          : <svg width="11" height="11" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z" /></svg>}
      </span>
      <VrWave progress={progress} count={36} />
      <span className="vr-player-time">{fmtDur(Math.round(shown))}</span>
    </span>
  );
};

// ===== Source preview — standard audio player (play · time · progress · volume · menu) =====
const VrSourcePreview = ({ durationSec, t = {}, bare = false }) => {
  const [state, setState] = useStateVS('idle'); // idle | playing | paused
  const [elapsed, setElapsed] = useStateVS(0);
  const dur = durationSec || 8;
  const timerRef = useRefVS(null);
  useEffectVS(() => {
    if (state !== 'playing') { clearInterval(timerRef.current); return; }
    timerRef.current = setInterval(() => setElapsed((e) => +(e + 0.2).toFixed(2)), 200);
    return () => clearInterval(timerRef.current);
  }, [state]);
  useEffectVS(() => { if (state === 'playing' && elapsed >= dur) setState('idle'); }, [elapsed, dur, state]);
  const toggle = () => setState((s) => {
    if (s === 'playing') return 'paused';
    if (elapsed >= dur) setElapsed(0);
    return 'playing';
  });
  const pct = dur ? Math.min(100, (elapsed / dur) * 100) : 0;
  const player = (
    <div className="vr-audio">
      <button type="button" className="vr-audio-play" onClick={toggle} aria-label={state === 'playing' ? 'Pause' : 'Play'}>
        {state === 'playing'
          ? <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><rect x="6" y="5" width="4" height="14" rx="1" /><rect x="14" y="5" width="4" height="14" rx="1" /></svg>
          : <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z" /></svg>}
      </button>
      <span className="vr-audio-time">{fmtDur(Math.round(elapsed))} / {fmtDur(dur)}</span>
      <div className="vr-audio-track"><div className="vr-audio-fill" style={{ width: `${pct}%` }} /></div>
      <span className="vr-audio-vol" aria-hidden="true">
        <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5" /><path d="M15.54 8.46a5 5 0 0 1 0 7.07M19.07 4.93a10 10 0 0 1 0 14.14" /></svg>
      </span>
      <button type="button" className="vr-audio-more" aria-label="More options">
        <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="5" r="1.6" /><circle cx="12" cy="12" r="1.6" /><circle cx="12" cy="19" r="1.6" /></svg>
      </button>
    </div>
  );
  // `bare` → just the player bar (used INSIDE the upload / mic box).
  if (bare) return player;
  return (
    <div className="vr-preview-box">
      <span className="vr-audio-label">{t.vrPreview || 'Preview'}</span>
      {player}
    </div>
  );
};

// ===== Waveform building blocks — shared by Upload / Convert Text / Record =====
// Deterministic bar heights so every waveform looks "real" and stays stable across renders.
// A generous pool of deterministic bar heights; each player slices the count it needs.
const VR_WAVE = Array.from({ length: 200 }, (_, i) => {
  const a = Math.sin(i * 0.5) * 0.55 + Math.sin(i * 0.17 + 1) * 0.30 + Math.sin(i * 1.1 + 2) * 0.20;
  return 0.16 + Math.min(1, Math.abs(a)) * 0.84;
});
const fmtFileSize = (b) => {
  if (b == null) return '';
  if (b < 1024) return b + ' B';
  if (b < 1024 * 1024) return Math.round(b / 1024) + ' KB';
  return (b / 1048576).toFixed(1) + ' MB';
};
// A row of waveform bars. `progress` (0..1) paints the played portion teal; `recording`
// makes the bars pulse like a live mic level.
const VrWave = ({ progress = 0, recording = false, count = 56 }) => {
  const bars = VR_WAVE.slice(0, count);
  return (
    <div className={`vr-wave ${recording ? 'is-rec' : ''}`} aria-hidden="true">
      {bars.map((h, i) => {
        const filled = progress > 0 && (i + 0.5) / bars.length <= progress;
        return <span key={i} className={`vr-wave-bar ${filled ? 'on' : ''}`}
          style={{ height: `${Math.round(h * 100)}%`, animationDelay: recording ? `${(i % 10) * 0.09}s` : undefined }} />;
      })}
    </div>
  );
};
// Waveform audio player — play/pause + waveform (with played-progress) + time, plus an
// optional delete. Used for the uploaded file, the TTS result and the mic preview.
const VrWavePlayer = ({ durationSec, t = {}, onDelete, bars }) => {
  const [state, setState] = useStateVS('idle'); // idle | playing | paused
  const [elapsed, setElapsed] = useStateVS(0);
  const dur = durationSec || 8;
  const timerRef = useRefVS(null);
  useEffectVS(() => {
    if (state !== 'playing') { clearInterval(timerRef.current); return; }
    timerRef.current = setInterval(() => setElapsed((e) => +(e + 0.1).toFixed(2)), 100);
    return () => clearInterval(timerRef.current);
  }, [state]);
  useEffectVS(() => { if (state === 'playing' && elapsed >= dur) { setState('idle'); setElapsed(0); } }, [elapsed, dur, state]);
  const toggle = () => setState((s) => { if (s === 'playing') return 'paused'; if (elapsed >= dur) setElapsed(0); return 'playing'; });
  const progress = dur ? Math.min(1, elapsed / dur) : 0;
  const shown = state === 'idle' && elapsed === 0 ? dur : elapsed;
  return (
    <div className="vr-wplayer">
      <button type="button" className="vr-wplayer-play" onClick={toggle} aria-label={state === 'playing' ? 'Pause' : 'Play'}>
        {state === 'playing'
          ? <svg width="17" height="17" viewBox="0 0 24 24" fill="currentColor"><rect x="6" y="5" width="4" height="14" rx="1" /><rect x="14" y="5" width="4" height="14" rx="1" /></svg>
          : <svg width="17" height="17" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z" /></svg>}
      </button>
      <VrWave progress={progress} count={bars} />
      <span className="vr-wplayer-time">{fmtDur(Math.round(shown))}</span>
      {onDelete && (
        <button type="button" className="vr-wplayer-del" onClick={onDelete} aria-label="Delete">
          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><line x1="10" y1="11" x2="10" y2="17"/><line x1="14" y1="11" x2="14" y2="17"/></svg>
        </button>
      )}
    </div>
  );
};

// ===== Reusable share editor (chips + add-users dropdown) =====
const VRShareEditor = ({ value = [], onChange, t = {} }) => {
  const pool = window.seedShareUsers || [];
  const [open, setOpen] = useStateVS(false);
  const [q, setQ] = useStateVS('');
  const wrapRef = useRefVS(null);
  useEffectVS(() => {
    if (!open) return;
    const onDown = (e) => { if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', onDown);
    return () => document.removeEventListener('mousedown', onDown);
  }, [open]);
  const available = pool.filter((n) => !value.includes(n) && n.toLowerCase().includes(q.trim().toLowerCase()));
  return (
    <div className="vr-share-edit">
      <div className="vr-share-chips">
        {value.length === 0 && <span className="vr-share-empty">{t.vrShareNone || 'Not shared with anyone yet.'}</span>}
        {value.map((n) => (
          <span key={n} className="vr-share-chip">
            <span className="nm">{n}</span>
            <button type="button" className="x" onClick={() => onChange(value.filter((x) => x !== n))}><IcClose size={10} stroke={2.2} /></button>
          </span>
        ))}
      </div>
      <div className="vr-share-combo" ref={wrapRef}>
        <button type="button" className="vr-share-add" onClick={() => setOpen((o) => !o)}><IcUserPlus size={14} stroke={1.8} /> {t.vrShareAdd || 'Add users'}</button>
        {open && (
          <div className="vr-share-pop">
            <div className="vr-share-search"><IcSearch size={13} stroke={1.8} /><input placeholder={t.search || 'Search'} value={q} onChange={(e) => setQ(e.target.value)} autoFocus /></div>
            <div className="vr-share-list">
              {available.length === 0
                ? <div className="vr-share-noopt">{t.vrShareNoUsers || 'No more users'}</div>
                : available.map((n) => <button type="button" key={n} className="vr-share-opt" onClick={() => { onChange([...value, n]); setQ(''); }}>{n}</button>)}
            </div>
          </div>
        )}
      </div>
    </div>
  );
};

// ===== Share record pop-up =====
const VoiceRecordShareModal = ({ rec, t, onCancel, onSave }) => {
  const [list, setList] = useStateVS([...(rec.sharedWith || [])]);
  return ReactDOM.createPortal(
    <div className="vr-modal-overlay" onMouseDown={onCancel}>
      <div className="vr-modal" onMouseDown={(e) => e.stopPropagation()}>
        <div className="vr-modal-head">
          <div className="vr-modal-title">{t.vrShareTitle || 'Share record'}</div>
          <button className="vr-modal-x" onClick={onCancel}><IcClose size={16} stroke={2} /></button>
        </div>
        <div className="vr-modal-sub">{(t.vrShareSub || 'Manage who can access "{name}".').replace('{name}', rec.name)}</div>
        <div className="vr-modal-body"><VRShareEditor value={list} onChange={setList} t={t} /></div>
        <div className="vr-modal-foot">
          <button className="btn btn-secondary" onClick={onCancel}>Cancel</button>
          <button className="btn btn-primary" onClick={() => onSave(list)}>{t.vrShareSave || 'Save'}</button>
        </div>
      </div>
    </div>,
    document.body
  );
};

// ===== Delete confirm =====
const VoiceRecordDeleteConfirm = ({ rec, t, onCancel, onConfirm }) => ReactDOM.createPortal(
  <div className="vr-modal-overlay" onMouseDown={onCancel}>
    <div className="vr-confirm" onMouseDown={(e) => e.stopPropagation()}>
      <div className="vr-confirm-icon"><IcTrash size={20} stroke={1.8} /></div>
      <div className="vr-confirm-title">{t.vrDeleteTitle || 'Delete voice record?'}</div>
      <div className="vr-confirm-body">{(t.vrDeleteBody || 'This will remove "{name}". This action cannot be undone.').replace('{name}', rec.name)}</div>
      <div className="vr-confirm-actions">
        <button className="btn btn-secondary" onClick={onCancel}>Cancel</button>
        <button className="btn vr-confirm-del" onClick={onConfirm}>{t.vrDelete || 'Delete'}</button>
      </div>
    </div>
  </div>,
  document.body
);

// ===== Records row menu =====
const VoiceRecordRowMenu = ({ open, rec, onMore, onShare, onDelete, t }) => {
  const ref = useRefVS(null);
  const [up, setUp] = useStateVS(false);
  React.useLayoutEffect(() => {
    if (!open || !ref.current) { setUp(false); return; }
    const wrap = ref.current.closest('.tpl-table-wrap') || ref.current.closest('.table-scroll');
    if (!wrap) return;
    const mr = ref.current.getBoundingClientRect();
    const wr = wrap.getBoundingClientRect();
    setUp(mr.bottom > wr.bottom - 4 && (mr.top - wr.top) > mr.height);
  }, [open]);
  if (!open) return null;
  const locked = !!rec.usedInIvr;
  return (
    <div className={`row-menu ${up ? 'up' : ''}`} ref={ref} onClick={(e) => e.stopPropagation()}>
      <button className="row-menu-item" onClick={onMore}><IcInfo size={14} stroke={1.8} /> <span>{t.vsMoreDetails || 'More Details'}</span></button>
      <button className="row-menu-item" onClick={onShare}><IcUserPlus size={14} stroke={1.8} /> <span>{t.vrShare || 'Share'}</span></button>
      <button className={`row-menu-item cg-danger-item ${locked ? 'is-locked' : ''}`} onClick={locked ? undefined : onDelete} disabled={locked} title={locked ? (t.vrLockedHint || 'Used in an approved IVR — delete the IVR first.') : ''}>
        {locked ? <IcLock size={13} stroke={1.8} /> : <IcTrash size={14} stroke={1.8} />} <span>{t.vrDelete || 'Delete'}</span>
      </button>
    </div>
  );
};

// ===== Voice records grid (Voice records tab) =====
const VoiceRecordsList = ({ rows, t, isFalcon, onView, onShare, onDelete, openMenuId, setOpenMenuId, page, pageSize, setPage, setPageSize }) => {
  const total = rows.length;
  const pageRows = rows.slice((page - 1) * pageSize, page * pageSize);
  const SharedWithChip = window.TplSharedWithChip;
  return (
    <div className="table-panel tpl-table-panel vs-table-panel">
      <div className="table-head-bar tpl-head-bar"><div className="table-head-title">{t.vsTabRecords || 'Voice records'}</div></div>
      <div className="table-scroll tpl-table-wrap">
        <table className="tpl-table vs-records-table">
          <thead>
            <tr>
              <th>{t.vrColName || 'Record name'}</th>
              <th>{t.vrColPreview || 'Preview'}</th>
              <th>{t.vsColDate || 'Creation Date'}</th>
              {isFalcon && <th>{t.vrColCreatedBy || 'Created by'}</th>}
              <th>{t.vrColSource || 'Source'}</th>
              <th>{t.vrColShared || 'Shared with'}</th>
              <th className="col-actions">{t.vsColActions || 'Actions'}</th>
            </tr>
          </thead>
          <tbody>
            {pageRows.map((r) => {
              const isOpen = openMenuId === r.id;
              return (
                <tr key={r.id} onClick={() => onView(r)} style={{ cursor: 'pointer' }}>
                  <td className="vs-cell-name">{r.name}{r.usedInIvr && <span className="vr-lock-tag" title={t.vrLockedHint || 'Used in an approved IVR'}><IcLock size={11} stroke={1.9} /></span>}</td>
                  <td onClick={(e) => e.stopPropagation()}><VoiceRecordPlayer rec={r} /></td>
                  <td><VsDateCell stamp={r.createdAt} /></td>
                  {isFalcon && <td>{r.createdBy?.name}</td>}
                  <td><span className={`vr-src-tag ${r.source}`}>{vsSourceLabel(r.source, t)}</span></td>
                  <td onClick={(e) => e.stopPropagation()}>{(r.sharedWith && r.sharedWith.length && SharedWithChip) ? <SharedWithChip list={r.sharedWith} extra={r.sharedWith.length - 1} t={t} /> : <span className="muted">—</span>}</td>
                  <td className="col-actions" style={{ position: 'relative' }} onClick={(e) => e.stopPropagation()}>
                    <button className={`row-action-btn ${isOpen ? 'open' : ''}`} onClick={() => setOpenMenuId(isOpen ? null : r.id)} aria-label="Actions"><IcMore size={16} /></button>
                    <VoiceRecordRowMenu open={isOpen} rec={r} t={t}
                      onMore={() => { setOpenMenuId(null); onView(r); }}
                      onShare={() => { setOpenMenuId(null); onShare(r); }}
                      onDelete={() => { setOpenMenuId(null); onDelete(r); }} />
                  </td>
                </tr>
              );
            })}
            {pageRows.length === 0 && <tr><td colSpan={isFalcon ? 8 : 7} className="vs-empty-row">{t.vrEmpty || 'No voice records yet.'}</td></tr>}
          </tbody>
        </table>
      </div>
      {window.TablePagination && <window.TablePagination total={total} page={page} pageSize={pageSize} onPageChange={setPage} onPageSizeChange={(n) => { setPageSize(n); setPage(1); }} t={t} />}
    </div>
  );
};

// ===== Shared records grid (Shared records tab — read only) =====
const SharedRecordsList = ({ rows, t, page, pageSize, setPage, setPageSize }) => {
  const total = rows.length;
  const pageRows = rows.slice((page - 1) * pageSize, page * pageSize);
  const SharedWithChip = window.TplSharedWithChip;
  return (
    <div className="table-panel tpl-table-panel vs-table-panel">
      <div className="table-head-bar tpl-head-bar"><div className="table-head-title">{t.vsTabShared || 'Shared records'}</div></div>
      <div className="table-scroll tpl-table-wrap">
        <table className="tpl-table vs-records-table">
          <thead>
            <tr>
              <th>{t.vrColName || 'Record name'}</th>
              <th>{t.vrColPreview || 'Preview'}</th>
              <th>{t.vsColDate || 'Creation Date'}</th>
              <th>{t.vrColCreatedBy || 'Created by'}</th>
              <th>{t.vrColShared || 'Shared with'}</th>
            </tr>
          </thead>
          <tbody>
            {pageRows.map((r) => (
              <tr key={r.id}>
                <td className="vs-cell-name">{r.name}</td>
                <td><VoiceRecordPlayer rec={r} /></td>
                <td><VsDateCell stamp={r.createdAt} /></td>
                <td>{r.createdBy?.name}</td>
                <td>{(r.sharedWith && r.sharedWith.length && SharedWithChip) ? <SharedWithChip list={r.sharedWith} extra={r.sharedWith.length - 1} t={t} /> : <span className="muted">—</span>}</td>
              </tr>
            ))}
            {pageRows.length === 0 && <tr><td colSpan={5} className="vs-empty-row">{t.vrSharedEmpty || 'No records shared with you yet.'}</td></tr>}
          </tbody>
        </table>
      </div>
      {window.TablePagination && <window.TablePagination total={total} page={page} pageSize={pageSize} onPageChange={setPage} onPageSizeChange={(n) => { setPageSize(n); setPage(1); }} t={t} />}
    </div>
  );
};

// ===== Record details (read-only; sharing is edited via the Share pop-up) =====
// More Details (read-only) and Share (the same screen with the "Shared with"
// list editable — like the Template share screen). shareEdit toggles the field.
const VoiceRecordDetails = ({ rec, t, shareEdit = false, editShared, onSharedChange }) => {
  if (!rec) return null;
  const ShareCombo = window.CgShareCombo;
  return (
    <div className="tpl-details-card vs-details-card">
      <div className="hd">{shareEdit ? (t.vrShareTitle || 'Share record') : (t.vrDetailsTitle || 'Voice record details')}</div>
      <div className="tpl-details-grid">
        <div className="tpl-d-field"><span className="lab">{t.vrColName || 'Record name'}</span><span className="val">{rec.name}{rec.usedInIvr && <span className="vr-lock-tag" title={t.vrLockedHint || 'Used in an approved IVR'}><IcLock size={11} stroke={1.9} /></span>}</span></div>
        <div className="tpl-d-field"><span className="lab">{t.vrColSource || 'Source'}</span><span className="val">{vsSourceLabel(rec.source, t)}</span></div>
        <div className="tpl-d-field"><span className="lab">{t.vrColDuration || 'Duration'}</span><span className="val">{fmtDur(rec.durationSec)}</span></div>
        <div className="tpl-d-field"><span className="lab">{t.vsColDate || 'Creation Date'}</span><span className="val">{vsFmtStamp(rec.createdAt)}</span></div>
        <div className="tpl-d-field"><span className="lab">{t.vrColCreatedBy || 'Created by'}</span><span className="val">{rec.createdBy?.name}{rec.createdBy?.email && <span className="cg-info-sub">{rec.createdBy.email}</span>}</span></div>
        <div className="tpl-d-field"><span className="lab">{t.vrColPreview || 'Preview'}</span><div className="pill-row"><VoiceRecordPlayer rec={rec} /></div></div>
        <div className={`tpl-d-field full ${shareEdit ? 'vr-d-share-edit' : ''}`}>
          <span className="lab">{t.vrColShared || 'Shared with'}{shareEdit && <span className="opt"> {t.multiSelectHint || '(Multiple Select)'}</span>}</span>
          {shareEdit && ShareCombo
            ? <ShareCombo value={editShared || []} onChange={onSharedChange} t={t} />
            : <div className="pill-row">{(rec.sharedWith && rec.sharedWith.length) ? rec.sharedWith.map((n, i) => <span key={i} className="tpl-shared-pill no-extra">{n}</span>) : <span className="val">—</span>}</div>}
        </div>
      </div>
    </div>
  );
};

// ===== Create Voice Record wizard — Step 1 (source) sub-panels =====
const VRUploadPanel = ({ form, update, t }) => {
  const ref = useRefVS(null);
  const accept = (file) => { if (!file) return; update('file', { name: file.name, size: file.size }); update('durationSec', 12); };
  const musicIc = <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#0d3f44" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M9 18V5l12-2v13" /><circle cx="6" cy="18" r="3" /><circle cx="18" cy="16" r="3" /></svg>;
  return (
    <div className="vr-src-box">
      {!form.file ? (
        <div className="vr-upload-drop" onDragOver={(e) => e.preventDefault()} onDrop={(e) => { e.preventDefault(); accept(e.dataTransfer.files[0]); }}>
          <div className="vr-upload-left">
            <div className="vr-upload-ic">{musicIc}</div>
            <div className="vr-upload-meta">
              <strong>{t.vrUploadAudioTitle || 'Audio file'}</strong>
              <span>{t.vrUploadSub || 'MP3, WAV up to 10MB'}</span>
            </div>
          </div>
          <div className="vr-upload-right">
            <span className="vr-upload-hint">
              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><polyline points="16 16 12 12 8 16" /><line x1="12" y1="12" x2="12" y2="21" /><path d="M20.39 18.39A5 5 0 0 0 18 9h-1.26A8 8 0 1 0 3 16.3" /></svg>
              {t.vrUploadDrag || 'Drag a file here or'}
            </span>
            <button type="button" className="btn btn-primary" onClick={() => ref.current?.click()}>{t.vrUploadBtn || 'Upload File'}</button>
          </div>
          <input ref={ref} type="file" accept="audio/*,.mp3,.wav" style={{ display: 'none' }} onChange={(e) => accept(e.target.files[0])} />
        </div>
      ) : (
        <div className="vr-upload-loaded">
          <div className="vr-upload-fileinfo">
            <div className="vr-upload-ic sm">{musicIc}</div>
            <div className="vr-upload-meta">
              <strong title={form.file.name}>{form.file.name}</strong>
              <span>{form.file.size != null ? fmtFileSize(form.file.size) + ' · ' : ''}{fmtDur(form.durationSec || 12)}</span>
            </div>
            <span className="vr-upload-ready">{t.vrUploaded || 'Uploaded'}</span>
          </div>
          {/* The audio "process" plays out as a waveform right inside the box. */}
          <VrWavePlayer durationSec={form.durationSec || 12} t={t} bars={160} onDelete={() => update('file', null)} />
        </div>
      )}
    </div>
  );
};

// Voice picker — dropdown (DDL) listing the predefined TTS voices.
const VrVoiceDropdown = ({ voices, value, onChange, t }) => {
  const [open, setOpen] = useStateVS(false);
  const [dropUp, setDropUp] = useStateVS(false);
  const wrapRef = useRefVS(null);
  useEffectVS(() => {
    if (!open) return;
    const onDown = (e) => { if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', onDown);
    return () => document.removeEventListener('mousedown', onDown);
  }, [open]);
  const toggleOpen = () => {
    if (!open) {
      const r = wrapRef.current && wrapRef.current.getBoundingClientRect();
      if (r) { const below = window.innerHeight - r.bottom; setDropUp(below < 300 && r.top > below); }
    }
    setOpen((o) => !o);
  };
  const cur = voices.find((v) => v.id === value);
  return (
    <div className={`vs-ddl vr-voice-ddl ${open ? 'open' : ''} ${dropUp ? 'drop-up' : ''}`} ref={wrapRef}>
      <button type="button" className="vs-ddl-control" onClick={toggleOpen}>
        {cur ? (
          <span className="vr-voice-cur"><span className="vr-voice-av">{cur.name[0]}</span><span className="vr-voice-meta"><strong>{cur.name}</strong><span>{cur.desc}</span></span></span>
        ) : (
          <span className="vs-ddl-ph">{t.vrTtsPickVoice || 'Choose a voice…'}</span>
        )}
        <svg className="vs-ddl-caret" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><polyline points="6 9 12 15 18 9"/></svg>
      </button>
      {open && (
        <div className="vs-ddl-pop">
          {voices.map((v) => (
            <button key={v.id} type="button" className={`vs-ddl-item vr-voice-opt ${v.id === value ? 'on' : ''}`} onClick={() => { onChange(v.id); setOpen(false); }}>
              <span className="vr-voice-av">{v.name[0]}</span>
              <span className="vr-voice-meta"><strong>{v.name}</strong><span>{v.desc}</span></span>
            </button>
          ))}
        </div>
      )}
    </div>
  );
};

const VRTtsPanel = ({ form, update, t }) => {
  const voices = window.seedTtsVoices || [];
  const cur = voices.find((v) => v.id === form.voice);
  const ready = !!(form.ttsText || '').trim() && !!form.voice;
  const dur = Math.max(3, Math.round((form.ttsText || '').trim().length * 0.07));
  return (
    <div className="vr-src-box vr-tts-box">
      <div className="vr-tts-field">
        <label className="ac-label">{t.vrTtsText || 'Text to convert'} <span className="req">*</span></label>
        <textarea className="ac-input vr-tts-text" rows={3} placeholder={t.vrTtsPh || 'Type the message to convert to speech…'} value={form.ttsText} onChange={(e) => { update('ttsText', e.target.value); update('ttsGenerated', false); }} />
      </div>
      {/* Voice dropdown · Convert button · generated preview all share ONE row so the panel
          stays short (no page scroll). The dropdown is the narrowest, the preview fills the rest. */}
      <div className="vr-tts-controls">
        <div className="vr-tts-voice">
          <label className="ac-label">{t.vrTtsVoice || 'Voice'} <span className="req">*</span></label>
          <VrVoiceDropdown voices={voices} value={form.voice} onChange={(id) => { update('voice', id); update('ttsGenerated', false); }} t={t} />
        </div>
        <button type="button" className="btn btn-primary vr-tts-gen" disabled={!ready} onClick={() => update('ttsGenerated', true)}>
          <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M11 5 6 9H2v6h4l5 4V5z" /><path d="M15.54 8.46a5 5 0 0 1 0 7.07M19.07 4.93a10 10 0 0 1 0 14.14" /></svg>
          {t.vrTtsConvert || 'Convert to Speech'}
        </button>
        {form.ttsGenerated && ready
          ? <div className="vr-tts-result" title={cur ? `${cur.name} · ${cur.desc}` : ''}><VrWavePlayer durationSec={dur} t={t} bars={160} /></div>
          : <div className="vr-preview-hint"><IcInfo size={13} stroke={1.8} /> {ready ? (t.vrTtsConvertHint || 'Click “Convert to Speech” to generate a preview.') : (t.vrTtsPreviewHint || 'Enter text and choose a voice to preview the generated speech.')}</div>}
      </div>
    </div>
  );
};

// Mic capture modelled on WhatsApp's voice note: tap to record → a live bar with a red dot,
// running timer, animated waveform, cancel (trash) and stop; then a preview bar you can play
// back, with a re-record (trash) to start over.
const VRRecordPanel = ({ form, update, t }) => {
  const [rec, setRec] = useStateVS((form.recordedSec || 0) > 0 ? 'done' : 'idle');
  const [sec, setSec] = useStateVS(form.recordedSec || 0);
  const timerRef = useRefVS(null);
  useEffectVS(() => () => clearInterval(timerRef.current), []);
  const start = () => { setRec('recording'); setSec(0); update('recordedSec', 0); clearInterval(timerRef.current); timerRef.current = setInterval(() => setSec((s) => s + 1), 1000); };
  const stop = () => { clearInterval(timerRef.current); setRec('done'); update('recordedSec', sec); update('durationSec', sec); };
  const cancel = () => { clearInterval(timerRef.current); setRec('idle'); setSec(0); update('recordedSec', 0); update('durationSec', 0); };
  const trash = <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>;
  return (
    <div className="vr-src-box vr-rec-box">
      {rec === 'idle' && (
        <div className="vr-rec-idle">
          <button type="button" className="vr-rec-mic" onClick={start} aria-label={t.vrTapRecord || 'Start recording'}>
            <svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><rect x="9" y="2" width="6" height="12" rx="3" /><path d="M5 10a7 7 0 0 0 14 0" /><line x1="12" y1="19" x2="12" y2="22" /></svg>
          </button>
          <span className="vr-rec-hint">{t.vrTapRecord || 'Tap the microphone to start recording'}</span>
        </div>
      )}

      {rec === 'recording' && (
        <div className="vr-wa-bar recording">
          <button type="button" className="vr-wa-send stop" onClick={stop} aria-label={t.vrStopRec || 'Stop recording'}>
            <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><rect x="6" y="6" width="12" height="12" rx="2.5" /></svg>
          </button>
          <span className="vr-wa-dot" />
          <span className="vr-wa-time">{fmtDur(sec)}</span>
          <VrWave recording count={160} />
          <button type="button" className="vr-wa-side cancel" onClick={cancel} aria-label={t.cancel || 'Cancel'}>{trash}</button>
        </div>
      )}

      {rec === 'done' && (
        <div className="vr-wa-bar done">
          <VrWavePlayer durationSec={sec} t={t} bars={160} />
          <button type="button" className="vr-wa-side" onClick={cancel} aria-label={t.vrReRecord || 'Re-record'}>{trash}</button>
        </div>
      )}
    </div>
  );
};

// ===== Source selector — three large choice cards (Upload / Convert Text / Record) =====
const VRSourceSelector = ({ form, update, t }) => {
  const srcs = [
    { id: 'upload', tint: 'amber', label: t.vrSrcUpload || 'Upload', desc: t.vrSrcUploadDesc || 'Browse and upload an audio file from your device.',
      icon: <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#d68a08" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /><polyline points="17 8 12 3 7 8" /><line x1="12" y1="3" x2="12" y2="15" /></svg> },
    { id: 'tts', tint: 'blue', label: t.vrSrcTts || 'Convert Text', desc: t.vrSrcTtsDesc || 'Type text and convert it to voice using a selected voice.',
      icon: <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#3b82f6" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><polyline points="4 7 4 4 13 4 13 7" /><line x1="8.5" y1="4" x2="8.5" y2="20" /><line x1="6" y1="20" x2="11" y2="20" /><line x1="15" y1="9" x2="20" y2="9" /><line x1="15" y1="13" x2="20" y2="13" /><line x1="15" y1="17" x2="20" y2="17" /></svg> },
    { id: 'record', tint: 'teal', label: t.vrSrcRecord || 'Record', desc: t.vrSrcRecordDesc || "Record audio using your device's microphone.",
      icon: <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#0d7a72" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><rect x="9" y="2" width="6" height="12" rx="3" /><path d="M5 10a7 7 0 0 0 14 0" /><line x1="12" y1="19" x2="12" y2="22" /></svg> },
  ];
  return (
    <div className="vr-src-section">
      <div className="ac-label vr-src-label">{t.vrSourceLabel || 'Source'} <span className="req">*</span></div>
      <div className="vr-src-cards" role="radiogroup">
        {srcs.map((s) => (
          <button key={s.id} type="button" role="radio" aria-checked={form.source === s.id} className={`vr-src-card ${form.source === s.id ? 'on' : ''}`} onClick={() => update('source', s.id)}>
            <span className={`vr-src-card-ic ${s.tint}`}>{s.icon}</span>
            <span className="vr-src-card-title">{s.label}</span>
            <span className="vr-src-card-desc">{s.desc}</span>
          </button>
        ))}
      </div>
    </div>
  );
};

// ===== Record Wizard Step 1 — Record details (name + source selector + the chosen source's input/preview) =====
const VRStepDetails = ({ form, update, errors, t }) => (
  <div className="ac-step-pane">
    <div className="ac-field-grid ac-grid-4">
      <div className="ac-field wide">
        <label className="ac-label">{t.vrName || 'Record Name'} <span className="req">*</span></label>
        <input className={`ac-input ${errors.name ? 'error' : ''}`} placeholder={t.vrNamePh || 'e.g. Welcome Greeting'} value={form.name} maxLength={40} onChange={(e) => update('name', e.target.value)} />
        {errors.name && <span className="ac-error-msg">{errors.name}</span>}
      </div>
    </div>
    <VRSourceSelector form={form} update={update} t={t} />
    {form.source === 'upload' && <VRUploadPanel form={form} update={update} t={t} />}
    {form.source === 'tts' && <VRTtsPanel form={form} update={update} t={t} />}
    {form.source === 'record' && <VRRecordPanel form={form} update={update} t={t} />}
  </div>
);

// ===== Record Wizard Step 3 — Share record (mirrors Contact Groups' "Share group" step) =====
const vrUserEmail = (name) => {
  const p = String(name).trim().toLowerCase().split(/\s+/);
  return p.length > 1 ? `${p[0][0]}.${p[p.length - 1]}@aramco.sa` : `${p[0]}@aramco.sa`;
};
const VRStepShare = ({ form, update, t }) => {
  const pool = (window.seedShareUsers || []).map((n) => ({ name: n, email: vrUserEmail(n) }));
  const [open, setOpen] = useStateVS(false);
  const [q, setQ] = useStateVS('');
  const wrapRef = useRefVS(null);
  useEffectVS(() => {
    const onDoc = (e) => { if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', onDoc);
    return () => document.removeEventListener('mousedown', onDoc);
  }, []);
  const shared = form.sharedWith || [];
  const ql = q.trim().toLowerCase();
  const filtered = ql ? pool.filter((u) => u.name.toLowerCase().includes(ql) || u.email.toLowerCase().includes(ql)) : pool;
  const toggle = (name) => { const set = new Set(shared); set.has(name) ? set.delete(name) : set.add(name); update('sharedWith', Array.from(set)); };
  const allOn = pool.length > 0 && pool.every((u) => shared.includes(u.name));
  const toggleAll = () => update('sharedWith', allOn ? [] : pool.map((u) => u.name));
  const selected = pool.filter((u) => shared.includes(u.name));
  const visibleChips = selected.slice(0, 3);
  const overflow = Math.max(0, selected.length - 3);
  return (
    <div className="ac-step-pane">
      <div className="acg-share-grid">
        {/* LEFT — searcher with chips */}
        <div className="acg-share-left">
          <div className="acg-share-label">{t.vrShareWith || 'Shared With'} <span className="muted">{t.vrShareMulti || 'Normal User (Multiple Select)'}</span></div>
          <div className={`acg-combo ${open ? 'open' : ''}`} ref={wrapRef}>
            <button type="button" className="acg-combo-control" onClick={() => setOpen((o) => !o)}>
              <span className="acg-combo-chips">
                {selected.length === 0 && <span className="acg-combo-placeholder">{t.vrShareSelectPh || 'Select users…'}</span>}
                {visibleChips.map((u) => (
                  <span key={u.name} className="acg-combo-chip" onClick={(e) => { e.stopPropagation(); toggle(u.name); }}>
                    {u.name}
                    <svg width="9" height="9" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round"><path d="M18 6 6 18M6 6l12 12"/></svg>
                  </span>
                ))}
                {overflow > 0 && <span className="acg-combo-chip more">+{overflow} more</span>}
              </span>
              <svg className="acg-combo-caret" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><polyline points="6 9 12 15 18 9"/></svg>
            </button>
            {open && (
              <div className="acg-combo-pop">
                <div className="acg-combo-search">
                  <IcSearch size={13} stroke={1.8} />
                  <input placeholder={t.search || 'Search'} value={q} onChange={(e) => setQ(e.target.value)} autoFocus />
                </div>
                <label className={`acg-combo-row all ${allOn ? 'on' : ''}`}>
                  <input type="checkbox" checked={allOn} onChange={toggleAll} />
                  <span className="acg-cb-mark">{allOn && <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="3.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12" /></svg>}</span>
                  <span className="acg-combo-row-name">{t.vrShareAllUsers || 'All Users'}</span>
                </label>
                <div className="acg-combo-list">
                  {filtered.length === 0 ? (
                    <div className="acg-combo-empty">{t.vrShareNoMatch || 'No users match'} "{q}"</div>
                  ) : filtered.map((u) => {
                    const on = shared.includes(u.name);
                    return (
                      <label key={u.name} className={`acg-combo-row ${on ? 'on' : ''}`}>
                        <input type="checkbox" checked={on} onChange={() => toggle(u.name)} />
                        <span className="acg-cb-mark">{on && <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="3.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12" /></svg>}</span>
                        <div className="acg-combo-row-meta">
                          <span className="acg-combo-row-name">{u.name}</span>
                          <span className="acg-combo-row-sub">{u.email}</span>
                        </div>
                      </label>
                    );
                  })}
                </div>
              </div>
            )}
          </div>
        </div>

        {/* RIGHT — Selected users panel */}
        <div className="acg-share-right">
          <div className="acg-share-right-head">
            <span className="acg-share-label">{t.vrShareSelected || 'Selected Users'}</span>
            <button type="button" className="acg-unselect" onClick={() => update('sharedWith', selected.length > 0 ? [] : pool.map((u) => u.name))}>
              <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round"><path d="M18 6 6 18M6 6l12 12"/></svg>
              {selected.length > 0 ? (t.vrUnselectAll || 'Unselect All') : (t.vrSelectAll || 'Select All')}
            </button>
          </div>
          {selected.length === 0 ? (
            <div className="acg-share-right-empty">{t.vrShareNoneYet || 'No users selected yet.'}</div>
          ) : (
            <div className="acg-share-right-list">
              {selected.map((u) => (
                <div key={u.name} className="acg-share-right-item">
                  <div className="acg-share-right-meta">
                    <strong>{u.name}</strong>
                    <span>{u.email}</span>
                  </div>
                  <button type="button" className="acg-share-right-x" onClick={() => toggle(u.name)}>
                    <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M18 6 6 18M6 6l12 12"/></svg>
                  </button>
                </div>
              ))}
            </div>
          )}
        </div>
      </div>
    </div>
  );
};

const CreateVoiceRecordFlow = ({ t, scopeNode, onClose, onSave }) => {
  const [step, setStep] = useStateVS(0);
  const [direction, setDirection] = useStateVS(1);
  const [errors, setErrors] = useStateVS({});
  const [form, setForm] = useStateVS({ name: '', source: '', file: null, ttsText: '', voice: '', ttsGenerated: false, recordedSec: 0, durationSec: 0, sharedWith: [] });
  const update = (k, v) => { setForm((f) => ({ ...f, [k]: v })); if (errors[k]) setErrors((e) => ({ ...e, [k]: undefined })); };
  const isLast = step === VR_STEPS.length - 1;
  const sourceReady = () => {
    if (form.source === 'upload') return !!form.file;
    if (form.source === 'tts') return !!form.ttsText.trim() && !!form.voice && !!form.ttsGenerated;
    if (form.source === 'record') return (form.recordedSec || 0) > 0;
    return false;
  };
  const stepIsValid = (s) => {
    // Step 0 = Record details: name + a chosen source + that source's input is ready.
    if (s === 0) return !!form.name.trim() && form.name.trim().length <= 40 && !!form.source && sourceReady();
    return true;                                                                              // share (optional)
  };
  const validateStep = (s) => {
    const errs = {};
    if (s === 0) { if (!form.name.trim()) errs.name = '*Required'; else if (form.name.trim().length > 40) errs.name = 'Max 40 characters'; }
    setErrors(errs);
    return Object.keys(errs).length === 0 && stepIsValid(s);
  };
  const nextDisabled = !stepIsValid(step);
  const computeDuration = () => {
    if (form.source === 'tts') return Math.max(3, Math.round(form.ttsText.trim().length * 0.07));
    if (form.source === 'record') return form.recordedSec || 5;
    return form.durationSec || 12;
  };
  const handleNext = () => {
    if (!validateStep(step)) return;
    if (isLast) { onSave({ name: form.name.trim(), source: form.source, durationSec: computeDuration(), sharedWith: form.sharedWith }); return; }
    setDirection(1); setStep((s) => s + 1);
  };
  const handlePrev = () => { if (step > 0) { setDirection(-1); setStep((s) => s - 1); } };
  return (
    <div className="ac-page vs-page">
      <div className="ac-topbar">
        <div className="ac-topbar-brand cw-topbar-brand">
          {window.CgScopeLogo ? <window.CgScopeLogo node={scopeNode} size={32} /> : <BrandLogo brand={scopeNode?.brand} size={32} />}
          <span className="ac-brand-name cw-brand-name">{scopeNode?.name || 'Falcon'}</span>
        </div>
        <div className="ac-topbar-actions">
          <button className="btn btn-secondary" type="button" onClick={onClose}>Cancel</button>
          <button className="btn btn-secondary" type="button" onClick={handlePrev} disabled={step === 0}>Previous</button>
          <button className="btn btn-primary" type="button" onClick={handleNext} disabled={nextDisabled}>{isLast ? 'Create' : 'Next'}</button>
        </div>
      </div>
      <div className="ac-body">
        <div className="ac-card">
          <div className="ac-card-head">
            <div className="ac-card-title">{t.vrNewTitle || 'New Voice Record'}</div>
            <div className="ac-step-counter">step <strong>{step + 1}/{VR_STEPS.length}</strong></div>
          </div>
          <VSStepBar steps={VR_STEPS} current={step} onJump={(i) => { setDirection(i > step ? 1 : -1); setStep(i); }} />
          <div className="ac-card-body">
            <div key={step} className={`ac-anim-pane ${direction > 0 ? 'in-right' : 'in-left'}`}>
              {step === 0 && <VRStepDetails form={form} update={update} errors={errors} t={t} />}
              {step === 1 && <VRStepShare form={form} update={update} t={t} />}
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

// ===== Falcon / Client perspective picker =====
const VoiceViewPicker = ({ t, onPick }) => (
  <div className="tpl-picker-page">
    <div className="tpl-picker-card">
      <div className="tpl-picker-head">
        <div>
          <h2>{t.vsPickerTitle || 'Whose channels & services are you reviewing?'}</h2>
          <p>{t.vsPickerSub || 'Falcon admins manage across clients; client admins manage a single organization.'}</p>
        </div>
      </div>
      <div className="tpl-picker-grid">
        <button className="tpl-picker-tile falcon" onClick={() => onPick('falcon')}>
          <span className="ic-wrap"><T2Mark size={28} color="white" /></span>
          <span className="ttl">{t.vsViewFalcon || 'View as Falcon'}</span>
          <span className="dsc">{t.vsViewFalconDesc || 'Browse clients and manage their channels & services.'}</span>
          <span className="cta">{t.vsViewFalcon || 'View as Falcon'} <IcChevronRight size={14} stroke={2} /></span>
        </button>
        <button className="tpl-picker-tile client" onClick={() => onPick('client')}>
          <span className="ic-wrap"><IcBuildingS size={26} stroke={1.7} /></span>
          <span className="ttl">{t.vsViewClient || 'View as Client'}</span>
          <span className="dsc">{t.vsViewClientDesc || 'Manage channels and services for a single organization.'}</span>
          <span className="cta">{t.vsViewClient || 'View as Client'} <IcChevronRight size={14} stroke={2} /></span>
        </button>
      </div>
    </div>
  </div>
);

// ===== Falcon accounts rail — clients only, no sub-nodes =====
const VoiceClientsRail = ({ tree, selectedId, onSelect, t }) => {
  const clients = (tree && tree.children) || [];
  return (
    <div className="clients-panel vs-clients-rail">
      <div className="vs-rail-head">{t.vsAccounts || 'Accounts'}</div>
      <div className="vs-rail-list">
        {clients.map((c) => (
          <button key={c.id} type="button" className={`vs-rail-item ${selectedId === c.id ? 'on' : ''}`} onClick={() => onSelect(c.id)}>
            <BrandLogo brand={c.brand} size={24} />
            <span className="vs-rail-name">{c.name}</span>
          </button>
        ))}
      </div>
    </div>
  );
};

// ===== Page orchestrator =====
const VS_CURRENT_USER = { name: 'Ahmad Ali', email: 'a.ali@aramco.sa' };

const VoiceServicePage = ({ tree, selected, selectNode = () => {}, expanded, toggleExpand = () => {}, t = {}, pushToast }) => {
  const clients = (tree && tree.children) || [];

  // Optional cross-page entry intent (e.g. "Activate" from a commchannel) — opens straight on a view/tab.
  const [viewAs, setViewAs] = useStateVS(() => (window.__vsInitial && window.__vsInitial.viewAs) || null); // null (picker) | 'falcon' | 'client'
  const [falconSel, setFalconSel] = useStateVS(() => (clients.find((c) => c.id === 'aramco') || clients[0] || {}).id);

  const [tab, setTab] = useStateVS(() => (window.__vsInitial && window.__vsInitial.tab) || 'account'); // account | records | shared
  const [mode, setMode] = useStateVS('list');   // list | details | create | edit  (per active tab)
  const [activeRow, setActiveRow] = useStateVS(null);
  const [editNumbers, setEditNumbers] = useStateVS([]);
  const [editProvider, setEditProvider] = useStateVS('t2');
  const [editName, setEditName] = useStateVS('');
  const [recShareEdit, setRecShareEdit] = useStateVS(false); // records details opened in editable-share mode
  const [editShared, setEditShared] = useStateVS([]);
  const [page, setPage] = useStateVS(1);
  const [pageSize, setPageSize] = useStateVS(10);
  const [openMenuId, setOpenMenuId] = useStateVS(null);

  const [accounts, setAccounts] = useStateVS(() =>
    (window.seedVoiceAccounts || []).map((a) => ({ ...a, numbers: a.numbers.map((n) => ({ ...n })) }))
  );
  const [records, setRecords] = useStateVS(() =>
    (window.seedVoiceRecords || []).map((r) => ({ ...r, sharedWith: [...(r.sharedWith || [])] }))
  );
  const [sharedRecords] = useStateVS(() => (window.seedSharedRecords || []));
  const [deleteRec, setDeleteRec] = useStateVS(null);

  const isFalcon = viewAs === 'falcon';

  // Consume the one-shot entry intent so a later normal visit shows the picker again.
  useEffectVS(() => { if (window.__vsInitial) window.__vsInitial = null; }, []);

  // Falcon view scopes to the client picked in the rail; client view uses the selected node / Aramco.
  const scopeNode = (() => {
    if (isFalcon) return clients.find((c) => c.id === falconSel) || clients[0] || { name: 'Voice Service', brand: 'aramco' };
    const n = (window.findNode && tree) ? window.findNode(tree, selected) : null;
    if (n && n.type === 'client') return n;
    return clients.find((c) => c.id === 'aramco') || clients[0] || n || { name: 'Voice Service', brand: 'aramco' };
  })();

  useEffectVS(() => {
    if (openMenuId == null) return;
    const onDown = (e) => { if (!e.target.closest('.row-menu') && !e.target.closest('.row-action-btn')) setOpenMenuId(null); };
    document.addEventListener('mousedown', onDown);
    return () => document.removeEventListener('mousedown', onDown);
  }, [openMenuId]);

  const switchTab = (x) => { setTab(x); setMode('list'); setActiveRow(null); setPage(1); setOpenMenuId(null); };
  const switchPerspective = () => { setViewAs(null); setMode('list'); setActiveRow(null); setTab('account'); setPage(1); setOpenMenuId(null); };
  const openCreate = () => { setActiveRow(null); setMode('create'); };
  const openDetails = (r) => { setActiveRow(r); setRecShareEdit(false); setMode('details'); setOpenMenuId(null); };
  const openEdit = (r) => { setActiveRow(r); setEditName(r.name || ''); setEditNumbers(r.numbers.map((n) => ({ ...n }))); setEditProvider(r.provider); setMode('edit'); setOpenMenuId(null); };
  // Share (records) → opens the "More Details" screen with the Shared-with list editable.
  const openRecShare = (r) => { setActiveRow(r); setEditShared([...(r.sharedWith || [])]); setRecShareEdit(true); setMode('details'); setOpenMenuId(null); };
  const backToList = () => { setMode('list'); setActiveRow(null); setRecShareEdit(false); };

  // --- account handlers ---
  const toggleStatus = (r) => {
    const next = r.status === 'active' ? 'disabled' : 'active';
    setAccounts((list) => list.map((a) => (a.id === r.id ? { ...a, status: next } : a)));
    setActiveRow((cur) => (cur && cur.id === r.id ? { ...cur, status: next } : cur));
    pushToast(next === 'active' ? (t.vsActivatedToast || 'Voice account activated ✓') : (t.vsDisabledToast || 'Voice account disabled'));
  };
  const saveAccount = (data) => {
    const id = 'va' + Date.now();
    const now = vsNowStamp();
    setAccounts((list) => [{ id, name: data.name, provider: data.provider, numbers: data.numbers, status: 'active', createdAt: now, createdBy: VS_CURRENT_USER, modifiedAt: now, modifiedBy: VS_CURRENT_USER }, ...list]);
    pushToast(t.vsCreatedToast || 'Voice account created ✓');
    backToList();
  };
  // Edit = "More Details" screen with the provider + phone numbers editable.
  const saveAccountNumbers = () => {
    if (!editName.trim()) return;    // guard: account name is required
    if (!editNumbers.length) return; // guard: at least one phone number is required
    const id = activeRow.id;
    setAccounts((list) => list.map((a) => (a.id === id ? { ...a, name: editName.trim(), provider: editProvider, numbers: editNumbers, modifiedAt: vsNowStamp(), modifiedBy: VS_CURRENT_USER } : a)));
    pushToast(t.vsUpdatedToast || 'Voice account updated ✓');
    backToList();
  };

  // --- record handlers ---
  const saveRecord = (data) => {
    const id = 'vr' + Date.now();
    setRecords((list) => [{ id, name: data.name, source: data.source, durationSec: data.durationSec, sharedWith: data.sharedWith || [], createdBy: VS_CURRENT_USER, createdAt: vsNowStamp(), usedInIvr: false }, ...list]);
    pushToast(t.vrCreatedToast || 'Voice record created ✓');
    backToList();
  };
  const saveRecShare = () => {
    const id = activeRow.id;
    setRecords((rs) => rs.map((r) => (r.id === id ? { ...r, sharedWith: editShared } : r)));
    pushToast(t.vrSharedToast || 'Record sharing updated ✓');
    backToList();
  };
  const confirmDeleteRec = () => {
    const id = deleteRec.id;
    setRecords((rs) => rs.filter((r) => r.id !== id));
    setDeleteRec(null);
    if (mode === 'details' && activeRow && activeRow.id === id) backToList();
    pushToast(t.vrDeletedToast || 'Voice record deleted');
  };

  // --- perspective picker (landing) ---
  if (!viewAs) {
    return <VoiceViewPicker t={t} onPick={(v) => {
      setViewAs(v); setTab('account'); setMode('list'); setActiveRow(null); setPage(1);
      if (v === 'falcon') setFalconSel((s) => s || (clients.find((c) => c.id === 'aramco') || clients[0] || {}).id);
    }} />;
  }

  // Create wizards (Voice Account + Voice Record) render inside .content-panel,
  // mirroring the Contact Groups "New Group" wizard (not a full-screen takeover).
  // The hierarchy tree shows on the Falcon side only — the client side is
  // accounts-scoped, so it has no hierarchy tree.
  const isCreate = mode === 'create';
  const showTree = isFalcon;

  const active = activeRow;
  // Falcon view drops the "Shared records" tab.
  const tabs = [
    { id: 'account', label: t.vsTabAccount || 'Voice account' },
    { id: 'records', label: t.vsTabRecords || 'Voice records' },
    ...(isFalcon ? [] : [{ id: 'shared', label: t.vsTabShared || 'Shared records' }]),
  ];

  return (
    <div className={`templates-page vs-service-page ${showTree ? 'with-tree' : 'no-tree'}`}>
      {showTree && (
        <TmClientsTree tree={tree} selected={falconSel} t={t}
          onSelect={(id) => { setFalconSel(id); setMode('list'); setActiveRow(null); setPage(1); setOpenMenuId(null); }} />
      )}
      <div className="content-panel">
        {isCreate ? (
          tab === 'records'
            ? <CreateVoiceRecordFlow t={t} scopeNode={scopeNode} onClose={backToList} onSave={saveRecord} />
            : <CreateVoiceAccountFlow t={t} pushToast={pushToast} scopeNode={scopeNode} initial={null} existingNames={accounts.map((a) => a.name)} onClose={backToList} onSave={saveAccount} />
        ) : (
        <>
        {/* Tabs on top — same structure as the Organization Hierarchy page. */}
        <div className="tabs-bar tabs-bar-with-toggle vs-tabs">
          <div className="tabs-bar-left">
            {tabs.map((tb) => (
              <button key={tb.id} type="button" className={`tab ${tab === tb.id ? 'active' : ''}`} onClick={() => tab !== tb.id && switchTab(tb.id)}>{tb.label}</button>
            ))}
          </div>
        </div>

        <div className="content-body">
          <div className="node-header">
            <div className="node-title">
              {window.CgScopeLogo ? <window.CgScopeLogo node={scopeNode} size={32} /> : <BrandLogo brand={scopeNode?.brand} size={32} />}
              <span>{scopeNode ? scopeNode.name : (t.vsTitle || 'Voice Service')}</span>
            </div>
            <div className="node-actions">
              {mode === 'list' && (
                <button className="btn btn-secondary" onClick={switchPerspective}><IcArrowLeft size={12} stroke={1.7} /> {t.vsSwitchPerspective || 'Switch perspective'}</button>
              )}
              {mode === 'details' && tab === 'account' && (
                <>
                  <button className="btn btn-secondary" onClick={backToList}><IcArrowLeft size={12} stroke={1.7} /> {t.vsBackToList || 'Back to list'}</button>
                  <button className="btn btn-secondary" onClick={() => openEdit(active)}><IcEdit size={13} stroke={1.7} /> {t.vsEdit || 'Edit'}</button>
                  <button className="btn btn-primary" onClick={() => toggleStatus(active)}>{active && active.status === 'active' ? (t.vsDisable || 'Disable') : (t.vsActivate || 'Activate')}</button>
                </>
              )}
              {mode === 'edit' && tab === 'account' && (
                <>
                  <button className="btn btn-secondary" onClick={backToList}><IcArrowLeft size={12} stroke={1.7} /> {t.vsBackToList || 'Back to list'}</button>
                  <button className="btn btn-primary" onClick={saveAccountNumbers} disabled={!editName.trim() || editNumbers.length === 0} title={!editName.trim() ? (t.vsNameReq || 'Account name is required') : (editNumbers.length === 0 ? (t.vsNumbersReq || 'Add at least one phone number') : '')}>{t.vsSaveChanges || 'Save Changes'}</button>
                </>
              )}
              {mode === 'details' && tab === 'records' && (
                recShareEdit ? (
                  <>
                    <button className="btn btn-secondary" onClick={backToList}><IcArrowLeft size={12} stroke={1.7} /> {t.vsBackToList || 'Back to list'}</button>
                    <button className="btn btn-primary" onClick={saveRecShare}>{t.vsSaveChanges || 'Save Changes'}</button>
                  </>
                ) : (
                  <button className="btn btn-secondary" onClick={backToList}><IcArrowLeft size={12} stroke={1.7} /> {t.vsBackToList || 'Back to list'}</button>
                )
              )}
              {mode === 'list' && tab === 'account' && (
                <button className="btn btn-primary" onClick={openCreate}><IcPlus size={15} stroke={2} /> {t.vsCreateAccount || 'Create Voice Account'}</button>
              )}
              {mode === 'list' && tab === 'records' && (
                <button className="btn btn-primary" onClick={openCreate}><IcPlus size={15} stroke={2} /> {t.vrCreateRecord || 'Create Voice Record'}</button>
              )}
            </div>
          </div>

          {tab === 'account' && (
            mode === 'details' ? <VoiceAccountDetails row={active} t={t} />
            : mode === 'edit' ? <VoiceAccountEditForm row={active} name={editName} onNameChange={setEditName} provider={editProvider} onProviderChange={(p) => { setEditProvider(p); setEditNumbers([]); }} numbers={editNumbers} onNumbersChange={setEditNumbers} t={t} />
            : <VoiceAccountsList rows={accounts} t={t} onView={openDetails} onEdit={openEdit} onToggle={toggleStatus} openMenuId={openMenuId} setOpenMenuId={setOpenMenuId} page={page} pageSize={pageSize} setPage={setPage} setPageSize={setPageSize} />
          )}

          {tab === 'records' && (mode === 'details'
            ? <VoiceRecordDetails rec={active} t={t} shareEdit={recShareEdit} editShared={editShared} onSharedChange={setEditShared} />
            : <VoiceRecordsList rows={records} t={t} isFalcon={isFalcon} onView={openDetails} onShare={openRecShare} onDelete={(r) => setDeleteRec(r)} openMenuId={openMenuId} setOpenMenuId={setOpenMenuId} page={page} pageSize={pageSize} setPage={setPage} setPageSize={setPageSize} />)}

          {tab === 'shared' && !isFalcon && (
            <SharedRecordsList rows={sharedRecords} t={t} page={page} pageSize={pageSize} setPage={setPage} setPageSize={setPageSize} />
          )}
        </div>
        </>
        )}
      </div>

      {deleteRec && <VoiceRecordDeleteConfirm rec={deleteRec} t={t} onCancel={() => setDeleteRec(null)} onConfirm={confirmDeleteRec} />}
    </div>
  );
};

window.VoiceServicePage = VoiceServicePage;
