// Configuración — gestión de roles, permisos, usuarios, integraciones, seguridad
// Pieza central: matriz de permisos por rol con toggles editables + creación de roles custom.

function ConfiguracionTab({ id, label, count, active, icon }) {
  return (
    <div className={`cfg-tab ${active ? 'is-active' : ''}`}>
      {icon}
      <span>{label}</span>
      {count != null && <span className="cfg-tab__count">{count}</span>}
    </div>
  );
}

// Renderiza el indicador de permiso en una celda
function PermDot({ value }) {
  if (value === 'full') return <div className="perm perm--full" title="Acceso total"><svg viewBox="0 0 12 12"><path d="M2 6.5l3 3 5-6" stroke="currentColor" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round"/></svg></div>;
  if (value === 'parcial') return <div className="perm perm--parcial" title="Acceso parcial / propio"><svg viewBox="0 0 12 12"><circle cx="6" cy="6" r="3" fill="currentColor"/></svg></div>;
  if (value === 'none') return <div className="perm perm--none" title="Sin acceso"><svg viewBox="0 0 12 12"><path d="M3 3l6 6M9 3l-6 6" stroke="currentColor" strokeWidth="1.5" fill="none" strokeLinecap="round"/></svg></div>;
  return <div className="perm perm--na">—</div>;
}

const MOD_LABEL = {
  solicitudes: { lbl: 'Solicitudes (tickets)', icon: 'ticket'   },
  pagos:       { lbl: 'Pagos y vouchers',       icon: 'cash'     },
  clientes:    { lbl: 'Empresas clientes (CRM)', icon: 'users'    },
  catalogo:    { lbl: 'Catálogo y precios',      icon: 'catalog'  },
  logistica:   { lbl: 'Logística y despachos',   icon: 'truck'    },
  reportes:    { lbl: 'Reportes y exportes',     icon: 'reports'  },
  auditoria:   { lbl: 'Auditoría',               icon: 'audit'    },
  config:      { lbl: 'Configuración',           icon: 'settings' },
  soporte:     { lbl: 'Centro de ayuda',         icon: 'help'     },
  'mi-cuenta': { lbl: 'Mi cuenta',               icon: 'user'     },
};

function nextNivel(actual) {
  if (actual === 'full') return 'parcial';
  if (actual === 'parcial') return 'none';
  return 'full';
}

// ───────── Sub-pantalla: Roles + matriz ─────────
function CfgRolesPermisos() {
  const meUser    = DesasfAPI.getCurrentUser() || {};
  const isDev     = meUser.rol === 'dev';
  const canEdit   = DesasfAPI.hasPerm('config.roles.editar', { strict: true });

  const [roles,     setRoles]     = React.useState([]);
  const [permisos,  setPermisos]  = React.useState([]);
  const [matriz,    setMatriz]    = React.useState({});
  const [selected,  setSelected]  = React.useState(null);
  const [loading,   setLoading]   = React.useState(true);
  const [savingKey, setSavingKey] = React.useState(null);
  const [error,     setError]     = React.useState('');
  const [showNew,   setShowNew]   = React.useState(false);
  const [nuevoRol,  setNuevoRol]  = React.useState({ slug: '', nombre: '', color: 'var(--ink-3)', descripcion: '' });
  const [showEdit,  setShowEdit]  = React.useState(false);
  const [editRol,   setEditRol]   = React.useState({ slug: '', nombre: '', color: 'var(--ink-3)', descripcion: '' });
  const [showDup,   setShowDup]   = React.useState(false);
  const [dupRol,    setDupRol]    = React.useState({ src: '', slug: '', nombre: '' });
  const [reorderMode, setReorderMode] = React.useState(false);
  const [orderList, setOrderList] = React.useState([]); // arreglo de slugs
  const [dragIdx,   setDragIdx]   = React.useState(null);

  function cargar() {
    setLoading(true);
    DesasfAPI.get('/config/roles-permisos').then(d => {
      setRoles(d.roles || []);
      setPermisos(d.permisos || []);
      setMatriz(d.matriz || {});
      if (!selected && d.roles?.length) {
        const def = d.roles.find(r => r.slug === 'vendedor') || d.roles[0];
        setSelected(def?.slug);
      }
    }).catch(e => setError(e.message || 'No se pudo cargar la matriz'))
      .finally(() => setLoading(false));
  }

  React.useEffect(cargar, []);

  const visibleRoles = isDev ? roles : roles.filter(r => !r.dev_only);
  const sel = visibleRoles.find(r => r.slug === selected) || visibleRoles[0];

  // Agrupar permisos por módulo (orden por permisos.orden)
  const grupos = React.useMemo(() => {
    const g = {};
    for (const p of permisos) {
      g[p.modulo] = g[p.modulo] || [];
      g[p.modulo].push(p);
    }
    return Object.keys(g).map(k => ({ modulo: k, items: g[k] }));
  }, [permisos]);

  async function setCelda(rolSlug, permisoSlug, currentNivel) {
    if (!canEdit) return;
    const r = roles.find(x => x.slug === rolSlug);
    if (r?.slug === 'dev') return; // dev no es editable, siempre full
    const nuevo = nextNivel(currentNivel);
    const key = `${rolSlug}.${permisoSlug}`;
    setSavingKey(key);
    setError('');
    setMatriz(prev => ({ ...prev, [rolSlug]: { ...(prev[rolSlug] || {}), [permisoSlug]: nuevo } }));
    try {
      await DesasfAPI.put('/config/roles-permisos/nivel', { rol_slug: rolSlug, permiso_slug: permisoSlug, nivel: nuevo });
    } catch (e) {
      setError(e.message || 'No se pudo guardar el permiso');
      setMatriz(prev => ({ ...prev, [rolSlug]: { ...(prev[rolSlug] || {}), [permisoSlug]: currentNivel } }));
    } finally { setSavingKey(null); }
  }

  async function crearRol() {
    if (!nuevoRol.slug || !nuevoRol.nombre) { setError('Slug y nombre requeridos'); return; }
    setError('');
    try {
      await DesasfAPI.post('/config/roles-permisos/roles', nuevoRol);
      setShowNew(false);
      setNuevoRol({ slug: '', nombre: '', color: 'var(--ink-3)', descripcion: '' });
      cargar();
    } catch (e) { setError(e.message || 'No se pudo crear el rol'); }
  }

  async function eliminarRol(r) {
    if (r.slug === 'dev' || r.slug === 'admin') return;
    if (!confirm(`¿Eliminar el rol "${r.nombre}"?\n(No se permite si tiene usuarios activos asignados)`)) return;
    try {
      await DesasfAPI.delete('/config/roles-permisos/roles/' + r.slug);
      if (selected === r.slug) setSelected(null);
      cargar();
    } catch (e) { alert(e.message || 'No se pudo eliminar'); }
  }

  function abrirEditar(r) {
    setError('');
    setEditRol({ slug: r.slug, nombre: r.nombre || '', color: r.color || 'var(--ink-3)', descripcion: r.descripcion || '' });
    setShowEdit(true);
  }
  async function guardarEdicion() {
    if (!editRol.nombre.trim()) { setError('El nombre es requerido'); return; }
    setError('');
    try {
      await DesasfAPI.put('/config/roles-permisos/roles/' + editRol.slug, {
        nombre: editRol.nombre, descripcion: editRol.descripcion, color: editRol.color,
      });
      setShowEdit(false);
      cargar();
    } catch (e) { setError(e.message || 'No se pudo actualizar el rol'); }
  }

  function abrirDuplicar(r) {
    setError('');
    setDupRol({ src: r.slug, slug: r.slug + '_copia', nombre: (r.nombre || r.slug) + ' (copia)' });
    setShowDup(true);
  }
  async function guardarDuplicado() {
    if (!dupRol.slug || !dupRol.nombre) { setError('Slug y nombre requeridos'); return; }
    setError('');
    try {
      await DesasfAPI.post('/config/roles-permisos/roles/' + dupRol.src + '/duplicar', {
        slug: dupRol.slug, nombre: dupRol.nombre,
      });
      setShowDup(false);
      setSelected(dupRol.slug);
      cargar();
    } catch (e) { setError(e.message || 'No se pudo duplicar el rol'); }
  }

  function startReorder() {
    setError('');
    setOrderList(visibleRoles.map(r => r.slug));
    setDragIdx(null);
    setReorderMode(true);
  }
  function moveRole(idx, dir) {
    setOrderList(list => {
      const ni = idx + dir;
      if (ni < 0 || ni >= list.length) return list;
      const copy = [...list];
      [copy[idx], copy[ni]] = [copy[ni], copy[idx]];
      return copy;
    });
  }
  function dropRole(idx) {
    setOrderList(list => {
      if (dragIdx === null || dragIdx === idx) return list;
      const copy = [...list];
      const [moved] = copy.splice(dragIdx, 1);
      copy.splice(idx, 0, moved);
      return copy;
    });
    setDragIdx(null);
  }
  async function saveRolesOrder() {
    try {
      await DesasfAPI.put('/config/roles-permisos/orden', { orden: orderList });
      setReorderMode(false);
      cargar();
    } catch (e) { setError(e.message || 'No se pudo guardar el orden'); }
  }

  if (loading) return <div style={{ padding: 24, color: 'var(--ink-5)' }}>Cargando matriz…</div>;

  if (reorderMode) return (
    <>
      <div className="sect-h" style={{ margin: '0 0 12px' }}>
        <h2>Reordenar roles</h2>
        <div className="row" style={{ gap: 8 }}>
          <button className="btn btn--ghost btn--sm" onClick={() => setReorderMode(false)}>Cancelar</button>
          <button className="btn btn--accent btn--sm" onClick={saveRolesOrder}>{Icon.check}<span>Guardar orden</span></button>
        </div>
      </div>
      {error && (
        <div style={{ marginBottom: 12, padding: 10, background: 'var(--status-danger-bg, #fef0f0)', color: 'var(--status-danger)', borderRadius: 6, fontSize: 12 }}>{error}</div>
      )}
      <div className="card">
        <div style={{ padding: '10px 14px', fontSize: 12, color: 'var(--ink-5)', borderBottom: '1px solid var(--line)' }}>
          Arrastra cada fila o usa las flechas para definir el orden de los roles (afecta las tarjetas y las columnas de la matriz de permisos).
        </div>
        <div className="card__body" style={{ padding: 0 }}>
          {orderList.map((slug, idx) => {
            const r = visibleRoles.find(x => x.slug === slug);
            if (!r) return null;
            return (
              <div key={slug} draggable
                onDragStart={() => setDragIdx(idx)}
                onDragOver={e => e.preventDefault()}
                onDrop={() => dropRole(idx)}
                style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '10px 14px',
                         borderBottom: idx < orderList.length - 1 ? '1px solid var(--line)' : 0,
                         background: dragIdx === idx ? 'var(--ink-9)' : 'transparent', cursor: 'grab' }}>
                <span style={{ color: 'var(--ink-6)', display: 'grid', placeItems: 'center' }}>{Icon.more}</span>
                <span style={{ width: 24, textAlign: 'right', fontFamily: 'var(--mono)', fontSize: 12, color: 'var(--ink-5)' }}>{idx + 1}</span>
                <span className="role-card__chip" style={{ background: r.color, width: 22, height: 22, fontSize: 11 }}>{r.nombre.slice(0, 1)}</span>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontWeight: 600, color: 'var(--ink-0)' }}>{r.nombre}</div>
                  <div className="muted k" style={{ fontSize: 11 }}>{r.slug}</div>
                </div>
                <div className="row" style={{ gap: 4 }}>
                  <button className="btn btn--ghost btn--sm" disabled={idx === 0} onClick={() => moveRole(idx, -1)} title="Subir">↑</button>
                  <button className="btn btn--ghost btn--sm" disabled={idx === orderList.length - 1} onClick={() => moveRole(idx, 1)} title="Bajar">↓</button>
                </div>
              </div>
            );
          })}
        </div>
      </div>
    </>
  );

  // Layout de la matriz: columnas con ancho mínimo + scroll horizontal cuando
  // hay muchos roles (si caben, se reparten el ancho; si no, aparece scroll).
  const nRoles     = Math.max(visibleRoles.length, 1);
  const matrixCols = `280px repeat(${nRoles}, minmax(100px, 1fr))`;
  const matrixMinW = 280 + nRoles * 100;

  return (
    <>
      {/* Tarjetas de roles */}
      <div className="sect-h" style={{ margin: '0 0 12px' }}>
        <h2>Roles del sistema</h2>
        <div className="row" style={{ gap: 8 }}>
          <span className="muted" style={{ fontSize: 12 }}>{canEdit ? 'Click en celda para alternar nivel' : 'Solo lectura'}</span>
          {canEdit && visibleRoles.length > 1 && <button className="btn btn--ghost btn--sm" onClick={startReorder}>{Icon.more}<span>Editar orden</span></button>}
          {canEdit && <button className="btn btn--accent btn--sm" onClick={() => setShowNew(true)}>{Icon.plus}<span>Nuevo rol</span></button>}
        </div>
      </div>

      {error && (
        <div style={{ marginBottom: 12, padding: 10, background: 'var(--status-danger-bg, #fef0f0)', color: 'var(--status-danger)', borderRadius: 6, fontSize: 12 }}>
          {error}
        </div>
      )}

      <div style={{ display: 'flex', gap: 10, marginBottom: 18, overflowX: 'auto', paddingBottom: 4 }}>
        {visibleRoles.map(r => (
          <div key={r.slug}
            className={`role-card ${r.slug === selected ? 'is-selected' : ''}`}
            onClick={() => setSelected(r.slug)}
            style={{ '--role-c': r.color, cursor: 'pointer', flex: '1 0 200px', minWidth: 200 }}>
            <div className="role-card__bar"/>
            <div className="row" style={{ justifyContent: 'space-between', marginBottom: 6 }}>
              <span className="role-card__chip" style={{ background: r.color }}>{r.nombre.slice(0,1)}</span>
              {(r.slug === 'dev' || r.slug === 'admin') && <span className="tag">SISTEMA</span>}
            </div>
            <div style={{ fontWeight: 600, fontSize: 13, color: 'var(--ink-0)' }}>{r.nombre}</div>
            <div className="muted" style={{ fontSize: 11, marginTop: 2, lineHeight: 1.35, minHeight: 28 }}>{r.descripcion || '—'}</div>
            <div className="row" style={{ marginTop: 8, justifyContent: 'space-between', alignItems: 'baseline' }}>
              <span style={{ fontFamily: 'var(--mono)', fontSize: 11, color: 'var(--ink-5)' }}>{r.users || 0} usuarios</span>
              <span style={{ fontSize: 10, color: 'var(--ink-5)', letterSpacing: '0.06em', textTransform: 'uppercase', fontWeight: 600 }}>{r.slug === selected ? 'EDITANDO' : 'EDITAR →'}</span>
            </div>
          </div>
        ))}
      </div>

      {/* Edición rápida del rol seleccionado */}
      {sel && (
        <div className="card" style={{ marginBottom: 18, borderTop: `3px solid ${sel.color}` }}>
          <div className="card__body" style={{ padding: '14px 18px' }}>
            <div className="row" style={{ justifyContent: 'space-between', alignItems: 'flex-start' }}>
              <div>
                <div className="muted" style={{ fontSize: 11, textTransform: 'uppercase', letterSpacing: '0.08em', fontWeight: 600 }}>Rol activo</div>
                <div style={{ fontFamily: 'var(--cond)', fontSize: 22, fontWeight: 700, marginTop: 2 }}>{sel.nombre} <span className="muted k" style={{ fontSize: 12, fontWeight: 400 }}>· {sel.slug}</span></div>
                <div className="muted" style={{ fontSize: 12, marginTop: 2 }}>{sel.descripcion || '—'} · {sel.users || 0} usuarios asignados</div>
              </div>
              <div className="row" style={{ gap: 8 }}>
                {canEdit && sel.slug !== 'dev' && <button className="btn btn--ghost btn--sm" onClick={() => abrirEditar(sel)}>{Icon.doc}<span>Editar</span></button>}
                {canEdit && sel.slug !== 'dev' && <button className="btn btn--ghost btn--sm" onClick={() => abrirDuplicar(sel)}>{Icon.copy || Icon.plus}<span>Duplicar</span></button>}
                {canEdit && sel.slug !== 'dev' && sel.slug !== 'admin' && <button className="btn btn--ghost btn--sm" style={{ color: 'var(--status-danger)' }} onClick={() => eliminarRol(sel)}>Eliminar</button>}
              </div>
            </div>
            {sel.slug === 'dev' && (
              <div className="muted" style={{ fontSize: 12, marginTop: 10, padding: 8, background: 'var(--ink-9)', borderRadius: 4 }}>
                ⚠️ El rol <b>dev</b> tiene acceso total por diseño y no es editable desde aquí.
              </div>
            )}
          </div>
        </div>
      )}

      {/* Matriz */}
      <div className="sect-h"><h2>Matriz de permisos</h2>
        <div className="row" style={{ gap: 14, fontSize: 11 }}>
          <span className="row" style={{ gap: 6 }}><PermDot value="full"/> <span>Total</span></span>
          <span className="row" style={{ gap: 6 }}><PermDot value="parcial"/> <span>Parcial / propio</span></span>
          <span className="row" style={{ gap: 6 }}><PermDot value="none"/> <span>Sin acceso</span></span>
          <span className="muted" style={{ marginLeft: 12, fontSize: 11 }}>{canEdit ? 'Click cicla full → parcial → none' : 'Lectura'}</span>
        </div>
      </div>

      <div className="card perm-matrix" style={{ overflowX: 'auto' }}>
        <div className="perm-matrix__head" style={{ gridTemplateColumns: matrixCols, minWidth: matrixMinW }}>
          <div className="perm-matrix__corner">Módulo / Acción</div>
          {visibleRoles.map(r => (
            <div key={r.slug} className={`perm-matrix__col ${r.slug === selected ? 'is-selected' : ''}`}
              onClick={() => setSelected(r.slug)} style={{ cursor: 'pointer' }}>
              <span className="role-card__chip" style={{ background: r.color, width: 18, height: 18, fontSize: 10 }}>{r.nombre.slice(0,1)}</span>
              <span>{r.nombre.split(' ')[0]}</span>
            </div>
          ))}
        </div>

        {grupos.map((g, gi) => {
          const meta = MOD_LABEL[g.modulo] || { lbl: g.modulo, icon: 'settings' };
          return (
            <React.Fragment key={gi}>
              <div className="perm-matrix__sect" style={{ minWidth: matrixMinW }}>
                <div className="row" style={{ gap: 8 }}>
                  {Icon[meta.icon] || Icon.settings}
                  <span>{meta.lbl}</span>
                </div>
                <span className="muted" style={{ fontSize: 11 }}>{g.items.length} acciones</span>
              </div>
              {g.items.map(p => (
                <div key={p.slug} className="perm-matrix__row" style={{ gridTemplateColumns: matrixCols, minWidth: matrixMinW }}>
                  <div className="perm-matrix__cell perm-matrix__action" title={p.slug}>{p.accion}</div>
                  {visibleRoles.map(r => {
                    const nivel = r.slug === 'dev' ? 'full' : (matriz[r.slug]?.[p.slug] || 'none');
                    const key = `${r.slug}.${p.slug}`;
                    const isLoading = savingKey === key;
                    const editable = canEdit && r.slug !== 'dev';
                    return (
                      <div key={r.slug}
                        className={`perm-matrix__cell ${r.slug === selected ? 'is-selected' : ''}`}
                        style={{ cursor: editable ? 'pointer' : 'default', opacity: isLoading ? 0.5 : 1 }}
                        onClick={editable && !isLoading ? () => setCelda(r.slug, p.slug, nivel) : undefined}
                        title={editable ? `${r.nombre} · ${p.slug} = ${nivel}` : `${r.nombre} · ${nivel}`}>
                        <PermDot value={nivel}/>
                      </div>
                    );
                  })}
                </div>
              ))}
            </React.Fragment>
          );
        })}

        <div className="perm-matrix__foot" style={{ minWidth: matrixMinW }}>
          <span className="muted" style={{ fontSize: 12 }}>
            {canEdit
              ? 'Los cambios se guardan automáticamente al hacer click en cada celda.'
              : 'Solo lectura — pídele a un administrador que modifique los permisos.'}
          </span>
          <div className="row" style={{ gap: 8 }}>
            <button className="btn btn--ghost btn--sm" onClick={cargar}>{Icon.refresh || Icon.reports}<span>Refrescar</span></button>
          </div>
        </div>
      </div>

      {/* Modal nuevo rol */}
      {showNew && (
        <div className="modal-overlay" onClick={e => { if (e.target === e.currentTarget) setShowNew(false); }}>
          <div style={{ width: 440, background: '#fff', borderRadius: 12, boxShadow: '0 30px 80px rgba(0,0,0,0.4)', overflow: 'hidden' }}>
            <div style={{ padding: '16px 22px', borderBottom: '1px solid var(--line)', display: 'flex', justifyContent: 'space-between' }}>
              <h3 style={{ margin: 0, fontFamily: 'var(--cond)', fontWeight: 700 }}>Nuevo rol custom</h3>
              <div className="icon-btn" onClick={() => setShowNew(false)}>×</div>
            </div>
            <div style={{ padding: 22, display: 'grid', gap: 12 }}>
              <div>
                <label className="label">Slug (a-z 0-9 _ -)</label>
                <input className="input k" placeholder="auditor_externo" value={nuevoRol.slug}
                  onChange={e => setNuevoRol({ ...nuevoRol, slug: e.target.value.toLowerCase().replace(/[^a-z0-9_-]/g,'') })}/>
              </div>
              <div>
                <label className="label">Nombre visible</label>
                <input className="input" value={nuevoRol.nombre}
                  onChange={e => setNuevoRol({ ...nuevoRol, nombre: e.target.value })}/>
              </div>
              <div>
                <label className="label">Color (CSS var o hex)</label>
                <input className="input k" placeholder="var(--ink-3) o #888"
                  value={nuevoRol.color} onChange={e => setNuevoRol({ ...nuevoRol, color: e.target.value })}/>
              </div>
              <div>
                <label className="label">Descripción</label>
                <textarea className="input" rows={2} value={nuevoRol.descripcion}
                  onChange={e => setNuevoRol({ ...nuevoRol, descripcion: e.target.value })}/>
              </div>
            </div>
            <div style={{ padding: '14px 22px', borderTop: '1px solid var(--line)', display: 'flex', justifyContent: 'flex-end', gap: 8 }}>
              <button className="btn btn--ghost" onClick={() => setShowNew(false)}>Cancelar</button>
              <button className="btn btn--accent" onClick={crearRol}>{Icon.check}<span>Crear</span></button>
            </div>
          </div>
        </div>
      )}

      {/* Modal editar rol */}
      {showEdit && (
        <div className="modal-overlay" onClick={e => { if (e.target === e.currentTarget) setShowEdit(false); }}>
          <div style={{ width: 440, background: '#fff', borderRadius: 12, boxShadow: '0 30px 80px rgba(0,0,0,0.4)', overflow: 'hidden' }}>
            <div style={{ padding: '16px 22px', borderBottom: '1px solid var(--line)', display: 'flex', justifyContent: 'space-between' }}>
              <h3 style={{ margin: 0, fontFamily: 'var(--cond)', fontWeight: 700 }}>Editar rol</h3>
              <div className="icon-btn" onClick={() => setShowEdit(false)}>×</div>
            </div>
            <div style={{ padding: 22, display: 'grid', gap: 12 }}>
              <div>
                <label className="label">Slug</label>
                <input className="input k" value={editRol.slug} disabled
                  style={{ background: 'var(--ink-9)', cursor: 'not-allowed' }}/>
                <div className="muted" style={{ fontSize: 11, marginTop: 4 }}>El identificador del rol no se puede cambiar.</div>
              </div>
              <div>
                <label className="label">Nombre visible</label>
                <input className="input" value={editRol.nombre}
                  onChange={e => setEditRol({ ...editRol, nombre: e.target.value })}/>
              </div>
              <div>
                <label className="label">Color (CSS var o hex)</label>
                <input className="input k" value={editRol.color}
                  onChange={e => setEditRol({ ...editRol, color: e.target.value })}/>
              </div>
              <div>
                <label className="label">Descripción</label>
                <textarea className="input" rows={2} value={editRol.descripcion}
                  onChange={e => setEditRol({ ...editRol, descripcion: e.target.value })}/>
              </div>
              {error && <div style={{ padding: 8, background: 'var(--status-danger-bg, #fef0f0)', color: 'var(--status-danger)', borderRadius: 6, fontSize: 12 }}>{error}</div>}
            </div>
            <div style={{ padding: '14px 22px', borderTop: '1px solid var(--line)', display: 'flex', justifyContent: 'flex-end', gap: 8 }}>
              <button className="btn btn--ghost" onClick={() => setShowEdit(false)}>Cancelar</button>
              <button className="btn btn--accent" onClick={guardarEdicion}>{Icon.check}<span>Guardar</span></button>
            </div>
          </div>
        </div>
      )}

      {/* Modal duplicar rol */}
      {showDup && (
        <div className="modal-overlay" onClick={e => { if (e.target === e.currentTarget) setShowDup(false); }}>
          <div style={{ width: 440, background: '#fff', borderRadius: 12, boxShadow: '0 30px 80px rgba(0,0,0,0.4)', overflow: 'hidden' }}>
            <div style={{ padding: '16px 22px', borderBottom: '1px solid var(--line)', display: 'flex', justifyContent: 'space-between' }}>
              <h3 style={{ margin: 0, fontFamily: 'var(--cond)', fontWeight: 700 }}>Duplicar rol</h3>
              <div className="icon-btn" onClick={() => setShowDup(false)}>×</div>
            </div>
            <div style={{ padding: 22, display: 'grid', gap: 12 }}>
              <div className="muted" style={{ fontSize: 12 }}>
                Se creará un rol nuevo (custom) copiando todos los permisos de <b>{dupRol.src}</b>.
              </div>
              <div>
                <label className="label">Slug del nuevo rol (a-z 0-9 _ -)</label>
                <input className="input k" value={dupRol.slug}
                  onChange={e => setDupRol({ ...dupRol, slug: e.target.value.toLowerCase().replace(/[^a-z0-9_-]/g, '') })}/>
              </div>
              <div>
                <label className="label">Nombre visible</label>
                <input className="input" value={dupRol.nombre}
                  onChange={e => setDupRol({ ...dupRol, nombre: e.target.value })}/>
              </div>
              {error && <div style={{ padding: 8, background: 'var(--status-danger-bg, #fef0f0)', color: 'var(--status-danger)', borderRadius: 6, fontSize: 12 }}>{error}</div>}
            </div>
            <div style={{ padding: '14px 22px', borderTop: '1px solid var(--line)', display: 'flex', justifyContent: 'flex-end', gap: 8 }}>
              <button className="btn btn--ghost" onClick={() => setShowDup(false)}>Cancelar</button>
              <button className="btn btn--accent" onClick={guardarDuplicado}>{Icon.check}<span>Duplicar</span></button>
            </div>
          </div>
        </div>
      )}
    </>
  );
}

