#include"bits/stdc++.h"
using namespace std;
#define LL long long
#define PB push_back
#define MP make_pair

//RMQ
struct SegmentTree{
private:
	int N;
	vector<long long> node, lazy;
	vector<bool> lazyFlg;
	const long long DEFAULT = 9223372036854775807LL;
public:
	void init(int n){ //初期化 O(N)
		node.clear();
		lazy.clear();
		lazyFlg.clear();
		N = 1;
		while(N < n) N = (N<<1);
		for(int i=0; i<2*N-1; i++){
			node.push_back(DEFAULT);
			lazy.push_back(0LL);
			lazyFlg.push_back(false);
		}
	}
	void eval(int k, int l, int r){ //遅延評価 O(1)
		if(lazyFlg[k]){ //RUQ：下に更新クエリが溜まっていても加算クエリが溜まっていても上書き
			node[k] = lazy[k];
			if(r-l > 1){
				lazy[2*k+1] = lazy[k];
				lazyFlg[2*k+1] = true;
				lazy[2*k+2] = lazy[k];
				lazyFlg[2*k+2] = true;
			}
			lazy[k] = 0LL;
			lazyFlg[k] = false;
		}
		else{ //RAQ：下に更新クエリが溜まっていても加算クエリが溜まっていても加算
			node[k] += lazy[k];
			if(r-l > 1){
				lazy[2*k+1] += lazy[k];
				lazy[2*k+2] += lazy[k];
			}
			lazy[k] = 0LL;
		}
	}
	void update(int a, long long x){ //[a,a+1)の値を変更 O(log N)
		update(a, a+1, x);
	}
	void update(int a, int b, long long x, int k=0, int l=0, int r=-1){ //[a,b)の値を変更 O(log N)
		if(r == -1) r = N;
		eval(k, l, r);
		if(b <= l || r <= a) return; //交差する場合
		if(a <= l && r <= b){ //完全に含む場合
			lazy[k] = x;
			lazyFlg[k] = true;
			eval(k, l, r);
		}
		else{
			update(a, b, x, 2*k+1, l, (l+r)/2);
			update(a, b, x, 2*k+2, (l+r)/2, r);
			node[k] = std::min(node[2*k+1], node[2*k+2]);
		}
	}
	void add(int a, long long x){ //[a,a+1)の値に加算 O(log N)
		add(a, a+1, x);
	}
	void add(int a, int b, long long x, int k=0, int l=0, int r=-1){ //[a,b)の値に加算 O(log N)
		if(r == -1) r = N;
		eval(k, l, r);
		if(b <= l || r <= a) return; //交差する場合
		if(a <= l && r <= b){ //完全に含む場合
			lazy[k] += x;
			eval(k, l, r);
		}
		else{
			add(a, b, x, 2*k+1, l, (l+r)/2);
			add(a, b, x, 2*k+2, (l+r)/2, r);
			node[k] = std::min(node[2*k+1], node[2*k+2]);
		}
	}
	long long min(int a, int b, int k=0, int l=0, int r=-1){ //[a,b)の最小値 O(log N)
		if(r == -1) r = N;
		if(b <= l || r <= a) return DEFAULT; //交差する場合
		eval(k, l, r);
		if(a <= l && r <= b) return node[k]; //完全に含む場合
		return std::min(min(a, b, k*2+1, l, (l+r)/2), min(a, b, k*2+2, (l+r)/2, r));
	}
};

//Union-Find木(ランク有り)
struct UnionFind{
private:
	vector<int> parent;
	vector<int> rank;
public:
	void init(int N){ //初期化する O(N)
		parent.clear();
		rank.clear();
		for(int i=0; i<N; i++) parent.push_back(i);
		for(int i=0; i<N; i++) rank.push_back(0);
	}
	int root(int a){ //親を返す O(log N)
		if(parent[a] == a) return a;
		return parent[a] = root(parent[a]);
	}
	void unite(int a, int b){ //木を併合する O(log N)
		int rootA = root(a);
		int rootB = root(b);
		if(rootA == rootB) return;
		if(rank[rootA] < rank[rootB]) parent[rootA] = rootB;
		else{
			parent[rootB] = rootA;
			if(rank[rootA] == rank[rootB]) rank[rootA]++;
		}
	}
	bool same(int a, int b){ //属する木が同じかを返す O(log N)
		return root(a) == root(b);
	}
};

int N, M, K, Q;
vector<pair<int, int>> edge[100001];
int dist[100001];
vector<pair<int, pair<int, int>>> v;
UnionFind uf;
vector<pair<int, int>> edge2[100001];

