import std.stdio;
import core.stdc.stdio;
import std.stdio;
import std.typetuple;
import std.algorithm;
import std.container;
import std.typecons;

alias Tuple!(int,"l",int,"a",int,"b") edge;

int[] get_near(int n,int m,int k,edge[] es,int[] festa){
	alias Tuple!(int,"cost",int,"idx") ci;
	
	ci[][] graph = new ci[][n];
	foreach(e;es){
		graph[e.a] ~= ci(e.l,e.b);
		graph[e.b] ~= ci(e.l,e.a);
	}

	alias BinaryHeap!(ci[],"a>b") queue;
	queue q = queue(new ci[m+k],0);
	
	foreach(f;festa){
		q.insert(ci(0,f));
	}
	
	int[] near = new int[n];
	bool[] visited = new bool[n];
	int c=0;
	while(!q.empty){
		ci top = q.front;
		q.removeFront;
		with(top){
			if(visited[idx])
				continue;
			visited[idx] = true;
			near[idx] = cost;
			if(++c == n)
				break;
			foreach(tc;graph[idx])
				if(!visited[tc.idx])
					q.insert(ci(cost+tc.cost,tc.idx));
		}
	}
	
	foreach(ref e;es)
		e.l = min(near[e.a],near[e.b]);
		
	return near;
}

int[][] get_tree(int n,edge[] es){
	int[] parent = new int[n];
	for(int i=0;i<n;i++)
		parent[i] = i;
	int[] rank = new int[n];
	rank[] = 1;
		
	int findParent(int idx){
		if(parent[idx] == idx)
			return idx;
		else
			return parent[idx] = findParent(parent[idx]);
	}
	
	int unite(int a,int b){
		a = findParent(a);
		b = findParent(b);
		if(a != b){
			if(rank[a] < rank[b])
				parent[a] = b;
			else if(rank[a] > rank[b])
				parent[b] = a;
			else
				parent[b] = a,
				rank[a]++;
			return 1;
		}
		return 0;
	}
	
	int[][] tree = new int[][n];
	es.sort!"a>b";
	//es.writeln;
	int uc=1;
	foreach(e;es){
		if(unite(e.a,e.b)){
			tree[e.a] ~= e.b;
			tree[e.b] ~= e.a;
			if(++uc == n)
				return tree;
		}
	}
	assert(false);
}

struct RMQ(T){
	T[][] data;
	int TopBit(int a){
		for(int i=20;i>=0;i--)
			if(a&(1<<i))
				return i;
		assert(false);
	}
	void Initialize(T[] d){
		int h=TopBit(cast(int)d.length)+1;
		data = new T[][h];
		data[0] = d;
		for(int j=1;j<h;j++){
			data[j] = new T[d.length-(1<<j)+1];
			for(int i=0;i<d.length-(1<<j)+1;i++){
				data[j][i] = min(data[j-1][i],data[j-1][i+(1<<(j-1))]);
			}
		}
	}
	T Get(int b,int e){
		int h=TopBit(e-b);
		return min(data[h][b],data[h][e-(1<<h)]);
	}
}

struct LCA{
	alias Tuple!(int,"depth",int,"idx") di;
	RMQ!di rmq;
	
	int[] firstIdx;
	void Initialize(int n,int[][] tree){
		di[] ds = new di[n*2-1];
		int ec=0;
		firstIdx = new int[n];
		firstIdx[] = -1;
		
		di[] q = new di[n*2-1];
		int qc=0;
		q[qc++] = di(0,0);
		
		bool[] visited = new bool[n];
		for(int _=0;_<n*2-1;_++){
			di d = q[--qc];
			//d.writeln;
			with(d){
				ds[ec++] = d;
				visited[idx] = true;
				if(firstIdx[idx] == -1){
					firstIdx[idx] = ec-1;
					foreach(c;tree[idx]){
						if(!visited[c]){
							q[qc++] = d;
							q[qc++] = di(depth+1,c);
						}
					}
				}
			}
		}
		
		rmq.Initialize(ds);
	}
	int Get(int a,int b){
		a = firstIdx[a];
		b = firstIdx[b];
		if(a>b)
			swap(a,b);
		return rmq.Get(a,b+1).idx; 
	}
}