// ───────── Toggle ─────────
function Toggle({ on, sm }) {
  return (
    <span className={`tog ${on ? 'is-on' : ''} ${sm ? 'tog--sm' : ''}`}>
      <i/>
    </span>
  );
}

// ───────── Sub-pantalla: Usuarios ─────────
function CfgUsuarios() {
  const meRol = (DesasfAPI.getCurrentUser() || {}).rol;
  const isDev = meRol === 'dev';

  const [usersAPI,  setUsersAPI]  = React.useState([]);
  const [empresas,  setEmpresas]  = React.useState([]);
  const [loading,   setLoading]   = React.useState(true);
  const [showForm,  setShowForm]  = React.useState(false);
  const [search,    setSearch]    = React.useState('');
  const [filtroRol, setFiltroRol] = React.useState('');
  const [filtroEst, setFiltroEst] = React.useState('');
  const [nuevo,     setNuevo]     = React.useState({ apellidos: '', nombres: '', email: '', username: '', telefono: '', rol: 'vendedor', password: '', meta_mensual: 0, empresa_id: '' });
  const [editing,        setEditing]        = React.useState(null);
  const [editEmpresas,   setEditEmpresas]   = React.useState([]);  // IDs asociadas al user en edición
  const [addEmpresaId,   setAddEmpresaId]   = React.useState('');
  const [saving,    setSaving]    = React.useState(false);
  const [err,       setErr]       = React.useState('');
  const [roles,     setRoles]     = React.useState([]); // roles dinámicos (orden configurable)

  // Nombres por defecto del sistema; se complementan con los roles de la BD
  // (incluye renombrados y roles custom) en rolLabelDyn más abajo.
  const rolLabel = {
    dev: 'Dev', admin: 'Administrador', gerente_general: 'Gerente general',
    gerente_comercial: 'Gerente comercial',
    vendedor: 'Asistente Técnico comercial', operador_planta: 'Operador planta',
    finanzas: 'Finanzas', conductor: 'Conductor',
    cliente: 'Cliente final', solo_lectura: 'Solo lectura',
  };
  const rolKey = { dev: 'dev', admin: 'admin', gerente_general: 'gerente', gerente_comercial: 'gerente', vendedor: 'vendedor',
                   operador_planta: 'operador', finanzas: 'finanzas', conductor: 'operador',
                   cliente: 'cliente', solo_lectura: 'lectura' };
  function ini(name) {
    return (name || '?').split(/\s+/).slice(0, 2).map(w => w[0]).join('').toUpperCase();
  }

  function fetchAll() {
    setLoading(true);
    Promise.all([
      DesasfAPI.get('/config/usuarios').catch(() => []),
      DesasfAPI.get('/clientes?limit=500').catch(() => ({ rows: [] })),
      DesasfAPI.get('/config/roles').catch(() => []),
    ]).then(([users, emps, rolesList]) => {
      setUsersAPI(Array.isArray(users) ? users : []);
      setEmpresas(emps.rows || []);
      setRoles(Array.isArray(rolesList) ? rolesList : []);
    }).finally(() => setLoading(false));
  }
  React.useEffect(fetchAll, []);

  // Mapa de nombres (sistema + BD) y opciones ordenadas para los desplegables.
  const rolLabelDyn = { ...rolLabel, ...Object.fromEntries(roles.map(r => [r.slug, r.nombre])) };
  const roleOpts = roles.length
    ? roles
    : Object.entries(rolLabel).filter(([k]) => isDev || k !== 'dev').map(([slug, nombre]) => ({ slug, nombre }));

  const filtered = usersAPI
    .filter(u => isDev || u.rol !== 'dev')
    .filter(u => {
      if (filtroRol && u.rol !== filtroRol) return false;
      if (filtroEst === 'activo'    && !u.activo) return false;
      if (filtroEst === 'suspendido' &&  u.activo) return false;
      if (search.trim()) {
        const q = search.toLowerCase();
        const haystack = [u.nombre, u.nombres, u.apellidos, u.email, u.username, u.telefono, u.empresa_nombre]
          .filter(Boolean).join(' ').toLowerCase();
        if (!haystack.includes(q)) return false;
      }
      return true;
    });

  const counters = {
    todos:    usersAPI.filter(u => isDev || u.rol !== 'dev').length,
    activos:  usersAPI.filter(u => (isDev || u.rol !== 'dev') && u.activo).length,
    susp:     usersAPI.filter(u => (isDev || u.rol !== 'dev') && !u.activo).length,
    cliente:  usersAPI.filter(u => u.rol === 'cliente').length,
    sinEmp:   usersAPI.filter(u => u.rol === 'cliente' && !u.empresa_id).length,
  };

  async function crearUsuario() {
    if (!nuevo.apellidos.trim() || !nuevo.nombres.trim()) {
      setErr('Apellidos y nombres son requeridos.'); return;
    }
    if (!nuevo.email || !nuevo.password) {
      setErr('Email y contraseña son requeridos.'); return;
    }
    if (nuevo.password.length < 6) {
      setErr('La contraseña debe tener al menos 6 caracteres.'); return;
    }
    if (nuevo.username && !/^[a-zA-Z0-9._-]{3,30}$/.test(nuevo.username)) {
      setErr('Usuario inválido (3-30, letras/números/._-)'); return;
    }
    setErr(''); setSaving(true);
    try {
      const payload = {
        apellidos: nuevo.apellidos.trim(),
        nombres:   nuevo.nombres.trim(),
        email:     nuevo.email.trim().toLowerCase(),
        username:  nuevo.username.trim() || null,
        telefono:  nuevo.telefono.trim() || null,
        rol:       nuevo.rol,
        password:  nuevo.password,
        meta_mensual: Number(nuevo.meta_mensual) || 0,
        empresa_id: nuevo.empresa_id ? Number(nuevo.empresa_id) : null,
      };
      await DesasfAPI.post('/config/usuarios', payload);
      setNuevo({ apellidos: '', nombres: '', email: '', username: '', telefono: '', rol: 'vendedor', password: '', meta_mensual: 0, empresa_id: '' });
      setShowForm(false);
      fetchAll();
    } catch (e) { setErr(e.message || 'No se pudo crear el usuario.'); }
    finally { setSaving(false); }
  }

  async function guardarEdicion() {
    if (!editing) return;
    if (!editing.apellidos?.trim() || !editing.nombres?.trim()) {
      setErr('Apellidos y nombres son requeridos.'); return;
    }
    if (editing.username && !/^[a-zA-Z0-9._-]{3,30}$/.test(editing.username)) {
      setErr('Usuario inválido (3-30, letras/números/._-)'); return;
    }
    setErr(''); setSaving(true);
    try {
      const payload = {
        apellidos: editing.apellidos.trim(),
        nombres:   editing.nombres.trim(),
        email:     editing.email.trim().toLowerCase(),
        username:  editing.username ? editing.username.trim() : null,
        telefono:  editing.telefono ? editing.telefono.trim() : null,
        rol:       editing.rol,
        meta_mensual: Number(editing.meta_mensual) || 0,
        empresa_id: editing.rol === 'cliente' ? (editEmpresas[0] || null) : null,
        activo:    editing.activo ? 1 : 0,
      };
      await DesasfAPI.put('/config/usuarios/' + editing.id, payload);

      // Sincroniza la N:M solo para clientes
      if (editing.rol === 'cliente') {
        await DesasfAPI.put('/config/usuarios/' + editing.id + '/empresas', {
          empresa_ids: editEmpresas,
        });
      }
      setEditing(null);
      fetchAll();
    } catch (e) { setErr(e.message || 'No se pudo guardar.'); }
    finally { setSaving(false); }
  }

  // Al abrir/cerrar modal: carga empresas asociadas del cliente
  React.useEffect(() => {
    if (!editing) { setEditEmpresas([]); setAddEmpresaId(''); return; }
    if (editing.rol !== 'cliente') { setEditEmpresas([]); return; }
    DesasfAPI.get('/config/usuarios/' + editing.id + '/empresas')
      .then(r => setEditEmpresas((r.rows || []).map(e => e.id)))
      .catch(() => setEditEmpresas(editing.empresa_id ? [editing.empresa_id] : []));
  }, [editing?.id, editing?.rol]);

  function removeEmpresaEdit(id) {
    setEditEmpresas(prev => prev.filter(x => x !== id));
  }
  function addEmpresaEdit() {
    const id = Number(addEmpresaId);
    if (!id) return;
    setEditEmpresas(prev => prev.includes(id) ? prev : [...prev, id]);
    setAddEmpresaId('');
  }

  async function toggleActivo(u) {
    try {
      await DesasfAPI.put('/config/usuarios/' + u.id, { activo: !u.activo });
      fetchAll();
    } catch (e) { alert('Error: ' + e.message); }
  }

  function exportarUsuarios() {
    exportCSV(
      'usuarios-' + new Date().toISOString().slice(0,10) + '.csv',
      filtered,
      [
        { label: 'Apellidos', value: r => r.apellidos || '' },
        { label: 'Nombres',   value: r => r.nombres   || '' },
        { label: 'Nombre completo', value: 'nombre' },
        { label: 'Email',     value: 'email' },
        { label: 'Usuario',   value: r => r.username || '' },
        { label: 'Teléfono',  value: r => r.telefono || '' },
        { label: 'Rol',       value: r => rolLabelDyn[r.rol] || r.rol },
        { label: 'Empresa',   value: r => r.empresa_nombre || '' },
        { label: 'Activo',    value: r => r.activo ? 'sí' : 'no' },
        { label: 'Meta S/',   value: r => Number(r.meta_mensual || 0) },
        { label: 'Último login', value: r => r.ultimo_login ? new Date(r.ultimo_login).toLocaleString('es-PE') : '' },
      ]
    );
  }

  return (
    <>
      <div className="filterbar" style={{ borderRadius: 'var(--r-2)', marginBottom: 14, flexWrap: 'wrap' }}>
        <span className={'chip' + (!filtroEst ? ' is-active' : '')} style={{ cursor: 'pointer' }}
          onClick={() => setFiltroEst('')}>Todos <span className="n">{counters.todos}</span></span>
        <span className={'chip' + (filtroEst === 'activo' ? ' is-active' : '')} style={{ cursor: 'pointer' }}
          onClick={() => setFiltroEst('activo')}>Activos <span className="n">{counters.activos}</span></span>
        <span className={'chip' + (filtroEst === 'suspendido' ? ' is-active' : '')} style={{ cursor: 'pointer' }}
          onClick={() => setFiltroEst('suspendido')}>Suspendidos <span className="n">{counters.susp}</span></span>
        <span className={'chip' + (filtroRol === 'cliente' ? ' is-active' : '')} style={{ cursor: 'pointer' }}
          onClick={() => setFiltroRol(filtroRol === 'cliente' ? '' : 'cliente')}>
          Usuarios cliente <span className="n">{counters.cliente}</span>
        </span>
        {counters.sinEmp > 0 && (
          <span className="chip" style={{ background: 'var(--status-pending-bg)', color: 'var(--status-pending)' }}>
            Sin empresa <span className="n">{counters.sinEmp}</span>
          </span>
        )}
        <div style={{ marginLeft: 'auto', display: 'flex', gap: 8, alignItems: 'center' }}>
          <input className="input" placeholder="Buscar usuario, email, empresa…"
            value={search} onChange={e => setSearch(e.target.value)}
            style={{ height: 30, fontSize: 12, width: 240 }}/>
          <select className="input" value={filtroRol} onChange={e => setFiltroRol(e.target.value)}
            style={{ height: 30, padding: '0 8px', fontSize: 12, width: 160 }}>
            <option value="">Rol: todos</option>
            {roleOpts.map(r => <option key={r.slug} value={r.slug}>{r.nombre}</option>)}
          </select>
          <button className="btn btn--ghost btn--sm" onClick={exportarUsuarios}>{Icon.download}<span>Exportar</span></button>
          <button className="btn btn--accent btn--sm" onClick={() => { setShowForm(s => !s); setErr(''); }}>
            {Icon.plus}<span>{showForm ? 'Cancelar' : 'Crear usuario'}</span>
          </button>
        </div>
      </div>

      {showForm && (
        <div className="card" style={{ marginBottom: 14 }}>
          <div className="card__head">
            <h3>Nuevo usuario</h3>
            <span className="meta">Nombre completo = nombres + apellidos · podrá iniciar sesión con email o usuario</span>
          </div>
          <div className="card__body">
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 12 }}>
              <div>
                <label className="label">Apellidos *</label>
                <input className="input" value={nuevo.apellidos}
                  placeholder="Pérez García"
                  onChange={e => setNuevo({ ...nuevo, apellidos: e.target.value })}/>
              </div>
              <div>
                <label className="label">Nombres *</label>
                <input className="input" value={nuevo.nombres}
                  placeholder="Juan María"
                  onChange={e => setNuevo({ ...nuevo, nombres: e.target.value })}/>
              </div>
              <div>
                <label className="label">Email *</label>
                <input className="input" type="email" value={nuevo.email}
                  placeholder="usuario@dominio.com"
                  onChange={e => setNuevo({ ...nuevo, email: e.target.value })}/>
              </div>
              <div>
                <label className="label">Usuario (login)</label>
                <input className="input k" value={nuevo.username}
                  placeholder="jperez (opcional)"
                  onChange={e => setNuevo({ ...nuevo, username: e.target.value })}/>
              </div>
              <div>
                <label className="label">Teléfono</label>
                <input className="input k" value={nuevo.telefono}
                  placeholder="+51 999 123 456"
                  onChange={e => setNuevo({ ...nuevo, telefono: e.target.value })}/>
              </div>
              <div>
                <label className="label">Contraseña inicial *</label>
                <input className="input k" type="password" value={nuevo.password}
                  placeholder="Mínimo 6 caracteres"
                  onChange={e => setNuevo({ ...nuevo, password: e.target.value })}/>
              </div>
              <div>
                <label className="label">Rol *</label>
                <select className="input" value={nuevo.rol}
                  onChange={e => setNuevo({ ...nuevo, rol: e.target.value, empresa_id: '' })}>
                  {roleOpts.map(r => <option key={r.slug} value={r.slug}>{r.nombre}</option>)}
                </select>
              </div>
              {nuevo.rol === 'vendedor' && (
                <div>
                  <label className="label">Meta mensual (S/)</label>
                  <input className="input k" type="number" step="100" value={nuevo.meta_mensual}
                    onChange={e => setNuevo({ ...nuevo, meta_mensual: Number(e.target.value) })}/>
                </div>
              )}
              {nuevo.rol === 'cliente' && (
                <div style={{ gridColumn: '1 / -1' }}>
                  <label className="label">Empresa asignada</label>
                  <select className="input" value={nuevo.empresa_id || ''}
                    onChange={e => setNuevo({ ...nuevo, empresa_id: e.target.value })}>
                    <option value="">Sin asignar (vincular después desde "Empresas clientes")</option>
                    {empresas.map(e => <option key={e.id} value={e.id}>{e.razon_social} · RUC {e.ruc}</option>)}
                  </select>
                  <div className="muted" style={{ fontSize: 11, marginTop: 4 }}>
                    El cliente solo podrá ver gestiones de la empresa vinculada.
                  </div>
                </div>
              )}
            </div>
            {(nuevo.nombres || nuevo.apellidos) && (
              <div className="muted" style={{ marginTop: 10, fontSize: 12 }}>
                Nombre completo: <b style={{ color: 'var(--ink-1)' }}>{[nuevo.nombres.trim(), nuevo.apellidos.trim()].filter(Boolean).join(' ') || '—'}</b>
              </div>
            )}
            {err && <div style={{ marginTop: 10, padding: 8, background: 'var(--status-danger-bg, #fef0f0)', color: 'var(--status-danger)', borderRadius: 6, fontSize: 12 }}>{err}</div>}
            <div className="row" style={{ marginTop: 14, gap: 8, justifyContent: 'flex-end' }}>
              <button className="btn btn--ghost" onClick={() => { setShowForm(false); setErr(''); }} disabled={saving}>Cancelar</button>
              <button className="btn btn--accent" onClick={crearUsuario} disabled={saving}>
                {Icon.check}<span>{saving ? 'Creando…' : 'Crear usuario'}</span>
              </button>
            </div>
          </div>
        </div>
      )}

      <div className="card" style={{ marginBottom: 18 }}>
        <table className="tbl">
          <thead><tr>
            <th>Usuario</th><th>Rol</th><th>Empresa</th><th>Última actividad</th><th>Meta / extras</th><th>Estado</th><th></th>
          </tr></thead>
          <tbody>
            {filtered.map(u => (
              <tr key={u.id} style={{ background: editing?.id === u.id ? 'var(--ink-9)' : '#fff' }}>
                <td>
                  <div className="row" style={{ gap: 10 }}>
                    <div className="avatar" style={{ width: 32, height: 32, fontSize: 11 }}>{ini(u.nombre)}</div>
                    <div>
                      <div style={{ fontWeight: 600, color: 'var(--ink-0)' }}>{u.nombre}</div>
                      <div className="muted k" style={{ fontSize: 11 }}>
                        {u.email}{u.username ? ' · @' + u.username : ''}
                      </div>
                      {u.telefono && <div className="muted k" style={{ fontSize: 11 }}>{u.telefono}</div>}
                    </div>
                  </div>
                </td>
                <td><span className="role-tag" data-role={rolKey[u.rol] || u.rol}>{rolLabelDyn[u.rol] || u.rol}</span></td>
                <td>
                  {u.empresa_nombre
                    ? <><div style={{ fontSize: 13, fontWeight: 500 }}>{u.empresa_nombre}</div>
                        <div className="muted k" style={{ fontSize: 11 }}>{u.empresa_ruc || ''}</div></>
                    : (u.rol === 'cliente'
                       ? <span className="muted" style={{ fontSize: 12, color: 'var(--status-pending)' }}>Sin vincular</span>
                       : <span className="muted" style={{ fontSize: 12 }}>—</span>)}
                </td>
                <td className="num" style={{ fontSize: 12 }}>{u.ultimo_login ? window.formatRelativeTime(u.ultimo_login) : 'sin acceder'}</td>
                <td style={{ fontSize: 12 }}>
                  {u.rol === 'vendedor' && Number(u.meta_mensual) > 0
                    ? <span className="num">S/ {Number(u.meta_mensual).toLocaleString('es-PE')} <span className="muted">meta</span></span>
                    : <span className="muted">—</span>}
                </td>
                <td><Pill kind={u.activo ? 'ok' : 'pending'}>{u.activo ? 'Activo' : 'Suspendido'}</Pill></td>
                <td>
                  <div className="row" style={{ gap: 4, justifyContent: 'flex-end' }}>
                    <button className="btn btn--ghost btn--sm" onClick={() => { setEditing({ ...u, activo: !!u.activo }); setErr(''); }}>Editar</button>
                    <button className="btn btn--ghost btn--sm" onClick={() => toggleActivo(u)}>
                      {u.activo ? 'Suspender' : 'Activar'}
                    </button>
                  </div>
                </td>
              </tr>
            ))}
            {!loading && filtered.length === 0 && (
              <tr><td colSpan={7} style={{ padding: 32, textAlign: 'center', color: 'var(--ink-5)' }}>
                {usersAPI.length === 0 ? 'Sin usuarios registrados.' : 'Sin resultados para los filtros.'}
              </td></tr>
            )}
          </tbody>
        </table>
      </div>

      {editing && (
        <div className="modal-overlay" onClick={(e) => { if (e.target === e.currentTarget) setEditing(null); }}>
          <div style={{ width: 540, background: '#fff', borderRadius: 12, boxShadow: '0 30px 80px rgba(0,0,0,0.4)', overflow: 'hidden' }}>
            <div style={{ padding: '18px 22px', borderBottom: '1px solid var(--line)', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
              <div>
                <div style={{ fontSize: 11, color: 'var(--ink-5)', textTransform: 'uppercase', letterSpacing: '0.1em', fontWeight: 600 }}>Usuario</div>
                <div style={{ fontFamily: 'var(--cond)', fontSize: 20, fontWeight: 700, marginTop: 2 }}>Editar · {editing.nombre}</div>
              </div>
              <div className="icon-btn" style={{ fontSize: 18 }} onClick={() => setEditing(null)}>×</div>
            </div>
            <div style={{ padding: 22, maxHeight: '70vh', overflow: 'auto' }}>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
                <div>
                  <label className="label">Apellidos</label>
                  <input className="input" value={editing.apellidos || ''}
                    onChange={e => setEditing({ ...editing, apellidos: e.target.value })}/>
                </div>
                <div>
                  <label className="label">Nombres</label>
                  <input className="input" value={editing.nombres || ''}
                    onChange={e => setEditing({ ...editing, nombres: e.target.value })}/>
                </div>
                <div>
                  <label className="label">Email</label>
                  <input className="input" type="email" value={editing.email || ''}
                    onChange={e => setEditing({ ...editing, email: e.target.value })}/>
                </div>
                <div>
                  <label className="label">Usuario (login)</label>
                  <input className="input k" value={editing.username || ''}
                    placeholder="opcional"
                    onChange={e => setEditing({ ...editing, username: e.target.value })}/>
                </div>
                <div>
                  <label className="label">Teléfono</label>
                  <input className="input k" value={editing.telefono || ''}
                    placeholder="+51 …"
                    onChange={e => setEditing({ ...editing, telefono: e.target.value })}/>
                </div>
                <div>
                  <label className="label">Rol</label>
                  <select className="input" value={editing.rol || 'vendedor'}
                    onChange={e => setEditing({ ...editing, rol: e.target.value, empresa_id: e.target.value === 'cliente' ? editing.empresa_id : null })}>
                    {roleOpts.map(r => <option key={r.slug} value={r.slug}>{r.nombre}</option>)}
                  </select>
                </div>
                <div>
                  <label className="label">Estado</label>
                  <select className="input" value={editing.activo ? '1' : '0'}
                    onChange={e => setEditing({ ...editing, activo: e.target.value === '1' })}>
                    <option value="1">Activo</option>
                    <option value="0">Suspendido</option>
                  </select>
                </div>
                {editing.rol === 'cliente' && (
                  <div style={{ gridColumn: '1 / -1' }}>
                    <label className="label">Empresas asociadas</label>
                    <div className="muted" style={{ fontSize: 11, marginBottom: 6 }}>
                      El cliente podrá elegir entre estas empresas al crear solicitudes.
                      La primera es la principal y se usa por defecto.
                    </div>
                    {editEmpresas.length === 0 ? (
                      <div style={{ padding: 10, background: 'var(--status-pending-bg)', color: 'var(--status-pending)', borderRadius: 6, fontSize: 12 }}>
                        Sin empresas. Agrega al menos una abajo.
                      </div>
                    ) : (
                      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginBottom: 8 }}>
                        {editEmpresas.map((id, idx) => {
                          const emp = empresas.find(e => e.id === id);
                          return (
                            <span key={id} className="chip" style={{ background: idx === 0 ? 'var(--hi-vis)' : 'var(--ink-9)', color: idx === 0 ? 'var(--ink-0)' : 'var(--ink-1)' }}>
                              {idx === 0 && <b style={{ fontSize: 10, marginRight: 4 }}>★</b>}
                              {emp ? emp.razon_social : '#' + id}
                              <span onClick={() => removeEmpresaEdit(id)}
                                style={{ marginLeft: 6, cursor: 'pointer', fontWeight: 700 }}>×</span>
                            </span>
                          );
                        })}
                      </div>
                    )}
                    <div className="row" style={{ gap: 6 }}>
                      <select className="input" value={addEmpresaId} onChange={e => setAddEmpresaId(e.target.value)} style={{ flex: 1 }}>
                        <option value="">— Selecciona empresa para añadir —</option>
                        {empresas.filter(e => !editEmpresas.includes(e.id)).map(e => (
                          <option key={e.id} value={e.id}>{e.razon_social} · RUC {e.ruc}</option>
                        ))}
                      </select>
                      <button className="btn btn--ghost btn--sm" type="button" onClick={addEmpresaEdit} disabled={!addEmpresaId}>+ Añadir</button>
                    </div>
                  </div>
                )}
                {editing.rol === 'vendedor' && (
                  <div style={{ gridColumn: '1 / -1' }}>
                    <label className="label">Meta mensual (S/)</label>
                    <input className="input k" type="number" step="100" value={editing.meta_mensual || 0}
                      onChange={e => setEditing({ ...editing, meta_mensual: e.target.value })}/>
                  </div>
                )}
              </div>

              <div className="muted" style={{ marginTop: 10, fontSize: 12 }}>
                Nombre completo: <b style={{ color: 'var(--ink-1)' }}>{[(editing.nombres || '').trim(), (editing.apellidos || '').trim()].filter(Boolean).join(' ') || '—'}</b>
              </div>

              <div className="divider" style={{ margin: '16px 0' }}/>
              <div className="muted" style={{ fontSize: 11, marginBottom: 6 }}>Información</div>
              <dl className="dl">
                <dt>ID</dt><dd className="k">#{editing.id}</dd>
                <dt>Creado</dt><dd>{editing.created_at ? new Date(editing.created_at).toLocaleString('es-PE') : '—'}</dd>
                <dt>Último login</dt><dd>{editing.ultimo_login ? new Date(editing.ultimo_login).toLocaleString('es-PE') : 'sin acceder'}</dd>
              </dl>

              {err && <div style={{ marginTop: 12, padding: 8, background: 'var(--status-danger-bg, #fef0f0)', color: 'var(--status-danger)', borderRadius: 6, fontSize: 12 }}>{err}</div>}
            </div>
            <div style={{ padding: 16, borderTop: '1px solid var(--line)', display: 'flex', justifyContent: 'space-between', gap: 8 }}>
              <button className="btn btn--ghost btn--sm" onClick={() => toggleActivo(editing)} disabled={saving}>
                {editing.activo ? 'Suspender usuario' : 'Activar usuario'}
              </button>
              <div className="row" style={{ gap: 8 }}>
                <button className="btn btn--ghost" onClick={() => setEditing(null)} disabled={saving}>Cancelar</button>
                <button className="btn btn--accent" onClick={guardarEdicion} disabled={saving}>
                  {Icon.check}<span>{saving ? 'Guardando…' : 'Guardar cambios'}</span>
                </button>
              </div>
            </div>
          </div>
        </div>
      )}

      {/* Política de invitación + sesión */}
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
        <div className="card">
          <div className="card__head"><h3>Política de invitaciones</h3></div>
          <div className="card__body">
            <SettingRow
              label="Restringir dominios al invitar"
              hint="Solo se aceptan correos de los dominios permitidos para roles internos"
              control={<div className="row" style={{ gap: 6, flexWrap: 'wrap', justifyContent: 'flex-end' }}>
                <span className="tag muted">Sin restricciones</span>
                <button className="btn btn--ghost btn--sm">{Icon.plus}</button>
              </div>}/>
            <SettingRow
              label="Caducidad de invitación"
              hint="Si no acepta en este plazo, la invitación expira."
              control={<select className="select-ghost" defaultValue="7"><option value="3">3 días</option><option value="7">7 días</option><option value="14">14 días</option></select>}/>
            <SettingRow
              label="Auto-asignar sucursal por dominio"
              hint="Detecta sucursal desde el correo (ej. @sucursal.tudominio.com → Sur)"
              control={<Toggle on={true}/>}/>
            <SettingRow
              label="Aprobación del Admin para roles sensibles"
              hint="Finanzas y Administrador requieren aprobación adicional"
              control={<Toggle on={true}/>}
              isLast/>
          </div>
        </div>

        <div className="card">
          <div className="card__head"><h3>Bloqueo y sesión</h3></div>
          <div className="card__body">
            <SettingRow
              label="Cierre de sesión por inactividad"
              control={<select className="select-ghost" defaultValue="30"><option value="15">15 min</option><option value="30">30 min</option><option value="60">1 h</option><option value="never">Nunca</option></select>}/>
            <SettingRow
              label="Sesiones simultáneas máximas por usuario"
              control={<select className="select-ghost" defaultValue="3"><option>1</option><option>2</option><option>3</option><option>5</option><option>Ilimitadas</option></select>}/>
            <SettingRow
              label="Bloquear tras N intentos fallidos"
              control={<select className="select-ghost" defaultValue="5"><option>3</option><option>5</option><option>10</option></select>}/>
            <SettingRow
              label="Cerrar todas las sesiones al cambiar contraseña"
              control={<Toggle on={true}/>}
              isLast/>
          </div>
        </div>
      </div>
    </>
  );
}

