diff --git a/e2e/a11y.spec.ts b/e2e/a11y.spec.ts
index dc6ece761..090f940d2 100644
--- a/e2e/a11y.spec.ts
+++ b/e2e/a11y.spec.ts
@@ -778,13 +778,23 @@ const ADMIN_AND_BASELINE_ROUTES: readonly RouteCase[] = [
// is `app/loading.tsx` — a silhouette with no heading in it. The scan was
// therefore free to run against a document that legitimately had no `
`
// yet and report the app as missing one. Measured under an 8× CPU
- // throttle: `#main-content` at 759 ms, the tile strip at 4896 ms. The
- // strip is the dashboard's own content and has a separate
- // `dashboard-tile-strip-skeleton` marker for its loading phase, so the two
- // states cannot be confused for one another.
+ // throttle: `#main-content` at 759 ms, the tile strip at 4896 ms.
+ //
+ // The gate is the revealed chart cell rather than the tile strip because
+ // an unrevealed cell is `invisible` AND `aria-hidden` — the chart cards
+ // are simply not in the accessibility tree, so axe walks past the whole
+ // chart row without reading it. Gating on the strip alone left it a
+ // coin flip whether the row was scanned at all: locally the cells were
+ // still hidden at strip time and revealed six seconds later, while the
+ // runner that reported the heading-order break had already revealed
+ // them. The row is the dashboard's largest surface; the scan has to see
+ // it every time or not claim to have checked the page.
name: "/ dashboard",
path: "/",
- painted: (page) => page.locator('[data-slot="dashboard-tile-strip"]'),
+ painted: (page) =>
+ page
+ .locator('[data-slot="dashboard-chart-cell"][data-revealed="true"]')
+ .first(),
},
{
// The settings heading belongs to ``, i.e. the chrome —