struct HeavyLight{
	int[] subCount;
	void SetSubCount(int n,int[][] tree){
		subCount = new int[n];
		subCount[] = 1;
		bool[] visited = new bool[n];
		int[] q = new int[n];
		int qc=0;
		int[] d = new int[n];
		int dc=0;
		d[dc++] = 0;
		int[] cs = new int[n];
		foreach(i;d){
			visited[i] = true;
			foreach(c;tree[i])
				if(!visited[c]){
					cs[i]++;
					d[dc++] = c;
				}
			if(cs[i]==0)
				q[qc++] = i;
		}
		visited[] = false;
		foreach(i;q){
			visited[i] = true;
			foreach(c;tree[i])
				if(!visited[c]){
					subCount[c] += subCount[i];
					if(--cs[c] == 0)
						q[qc++] = c;
				}
		}
	}
	RMQ!(int)[] rmqs;
	int[] parent;
	int[] path;
	int[] pIdx;
	void Decompose(int n,int[][] tree,int[] near){
		rmqs = new RMQ!(int)[n];
		parent = new int[n];
		path = new int[n];
		pIdx = new int[n];
		alias Tuple!(int,"idx",int,"g",int,"d",int,"p",int[],"t") arg;
		arg[] args = new arg[n];
		int ac=0;
		int pc=0;
		args[ac++] = arg(0,pc++,0,-1,[]);
		bool[] visited = new bool[n];
		foreach(a;args){
			with(a){
				path[idx] = g;
				pIdx[idx] = d;
				parent[idx] = p;
				t ~= near[idx];
				visited[idx] = true;
				int maxC=-1;
				foreach(c;tree[idx])
					if(!visited[c])
						if(maxC==-1||subCount[maxC]<subCount[c])
							maxC = c;
				if(maxC==-1)
					rmqs[g].Initialize(t);
				else{
					foreach(c;tree[idx])
						if(!visited[c]){
							if(c==maxC)
								args[ac++] = arg(c,g,d+1,p,t);
							else
								args[ac++] = arg(c,pc++,0,idx,[]);
						}
				}
			}
		}
	}
	void Initialize(int n,int[][] tree,int[] near){
		SetSubCount(n,tree);
		Decompose(n,tree,near);
	}
	int Get(int p,int c){
		int ret=1145141919;
		while(path[p] != path[c]){
			ret = min(ret,rmqs[path[c]].Get(0,pIdx[c]+1));
			c = parent[c];
		}
		ret = min(ret,rmqs[path[p]].Get(pIdx[p],pIdx[c]+1));
		return ret;
	}
}

void main(){
	int n,m,k,q;
	scanf("%d%d%d%d",&n,&m,&k,&q);
	
	edge[] es = new edge[m];
	for(int i=0;i<m;i++){
		int a,b,l;
		scanf("%d%d%d",&a,&b,&l);
		es[i] = edge(l,--a,--b);
	}
	
	int[] festa = new int[k];
	for(int i=0;i<k;i++){
		scanf("%d",&festa[i]);
		festa[i]--;
	}
	
	int[] near = get_near(n,m,k,es,festa);
	
	//writeln("near ",near);
	
	int[][] tree = get_tree(n,es);
	
	//writeln("tree");
	//foreach(cs;tree)
	//	cs.writeln;
	
	LCA lca;
	lca.Initialize(n,tree);
	
	HeavyLight hl;
	hl.Initialize(n,tree,near);
	
	for(int i=0;i<q;i++){
		int s,t;
		scanf("%d%d",&s,&t);
		int p = lca.Get(--s,--t);
		int ans = min(hl.Get(p,s),hl.Get(p,t));
		printf("%d\n",ans);
	}
}