function SettingRow({ label, hint, control, isLast, danger }) {
  return (
    <div className={`setting-row ${isLast ? 'setting-row--last' : ''} ${danger ? 'setting-row--danger' : ''}`}>
      <div>
        <div style={{ fontWeight: 500, fontSize: 13, color: 'var(--ink-1)' }}>{label}</div>
        {hint && <div className="muted" style={{ fontSize: 12, marginTop: 2, maxWidth: 480 }}>{hint}</div>}
      </div>
      <div>{control}</div>
    </div>
  );
}

// ───────── Sub-pantalla: Organización ─────────
function CfgOrganizacion() {
  const [cfg,    setCfg]    = React.useState({});
  const [edits,  setEdits]  = React.useState({});
  const [saving, setSaving] = React.useState(false);
  const meRol = (DesasfAPI.getCurrentUser() || {}).rol;
  const isDev = meRol === 'dev';
  const canManageOrg = DesasfAPI.hasPerm('config.organizacion');

  React.useEffect(() => {
    DesasfAPI.get('/config').then(setCfg).catch(console.error);
  }, []);

  function field(key, valuesource) {
    return edits[key] !== undefined ? edits[key] : (valuesource ?? cfg[key] ?? '');
  }
  function setField(key, v) {
    setEdits(e => ({ ...e, [key]: v }));
  }

  async function guardar() {
    if (Object.keys(edits).length === 0) return;
    setSaving(true);
    try {
      await DesasfAPI.put('/config', edits);
      const fresh = await DesasfAPI.get('/config');
      setCfg(fresh); setEdits({});
      // Propagate brand changes to runtime globals so sidebar/login update on next render
      fetch('/api/config/apariencia').then(r => r.ok ? r.json() : {}).then(cfg => { window._appConfig = cfg; }).catch(() => {});
    } catch (err) { alert('Error: ' + err.message); }
    finally { setSaving(false); }
  }

  const dirty = Object.keys(edits).length > 0;

  // ── Unidades de venta (editable solo por Dev) ──
  const fv = field('unidades_venta');
  const unidadesList = Array.isArray(fv) ? fv
    : (Array.isArray(cfg.unidades_venta) ? cfg.unidades_venta : []);
  const setUnidades = list => setField('unidades_venta', list);
  const updUnidad   = (i, k, v) => setUnidades(unidadesList.map((u, j) => j === i ? { ...u, [k]: v } : u));
  const addUnidad   = () => setUnidades([...unidadesList, { codigo: '', simbolo: '', nombre: '' }]);
  const removeUnidad = i => setUnidades(unidadesList.filter((_, j) => j !== i));

  return (
    <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
      <div className="card">
        <div className="card__head">
          <h3>Identidad fiscal</h3>
          {canManageOrg && dirty && (
            <button className="btn btn--accent btn--sm" onClick={guardar} disabled={saving}>
              {saving ? 'Guardando…' : 'Guardar cambios'}
            </button>
          )}
          {!canManageOrg && <span className="tag" style={{ fontSize: 11 }}>SOLO LECTURA</span>}
        </div>
        <div className="card__body">
          {!canManageOrg && (
            <div style={{ marginBottom: 14, padding: '8px 12px', background: 'var(--ink-9)', borderRadius: 6, fontSize: 12, color: 'var(--ink-4)' }}>
              Solo <b>Dev / Administrador / Gerente comercial</b> puede modificar los datos de contacto. La razón social, RUC y nombre del panel solo los puede cambiar <b>Dev</b>.
            </div>
          )}
          <div style={{ marginBottom: 12 }}>
            <label className="label">Nombre en el panel</label>
            <input className="input" placeholder="Ej. DESASF" value={field('panel_nombre')}
              onChange={isDev ? (e => setField('panel_nombre', e.target.value)) : undefined}
              readOnly={!isDev}
              style={!isDev ? { opacity: 0.7 } : {}}/>
            <div className="muted" style={{ fontSize: 11, marginTop: 4 }}>Aparece en el sidebar y la pantalla de login {isDev ? '' : '· solo Dev'}</div>
          </div>
          <div style={{ marginBottom: 12 }}>
            <label className="label">Razón social</label>
            <input className="input" value={field('empresa_nombre')}
              onChange={isDev ? (e => setField('empresa_nombre', e.target.value)) : undefined}
              readOnly={!isDev} style={!isDev ? { opacity: 0.7 } : {}}/>
            {!isDev && <div className="muted" style={{ fontSize: 11, marginTop: 4 }}>Solo Dev</div>}
          </div>
          <div style={{ marginBottom: 12 }}>
            <label className="label">RUC</label>
            <input className="input k" value={field('empresa_ruc')}
              onChange={isDev ? (e => setField('empresa_ruc', e.target.value)) : undefined}
              readOnly={!isDev} style={!isDev ? { opacity: 0.7 } : {}}/>
            {!isDev && <div className="muted" style={{ fontSize: 11, marginTop: 4 }}>Solo Dev</div>}
          </div>
          <fieldset disabled={!canManageOrg} style={{ border: 'none', padding: 0, margin: 0, opacity: canManageOrg ? 1 : 0.72 }}>
          <div className="row" style={{ gap: 12 }}>
            <div style={{ flex: 1, marginBottom: 12 }}>
              <label className="label">Teléfono</label>
              <input className="input" value={field('org_telefono')}
                onChange={e => setField('org_telefono', e.target.value)}/>
            </div>
            <div style={{ flex: 1, marginBottom: 12 }}>
              <label className="label">Email corporativo</label>
              <input className="input k" value={field('org_email')}
                onChange={e => setField('org_email', e.target.value)}/>
            </div>
          </div>
          <div style={{ marginBottom: 12 }}>
            <label className="label">Email de notificaciones internas</label>
            <input className="input k" value={field('email_notificaciones')}
              onChange={e => setField('email_notificaciones', e.target.value)}/>
          </div>
          <div style={{ marginBottom: 12 }}>
            <label className="label">Moneda</label>
            <select className="input" value={field('moneda') || 'PEN'}
              onChange={e => setField('moneda', e.target.value)}>
              <option value="PEN">PEN — Soles (S/)</option>
              <option value="USD">USD — Dólares ($)</option>
              <option value="EUR">EUR — Euros (€)</option>
            </select>
          </div>
          </fieldset>
        </div>
      </div>

      {isDev && (
        <div className="card" style={{ borderColor: 'var(--status-review)' }}>
          <div className="card__head">
            <h3>Parámetros del sistema · Dev</h3>
            {dirty && (
              <button className="btn btn--accent btn--sm" onClick={guardar} disabled={saving}>
                {saving ? 'Guardando…' : 'Guardar cambios'}
              </button>
            )}
          </div>
          <div className="card__body">
            <div style={{ marginBottom: 14, padding: '8px 12px', background: 'var(--ink-9)', borderRadius: 6, fontSize: 12, color: 'var(--ink-4)' }}>
              Esta sección solo es visible para el rol <b>Dev</b>. Los valores afectan a todo el sistema y rara vez deben cambiarse.
            </div>
            <div style={{ marginBottom: 12 }}>
              <label className="label">IGV (%)</label>
              <input className="input num" type="number" value={field('igv_porcentaje') || 18}
                onChange={e => setField('igv_porcentaje', e.target.value)}/>
              <div className="muted" style={{ fontSize: 11, marginTop: 4 }}>En Perú es 18%. Solo modificar si hay un cambio normativo.</div>
            </div>

            <div style={{ marginTop: 16, borderTop: '1px solid var(--line)', paddingTop: 14 }}>
              <label className="label">Unidades de venta</label>
              <div className="muted" style={{ fontSize: 11, marginBottom: 10 }}>
                Unidades disponibles al crear productos del catálogo. Quitar una no afecta a los productos que ya la usaban.
              </div>
              {unidadesList.length === 0 && (
                <div className="muted" style={{ fontSize: 12, marginBottom: 8 }}>No hay unidades definidas.</div>
              )}
              {unidadesList.map((u, i) => (
                <div key={i} className="row" style={{ gap: 8, marginBottom: 6, alignItems: 'center' }}>
                  <input className="input k" style={{ width: 96 }} placeholder="código"
                    value={u.codigo || ''} onChange={e => updUnidad(i, 'codigo', e.target.value)}/>
                  <input className="input" style={{ width: 64 }} placeholder="símb."
                    value={u.simbolo || ''} onChange={e => updUnidad(i, 'simbolo', e.target.value)}/>
                  <input className="input" style={{ flex: 1 }} placeholder="nombre"
                    value={u.nombre || ''} onChange={e => updUnidad(i, 'nombre', e.target.value)}/>
                  <button className="btn btn--ghost btn--sm" title="Quitar unidad"
                    onClick={() => removeUnidad(i)}>✕</button>
                </div>
              ))}
              <button className="btn btn--ghost btn--sm" style={{ marginTop: 6 }} onClick={addUnidad}>
                + Agregar unidad
              </button>
              <div className="muted" style={{ fontSize: 11, marginTop: 8 }}>
                El <b>código</b> se guarda en el producto; <b>símbolo</b> y <b>nombre</b> son etiquetas. Recuerda <b>Guardar cambios</b>.
              </div>
            </div>
          </div>
        </div>
      )}

      <div className="card">
        <div className="card__head"><h3>Marca y comunicación</h3></div>
        <div className="card__body">
          <SettingRow label="Vista previa del panel"
            hint="Así se verá el nombre en el sidebar"
            control={
              <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '6px 12px', background: 'var(--sidebar-bg)', borderRadius: 8, minWidth: 160 }}>
                <DesasfLogo size={24}/>
                <div style={{ fontFamily: 'var(--cond)', fontWeight: 700, fontSize: 14, color: '#fff', letterSpacing: '0.02em' }}>
                  {field('panel_nombre') || (window._appConfig?.panel_nombre) || 'DESASF'}
                </div>
              </div>
            }/>
          <SettingRow label="Prefijo de tickets" hint="Ej. SRV-0001"
            control={<div className="row" style={{ gap: 8 }}>
              <input className="input k" style={{ width: 72 }} value={field('ticket_prefijo')}
                onChange={e => setField('ticket_prefijo', e.target.value)}/>
              <span className="muted" style={{ fontSize: 12 }}>- 0001</span>
            </div>}/>
          <SettingRow label="Prefijo de vouchers" hint="Ej. vch-04298"
            control={<input className="input k" style={{ width: 72 }} value={field('voucher_prefijo')}
              onChange={e => setField('voucher_prefijo', e.target.value)}/>}/>
          <SettingRow label="Colores del sistema"
            hint="Ve a la pestaña Preferencias para personalizar colores"
            control={<button className="btn btn--ghost btn--sm" onClick={() => window._cfgSetTab?.('pref')}>Ir a colores →</button>}/>
          <SettingRow label="Idioma del panel" control={<select className="select-ghost" defaultValue="es-pe"><option>Español (Perú)</option><option>Español (LatAm)</option><option>English</option></select>} isLast/>
        </div>
      </div>

      <div className="card" style={{ gridColumn: '1 / -1' }}>
        <div className="card__head"><h3>Sucursales y plantas</h3>
          <button className="btn btn--ghost btn--sm">{Icon.plus}<span>Nueva sede</span></button>
        </div>
        <div className="card__body" style={{ padding: '32px 0', textAlign: 'center', color: 'var(--ink-5)' }}>
          <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" style={{ opacity: 0.4 }}><path d="M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
          <div style={{ marginTop: 10, fontSize: 13 }}>Sin sedes configuradas</div>
          <div className="muted" style={{ fontSize: 11, marginTop: 4 }}>Usa "Nueva sede" para agregar sucursales o plantas</div>
        </div>
      </div>
    </div>
  );
}

