diff --git a/src/charts/ArrestsOverTime.jsx b/src/charts/ArrestsOverTime.jsx
index 95cb522..f01d43c 100644
--- a/src/charts/ArrestsOverTime.jsx
+++ b/src/charts/ArrestsOverTime.jsx
@@ -14,6 +14,19 @@ import { MODEL_QUADRANT_LABEL } from '../utils/labels.js'
const WAVE_LABELS = { '15-16': '2015–16', '17-18': '2017–18', '21-22': '2021–22' }
const HEADROOM = 26 // px reserved at the top of the plot so a point's rate label has room to sit above it
+// Matches RateDensityPanel/GroupDifference. These SVGs are rendered at
+// width="100%" inside the same card, so the viewBox width sets the scale factor
+// for everything in it: at 360 this chart's 0.7rem text came out roughly twice
+// the size of the other charts' 0.62rem, which is what made it look like a
+// different chart from a different app.
+const WIDTH = 760
+const HEIGHT = 300
+const MARGIN = { top: 20, right: 24, bottom: 52, left: 56 }
+// Keeps the first and last wave off the plot edges. Without it the end markers
+// sit flush against the y-axis and the right border, so half of each diamond's
+// glyph is visually clipped and its label has nowhere to go.
+const X_PAD = 64
+
export default function ArrestsOverTime({ data, modelId }) {
const maxArrests = Math.max(
...data.map((d) => Math.max(d.arrests, d.modeledUpper ?? 0)),
@@ -22,13 +35,15 @@ export default function ArrestsOverTime({ data, modelId }) {
const { ticks, niceMax } = niceTicks(maxArrests)
const modelLabel = MODEL_QUADRANT_LABEL[modelId] || modelId
- const width = 360
- const height = 280
- const margin = { top: 40, right: 40, bottom: 60, left: 65 }
+ const width = WIDTH
+ const height = HEIGHT
+ const margin = MARGIN
const innerWidth = width - margin.left - margin.right
const innerHeight = height - margin.top - margin.bottom
- const xScale = (i) => (i / Math.max(data.length - 1, 1)) * innerWidth
+ const plotLeft = margin.left + X_PAD
+ const plotRight = margin.left + innerWidth - X_PAD
+ const xScale = (i) => plotLeft + (i / Math.max(data.length - 1, 1)) * (plotRight - plotLeft)
const yScale = (val) => HEADROOM + (innerHeight - HEADROOM) * (1 - val / niceMax)
return (
@@ -42,48 +57,55 @@ export default function ArrestsOverTime({ data, modelId }) {