{
const width = 700;
const height = 380;
const margin = {top: 30, right: 50, bottom: 55, left: 70};
const tradeRange = d3.range(0.35, 0.601, 0.001);
const meData = tradeRange.map(t => {
const meVal = beta1 + beta2 * t;
const seVal = Math.sqrt(se_beta1**2 + t**2 * se_beta2**2 + 2 * t * cov_beta12);
return { trade: t, me: meVal, se: seVal };
});
const x = d3.scaleLinear()
.domain([0.35, 0.60])
.range([margin.left, width - margin.right]);
const yExtent = d3.extent(meData, d => d.me);
const yPad = (yExtent[1] - yExtent[0]) * 0.3;
const y = d3.scaleLinear()
.domain([yExtent[0] - yPad, yExtent[1] + yPad])
.range([height - margin.bottom, margin.top]);
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
.attr("width", width)
.attr("height", height)
.style("max-width", "100%")
.style("font-family", "'Source Sans 3', sans-serif");
// Background zones
svg.append("rect")
.attr("x", margin.left).attr("y", margin.top)
.attr("width", x(crossover) - margin.left)
.attr("height", height - margin.top - margin.bottom)
.attr("fill", "#ffebee").attr("opacity", 0.4);
svg.append("rect")
.attr("x", x(crossover)).attr("y", margin.top)
.attr("width", width - margin.right - x(crossover))
.attr("height", height - margin.top - margin.bottom)
.attr("fill", "#e8f5e9").attr("opacity", 0.4);
// Zone labels
svg.append("text")
.attr("x", (margin.left + x(crossover)) / 2).attr("y", margin.top + 18)
.attr("text-anchor", "middle").attr("fill", "#c62828")
.attr("font-size", "11px").attr("font-weight", "600")
.text("CONFLICT ZONE");
svg.append("text")
.attr("x", (x(crossover) + width - margin.right) / 2).attr("y", margin.top + 18)
.attr("text-anchor", "middle").attr("fill", "#2e7d32")
.attr("font-size", "11px").attr("font-weight", "600")
.text("PEACE ZONE");
// 99% CI band
svg.append("path")
.datum(meData)
.attr("d", d3.area()
.x(d => x(d.trade))
.y0(d => y(d.me - 2.576 * d.se))
.y1(d => y(d.me + 2.576 * d.se)))
.attr("fill", "#42a5f5").attr("opacity", 0.12);
// 90% CI band
svg.append("path")
.datum(meData)
.attr("d", d3.area()
.x(d => x(d.trade))
.y0(d => y(d.me - 1.645 * d.se))
.y1(d => y(d.me + 1.645 * d.se)))
.attr("fill", "#42a5f5").attr("opacity", 0.22);
// ME line
svg.append("path")
.datum(meData)
.attr("d", d3.line().x(d => x(d.trade)).y(d => y(d.me)))
.attr("fill", "none").attr("stroke", "#1a237e").attr("stroke-width", 2.5);
// Zero line
svg.append("line")
.attr("x1", margin.left).attr("x2", width - margin.right)
.attr("y1", y(0)).attr("y2", y(0))
.attr("stroke", "#999").attr("stroke-dasharray", "4,3");
// Crossover line
svg.append("line")
.attr("x1", x(crossover)).attr("x2", x(crossover))
.attr("y1", margin.top).attr("y2", height - margin.bottom)
.attr("stroke", "#7e57c2").attr("stroke-dasharray", "6,3").attr("stroke-width", 1.5);
svg.append("text")
.attr("x", x(crossover) + 6).attr("y", margin.top + 35)
.attr("fill", "#7e57c2").attr("font-size", "11px")
.text(`Tipping point: ${crossover.toFixed(3)}`);
// Current position dot
svg.append("circle")
.attr("cx", x(tradeOpenness)).attr("cy", y(me))
.attr("r", 7)
.attr("fill", me > 0 ? "#e53935" : "#43a047")
.attr("stroke", "white").attr("stroke-width", 2.5);
// Axes
svg.append("g")
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x).ticks(6).tickFormat(d3.format(".2f")))
.call(g => g.append("text")
.attr("x", width / 2).attr("y", 42)
.attr("fill", "#555").attr("text-anchor", "middle")
.attr("font-size", "13px")
.text("World Trade Openness (share of GDP)"));
svg.append("g")
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y).ticks(6).tickFormat(d3.format("+.4f")))
.call(g => g.append("text")
.attr("transform", "rotate(-90)")
.attr("x", -height / 2).attr("y", -55)
.attr("fill", "#555").attr("text-anchor", "middle")
.attr("font-size", "13px")
.text("Change in Conflict Risk per 1,100 km Closer"));
return svg.node();
}