function FormField({ label, value, mono, textarea, flex }) {
  return (
    <div style={{ marginBottom: 12, flex: flex ? 1 : undefined }}>
      <label className="label">{label}</label>
      {textarea ? (
        <textarea className="input" rows={2} style={{ height: 'auto', padding: 10, resize: 'vertical', fontFamily: 'inherit' }} defaultValue={value}/>
      ) : (
        <input className="input" defaultValue={value} style={mono ? { fontFamily: 'var(--mono)' } : {}}/>
      )}
    </div>
  );
}

// ───────── Sub-pantalla: Notificaciones ─────────
function CfgNotificaciones() {
  const events = [
    { e: 'Nueva solicitud creada', desc: 'Un cliente o asistente técnico comercial crea una solicitud nueva',
      admin: ['app'], gerente: ['app','mail'], vendedor: ['app','push'], operador: [], finanzas: [], cliente: ['mail'] },
    { e: 'Voucher por validar', desc: 'Cliente carga un comprobante de pago',
      admin: ['app'], gerente: [], vendedor: ['app'], operador: [], finanzas: ['app','mail','push'], cliente: [] },
    { e: 'Despacho en tránsito', desc: 'Camión sale de planta',
      admin: ['app'], gerente: ['app'], vendedor: ['app','push'], operador: ['app'], finanzas: [], cliente: ['app','mail','sms','push'] },
    { e: 'Entrega confirmada', desc: 'Operador o cliente confirma recepción',
      admin: ['app'], gerente: ['app','mail'], vendedor: ['app','push'], operador: ['app'], finanzas: ['app'], cliente: ['app','mail'] },
    { e: 'Pago vencido', desc: 'Factura supera fecha de vencimiento',
      admin: ['app','mail'], gerente: ['app','mail'], vendedor: ['app','push'], operador: [], finanzas: ['app','mail','sms'], cliente: ['mail','sms'] },
    { e: 'Resultado de laboratorio', desc: 'QC publica un ensayo',
      admin: ['app'], gerente: ['app'], vendedor: ['app'], operador: ['app','push'], finanzas: [], cliente: ['mail'] },
    { e: 'Cambios en el catálogo', desc: 'Tarifa o producto modificado',
      admin: ['app','mail'], gerente: ['app','mail'], vendedor: ['app','mail'], operador: ['app'], finanzas: ['app'], cliente: [] },
    { e: 'Inicio de sesión sospechoso', desc: 'IP nueva, dispositivo desconocido o geo-anomalía',
      admin: ['app','mail','sms'], gerente: ['mail'], vendedor: ['mail'], operador: ['mail'], finanzas: ['mail','sms'], cliente: ['mail'] },
  ];
  const channels = ['app','mail','push','sms'];
  const channelLabels = { app: 'In-app', mail: 'Correo', push: 'Push', sms: 'SMS' };
  return (
    <>
      <div className="card" style={{ marginBottom: 16 }}>
        <div className="card__head"><h3>Matriz de notificaciones</h3>
          <span className="meta">qué recibe cada rol y por qué canal</span>
        </div>
        <div className="card__body" style={{ padding: 0 }}>
          <table className="tbl tbl--notif">
            <thead><tr>
              <th style={{ width: '32%' }}>Evento</th>
              {ROLES.filter(r=>r.id!=='lectura').map(r => <th key={r.id} style={{ textAlign: 'center' }}>{r.name.split(' ')[0]}</th>)}
            </tr></thead>
            <tbody>
              {events.map((ev,i)=>(
                <tr key={i}>
                  <td>
                    <div style={{ fontWeight: 600 }}>{ev.e}</div>
                    <div className="muted" style={{ fontSize: 11 }}>{ev.desc}</div>
                  </td>
                  {ROLES.filter(r=>r.id!=='lectura').map(r => (
                    <td key={r.id} style={{ textAlign: 'center' }}>
                      <div className="row" style={{ justifyContent: 'center', gap: 3 }}>
                        {channels.map(ch => {
                          const enabled = (ev[r.id] || []).includes(ch);
                          return (
                            <span key={ch} className={`ch-tag ${enabled ? 'is-on' : ''}`} title={`${channelLabels[ch]}${enabled ? ' · activo' : ' · inactivo'}`}>
                              {ch === 'app' && '●'}
                              {ch === 'mail' && '✉'}
                              {ch === 'push' && '◔'}
                              {ch === 'sms' && '◇'}
                            </span>
                          );
                        })}
                      </div>
                    </td>
                  ))}
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
        <div className="card">
          <div className="card__head"><h3>Canales y proveedores</h3></div>
          <div className="card__body">
            <SettingRow label="Correo saliente" hint="Servidor SMTP de la organización" control={<div className="row" style={{ gap: 8 }}><Pill kind="ok">Conectado</Pill><span className="k" style={{ fontSize: 11 }}>—</span></div>}/>
            <SettingRow label="SMS / WhatsApp" hint="Twilio · solo eventos críticos" control={<div className="row" style={{ gap: 8 }}><Pill kind="ok">Conectado</Pill><span className="k" style={{ fontSize: 11 }}>+51 1 6402930</span></div>}/>
            <SettingRow label="Push móvil" hint="Firebase Cloud Messaging" control={<Pill kind="ok">Conectado</Pill>}/>
            <SettingRow label="Webhooks salientes" hint="Eventos hacia ERP / sistema externo" control={<button className="btn btn--ghost btn--sm">3 webhooks →</button>} isLast/>
          </div>
        </div>

        <div className="card">
          <div className="card__head"><h3>Reglas y silencios</h3></div>
          <div className="card__body">
            <SettingRow label="No notificar fuera de horario" hint="08:00 — 19:00 hora Perú · excepto eventos críticos" control={<Toggle on={true}/>}/>
            <SettingRow label="Agrupar notificaciones similares" hint="Hasta 5 eventos en un solo mensaje, máximo cada 10 min" control={<Toggle on={true}/>}/>
            <SettingRow label="Resumen diario al admin" hint="Email a las 08:00 con KPIs del día anterior" control={<Toggle on={true}/>}/>
            <SettingRow label="Permitir snooze por usuario" hint="Cada persona puede silenciar canales por 1h / 1d" control={<Toggle on={false}/>} isLast/>
          </div>
        </div>
      </div>
    </>
  );
}

// ───────── Sub-pantalla: Integraciones ─────────
function CfgIntegraciones() {
  const integ = [
    { n: 'BCP — Banca empresas',  cat: 'Bancos', st: 'ok',     stl: 'Conectado',   d: 'Lectura de movimientos cada 15 min · sugerencias para revisión manual',   ic: '🏦', col: '#003E7E' },
    { n: 'BBVA — Net Cash',       cat: 'Bancos', st: 'ok',     stl: 'Conectado',   d: 'Lectura de movimientos cada 30 min · sugerencias para revisión manual',   col: '#004481' },
    { n: 'Interbank Empresas',    cat: 'Bancos', st: 'ok',     stl: 'Conectado',   d: 'Lectura de movimientos cada 1 h · sugerencias para revisión manual',      col: '#00B5BD' },
    { n: 'Scotiabank API',        cat: 'Bancos', st: 'review', stl: 'Token vence en 4 d', d: 'Renovar credenciales', col: '#E40520' },
    { n: 'SAP Business One',      cat: 'ERP',    st: 'ok',     stl: 'Conectado',   d: 'Sincroniza facturas y stock cada 10 min', col: '#003F7D' },
    { n: 'SUNAT · facturación',   cat: 'Impuestos', st: 'ok',  stl: 'Conectado',   d: 'OSE: Bizlinks',              col: '#0B6E3E' },
    { n: 'Google Calendar',       cat: 'Productividad', st: 'ok', stl: 'Conectado', d: 'Bloqueos de despacho automático', col: '#34A853' },
    { n: 'Slack',                 cat: 'Productividad', st: 'pending', stl: 'No conectado', d: 'Notificar canal #ops', col: '#4A154B' },
    { n: 'Power BI',              cat: 'Analítica', st: 'ok', stl: 'Conectado',    d: 'Dataset diario a las 02:00', col: '#F2C811' },
    { n: 'Twilio · WhatsApp',     cat: 'Mensajería', st: 'ok', stl: 'Conectado',   d: 'Plantillas aprobadas: 12',   col: '#25D366' },
    { n: 'Looker Studio',         cat: 'Analítica', st: 'pending', stl: 'No conectado', d: 'Conexión vía Service Acc.', col: '#669DF6' },
    { n: 'Webhook personalizado', cat: 'Otros', st: 'neutral', stl: 'Plantilla',   d: 'Crea hooks hacia tu sistema', col: 'var(--ink-3)' },
  ];
  return (
    <>
      <div className="filterbar" style={{ borderRadius: 'var(--r-2)', marginBottom: 14 }}>
        <span className="chip is-active">Todas <span className="n">12</span></span>
        <span className="chip">Bancos <span className="n">4</span></span>
        <span className="chip">ERP <span className="n">1</span></span>
        <span className="chip">Impuestos <span className="n">1</span></span>
        <span className="chip">Analítica <span className="n">2</span></span>
        <span className="chip">Mensajería <span className="n">2</span></span>
        <div style={{ marginLeft: 'auto' }}>
          <button className="btn btn--ghost btn--sm">{Icon.plus}<span>Conectar nueva</span></button>
        </div>
      </div>

      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 14, marginBottom: 18 }}>
        {integ.map(it => (
          <div className="card integ-card" key={it.n}>
            <div className="row" style={{ justifyContent: 'space-between', alignItems: 'flex-start', padding: 14 }}>
              <div style={{ width: 44, height: 44, borderRadius: 8, background: it.col, color: '#fff', display: 'grid', placeItems: 'center', fontFamily: 'var(--cond)', fontWeight: 700, fontSize: 16 }}>
                {it.n.split(' ')[0].slice(0, 2).toUpperCase()}
              </div>
              <Pill kind={it.st}>{it.stl}</Pill>
            </div>
            <div style={{ padding: '0 14px 14px' }}>
              <div style={{ fontWeight: 600, fontSize: 13 }}>{it.n}</div>
              <div className="muted" style={{ fontSize: 11, marginTop: 2 }}>{it.cat}</div>
              <div style={{ fontSize: 12, marginTop: 8, color: 'var(--ink-3)', minHeight: 30 }}>{it.d}</div>
              <div className="row" style={{ marginTop: 10, gap: 6 }}>
                {it.st === 'ok' && <button className="btn btn--ghost btn--sm" style={{ flex: 1 }}>Configurar</button>}
                {it.st === 'review' && <button className="btn btn--accent btn--sm" style={{ flex: 1 }}>Renovar token</button>}
                {it.st === 'pending' && <button className="btn btn--primary btn--sm" style={{ flex: 1 }}>Conectar</button>}
                {it.st === 'neutral' && <button className="btn btn--ghost btn--sm" style={{ flex: 1 }}>Crear hook</button>}
                {it.st === 'ok' && <div className="icon-btn">{Icon.more}</div>}
              </div>
            </div>
          </div>
        ))}
      </div>

      {/* API keys */}
      <div className="card">
        <div className="card__head"><h3>Claves de API</h3>
          <button className="btn btn--ghost btn--sm">{Icon.plus}<span>Nueva clave</span></button>
        </div>
        <div className="card__body" style={{ padding: 0 }}>
          <table className="tbl">
            <thead><tr><th>Etiqueta</th><th>Clave</th><th>Permisos</th><th>Creada</th><th>Último uso</th><th>Estado</th><th></th></tr></thead>
            <tbody>
              {[
                { l: 'integración SAP', k: 'srvsa_live_•••••••aBc8d2', p: 'lectura · escritura', c: '12 ene 2026', u: 'hace 4 min', st: 'ok', stl: 'Activa' },
                { l: 'analítica BI',    k: 'srvsa_live_•••••••99e1xA', p: 'solo lectura',       c: '03 mar 2026', u: 'hace 18 h',  st: 'ok', stl: 'Activa' },
                { l: 'webhook ERP test',k: 'srvsa_test_•••••••22fL90', p: 'webhooks',           c: '20 abr 2026', u: 'hace 2 d',   st: 'review', stl: 'Sandbox' },
                { l: 'consultor (Q1)',  k: 'srvsa_live_•••••••77kZ04', p: 'solo lectura',       c: '08 ene 2026', u: '21 mar',     st: 'pending', stl: 'Caducada' },
              ].map((k,i)=>(
                <tr key={i}>
                  <td><b>{k.l}</b></td>
                  <td className="k">{k.k}</td>
                  <td className="muted" style={{ fontSize: 12 }}>{k.p}</td>
                  <td className="num">{k.c}</td>
                  <td className="num">{k.u}</td>
                  <td><Pill kind={k.st}>{k.stl}</Pill></td>
                  <td>
                    <div className="row" style={{ gap: 4 }}>
                      <button className="btn btn--ghost btn--sm">Rotar</button>
                      <div className="icon-btn">{Icon.more}</div>
                    </div>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>
    </>
  );
}

// ───────── Sub-pantalla: Seguridad ─────────
function CfgSeguridad() {
  const meSeg = DesasfAPI.getCurrentUser() || {};
  const meNombreSeg = meSeg.nombre || 'Usuario';
  const loginTime = new Date().toLocaleTimeString('es-PE', { hour: '2-digit', minute: '2-digit' });
  return (
    <>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16, marginBottom: 16 }}>
        <div className="card">
          <div className="card__head"><h3>Autenticación</h3></div>
          <div className="card__body">
            <SettingRow label="Doble factor (2FA) obligatorio" hint="Aplicar a todos los usuarios internos"
              control={<select className="select-ghost" defaultValue="all"><option value="all">Todos los usuarios</option><option value="admin">Solo Admin + Finanzas</option><option value="off">Opcional</option></select>}/>
            <SettingRow label="Métodos 2FA permitidos" hint="App TOTP siempre habilitado"
              control={<div className="row" style={{ gap: 6 }}>
                <span className="tag tag--on">TOTP</span>
                <span className="tag tag--on">SMS</span>
                <span className="tag">Email</span>
                <span className="tag">Llave física</span>
              </div>}/>
            <SettingRow label="Inicio de sesión único (SSO)" hint="No configurado" control={<div className="row" style={{ gap: 8 }}><Pill kind="pending">Inactivo</Pill><button className="btn btn--ghost btn--sm">Configurar</button></div>}/>
            <SettingRow label="Login con Microsoft 365" control={<Toggle on={false}/>}/>
            <SettingRow label="Forzar cambio de contraseña cada" control={<select className="select-ghost" defaultValue="90"><option>30 d</option><option>60 d</option><option>90 d</option><option>Nunca</option></select>} isLast/>
          </div>
        </div>

        <div className="card">
          <div className="card__head"><h3>Política de contraseña</h3></div>
          <div className="card__body">
            <SettingRow label="Longitud mínima"
              control={<div className="row" style={{ gap: 8 }}><input type="range" min="8" max="24" defaultValue="12" style={{ width: 120, accentColor: 'var(--ink-0)' }}/><span className="k" style={{ minWidth: 26 }}>12</span></div>}/>
            <SettingRow label="Requerir mayúscula y minúscula" control={<Toggle on={true}/>}/>
            <SettingRow label="Requerir número" control={<Toggle on={true}/>}/>
            <SettingRow label="Requerir carácter especial" control={<Toggle on={true}/>}/>
            <SettingRow label="Bloquear contraseñas comunes" hint="Lista de las 10.000 más usadas + nombres del directorio" control={<Toggle on={true}/>}/>
            <SettingRow label="No reutilizar últimas N contraseñas" control={<select className="select-ghost" defaultValue="5"><option>3</option><option>5</option><option>10</option></select>} isLast/>
          </div>
        </div>

        <div className="card">
          <div className="card__head"><h3>Acceso por red</h3></div>
          <div className="card__body">
            <SettingRow label="Restringir IPs (lista blanca)" hint="Solo aceptar conexiones desde rangos de oficinas / VPN"
              control={<div className="row" style={{ gap: 8 }}><Toggle on={true}/><button className="btn btn--ghost btn--sm">3 rangos</button></div>}/>
            <SettingRow label="Bloquear país de origen" hint="Conexiones desde fuera de PE / latam serán bloqueadas"
              control={<div className="row" style={{ gap: 6 }}><span className="tag tag--on">PE</span><span className="tag tag--on">CL</span><span className="tag tag--on">EC</span><button className="btn btn--ghost btn--sm">{Icon.plus}</button></div>}/>
            <SettingRow label="Detectar dispositivos nuevos" hint="Avisar al usuario y al admin ante cada equipo nuevo" control={<Toggle on={true}/>}/>
            <SettingRow label="Auto-bloquear ante fuerza bruta" control={<Toggle on={true}/>} isLast/>
          </div>
        </div>

        <div className="card">
          <div className="card__head"><h3>Datos y privacidad</h3></div>
          <div className="card__body">
            <SettingRow label="Encriptación en reposo" hint="AES-256 en base de datos y backups" control={<Pill kind="ok">Activa</Pill>}/>
            <SettingRow label="Retención de logs" control={<select className="select-ghost" defaultValue="365"><option>90 d</option><option>180 d</option><option value="365">365 d</option><option>3 años</option></select>}/>
            <SettingRow label="Retención de datos de cliente final" hint="Tras inactividad de 24 m, anonimización automática (LPDP)"
              control={<Toggle on={true}/>}/>
            <SettingRow label="Exportar todos los datos (GDPR / LPDP)" control={<button className="btn btn--ghost btn--sm">{Icon.download}<span>Generar exporte</span></button>}/>
            <SettingRow label="Eliminar organización" hint="Acción irreversible. Solo super-admin." control={<button className="btn btn--ghost btn--sm" style={{ color: 'var(--status-danger)', borderColor: 'var(--status-danger-bg)' }}>Eliminar…</button>} isLast danger/>
          </div>
        </div>
      </div>

      {/* Sesiones activas */}
      <div className="card">
        <div className="card__head"><h3>Sesiones activas</h3>
          <button className="btn btn--ghost btn--sm" style={{ color: 'var(--status-danger)' }}>Cerrar todas excepto esta</button>
        </div>
        <div className="card__body" style={{ padding: 0 }}>
          <table className="tbl">
            <thead><tr><th>Usuario</th><th>Dispositivo</th><th>IP / ubicación</th><th>Inicio</th><th>Última actividad</th><th></th></tr></thead>
            <tbody>
              <tr style={{ background: 'rgba(255,212,0,0.06)' }}>
                <td><b>{meNombreSeg} (tú)</b><span className="tag" style={{ marginLeft: 8 }}>ESTA SESIÓN</span></td>
                <td>Navegador web</td>
                <td className="k" style={{ fontSize: 12 }}>—</td>
                <td className="num">{loginTime}</td>
                <td className="num">ahora</td>
                <td></td>
              </tr>
            </tbody>
          </table>
          <div className="muted" style={{ fontSize: 11, padding: '8px 16px' }}>El sistema no registra sesiones concurrentes en esta versión.</div>
        </div>
      </div>
    </>
  );
}

// ───────── Helper: color picker field ─────────
function ColorField({ label, hint, value, onChange }) {
  const [text, setText] = React.useState(value);
  React.useEffect(() => setText(value), [value]);
  return (
    <div>
      <div style={{ fontWeight: 500, fontSize: 13, color: 'var(--ink-1)', marginBottom: 2 }}>{label}</div>
      {hint && <div className="muted" style={{ fontSize: 11, marginBottom: 8 }}>{hint}</div>}
      <div className="row" style={{ gap: 10, alignItems: 'center' }}>
        <label style={{ position: 'relative', cursor: 'pointer', flexShrink: 0 }}>
          <div style={{ width: 44, height: 44, borderRadius: 8, background: value,
            border: '2px solid var(--line-strong)',
            boxShadow: '0 0 0 1px rgba(0,0,0,.08) inset' }}/>
          <input type="color" value={value} onChange={e => onChange(e.target.value)}
            style={{ position: 'absolute', inset: 0, opacity: 0, width: '100%', height: '100%', cursor: 'pointer' }}/>
        </label>
        <input className="input" value={text} maxLength={7} placeholder="#000000"
          onChange={e => { const v = e.target.value; setText(v); if (/^#[0-9a-fA-F]{6}$/.test(v)) onChange(v); }}
          style={{ width: 88, fontFamily: 'var(--mono)', fontSize: 12, letterSpacing: 1 }}/>
      </div>
    </div>
  );
}

// ───────── Sub-pantalla: Apariencia ─────────
function CfgApariencia() {
  const u      = DesasfAPI.getCurrentUser() || {};
  const isDev  = u.rol === 'dev';
  const isAdmin = isDev; // legacy alias kept for references below

  const DEFAULTS = { color_primario: '#FFD400', color_acento: '#FF5A1F', color_sidebar: '#0B0D0E' };
  const PRESETS = [
    { name: 'Default Desasf', primario: '#FFD400', acento: '#FF5A1F', sidebar: '#0B0D0E' },
    { name: 'Azul corp.',    primario: '#3B82F6', acento: '#1D4ED8', sidebar: '#0D1B2E' },
    { name: 'Verde',         primario: '#22C55E', acento: '#16A34A', sidebar: '#0A1A0E' },
    { name: 'Rojo señal',    primario: '#EF4444', acento: '#DC2626', sidebar: '#1C0808' },
    { name: 'Antracita',     primario: '#94A3B8', acento: '#475569', sidebar: '#1E293B' },
  ];

  const [saved,   setSaved]   = React.useState({ ...DEFAULTS });
  const [colors,  setColors]  = React.useState({ ...DEFAULTS });
  const [saving,  setSaving]  = React.useState(false);
  const [toast,   setToast]   = React.useState(null);

  React.useEffect(() => {
    if (!isAdmin) return;
    DesasfAPI.get('/config').then(cfg => {
      const c = {
        color_primario: cfg.color_primario || DEFAULTS.color_primario,
        color_acento:   cfg.color_acento   || DEFAULTS.color_acento,
        color_sidebar:  cfg.color_sidebar  || DEFAULTS.color_sidebar,
      };
      setSaved(c); setColors(c);
    }).catch(console.error);
  }, []);

  function applyLive(c) {
    const dk = window.darkenHex || (h => h);
    const root = document.documentElement;
    root.style.setProperty('--hi-vis',      c.color_primario);
    root.style.setProperty('--hi-vis-deep', dk(c.color_primario, 24));
    root.style.setProperty('--signal',      c.color_acento);
    root.style.setProperty('--signal-deep', dk(c.color_acento, 20));
    root.style.setProperty('--sidebar-bg',  c.color_sidebar);
  }

  function setColor(key, val) {
    const next = { ...colors, [key]: val };
    setColors(next);
    applyLive(next);
  }

  function applyPreset(p) {
    const next = { color_primario: p.primario, color_acento: p.acento, color_sidebar: p.sidebar };
    setColors(next);
    applyLive(next);
  }

  async function guardar() {
    setSaving(true);
    try {
      await DesasfAPI.put('/config', colors);
      setSaved({ ...colors });
      setToast('Cambios guardados');
      setTimeout(() => setToast(null), 3000);
    } catch (err) { alert('Error: ' + err.message); }
    finally { setSaving(false); }
  }

  function descartar() {
    setColors({ ...saved });
    applyLive(saved);
  }

  const dirty = JSON.stringify(colors) !== JSON.stringify(saved);

  const userName = u.nombre ? u.nombre.trim().split(/\s+/)[0] : 'tú';

  return (
    <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>

      {/* ── Sistema de colores — solo admin ── */}
      {isAdmin && (
        <div className="card" style={{ gridColumn: '1 / -1' }}>
          <div className="card__head">
            <h3>Colores del sistema</h3>
            <span className="tag" style={{ background: 'var(--hi-vis)', color: 'var(--ink-0)', borderColor: 'transparent' }}>Aplica a todos los usuarios</span>
            {toast && <span style={{ fontSize: 12, color: 'var(--status-ok)', marginLeft: 8 }}>✓ {toast}</span>}
            <div className="row" style={{ gap: 8, marginLeft: 'auto' }}>
              {dirty && <button className="btn btn--ghost btn--sm" onClick={descartar}>Descartar</button>}
              <button className="btn btn--accent btn--sm" onClick={guardar} disabled={saving || !dirty}>
                {saving ? 'Guardando…' : dirty ? 'Guardar cambios' : 'Sin cambios'}
              </button>
            </div>
          </div>
          <div className="card__body">

            {/* Color pickers */}
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 24, marginBottom: 24 }}>
              <ColorField label="Color principal" hint="Marca, badge activo, barra KPI"
                value={colors.color_primario} onChange={v => setColor('color_primario', v)}/>
              <ColorField label="Color de acento" hint="Botón primario, CTAs destacadas"
                value={colors.color_acento} onChange={v => setColor('color_acento', v)}/>
              <ColorField label="Color del sidebar" hint="Fondo de la barra de navegación"
                value={colors.color_sidebar} onChange={v => setColor('color_sidebar', v)}/>
            </div>

            {/* Presets */}
            <div style={{ marginBottom: 24 }}>
              <div className="label" style={{ marginBottom: 10 }}>Temas predefinidos</div>
              <div className="row" style={{ gap: 14, flexWrap: 'wrap' }}>
                {PRESETS.map(p => {
                  const isActive = colors.color_primario === p.primario && colors.color_acento === p.acento;
                  return (
                    <div key={p.name} onClick={() => applyPreset(p)}
                      style={{ cursor: 'pointer', display: 'flex', flexDirection: 'column', gap: 6, alignItems: 'center' }}>
                      <div style={{
                        display: 'flex', borderRadius: 8, overflow: 'hidden',
                        border: isActive ? '2px solid var(--ink-0)' : '2px solid var(--line-strong)',
                        boxShadow: isActive ? 'var(--shadow-md)' : 'none',
                      }}>
                        <div style={{ width: 28, height: 44, background: p.sidebar, flexShrink: 0 }}/>
                        <div style={{ width: 52, height: 44, display: 'flex', flexDirection: 'column' }}>
                          <div style={{ flex: 1, background: p.primario }}/>
                          <div style={{ flex: 1, background: p.acento }}/>
                        </div>
                      </div>
                      <span style={{ fontSize: 11, color: isActive ? 'var(--ink-0)' : 'var(--ink-5)', fontWeight: isActive ? 600 : 400 }}>{p.name}</span>
                    </div>
                  );
                })}
              </div>
            </div>

            {/* Live mini preview */}
            <div style={{ borderTop: '1px solid var(--line)', paddingTop: 18 }}>
              <div className="label" style={{ marginBottom: 10 }}>Vista previa en vivo</div>
              <div style={{ display: 'flex', borderRadius: 10, overflow: 'hidden', border: '1px solid var(--line-strong)', width: 440, boxShadow: 'var(--shadow-md)' }}>
                {/* mini sidebar */}
                <div style={{ width: 110, background: colors.color_sidebar, padding: '12px 8px', display: 'flex', flexDirection: 'column', gap: 5 }}>
                  <div className="row" style={{ gap: 5, marginBottom: 8 }}>
                    <div style={{ width: 16, height: 16, borderRadius: 4, background: colors.color_primario, flexShrink: 0 }}/>
                    <span style={{ fontSize: 8, fontWeight: 700, color: '#fff', fontFamily: 'var(--cond)', letterSpacing: '0.08em' }}>{window._appConfig?.panel_nombre || 'DESASF'}</span>
                  </div>
                  {['Dashboard', 'Solicitudes', 'Pagos', 'Empresas'].map((lbl, i) => (
                    <div key={lbl} style={{
                      fontSize: 9, padding: '4px 7px', borderRadius: 4,
                      color:       i === 0 ? colors.color_sidebar : 'rgba(255,255,255,0.55)',
                      background:  i === 0 ? colors.color_primario : 'transparent',
                      fontWeight:  i === 0 ? 600 : 400,
                    }}>{lbl}</div>
                  ))}
                </div>
                {/* mini content */}
                <div style={{ flex: 1, background: '#F2F4F5', padding: 12 }}>
                  <div style={{ height: 8, background: 'var(--ink-0)', borderRadius: 3, width: '55%', marginBottom: 10 }}/>
                  <div className="row" style={{ gap: 6, marginBottom: 10 }}>
                    {[1,2,3].map(i => (
                      <div key={i} style={{ flex: 1, height: 32, background: '#fff', borderRadius: 5, border: '1px solid var(--line)',
                        borderTop: i === 1 ? `2px solid ${colors.color_primario}` : '1px solid var(--line)' }}/>
                    ))}
                  </div>
                  <div className="row" style={{ gap: 6 }}>
                    <div style={{ height: 24, background: colors.color_acento, borderRadius: 4, padding: '0 10px', display: 'flex', alignItems: 'center' }}>
                      <span style={{ fontSize: 9, fontWeight: 600, color: '#fff' }}>+ Crear</span>
                    </div>
                    <div style={{ height: 24, background: colors.color_primario, borderRadius: 4, padding: '0 10px', display: 'flex', alignItems: 'center' }}>
                      <span style={{ fontSize: 9, fontWeight: 700, color: colors.color_sidebar }}>Guardar</span>
                    </div>
                    <div style={{ height: 24, background: 'var(--ink-8)', borderRadius: 4, padding: '0 10px', display: 'flex', alignItems: 'center' }}>
                      <span style={{ fontSize: 9, fontWeight: 500, color: 'var(--ink-3)' }}>Cancelar</span>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      )}

      {/* ── Apariencia personal ── */}
      <div className="card">
        <div className="card__head"><h3>Apariencia del panel</h3></div>
        <div className="card__body">
          <SettingRow label="Tema" hint="Aplica a tu cuenta solamente"
            control={<div className="row" style={{ gap: 6 }}>
              <span className="tema-pick is-on" style={{ background: '#fff', color: 'var(--ink-0)' }}>Claro</span>
              <span className="tema-pick" style={{ background: 'var(--ink-0)', color: '#fff' }}>Oscuro</span>
              <span className="tema-pick">Auto</span>
            </div>}/>
          <SettingRow label="Densidad de tablas" control={<div className="row" style={{ gap: 6 }}><span className="tema-pick is-on">Cómoda</span><span className="tema-pick">Compacta</span></div>}/>
          <SettingRow label="Ancho de panel" control={<div className="row" style={{ gap: 6 }}><span className="tema-pick">Estrecho</span><span className="tema-pick is-on">Estándar</span><span className="tema-pick">Pantalla completa</span></div>}/>
          <SettingRow label="Mostrar ID de registros" hint="SRV-2840, vch-04298 visibles en listados" control={<Toggle on={true}/>}/>
          <SettingRow label="Mostrar franjas de obra (decorativo)" control={<Toggle on={true}/>} isLast/>
        </div>
      </div>

      <div className="card">
        <div className="card__head"><h3>Formato regional</h3></div>
        <div className="card__body">
          <SettingRow label="Idioma" control={<select className="select-ghost" defaultValue="es-pe"><option>Español (Perú)</option><option>Español (LatAm)</option><option>English</option><option>Português</option></select>}/>
          <SettingRow label="Zona horaria" control={<select className="select-ghost" defaultValue="lima"><option value="lima">América/Lima — GMT−5</option><option>América/Bogotá</option><option>América/Santiago</option></select>}/>
          <SettingRow label="Moneda principal" hint="Para reportes consolidados"
            control={<select className="select-ghost" defaultValue="pen"><option value="pen">PEN — Soles</option><option>USD — Dólares</option><option>Multimoneda</option></select>}/>
          <SettingRow label="Formato de fecha" control={<div className="row" style={{ gap: 6 }}><span className="tema-pick is-on">DD/MM/YYYY</span><span className="tema-pick">YYYY-MM-DD</span><span className="tema-pick">D MMM YYYY</span></div>}/>
          <SettingRow label="Sistema de unidades" control={<div className="row" style={{ gap: 6 }}><span className="tema-pick is-on">Métrico (m³, t)</span><span className="tema-pick">Imperial</span></div>} isLast/>
        </div>
      </div>

      <div className="card" style={{ gridColumn: '1 / -1' }}>
        <div className="card__head">
          <h3>Preferencias de mi cuenta</h3>
          <span className="meta">solo aplica a {userName}</span>
        </div>
        <div className="card__body">
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '0 32px' }}>
            <SettingRow label="Pantalla de inicio" control={<select className="select-ghost" defaultValue="dash"><option value="dash">Dashboard</option><option>Solicitudes</option><option>Pagos</option></select>}/>
            <SettingRow label="Recordar último filtro usado" control={<Toggle on={true}/>}/>
            <SettingRow label="Atajos de teclado" hint="⌘K · g+s · g+p" control={<Toggle on={true}/>}/>
            <SettingRow label="Sonidos del panel" hint="Confirmaciones, alertas críticas" control={<Toggle on={false}/>}/>
            <SettingRow label="Movimiento reducido" hint="Desactiva animaciones decorativas" control={<Toggle on={false}/>}/>
            <SettingRow label="Vista de calendario" control={<div className="row" style={{ gap: 6 }}><span className="tema-pick is-on">Semana</span><span className="tema-pick">Mes</span><span className="tema-pick">Agenda</span></div>}/>
          </div>
        </div>
      </div>
    </div>
  );
}

// ───────── Sub-pantalla: Empresas ─────────
function CfgEmpresas() {
  const [empresas, setEmpresas] = React.useState([]);
  const [vendedores, setVendedores] = React.useState([]);
  const [loading, setLoading]   = React.useState(true);
  const [search, setSearch]     = React.useState('');
  const [editing, setEditing]   = React.useState(null);    // empresa actual o {} para nueva
  const [usuariosVinc, setUsuariosVinc] = React.useState([]); // de la empresa en edición
  const [clientesLibres, setClientesLibres] = React.useState([]);
  const [saving, setSaving]     = React.useState(false);
  const [err,    setErr]        = React.useState('');

  function recargar() {
    setLoading(true);
    Promise.all([
      DesasfAPI.get('/clientes?limit=300'),
      DesasfAPI.get('/config/usuarios?rol=vendedor').catch(() => []),
    ]).then(([emps, vends]) => {
      setEmpresas(emps.rows || []);
      setVendedores(Array.isArray(vends) ? vends : []);
    }).catch(e => setErr(e.message))
      .finally(() => setLoading(false));
  }
  React.useEffect(recargar, []);

  function openEdit(emp) {
    setErr('');
    setEditing(emp || {
      razon_social: '', ruc: '', direccion: '', sector: '',
      tamano: '', tipo_cliente: 'estandar', credito_limite: 0, vendedor_id: '',
    });
    if (emp?.id) {
      Promise.all([
        DesasfAPI.get(`/clientes/${emp.id}/usuarios`).catch(() => []),
        DesasfAPI.get('/config/usuarios?rol=cliente&empresa_id=null').catch(() => []),
      ]).then(([vinc, libres]) => {
        setUsuariosVinc(Array.isArray(vinc) ? vinc : []);
        setClientesLibres(Array.isArray(libres) ? libres : []);
      });
    } else {
      setUsuariosVinc([]); setClientesLibres([]);
    }
  }

  async function guardar() {
    if (!editing.razon_social?.trim() || !editing.ruc?.trim()) {
      setErr('Razón social y RUC son obligatorios.'); return;
    }
    if (!/^\d{11}$/.test(editing.ruc.trim())) {
      setErr('El RUC debe tener exactamente 11 dígitos.'); return;
    }
    setErr(''); setSaving(true);
    try {
      const payload = {
        razon_social:   editing.razon_social.trim(),
        ruc:            editing.ruc.trim(),
        direccion:      editing.direccion || null,
        sector:         editing.sector || null,
        tamano:         editing.tamano || null,
        tipo_cliente:   editing.tipo_cliente || 'estandar',
        credito_limite: Number(editing.credito_limite) || 0,
        vendedor_id:    editing.vendedor_id ? Number(editing.vendedor_id) : null,
      };
      if (editing.id) {
        await DesasfAPI.put('/clientes/' + editing.id, payload);
      } else {
        const r = await DesasfAPI.post('/clientes', payload);
        editing.id = r.id;
      }
      recargar();
      if (editing.id) {
        const [vinc, libres] = await Promise.all([
          DesasfAPI.get(`/clientes/${editing.id}/usuarios`).catch(() => []),
          DesasfAPI.get('/config/usuarios?rol=cliente&empresa_id=null').catch(() => []),
        ]);
        setUsuariosVinc(Array.isArray(vinc) ? vinc : []);
        setClientesLibres(Array.isArray(libres) ? libres : []);
      }
    } catch (e) {
      setErr(e.message || 'No se pudo guardar.');
    } finally { setSaving(false); }
  }

  async function eliminar(emp) {
    if (!confirm(`¿Desactivar la empresa "${emp.razon_social}"?\nLos usuarios vinculados perderán acceso. Esta acción se puede revertir contactando soporte.`)) return;
    try {
      await DesasfAPI.delete('/clientes/' + emp.id);
      recargar();
      if (editing?.id === emp.id) setEditing(null);
    } catch (e) { alert('Error: ' + e.message); }
  }

  async function vincularUsuario(userId) {
    if (!editing?.id || !userId) return;
    try {
      await DesasfAPI.put('/config/usuarios/' + userId, { empresa_id: editing.id });
      const [vinc, libres] = await Promise.all([
        DesasfAPI.get(`/clientes/${editing.id}/usuarios`).catch(() => []),
        DesasfAPI.get('/config/usuarios?rol=cliente&empresa_id=null').catch(() => []),
      ]);
      setUsuariosVinc(Array.isArray(vinc) ? vinc : []);
      setClientesLibres(Array.isArray(libres) ? libres : []);
      recargar();
    } catch (e) { alert('Error: ' + e.message); }
  }

  async function desvincularUsuario(userId) {
    if (!editing?.id || !userId) return;
    if (!confirm('¿Desvincular este cliente de la empresa?')) return;
    try {
      await DesasfAPI.put('/config/usuarios/' + userId, { empresa_id: null });
      const [vinc, libres] = await Promise.all([
        DesasfAPI.get(`/clientes/${editing.id}/usuarios`).catch(() => []),
        DesasfAPI.get('/config/usuarios?rol=cliente&empresa_id=null').catch(() => []),
      ]);
      setUsuariosVinc(Array.isArray(vinc) ? vinc : []);
      setClientesLibres(Array.isArray(libres) ? libres : []);
      recargar();
    } catch (e) { alert('Error: ' + e.message); }
  }

  const filtered = empresas.filter(e =>
    !search.trim()
    || e.razon_social.toLowerCase().includes(search.toLowerCase())
    || (e.ruc || '').includes(search)
  );

  return (
    <div style={{ display: 'grid', gridTemplateColumns: editing ? '1.2fr 1fr' : '1fr', gap: 16 }}>
      <div className="card">
        <div className="card__head">
          <h3>Empresas registradas</h3>
          <div className="row" style={{ gap: 8 }}>
            <input className="input" placeholder="Buscar por nombre o RUC…"
              value={search} onChange={e => setSearch(e.target.value)}
              style={{ height: 30, fontSize: 12, width: 220 }}/>
            <button className="btn btn--accent btn--sm" onClick={() => openEdit(null)}>
              {Icon.plus}<span>Nueva empresa</span>
            </button>
          </div>
        </div>
        <div className="card__body" style={{ padding: 0, maxHeight: 600, overflow: 'auto' }}>
          {loading
            ? <div style={{ padding: 24, color: 'var(--ink-5)', textAlign: 'center' }}>Cargando…</div>
            : (
              <table className="tbl">
                <thead><tr>
                  <th>Razón social</th><th>RUC</th><th>Sector</th>
                  <th>Asist. téc. comercial</th><th>Empresas</th><th></th>
                </tr></thead>
                <tbody>
                  {filtered.map(e => (
                    <tr key={e.id} style={{ background: editing?.id === e.id ? 'var(--ink-9)' : '#fff' }}>
                      <td><b>{e.razon_social}</b></td>
                      <td className="k">{e.ruc}</td>
                      <td>{e.sector || '—'}</td>
                      <td className="muted">{e.vendedor_nombre || 'Sin asignar'}</td>
                      <td className="num">{e.total_solicitudes || 0}</td>
                      <td style={{ textAlign: 'right' }}>
                        <button className="btn btn--ghost btn--sm" onClick={() => openEdit(e)}>Editar</button>
                        <button className="btn btn--ghost btn--sm" onClick={() => eliminar(e)}
                          style={{ color: 'var(--status-danger)' }}>{Icon.trash || '×'}</button>
                      </td>
                    </tr>
                  ))}
                  {!filtered.length && (
                    <tr><td colSpan={6} style={{ padding: 24, textAlign: 'center', color: 'var(--ink-5)' }}>
                      Sin empresas.
                    </td></tr>
                  )}
                </tbody>
              </table>
            )}
        </div>
      </div>

      {editing && (
        <div className="card">
          <div className="card__head">
            <h3>{editing.id ? `Editar · ${editing.razon_social}` : 'Nueva empresa'}</h3>
            <div className="icon-btn" onClick={() => setEditing(null)} style={{ fontSize: 18 }}>×</div>
          </div>
          <div className="card__body" style={{ maxHeight: 600, overflow: 'auto' }}>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
              <div>
                <label className="label">Razón social</label>
                <input className="input" value={editing.razon_social || ''}
                  onChange={e => setEditing({ ...editing, razon_social: e.target.value })}/>
              </div>
              <div>
                <label className="label">RUC</label>
                <input className="input k" maxLength={11} value={editing.ruc || ''}
                  onChange={e => setEditing({ ...editing, ruc: e.target.value.replace(/\D/g, '').slice(0, 11) })}/>
              </div>
            </div>
            <label className="label" style={{ marginTop: 10 }}>Dirección</label>
            <input className="input" value={editing.direccion || ''}
              onChange={e => setEditing({ ...editing, direccion: e.target.value })}/>

            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10, marginTop: 10 }}>
              <div>
                <label className="label">Sector</label>
                <input className="input" value={editing.sector || ''}
                  onChange={e => setEditing({ ...editing, sector: e.target.value })}/>
              </div>
              <div>
                <label className="label">Tamaño</label>
                <select className="input" value={editing.tamano || ''}
                  onChange={e => setEditing({ ...editing, tamano: e.target.value })}>
                  <option value="">—</option>
                  <option value="micro">Micro</option>
                  <option value="pequena">Pequeña</option>
                  <option value="mediana">Mediana</option>
                  <option value="grande">Grande</option>
                </select>
              </div>
            </div>

            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 10, marginTop: 10 }}>
              <div>
                <label className="label">Tipo</label>
                <select className="input" value={editing.tipo_cliente || 'estandar'}
                  onChange={e => setEditing({ ...editing, tipo_cliente: e.target.value })}>
                  <option value="estandar">Estándar</option>
                  <option value="premium">Premium</option>
                </select>
              </div>
              <div>
                <label className="label">Crédito (S/)</label>
                <input className="input k" type="number" step="100" value={editing.credito_limite || 0}
                  onChange={e => setEditing({ ...editing, credito_limite: e.target.value })}/>
                <div className="muted" style={{ fontSize: 11, marginTop: 4 }}>0 = crédito ilimitado</div>
              </div>
              <div>
                <label className="label">Asistente Técnico comercial</label>
                <select className="input" value={editing.vendedor_id || ''}
                  onChange={e => setEditing({ ...editing, vendedor_id: e.target.value })}>
                  <option value="">Sin asignar</option>
                  {vendedores.map(v => <option key={v.id} value={v.id}>{v.nombre}</option>)}
                </select>
              </div>
            </div>

            {err && <div style={{ marginTop: 10, padding: 8, background: 'var(--status-danger-bg, #fef0f0)', color: 'var(--status-danger)', borderRadius: 6, fontSize: 12 }}>{err}</div>}

            <div className="row" style={{ marginTop: 14, justifyContent: 'flex-end', gap: 8 }}>
              <button className="btn btn--ghost btn--sm" onClick={() => setEditing(null)} disabled={saving}>Cancelar</button>
              <button className="btn btn--accent btn--sm" onClick={guardar} disabled={saving}>
                {saving ? 'Guardando…' : (editing.id ? 'Guardar cambios' : 'Crear empresa')}
              </button>
            </div>

            {editing.id && (
              <>
                <div className="divider" style={{ margin: '16px 0' }}/>
                <div className="row" style={{ justifyContent: 'space-between', marginBottom: 8 }}>
                  <h4 style={{ margin: 0, fontFamily: 'var(--cond)', fontSize: 14, fontWeight: 700 }}>Empresas vinculadas</h4>
                  <span className="meta">{usuariosVinc.length} usuario{usuariosVinc.length === 1 ? '' : 's'}</span>
                </div>
                <div style={{ border: '1px solid var(--line)', borderRadius: 6, overflow: 'hidden' }}>
                  {usuariosVinc.length === 0
                    ? <div style={{ padding: 12, color: 'var(--ink-5)', fontSize: 12, textAlign: 'center' }}>Sin usuarios vinculados aún.</div>
                    : usuariosVinc.map(u => (
                      <div key={u.id} style={{ padding: '8px 10px', display: 'flex', alignItems: 'center', gap: 10, borderBottom: '1px solid var(--line)' }}>
                        <div style={{ width: 28, height: 28, borderRadius: 5, background: 'var(--ink-2)', color: '#fff', display: 'grid', placeItems: 'center', fontSize: 11, fontWeight: 700 }}>
                          {(u.nombre || '?').slice(0, 2).toUpperCase()}
                        </div>
                        <div style={{ flex: 1 }}>
                          <div style={{ fontSize: 13, fontWeight: 600 }}>{u.nombre}</div>
                          <div className="muted k" style={{ fontSize: 11 }}>{u.email} · {u.rol}{u.activo ? '' : ' · inactivo'}</div>
                        </div>
                        <div className="icon-btn" title="Desvincular" onClick={() => desvincularUsuario(u.id)}
                          style={{ color: 'var(--status-danger)' }}>×</div>
                      </div>
                    ))}
                </div>

                <div style={{ marginTop: 12, padding: 10, background: 'var(--ink-9)', borderRadius: 6 }}>
                  <div className="muted" style={{ fontSize: 11, marginBottom: 6 }}>Vincular cliente existente sin empresa</div>
                  <div className="row" style={{ gap: 8 }}>
                    <select id="vinc-libre" className="input" style={{ flex: 1, height: 30, fontSize: 12 }}>
                      <option value="">— Selecciona usuario cliente —</option>
                      {clientesLibres.map(c => <option key={c.id} value={c.id}>{c.nombre} ({c.email})</option>)}
                    </select>
                    <button className="btn btn--ghost btn--sm" onClick={() => {
                      const sel = document.getElementById('vinc-libre');
                      if (sel?.value) { vincularUsuario(Number(sel.value)); sel.value = ''; }
                    }}>{Icon.plus}<span>Vincular</span></button>
                  </div>
                  {clientesLibres.length === 0 && (
                    <div className="muted" style={{ fontSize: 11, marginTop: 6 }}>
                      No hay clientes sin empresa. Crea un usuario rol "cliente" desde la pestaña "Usuarios" para vincularlo aquí.
                    </div>
                  )}
                </div>
              </>
            )}
          </div>
        </div>
      )}
    </div>
  );
}

