diff --git a/documentation/reference-library-index.md b/documentation/reference-library-index.md
index fdeb02027..4ac3f5ec5 100644
--- a/documentation/reference-library-index.md
+++ b/documentation/reference-library-index.md
@@ -84,6 +84,8 @@ and a change here can break other maps.
`import … from '@map/components/poster/slots/TitleSlot.vue'`
- **QaCorner** — QaCorner.vue — the embeddable QA corner widget (bottom-right of any map).
`import … from '@map/components/qa/QaCorner.vue'`
+- **RegionSectionCard** — RegionSectionCard.vue — one region as a card: a heading, a summary line, a grid of its countries,
+ `import … from '@map/components/regions/RegionSectionCard.vue'`
- **FilterDropdown** — FilterDropdown.vue — SHARED (@map/components/search/FilterDropdown.vue).
`import … from '@map/components/search/FilterDropdown.vue'`
- **SearchBar** — SearchBar.vue — SHARED (@map/components/search/SearchBar.vue).
diff --git a/library/components/regions/RegionSectionCard.vue b/library/components/regions/RegionSectionCard.vue
new file mode 100644
index 000000000..66ca37dc4
--- /dev/null
+++ b/library/components/regions/RegionSectionCard.vue
@@ -0,0 +1,275 @@
+
+
+
+
+
+
+
+
+
{{ summary }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/package.json b/package.json
index 52a33b775..8b947bf65 100644
--- a/package.json
+++ b/package.json
@@ -195,6 +195,7 @@
"check:script-flags": "node internal/check-script-flags-reachable.mjs",
"check:settled": "node internal/settled-tree-gate.mjs",
"check:sfc-parse": "node internal/check-sfc-script-parses.mjs",
+ "check:regions-uses-library": "node plugin-designs/regions-dashboard/scripts/check-regions-uses-library.mjs",
"check:religion-label-not-value": "node plugin-designs/regions-dashboard/scripts/check-religion-label-not-value.mjs",
"check:share-policy": "node internal/check-share-policy.mjs",
"check:shared-reached": "node internal/check-shared-component-reached-bundles.mjs",
@@ -232,7 +233,7 @@
"dev": "bun x vite",
"fix:sri": "node internal/fix-sri.mjs",
"gallery": "node internal/generate-gallery.mjs",
- "gates:post": "node internal/run-parallel.mjs --label=post -- check:drop check:parse verify check:sri check:public-hygiene check:label-joins check:facet-cascade check:facet-one-field check:heartbeat check:kpi-unit check:composite-reg check:descriptions check:no-abs-paths check:no-codename check:dep-names check:composite-srcs check:embed-targets check:nested-token check:i18n-parity check:tabs-localized check:packets-all check:packet-strings-parity check:zone-census check:share-policy check:library-index check:csv-shape check:capability-citations check:catalog-identity check:no-orphan-gates check:quoted-scripts check:done-evidence check:script-flags check:backlog-status check:capability-exemplars check:packet-fixture check:packet-browser check:provider-switch check:locale-reachable check:url-resolver check:manifest-covers check:one-palette check:pin-dim-slider check:legend-source check:backlog-refs check:clustering-modes check:composite-embed-knobs check:composite-locale-leaks check:linked-vocabulary check:wp-embed check:dashboard-geocoder-provider check:leaf-engine-per-provider check:map-props-unit check:composites-mount check:composite-interop check:no-free-identifiers check:map-fly-reduced-motion check:mst-clustering check:list-prop-unit check:profiles-mount check:cdn-pins check:no-eval check:popup-escaping check:i18n-treeshake check:pinned-umd check:one-palette:self-test check:no-cross-plugin-imports check:no-cross-plugin-imports:self-test check:map-attribution check:studio-rail check:approx-pin-note check:embedder-keys check:provider-for check:legend-logical-insets check:legend-logical-insets:self-test check:optional-t-equivalence check:text-highlight check:religion-label-not-value check:i18n-stopped-scope check:no-silent-engine",
+ "gates:post": "node internal/run-parallel.mjs --label=post -- check:drop check:parse verify check:sri check:public-hygiene check:label-joins check:facet-cascade check:facet-one-field check:heartbeat check:kpi-unit check:composite-reg check:descriptions check:no-abs-paths check:no-codename check:dep-names check:composite-srcs check:embed-targets check:nested-token check:i18n-parity check:tabs-localized check:packets-all check:packet-strings-parity check:zone-census check:share-policy check:library-index check:csv-shape check:capability-citations check:catalog-identity check:no-orphan-gates check:quoted-scripts check:done-evidence check:script-flags check:backlog-status check:capability-exemplars check:packet-fixture check:packet-browser check:provider-switch check:locale-reachable check:url-resolver check:manifest-covers check:one-palette check:pin-dim-slider check:legend-source check:backlog-refs check:clustering-modes check:composite-embed-knobs check:composite-locale-leaks check:linked-vocabulary check:wp-embed check:dashboard-geocoder-provider check:leaf-engine-per-provider check:map-props-unit check:composites-mount check:composite-interop check:no-free-identifiers check:map-fly-reduced-motion check:mst-clustering check:list-prop-unit check:profiles-mount check:cdn-pins check:no-eval check:popup-escaping check:i18n-treeshake check:pinned-umd check:one-palette:self-test check:no-cross-plugin-imports check:no-cross-plugin-imports:self-test check:map-attribution check:studio-rail check:approx-pin-note check:embedder-keys check:provider-for check:legend-logical-insets check:legend-logical-insets:self-test check:optional-t-equivalence check:text-highlight check:religion-label-not-value check:i18n-stopped-scope check:no-silent-engine check:regions-uses-library",
"gates:pre": "node internal/run-parallel.mjs --label=pre -- check:template-literal check:mjs-parse check:sfc-parse check:tab-radius check:synced-css check:doc-scripts",
"internet:adm1": "node plugin-designs/cities-internet/scripts/build-adm1-internet.mjs",
"library-index": "node internal/generate-library-index.mjs",
diff --git a/plugin-designs/regions-dashboard/regions-list.vue b/plugin-designs/regions-dashboard/regions-list.vue
index 5af7fc8df..b73e2e6a0 100644
--- a/plugin-designs/regions-dashboard/regions-list.vue
+++ b/plugin-designs/regions-dashboard/regions-list.vue
@@ -65,6 +65,7 @@ import { DXLIFE_TOKENS } from './dxlife-tokens.js'
// labour country-poster-wall.vue states in its own header ("this profile supplies only its
// data and taxonomy").
import PosterWall from '@map/components/poster/PosterWall.vue'
+import RegionSectionCard from '@map/components/regions/RegionSectionCard.vue'
import posterData from '@map/data/country-posters.json'
import { resolveAssetsBase } from '@map/utils/embedBase.js'
import { onBeforeUnmount } from 'vue'
@@ -426,74 +427,6 @@ useShadowStyles(
'.rl-summary{ margin:0; color:var(--dx-muted); font-size:var(--font-size-md); }' +
'.rl-status{ padding:var(--spacing-xl); text-align:center; color:var(--dx-muted); font-size:var(--font-size-md); }' +
'.rl-status.rl-failed{ font-weight:600; }' +
- // .region-card: DX-life's --border-radius-xlg (35px), overflow hidden, white body, no border --
- // the dark header band supplies its own edge, unlike our old grey-outlined card.
- '.region-card{ flex:0 0 auto; border-radius:var(--dx-radius); overflow:hidden; background:var(--dx-elevated); }' +
- // .region-card__header: padding = --spacing-md / clamp(--spacing-md,4vw,--spacing-2xl) -- the clamp
- // is KEPT as a clamp (var(--spacing-md) / var(--spacing-2xl) are the resolved ends), not frozen at its desktop maximum.
- // gap = --spacing-2xs (7.5px), on --color-surface-brand.
- '.region-card__header{ display:flex; flex-direction:column; gap:var(--spacing-2xs);' +
- ' padding:var(--spacing-md) clamp(var(--spacing-md), 4cqw, var(--spacing-2xl));' +
- ' background:var(--dx-header-bg); color:var(--dx-header-text); }' +
- // .region-card__title: font-size clamp(--font-size-xl,4vw,--font-size-3xl) = clamp(var(--font-size-xl),_,var(--font-size-3xl)).
- // A phone gets the var(--font-size-xl) FLOOR, as theirs does; it only reaches var(--font-size-3xl) past ~1192px of width.
- // DX-life's heading font (Bebas Neue -- a bold condensed display face), regular weight (the
- // face itself carries the boldness), all-caps to match the source screenshot exactly.
- // Bebas Neue itself is DX-life's own self-hosted font file (doxa.life/assets/fonts/BebasNeue/),
- // not served with CORS -- cross-origin @font-face silently fails without it, so it cannot be
- // loaded here without shipping a font asset this repo doesn't own. Approximated with the
- // standard condensed-display-face fallback stack (bold weight + font-stretch:condensed + tight
- // tracking), since Bebas Neue's glyphs read as heavy/compressed even at its nominal "400" weight.
- // getComputedStyle() on the live card title reads weight 400 / line-height 1.0 (47.68px on a
- // 47.69px font-size) / letter-spacing normal / text-transform none -- Bebas Neue's own glyphs
- // are what read as bold+condensed+caps-like, DX-life applies NO transform on top of the font.
- // Our fallback stack has no true Bebas-Neue substitute, so font-stretch:condensed stands in for
- // the face's natural narrowness, but weight/leading/tracking/case now match the measured values
- // exactly rather than compounding extra "boldness" the source doesn't have.
- '.region-card__title{ margin:0; font-size:clamp(var(--font-size-xl), 4cqw, var(--font-size-3xl)); line-height:1;' +
- ' font-family:"Bebas Neue","Arial Narrow",BlinkMacSystemFont,"Segoe UI","Helvetica Neue",Arial,sans-serif;' +
- ' font-weight:400; letter-spacing:normal; }' +
- // Their link is an , so it carries their `a` rule's
- // font-weight: var(--font-weight-medium) = 500 (_globals.scss:132). Ours is a