diff --git a/crates/doc/src/library.rs b/crates/doc/src/library.rs
--- a/crates/doc/src/library.rs
+++ b/crates/doc/src/library.rs
@@ -205,21 +205,119 @@ pub fn standard_patterns() -> Vec<HatchPattern> {
             description: "Staircase effect",
             lines: vec![pl(0.0, (0.0, 0.0), (0.125, 0.125), &[0.125, -0.125]), pl(90.0, (0.125, 0.0), (0.125, 0.125), &[0.125, -0.125])],
         },
-        // Drawn at the scale of the pattern CAD programs ship under this name (about 16x
-        // coarser than first drawn here: AR-CONC hatches came out hundreds of times too dense).
-        scaled_pattern(
-            HatchPattern {
-                name: "AR-CONC",
-                description: "Random dot and stone pattern",
-                lines: vec![
-                    pl(50.0, (0.0, 0.0), (0.4, 0.25), &[0.15, -0.6]),
-                    pl(355.0, (0.0, 0.0), (0.27, 0.3), &[0.12, -0.5]),
-                    pl(100.0, (0.12, 0.05), (0.35, 0.33), &[0.1, -0.55]),
-                    pl(0.0, (0.07, 0.02), (0.21, 0.17), &[0.0, -0.4]),
-                ],
-            },
-            16.0,
-        ),
+        // CADCraft's own drawing (2026-10-18): small closed stones (triangles of 12-34 mm sides,
+        // angles 35° or more) among scattered dots, at the density a concrete hatch usually has
+        // on architectural drawings (one stone per ~12 000 mm², a dot per ~3 500 mm² in an ISO
+        // drawing at scale 1).
+        // No line is taken from any vendor's pattern file. Every family repeats on one lattice
+        // (540 mm, 0) / (190 mm, 450 mm), each stone side along a lattice direction, so the three
+        // sides of a stone repeat together and it stays closed. 20 stones and 70 dots per tile.
+        HatchPattern {
+            name: "AR-CONC",
+            description: "Random dot and stone pattern",
+            lines: vec![
+                pl(0.0, (20.481011, 9.635334), (7.480315, 17.716535), &[1.18168, -20.078162]),
+                pl(247.109448, (21.662691, 9.635334), (10.9615, 19.585621), &[1.068911, -18.162073]),
+                pl(307.874984, (20.481011, 9.635334), (13.052281, 16.781504), &[1.247521, -21.196877]),
+                pl(247.109448, (19.07861, 2.645564), (10.9615, 19.585621), &[0.619114, -18.61187]),
+                pl(127.874984, (18.837792, 2.075206), (13.052281, 16.781504), &[0.722565, -21.721833]),
+                pl(180.0, (19.07861, 2.645564), (7.480315, 17.716535), &[0.68443, -20.575412]),
+                pl(67.109448, (10.457133, 9.277081), (10.9615, 19.585621), &[0.877543, -18.35344]),
+                pl(333.178019, (10.798473, 10.085518), (18.972555, 9.592865), &[0.597223, -38.666413]),
+                pl(31.651273, (10.457133, 9.277081), (15.66439, 11.156058), &[1.027079, -32.734915]),
+                pl(31.651273, (3.050302, 8.149389), (15.66439, 11.156058), &[0.689582, -33.072412]),
+                pl(153.178019, (3.637314, 8.511246), (18.972555, 9.592865), &[0.801952, -38.461683]),
+                pl(100.080598, (3.050302, 8.149389), (19.854913, 10.465822), &[0.735061, -35.253584]),
+                pl(211.651273, (16.536125, 5.170862), (15.66439, 11.156058), &[1.220917, -32.541077]),
+                pl(333.178019, (15.49681, 4.530188), (18.972555, 9.592865), &[0.709935, -38.5537]),
+                pl(247.109448, (16.536125, 5.170862), (10.9615, 19.585621), &[1.04316, -18.187824]),
+                pl(307.874984, (6.927619, 10.461012), (13.052281, 16.781504), &[0.706048, -21.738351]),
+                pl(211.651273, (7.36109, 9.903692), (15.66439, 11.156058), &[1.062072, -32.699922]),
+                pl(247.109448, (6.927619, 10.461012), (10.9615, 19.585621), &[1.209922, -18.021062]),
+                pl(31.651273, (15.592914, 15.996194), (15.66439, 11.156058), &[0.621023, -33.140971]),
+                pl(153.178019, (16.121565, 16.322074), (18.972555, 9.592865), &[0.722221, -38.541414]),
+                pl(100.080598, (15.592914, 15.996194), (19.854913, 10.465822), &[0.66198, -35.326665]),
+                pl(280.080598, (9.685289, 17.107032), (19.854913, 10.465822), &[0.912763, -35.075882]),
+                pl(153.178019, (9.845053, 16.208359), (18.972555, 9.592865), &[0.995825, -38.267811]),
+                pl(211.651273, (9.685289, 17.107032), (15.66439, 11.156058), &[0.85629, -32.905705]),
+                pl(153.178019, (7.584278, 13.166973), (18.972555, 9.592865), &[0.644726, -38.61891]),
+                pl(280.080598, (7.008916, 13.457886), (19.854913, 10.465822), &[0.590949, -35.397696]),
+                pl(211.651273, (7.584278, 13.166973), (15.66439, 11.156058), &[0.554386, -33.207608]),
+                pl(127.874984, (5.550531, 4.253355), (13.052281, 16.781504), &[1.03043, -21.413968]),
+                pl(247.109448, (4.917908, 5.066727), (10.9615, 19.585621), &[0.882901, -18.348083]),
+                pl(180.0, (5.550531, 4.253355), (7.480315, 17.716535), &[0.976047, -20.283796]),
+                pl(247.109448, (9.798774, 2.144405), (10.9615, 19.585621), &[1.238914, -17.99207]),
+                pl(31.651273, (9.316871, 1.003056), (15.66439, 11.156058), &[1.087522, -32.674473]),
+                pl(307.874984, (9.798774, 2.144405), (13.052281, 16.781504), &[0.722966, -21.721432]),
+                pl(31.651273, (19.772647, 4.666939), (15.66439, 11.156058), &[1.1044, -32.657595]),
+                pl(127.874984, (20.712776, 5.24647), (13.052281, 16.781504), &[0.734186, -21.710212]),
+                pl(67.109448, (19.772647, 4.666939), (10.9615, 19.585621), &[1.258142, -17.972842]),
+                pl(333.178019, (15.27065, 10.074253), (18.972555, 9.592865), &[0.802463, -38.461173]),
+                pl(211.651273, (15.986778, 9.712166), (15.66439, 11.156058), &[0.690021, -33.071973]),
+                pl(280.080598, (15.27065, 10.074253), (19.854913, 10.465822), &[0.735529, -35.253116]),
+                pl(31.651273, (0.612588, 17.042707), (15.66439, 11.156058), &[0.933964, -32.82803]),
+                pl(153.178019, (1.407632, 17.532803), (18.972555, 9.592865), &[1.086157, -38.177478]),
+                pl(100.080598, (0.612588, 17.042707), (19.854913, 10.465822), &[0.995561, -34.993085]),
+                pl(307.874984, (8.960817, 5.655419), (13.052281, 16.781504), &[1.057289, -21.387109]),
+                pl(67.109448, (9.60993, 4.820846), (10.9615, 19.585621), &[0.905914, -18.325069]),
+                pl(0.0, (8.960817, 5.655419), (7.480315, 17.716535), &[1.001488, -20.258355]),
+                pl(0.0, (19.206725, 14.739017), (7.480315, 17.716535), &[0.655568, -20.604274]),
+                pl(127.874984, (19.862293, 14.739017), (13.052281, 16.781504), &[0.692095, -21.752303]),
+                pl(67.109448, (19.206725, 14.739017), (10.9615, 19.585621), &[0.593007, -18.637977]),
+                pl(211.651273, (13.119025, 15.340647), (15.66439, 11.156058), &[1.167, -32.594994]),
+                pl(307.874984, (12.125607, 14.728266), (13.052281, 16.781504), &[0.775802, -21.668596]),
+                pl(247.109448, (13.119025, 15.340647), (10.9615, 19.585621), &[1.329457, -17.901526]),
+                pl(31.651273, (3.773733, 12.39632), (15.66439, 11.156058), &[0.651956, -33.110039]),
+                pl(153.178019, (4.328715, 12.738433), (18.972555, 9.592865), &[0.758194, -38.505441]),
+                pl(100.080598, (3.773733, 12.39632), (19.854913, 10.465822), &[0.694953, -35.293692]),
+                pl(31.651273, (16.206885, 12.356449), (15.66439, 11.156058), &[1.083079, -32.678915]),
+                pl(153.178019, (17.128864, 12.924792), (18.972555, 9.592865), &[1.259571, -38.004065]),
+                pl(100.080598, (16.206885, 12.356449), (19.854913, 10.465822), &[1.15451, -34.834135]),
+                pl(67.109448, (12.849427, 5.722968), (10.9615, 19.585621), &[0.853249, -18.377735]),
+                pl(211.651273, (13.181317, 6.509024), (15.66439, 11.156058), &[0.998645, -32.763349]),
+                pl(153.178019, (12.849427, 5.722968), (18.972555, 9.592865), &[0.580689, -38.682947]),
+                pl(
+                    19.510799,
+                    (1.403172, 0.497187),
+                    (33.006905, 7.100458),
+                    &[
+                        0.0, -2.71837, 0.0, -2.122911, 0.0, -2.390294, 0.0, -3.002194, 0.0, -1.894831, 0.0, -4.06954, 0.0, -1.559613, 0.0, -2.89308,
+                        0.0, -2.040845, 0.0, -3.324317, 0.0, -5.628361, 0.0, -2.660667, 0.0, -2.096617, 0.0, -7.476064, 0.0, -3.29102, 0.0,
+                        -2.316796, 0.0, -1.70561, 0.0, -1.854847,
+                    ],
+                ),
+                pl(
+                    144.027613,
+                    (-6.861953, 4.98045),
+                    (38.763978, 6.243966),
+                    &[
+                        0.0, -4.052168, 0.0, -1.36175, 0.0, -9.967512, 0.0, -1.280637, 0.0, -4.170196, 0.0, -1.41432, 0.0, -2.367455, 0.0, -1.486285,
+                        0.0, -4.62738, 0.0, -4.002436, 0.0, -2.663116, 0.0, -1.934293, 0.0, -1.991448, 0.0, -2.637094, 0.0, -1.58119, 0.0, -2.780019,
+                        0.0, -2.049279, 0.0, -9.955783,
+                    ],
+                ),
+                pl(
+                    50.572198,
+                    (2.533777, 3.081621),
+                    (18.435492, 5.473881),
+                    &[
+                        0.0, -3.332314, 0.0, -2.666002, 0.0, -3.562468, 0.0, -4.494214, 0.0, -5.221485, 0.0, -3.224885, 0.0, -3.111831, 0.0,
+                        -3.642425, 0.0, -7.151893, 0.0, -3.462364, 0.0, -3.165007, 0.0, -3.544732, 0.0, -1.308374, 0.0, -2.351488, 0.0, -5.583112,
+                        0.0, -5.037525, 0.0, -7.948599,
+                    ],
+                ),
+                pl(
+                    162.531977,
+                    (-3.708719, 1.167079),
+                    (20.279437, 6.381641),
+                    &[
+                        0.0, -1.113427, 0.0, -2.237281, 0.0, -1.527796, 0.0, -6.108525, 0.0, -6.203357, 0.0, -4.227832, 0.0, -1.593644, 0.0,
+                        -4.181987, 0.0, -3.165928, 0.0, -2.379037, 0.0, -1.551491, 0.0, -6.066391, 0.0, -2.645402, 0.0, -4.557707, 0.0, -2.419574,
+                        0.0, -1.646997, 0.0, -7.394611,
+                    ],
+                ),
+            ],
+        },
         // Drawn at the scale of the pattern CAD programs ship under this name (about 16x
         // coarser than first drawn here: AR-CONC hatches came out hundreds of times too dense).
         scaled_pattern(
diff --git a/crates/render/src/hatch.rs b/crates/render/src/hatch.rs
--- a/crates/render/src/hatch.rs
+++ b/crates/render/src/hatch.rs
@@ -415,18 +415,24 @@ pub(crate) fn pattern_lines_until(
 const SWATCH_REPEATS: f64 = 6.0;
 /// Most runs a swatch holds.
 const MAX_SWATCH_RUNS: usize = 4000;
+/// Runs a swatch over [`MAX_SWATCH_RUNS`] zooms in to: about one tile of AR-CONC, its stones a
+/// few points across in a picker's swatch.
+const ZOOMED_SWATCH_RUNS: usize = 125;
+/// Most times a swatch zooms in (twice the scale each time).
+const SWATCH_ZOOMS: usize = 6;
 
 /// A preview of a pattern for a pattern picker: the runs of its line families `lines` inside the
 /// `side` × `side` square with a corner at the origin, at a scale that shows a few repeats of the
-/// pattern (each family's line spacing or dash repeat, whichever is longer). Empty for a pattern
-/// with no lines (SOLID).
+/// pattern (each family's line spacing or dash repeat, whichever is longer). A swatch that would
+/// hold more than [`MAX_SWATCH_RUNS`] runs zooms in to [`ZOOMED_SWATCH_RUNS`] or fewer, so every
+/// family shows at a readable size. Empty for a pattern with no lines (SOLID).
 pub fn pattern_swatch(lines: &[PatternLine], side: f64) -> Vec<Vec<Vec2>> {
     let repeat =
         lines.iter().map(|l| l.delta.1.abs().max(l.dashes.iter().map(|d| d.abs()).sum::<f64>())).filter(|r| r.is_finite()).fold(0.0, f64::max);
     if !side.is_finite() || side <= 0.0 || repeat < 1e-9 {
         return Vec::new();
     }
-    let h = Hatch {
+    let mut h = Hatch {
         pattern: String::new(),
         solid: false,
         loops: Vec::new(),
@@ -440,8 +446,18 @@ pub fn pattern_swatch(lines: &[PatternLine], side: f64) -> Vec<Vec<Vec2>> {
         background: None,
         pattern_lines: lines.to_vec(),
     };
-    let square = vec![Vec2::new(0.0, 0.0), Vec2::new(side, 0.0), Vec2::new(side, side), Vec2::new(0.0, side)];
-    let mut runs = pattern_lines(&Drawing::default(), &h, &[square], 0.0, None);
+    let square = [vec![Vec2::new(0.0, 0.0), Vec2::new(side, 0.0), Vec2::new(side, side), Vec2::new(0.0, side)]];
+    let mut runs = pattern_lines(&Drawing::default(), &h, &square, 0.0, None);
+    // A pattern whose dash repeats are mostly gaps (AR-CONC) spans many tiles at that scale: cut
+    // at the cap, its last families would not show at all, and its marks would be specks.
+    let target = if runs.len() > MAX_SWATCH_RUNS { ZOOMED_SWATCH_RUNS } else { MAX_SWATCH_RUNS };
+    for _ in 0..SWATCH_ZOOMS {
+        if runs.len() <= target {
+            break;
+        }
+        h.scale *= 2.0;
+        runs = pattern_lines(&Drawing::default(), &h, &square, 0.0, None);
+    }
     runs.truncate(MAX_SWATCH_RUNS);
     runs
 }
diff --git a/crates/render/src/tests.rs b/crates/render/src/tests.rs
--- a/crates/render/src/tests.rs
+++ b/crates/render/src/tests.rs
@@ -511,6 +511,35 @@ fn scattered_dot_pattern_reads_solid_once_dots_fill_the_pixels() {
     assert!(!crate::hatch::reads_solid(&cadcraft_doc::Drawing::new_imperial(), &h, 0.125));
 }
 
+/// AR-CONC's stones are closed triangles wherever they repeat: away from the boundary every side's
+/// two ends meet the ends of the stone's other two sides. Its dots stand alone.
+#[test]
+fn ar_conc_stones_are_closed_triangles() {
+    let d = cadcraft_doc::Drawing::new_imperial();
+    let h = pattern_hatch("AR-CONC", 1.0, (0.0, 0.0, 60.0, 60.0));
+    let runs = crate::hatch::pattern_lines(&d, &h, &loops_of(&h), 0.0, None);
+    let inside = |p: Vec2| p.x > 2.0 && p.x < 58.0 && p.y > 2.0 && p.y < 58.0;
+    let sides: Vec<[Vec2; 2]> = runs
+        .iter()
+        .filter_map(|r| match r.as_slice() {
+            [a, b] if inside(*a) && inside(*b) => Some([*a, *b]),
+            _ => None,
+        })
+        .collect();
+    let dots = runs.iter().filter(|r| r.len() == 1).count();
+    // 56² in² is about 8 tiles of 20 stones and 70 dots.
+    assert!(sides.len() > 3 * 100, "{} sides", sides.len());
+    assert!(dots > 400, "{dots} dots");
+    // A stone's sides are under 1.4 (34 mm): those 4 from the boundary have both neighbours.
+    let deep = |p: &Vec2| p.x > 4.0 && p.x < 56.0 && p.y > 4.0 && p.y < 56.0;
+    for (i, s) in sides.iter().enumerate().filter(|(_, s)| s.iter().all(deep)) {
+        for end in s {
+            let meets = sides.iter().enumerate().filter(|(j, o)| *j != i && o.iter().any(|q| q.near(*end, 1e-4))).count();
+            assert_eq!(meets, 1, "side {i} {s:?}: the end {end:?} meets {meets} other sides");
+        }
+    }
+}
+
 /// Same polylines up to float noise: the view-limited path rotates into the family frame and back,
 /// so a start point can differ by ~1e-16 between platforms (#804). Structure must match exactly.
 fn assert_lines_close(a: &[Vec<Vec2>], b: &[Vec<Vec2>], what: &str) {
diff --git a/crates/render/tests/it/hatch_fill.rs b/crates/render/tests/it/hatch_fill.rs
--- a/crates/render/tests/it/hatch_fill.rs
+++ b/crates/render/tests/it/hatch_fill.rs
@@ -182,6 +182,19 @@ fn pattern_swatches_stay_in_their_square_and_show_a_few_repeats() {
     assert!(cadcraft_render::pattern_swatch(&[], 1.0).is_empty());
 }
 
+#[test]
+fn a_swatch_mostly_made_of_gaps_shows_every_family() {
+    // AR-CONC's dash repeats are mostly gaps: at the usual scale its swatch would pass the cap and
+    // lose its last families (the dots), leaving open stones.
+    let Some(p) = cadcraft_doc::library::pattern("AR-CONC") else { panic!("AR-CONC") };
+    let runs = cadcraft_render::pattern_swatch(&p.lines, 1.0);
+    // Zoomed in to about one tile, so the stones read at a swatch's size.
+    assert!(runs.len() <= 125, "{} runs", runs.len());
+    assert!(runs.iter().any(|r| r.len() == 1), "no dots");
+    // Zoomed in only that far: stones (three sides each) still show.
+    assert!(runs.iter().filter(|r| r.len() == 2).count() >= 15, "too few stone sides");
+}
+
 #[test]
 fn brick_joints_are_drawn_once_half_a_brick_apart_course_to_course() {
     let l = draw(hatch("BRICK", vec![square(0.0, 2.0)]), "0");