// ───────── Pantalla principal ─────────
function Configuracion({ initialTab = 'roles' }) {
  const hasPerm = DesasfAPI.hasPerm;
  const meRol   = (DesasfAPI.getCurrentUser() || {}).rol;
  // Cada pestaña se muestra solo si el rol tiene el permiso que sus endpoints
  // exigen; sin permiso, la pestaña no aparece.
  const tabs = [
    hasPerm('config.roles.editar') &&
      { id: 'roles',   label: 'Roles y permisos',  count: 7,  icon: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7"><path d="M12 2l9 4-9 4-9-4 9-4z"/><path d="M3 10l9 4 9-4M3 16l9 4 9-4"/></svg> },
    hasPerm('config.usuarios.ver') &&
      { id: 'usuarios', label: 'Usuarios',          count: 25, icon: Icon.users },
    hasPerm('config.organizacion') &&
      { id: 'org',      label: 'Organización',      count: 5,  icon: Icon.building },
    hasPerm('config.ver', { strict: true }) &&
      { id: 'notif',    label: 'Notificaciones',    icon: Icon.bell },
    hasPerm('config.ver', { strict: true }) &&
      { id: 'integ',    label: 'Integraciones · API', count: 12, icon: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7"><path d="M14 6l6 6-6 6M10 18l-6-6 6-6"/></svg> },
    hasPerm('config.ver', { strict: true }) &&
      { id: 'seg',      label: 'Seguridad',         icon: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7"><path d="M12 2l8 4v6c0 5-3.5 9-8 10-4.5-1-8-5-8-10V6l8-4z"/><path d="M9 12l2 2 4-4"/></svg> },
    meRol === 'dev' &&
      { id: 'pref',     label: 'Apariencia',        icon: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7"><circle cx="12" cy="12" r="9"/><path d="M12 3v9l6 3"/></svg> },
  ].filter(Boolean);

  const defaultTab = tabs.some(x => x.id === initialTab) ? initialTab : (tabs[0]?.id || null);
  const [tab, setTab] = React.useState(defaultTab);
  React.useEffect(() => {
    window._cfgSetTab = id => { if (tabs.some(x => x.id === id)) setTab(id); };
    return () => { delete window._cfgSetTab; };
  }, []);

  // Sin ninguna pestaña visible no hay nada que configurar aquí — volver al inicio.
  React.useEffect(() => { if (!tabs.length) navigate('dashboard'); }, []);
  if (!tabs.length) return null;

  const t = tabs.find(x => x.id === tab) || tabs[0];

  return (
    <div className="app">
      <Sidebar active="config"/>
      <Topbar crumbs={['Información', 'Configuración', t.label]} cta={
        <button className="btn btn--accent">{Icon.check}<span>Guardar todo</span></button>
      }/>
      <main className="main">
        <div className="page-head">
          <div>
            <h1>Configuración</h1>
            <p>Gestiona roles, permisos, usuarios e integraciones de tu organización · solo Administradores</p>
          </div>
          <div className="row" style={{ gap: 12, alignItems: 'center' }}>
            <span className="muted" style={{ fontSize: 12, fontFamily: 'var(--mono)' }}>Panel: {window._appConfig?.panel_nombre || 'DESASF'}</span>
            <Pill kind="ok">Plan Enterprise</Pill>
          </div>
        </div>

        {/* Tabs horizontales */}
        <div className="cfg-tabs">
          {tabs.map(x => (
            <div key={x.id} className={`cfg-tab ${x.id === tab ? 'is-active' : ''}`} onClick={() => setTab(x.id)}>
              {x.icon}
              <span>{x.label}</span>
              {x.count != null && <span className="cfg-tab__count">{x.count}</span>}
            </div>
          ))}
        </div>

        {/* Aviso contextual por sección */}
        {tab === 'roles' && (
          <div className="cfg-banner">
            <div style={{ width: 32, height: 32, borderRadius: 6, background: 'var(--hi-vis)', display: 'grid', placeItems: 'center', flexShrink: 0 }}>
              {Icon.audit}
            </div>
            <div style={{ flex: 1 }}>
              <b>Cualquier cambio en permisos se aplica de inmediato.</b> Los usuarios afectados verán el cambio en su próxima recarga. Toda modificación queda registrada en <u>Auditoría</u>.
            </div>
            <button className="btn btn--ghost btn--sm">Ver historial de cambios →</button>
          </div>
        )}

        {tab === 'roles' && <CfgRolesPermisos/>}
        {tab === 'usuarios' && <CfgUsuarios/>}
        {tab === 'org' && <CfgOrganizacion/>}
        {tab === 'notif' && <CfgNotificaciones/>}
        {tab === 'integ' && <CfgIntegraciones/>}
        {tab === 'seg' && <CfgSeguridad/>}
        {tab === 'pref' && <CfgApariencia/>}
      </main>
    </div>
  );
}

window.Configuracion = Configuracion;
window.ConfiguracionUsuarios = () => <Configuracion initialTab="usuarios"/>;
window.ConfiguracionOrg = () => <Configuracion initialTab="org"/>;
window.ConfiguracionNotif = () => <Configuracion initialTab="notif"/>;
window.ConfiguracionInteg = () => <Configuracion initialTab="integ"/>;
window.ConfiguracionSeg = () => <Configuracion initialTab="seg"/>;
window.ConfiguracionPref = () => <Configuracion initialTab="pref"/>;
