}
/>
+
+
{saveNotice ? (
{saveNotice}
diff --git a/tests/ui-smoke.spec.ts b/tests/ui-smoke.spec.ts
index 738653fe9a..eb501f19ec 100644
--- a/tests/ui-smoke.spec.ts
+++ b/tests/ui-smoke.spec.ts
@@ -1063,7 +1063,10 @@ async function expectAccountProviderLayout(setup: Locator, layout: "row" | "stac
expect(boxes.every(Boolean)).toBe(true);
const [apple, google, microsoft] = boxes as NonNullable<(typeof boxes)[number]>[];
- expect(boxes.every((box) => box!.height >= 48)).toBe(true);
+ // Chromium can report a CSS-enforced 48px minimum as 47.999… after layout
+ // rounding. Keep the clinical touch-target contract while ignoring that
+ // sub-hundredth-pixel measurement noise.
+ expect(boxes.every((box) => box!.height >= 47.99)).toBe(true);
if (layout === "row") {
expect(Math.max(apple.y, google.y, microsoft.y) - Math.min(apple.y, google.y, microsoft.y)).toBeLessThanOrEqual(1);
expect(apple.x + apple.width).toBeLessThanOrEqual(google.x);
@@ -3340,13 +3343,28 @@ test.describe("PsychSift UI smoke coverage", () => {
{ width: 1920, height: 1080 },
]) {
await page.setViewportSize(viewport);
+ // Reading boundingBox() immediately after a resize can race the
+ // reflow — Chromium sometimes reports one sibling's box mid-transition
+ // (seen ~10px short) while the other has already settled. Two rAF
+ // round-trips let layout finish before we measure.
+ await page.evaluate(
+ () => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(() => resolve()))),
+ );
const statusBox = await statusRow.boundingBox();
const sourceOnlyBox = await sourceOnlyDisclosure.boundingBox();
const reviewDueBox = await reviewDueTab.boundingBox();
expect(statusBox).toBeTruthy();
expect(sourceOnlyBox).toBeTruthy();
expect(reviewDueBox).toBeTruthy();
- expect(Math.abs(sourceOnlyBox!.y - reviewDueBox!.y)).toBeLessThanOrEqual(1);
+ // The controls have deliberately different touch-target densities, so
+ // their top edges and centres may differ. Both must still be contained
+ // by the single compact status row rather than wrapping onto a second
+ // line.
+ const statusBottom = statusBox!.y + statusBox!.height;
+ expect(sourceOnlyBox!.y).toBeGreaterThanOrEqual(statusBox!.y - 1);
+ expect(sourceOnlyBox!.y + sourceOnlyBox!.height).toBeLessThanOrEqual(statusBottom + 1);
+ expect(reviewDueBox!.y).toBeGreaterThanOrEqual(statusBox!.y - 1);
+ expect(reviewDueBox!.y + reviewDueBox!.height).toBeLessThanOrEqual(statusBottom + 1);
expect(statusBox!.height).toBeLessThanOrEqual(42);
expect(sourceOnlyBox!.height).toBeLessThanOrEqual(42);
expect(reviewDueBox!.height).toBeLessThanOrEqual(42);
diff --git a/tests/ui-tools.spec.ts b/tests/ui-tools.spec.ts
index 9195b20428..d7623915a2 100644
--- a/tests/ui-tools.spec.ts
+++ b/tests/ui-tools.spec.ts
@@ -2650,6 +2650,31 @@ test.describe("PsychSift tools directory and legacy launcher", () => {
await expect(sectionTrigger).toBeVisible();
await expect(sectionTrigger).toContainText("Overview");
await expectMinTouchTarget(sectionTrigger);
+
+ // The page title already has a dedicated back control in the in-page
+ // header, so the old three-part breadcrumb is intentionally absent. Map
+ // and Related are useful enough to remain visible without opening the
+ // section sheet on a phone.
+ await expect(page.getByRole("navigation", { name: "Breadcrumb" })).toHaveCount(0);
+ const discoveryActions = detailPage.getByRole("navigation", { name: "Explore diagnosis" });
+ await expect(discoveryActions).toBeVisible();
+ const mapAction = discoveryActions.getByRole("button", { name: /^Map/ });
+ const relatedAction = discoveryActions.getByRole("button", { name: /^Related/ });
+ await expectMinTouchTarget(mapAction);
+ await expectMinTouchTarget(relatedAction);
+ await page.emulateMedia({ reducedMotion: "reduce", forcedColors: "active" });
+ await expect(discoveryActions).toBeVisible();
+ await expectNoPageHorizontalOverflow(page);
+ await page.emulateMedia({ reducedMotion: "no-preference", forcedColors: "none" });
+ await mapAction.focus();
+ await expect(mapAction).toBeFocused();
+ await page.keyboard.press("Enter");
+ await expect(sectionTrigger).toContainText("Map");
+ await expect(page).toHaveURL(/[?&]tab=map/);
+ await relatedAction.click();
+ await expect(sectionTrigger).toContainText("Related");
+ await expect(page).toHaveURL(/[?&]tab=related/);
+
await sectionTrigger.click();
const sectionSheet = page.getByTestId("differential-section-sheet");
await expect(sectionSheet).toBeVisible();