int par[100001];
int sz[100001];
int pre[100001];
int dfs_origin[100001];
SegmentTree EularTour;
int cnt1 = 0;
int cntE = 0;
int dfs(int now, int p){
	int d = cnt1;
	dfs_origin[cnt1] = now;
	cnt1++;
	EularTour.update(cntE, d);
	pre[now] = cntE;
	cntE++;

	par[now] = p;
	sz[now] = 1;
	for(int i=0; i<edge2[now].size(); i++){
		int nx = edge2[now][i].first;
		if(nx == p) continue;
		sz[now] += dfs(nx, now);

		EularTour.update(cntE, d);
		cntE++;
	}
	return sz[now];
}

int cnt2 = 0;
int pathIdx[100001];
int pathPar[100001];
SegmentTree st;
void HLD(int now, int p, int pp){
	pathIdx[now] = cnt2;
	st.update(cnt2, dist[now]);
	cnt2++;
	pathPar[now] = pp;

	int M = 0;
	int idx = -1;
	for(int i=0; i<edge2[now].size(); i++){
		int nx = edge2[now][i].first;
		if(nx == p) continue;
		if(M < sz[nx]){
			M = sz[nx];
			idx = nx;
		}
	}
	if(idx != -1) HLD(idx, now, pp);
	for(int i=0; i<edge2[now].size(); i++){
		int nx = edge2[now][i].first;
		if(nx == p) continue;
		if(nx == idx) continue;
		HLD(nx, now, nx);
	}
}

int LCA(int i, int j){
	if(pre[i] > pre[j]) swap(i, j);
	return dfs_origin[EularTour.min(pre[i], pre[j]+1)];
}

int main(){
	//入力
	cin >> N >> M >> K >> Q;
	for(int i=0; i<M; i++){
		int A, B, L;
		cin >> A >> B >> L;
		edge[A].PB(MP(B, L));
		edge[B].PB(MP(A, L));
	}

	//ダイクストラ
	for(int i=1; i<=N; i++) dist[i] = INT_MAX;
	priority_queue<pair<int, int>> que;
	for(int i=0; i<K; i++){
		int tmp;
		cin >> tmp;
		dist[tmp] = 0;
		que.push(MP(0, tmp));
	}
	while(!que.empty()){
		int now = que.top().second;
		int c = -que.top().first;
		que.pop();
		for(int i=0; i<edge[now].size(); i++){
			int nx = edge[now][i].first;
			int way = edge[now][i].second;
			if(dist[nx] > c + way){
				dist[nx] = c + way;
				que.push(MP(-dist[nx], nx));
			}
		}
	}
	
	//クラスカル
	for(int i=1; i<=N; i++){
		for(int j=0; j<edge[i].size(); j++){
			if(edge[i][j].first > i){
				v.PB({ min(dist[i], dist[edge[i][j].first]),{i, edge[i][j].first} });
			}
		}
	}
	sort(v.begin(), v.end());
	reverse(v.begin(), v.end());
	uf.init(N+1);
	for(int i=0; i<v.size(); i++){
		int a = v[i].second.first;
		int b = v[i].second.second;
		if(!uf.same(a, b)){
			uf.unite(a, b);
			edge2[a].PB(MP(b, v[i].first));
			edge2[b].PB(MP(a, v[i].first));
		}
	}
	
	//HL分解
	EularTour.init(2*N);
	dfs(1,0);
	st.init(N);
	HLD(1,0,1);

	//クエリ処理
	for(int i=0; i<Q; i++){
		int S, T;
		cin >> S >> T;
		int lca = LCA(S, T);
		LL ans = INT_MAX;
		while(true){
			if(pathPar[S] == pathPar[lca]){
				ans = min(ans, st.min(pathIdx[lca], pathIdx[S]+1));
				S = lca;
				break;
			}
			else{
				ans = min(ans, st.min(pathIdx[pathPar[S]], pathIdx[S]+1));
				S = par[pathPar[S]];
			}
		}
		while(true){
			if(pathPar[T] == pathPar[lca]){
				ans = min(ans, st.min(pathIdx[lca], pathIdx[T]+1));
				T = lca;
				break;
			}
			else{
				ans = min(ans, st.min(pathIdx[pathPar[T]], pathIdx[T]+1));
				T = par[pathPar[T]];
			}
		}
		cout << ans << endl;
	}
}


Measured memory_bytes: 40268