From fec8ae18d33d9ddcdeb615939a383d9afee2046e Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 5 Aug 2026 11:58:17 +0000 Subject: [PATCH 1/6] fix(privacy): keep the back control clear of the notch on phones /privacy sits outside the search shell, so it owns the OS top inset itself. searchPageShell's py-3 top pad is not enough under a status bar or Dynamic Island when apple-mobile-web-app-status-bar-style=black-translucent, leaving the back control partly behind the notch. Replaces the top pad with max(0.75rem, var(--safe-area-top)) (1.25rem at sm) and gives the back control a min-h-12 row, matching this repo's 48px production tap-target rule rather than the generic 44px guidance. Split out of the stale draft PR #1582, which mixed this production fix with three redesign mockups and 4.2 MB of PNG screenshots and has been red since 2026-08-02 on an unrelated type-scale violation (text-[10px]) in one of those mockup components. The fix is independent of that work and does not need to wait for it. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01T97Kqdj9Xh1Cubv5ms3KVy --- src/app/privacy/page.tsx | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/app/privacy/page.tsx b/src/app/privacy/page.tsx index 505c1a9c3d..a522d4ba27 100644 --- a/src/app/privacy/page.tsx +++ b/src/app/privacy/page.tsx @@ -75,12 +75,26 @@ const SECTIONS: Section[] = [ export default function PrivacyPage() { return (
-
+ {/* + Privacy sits outside the search shell, so this page owns the OS top inset. + Replacing searchPageShell's py-3 top pad with max(safe-area-top) keeps the + back control below the status bar / Dynamic Island on notched phones + (apple-mobile-web-app-status-bar-style=black-translucent) without stacking + a second unused pad under the notch. + */} +
-
- }> - - +
+
+ }> + + +

{privacyCopy.pageEyebrow}

From fa6d6276257c171d67a24b8a5af1d9350f31ffa7 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 5 Aug 2026 16:26:19 +0000 Subject: [PATCH 2/6] fix(ui): share standalone shell with safe-area top pad Extract searchPageShellStandalone so /privacy and /reference/colour-coding own the OS top inset without py-/pt- override ordering, use min-h-tap for the back row, and pin the contract with a static render test. Co-authored-by: BigSimmo --- src/app/privacy/page.tsx | 18 ++---- src/app/reference/colour-coding/page.tsx | 8 ++- src/components/ui-primitives.tsx | 6 ++ ...rch-page-shell-standalone.contract.test.ts | 62 +++++++++++++++++++ 4 files changed, 78 insertions(+), 16 deletions(-) create mode 100644 tests/search-page-shell-standalone.contract.test.ts diff --git a/src/app/privacy/page.tsx b/src/app/privacy/page.tsx index a522d4ba27..bbd2c26b65 100644 --- a/src/app/privacy/page.tsx +++ b/src/app/privacy/page.tsx @@ -10,7 +10,7 @@ import { raisedCard, searchPageCanvas, searchPageContainer, - searchPageShell, + searchPageShellStandalone, } from "@/components/ui-primitives"; import { privacyCopy } from "@/lib/ui-copy"; @@ -76,21 +76,13 @@ export default function PrivacyPage() { return (
{/* - Privacy sits outside the search shell, so this page owns the OS top inset. - Replacing searchPageShell's py-3 top pad with max(safe-area-top) keeps the - back control below the status bar / Dynamic Island on notched phones - (apple-mobile-web-app-status-bar-style=black-translucent) without stacking - a second unused pad under the notch. + Privacy sits outside the search shell, so this page owns the OS top inset + via searchPageShellStandalone (max(safe-area-top) baked into the pad). */} -
+
-
+
}> diff --git a/src/app/reference/colour-coding/page.tsx b/src/app/reference/colour-coding/page.tsx index 4e8cb09c77..306d8b6a08 100644 --- a/src/app/reference/colour-coding/page.tsx +++ b/src/app/reference/colour-coding/page.tsx @@ -9,7 +9,7 @@ import { raisedCard, searchPageCanvas, searchPageContainer, - searchPageShell, + searchPageShellStandalone, } from "@/components/ui-primitives"; import { CONTENT_DOMAIN_META, CONTENT_DOMAIN_ORDER, flagsForDomain } from "@/lib/semantic-flags"; import { SEMANTIC_TONE_META, SEMANTIC_TONES } from "@/lib/semantic-tone"; @@ -41,10 +41,12 @@ export default function ColourCodingReferencePage() { "focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-[color:var(--focus)]", )} > -
+
- +
+ +

Reference

diff --git a/src/components/ui-primitives.tsx b/src/components/ui-primitives.tsx index 98aa714c8b..0b3a14f177 100644 --- a/src/components/ui-primitives.tsx +++ b/src/components/ui-primitives.tsx @@ -157,6 +157,12 @@ export const searchPageCanvas = "bg-[color:var(--background)] text-[color:var(-- // a second dock-sized safe-area pad into page shells. export const searchPageShell = "min-h-0 overflow-x-clip px-3 py-3 pb-4 sm:min-h-[calc(100dvh-var(--shell-header-h))] sm:px-5 sm:py-5 sm:pb-8 lg:px-6"; +// Standalone pages outside the search shell own the OS top inset themselves +// (apple-mobile-web-app-status-bar-style=black-translucent). Bake max(safe-area) +// into the top pad and omit py-* so cn() call sites never rely on Tailwind's +// side-vs-axis utility sort order to win over searchPageShell's py-3/sm:py-5. +export const searchPageShellStandalone = + "min-h-0 overflow-x-clip px-3 pt-[max(0.75rem,var(--safe-area-top))] pb-4 sm:min-h-[calc(100dvh-var(--shell-header-h))] sm:px-5 sm:pt-[max(1.25rem,var(--safe-area-top))] sm:pb-8 lg:px-6"; export const searchPageContainer = "mx-auto w-full max-w-[1500px]"; // Canonical content-page width. Detail pages (service / form / differential), // medication record + prescribing workspace, and the forms results view converge diff --git a/tests/search-page-shell-standalone.contract.test.ts b/tests/search-page-shell-standalone.contract.test.ts new file mode 100644 index 0000000000..be1c21ba63 --- /dev/null +++ b/tests/search-page-shell-standalone.contract.test.ts @@ -0,0 +1,62 @@ +import { readFileSync } from "node:fs"; +import { createElement } from "react"; +import { renderToStaticMarkup } from "react-dom/server"; +import { describe, expect, it, vi } from "vitest"; + +import PrivacyPage from "@/app/privacy/page"; +import ColourCodingReferencePage from "@/app/reference/colour-coding/page"; +import { searchPageShell, searchPageShellStandalone } from "@/components/ui-primitives"; + +vi.mock("next/navigation", () => ({ + useSearchParams: () => new URLSearchParams(), + useRouter: () => ({ + back: vi.fn(), + push: vi.fn(), + }), +})); + +const read = (relativePath: string) => readFileSync(new URL(`../${relativePath}`, import.meta.url), "utf8"); + +const STANDALONE_PAGES = ["src/app/privacy/page.tsx", "src/app/reference/colour-coding/page.tsx"] as const; + +describe("searchPageShellStandalone contract", () => { + it("owns the OS top inset without axis py-* that would fight side-specific pt-*", () => { + expect(searchPageShellStandalone).toContain("pt-[max(0.75rem,var(--safe-area-top))]"); + expect(searchPageShellStandalone).toContain("sm:pt-[max(1.25rem,var(--safe-area-top))]"); + expect(searchPageShellStandalone).toContain("pb-4"); + expect(searchPageShellStandalone).toContain("sm:pb-8"); + // Axis padding would reintroduce the Tailwind sort-order dependency that + // made per-page pt-* overrides fragile when cn() does not de-dupe utilities. + expect(searchPageShellStandalone).not.toMatch(/(?:^|\s)py-\S+/); + expect(searchPageShellStandalone).not.toMatch(/(?:^|\s)sm:py-\S+/); + // Shell pages keep the ordinary py rhythm; standalone is the only owner of + // max(safe-area-top) so future routes do not re-derive the values. + expect(searchPageShell).toMatch(/(?:^|\s)py-3(?:\s|$)/); + expect(searchPageShell).not.toContain("var(--safe-area-top)"); + }); + + it("is the shell used by every production standalone search-page route", () => { + for (const relativePath of STANDALONE_PAGES) { + const source = read(relativePath); + expect(source, relativePath).toContain("searchPageShellStandalone"); + expect(source, relativePath).not.toMatch(/\bsearchPageShell\b(?!Standalone)/); + } + }); + + it("renders the safe-area top pad and tap-token back row on /privacy", () => { + const markup = renderToStaticMarkup(createElement(PrivacyPage)); + expect(markup).toContain(searchPageShellStandalone); + expect(markup).toContain("pt-[max(0.75rem,var(--safe-area-top))]"); + expect(markup).toContain("sm:pt-[max(1.25rem,var(--safe-area-top))]"); + expect(markup).toContain("min-h-tap"); + expect(markup).not.toContain("min-h-12"); + }); + + it("renders the same safe-area pad and tap-token back row on /reference/colour-coding", () => { + const markup = renderToStaticMarkup(createElement(ColourCodingReferencePage)); + expect(markup).toContain(searchPageShellStandalone); + expect(markup).toContain("pt-[max(0.75rem,var(--safe-area-top))]"); + expect(markup).toContain("sm:pt-[max(1.25rem,var(--safe-area-top))]"); + expect(markup).toContain("min-h-tap"); + }); +}); From 23a4b8f17236d219b84d4cac140e37d2efd2e48c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 5 Aug 2026 17:04:15 +0000 Subject: [PATCH 3/6] docs(ledger): record PR #1621 babysit closeout Co-authored-by: BigSimmo --- docs/branch-review-ledger.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index c3eff3d868..59f313a6f2 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -641,3 +641,5 @@ Records before 2026-07-28 were written by hand and had drifted: 146 lines carrie | 2026-08-05 | codex/v2-design-system-completion | 8863cea53bf4df59e8795dcaab1fa420b5109516 | PR #1616 v2 design system CI+reviews | fixed typecheck + review defects; baselines remain not-committed by design | tsc; vitest ui-v2/accessible-table/ui-primitives/design-system-adoption | | 2026-08-05 | codex/v2-design-system-completion | 8b49bfa2b66ed78577b08e1a50414db55898f2df | PR #1616 v2 design system CI+reviews | fixed typecheck + review defects; baselines remain not-committed by design | tsc; vitest ui-v2/accessible-table/ui-primitives/design-system-adoption | | 2026-08-05 | codex/v2-design-system-completion | 1ea30c19c48b3791519659abf85b20c9e8c7faab | PR #1616 review feedback disposition | COMPLETED: fixed mode-home empty composer reserve + chip category v2 tokens; dispositioned remaining Devin analysis findings to outstanding-issues; unresolved threads 0 | vitest search-route-ownership 12/12; ckb-v2-token-contract+related green; CI in progress on tip | +| 2026-08-05 | claude/privacy-notch-safe-area | efc29559ede819daeec99d46e9147fe08a73bdb0 | PR #1621 babysit standalone-shell review fixes | fixed: shared searchPageShellStandalone + min-h-tap + contract; main synced; CI green CLEAN; Bugbot clean; no open threads | test:5331-pass; build:pass; lint:pass; typecheck:pass; rag-fixtures:pass; CI#31025767142:pr-required-success; bugbot:no-bugs | +| 2026-08-05 | claude/privacy-notch-safe-area | ef92d628683cc6a47c07b1a901b73f22d9474149 | PR #1621 babysit standalone-shell review fixes | fixed: shared searchPageShellStandalone + min-h-tap + contract; main synced; CI green CLEAN; Bugbot clean; no open threads; ledger closeout tip | test:5331-pass; build:pass; lint:pass; typecheck:pass; rag-fixtures:pass; CI#31025767142:pr-required-success; bugbot:no-bugs | From a3967f8f0ee05b9a3ab922cd4a9feeebdf7efbbc Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 5 Aug 2026 19:29:45 +0000 Subject: [PATCH 4/6] chore(ledger): supersede unresolvable PR #1621 review HEAD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses Devin — the prior babysit closeout row pointed at a SHA that does not exist in the repo, so ledger lookup could never match it. --- docs/branch-review-ledger.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 1fd7d132b5..85c4f65b9b 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -650,3 +650,6 @@ Records before 2026-07-28 were written by hand and had drifted: 146 lines carrie | 2026-08-05 | cursor/phone-mode-sheet-yes-05c0 | 9152e239076ff823ad3bf812d282ffc87c3295b7 | Run PR sweep | threads already resolved; merged origin/main; CI was green pre-sync | CI: PR required SUCCESS pre-sync; merge-tree clean | | 2026-08-05 | codex/editable-search-pins | 67f2c71e8376fbc44615f765a73cb6a37c4ddb40 | editable search pins menu review follow-up | merged main; review threads cleared; auto-merge armed | vitest search-pins+mode-action+command-surface: Test Files 4 passed (4); Tests 33 passed (33); eslint max-warnings 0 on touched surfaces | | 2026-08-05 | claude/review-open-prs-fewxlh | 4e384f4cef1a0c3f15bc8ca3d907920b7c097541 | Run PR sweep | merged main; fixed 3 Devin findings (busy heuristic, typecheck excludes, scripts-index) | vitest: tests/guard-push.test.ts 23 passed; self-test passed | +| 2026-08-05 | claude/privacy-notch-safe-area | dba8c9f72fdb84495736b7c8a5c723f4153b3612 | PR #1621 babysit standalone-shell review fixes | supersede: prior row HEAD ef92d628 was unresolvable; tip after main sync is this SHA; product fixes unchanged | ledger:append correction; merge-tree clean vs main | +| 2026-08-05 | claude/privacy-notch-safe-area | dba8c9f72fdb84495736b7c8a5c723f4153b3612 | Run PR sweep | merged main (stale DIRTY); superseded unresolvable ledger HEAD per Devin; no product threads open | merge-tree clean | +| 2026-08-05 | claude/privacy-notch-safe-area | 0fbb5686ace5368f6a21b07089db886e639c095a | PR #1621 babysit standalone-shell review fixes | supersede: prior row HEAD ef92d628 was unresolvable; tip after main sync is this SHA; product fixes unchanged | ledger:append correction; merge-tree clean vs main | From fe352adb18d3260f0646f64ef992376e1449b0de Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 5 Aug 2026 19:30:16 +0000 Subject: [PATCH 5/6] chore(ledger): point PR #1621 babysit supersede at pushed tip --- docs/branch-review-ledger.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 85c4f65b9b..0c70ae580b 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -653,3 +653,4 @@ Records before 2026-07-28 were written by hand and had drifted: 146 lines carrie | 2026-08-05 | claude/privacy-notch-safe-area | dba8c9f72fdb84495736b7c8a5c723f4153b3612 | PR #1621 babysit standalone-shell review fixes | supersede: prior row HEAD ef92d628 was unresolvable; tip after main sync is this SHA; product fixes unchanged | ledger:append correction; merge-tree clean vs main | | 2026-08-05 | claude/privacy-notch-safe-area | dba8c9f72fdb84495736b7c8a5c723f4153b3612 | Run PR sweep | merged main (stale DIRTY); superseded unresolvable ledger HEAD per Devin; no product threads open | merge-tree clean | | 2026-08-05 | claude/privacy-notch-safe-area | 0fbb5686ace5368f6a21b07089db886e639c095a | PR #1621 babysit standalone-shell review fixes | supersede: prior row HEAD ef92d628 was unresolvable; tip after main sync is this SHA; product fixes unchanged | ledger:append correction; merge-tree clean vs main | +| 2026-08-05 | claude/privacy-notch-safe-area | a3967f8f0ee05b9a3ab922cd4a9feeebdf7efbbc | PR #1621 babysit standalone-shell review fixes | supersede: prior row HEAD ef92d628 was unresolvable; tip after main sync is this SHA; product fixes unchanged | ledger:append correction; merge-tree clean vs main | From f9a72c3072c10aadd730ca379ed4267f2a45c550 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 5 Aug 2026 19:30:16 +0000 Subject: [PATCH 6/6] chore(ledger): refresh PR #1621 Run PR sweep tip --- docs/branch-review-ledger.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 0c70ae580b..f8b002e788 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -654,3 +654,4 @@ Records before 2026-07-28 were written by hand and had drifted: 146 lines carrie | 2026-08-05 | claude/privacy-notch-safe-area | dba8c9f72fdb84495736b7c8a5c723f4153b3612 | Run PR sweep | merged main (stale DIRTY); superseded unresolvable ledger HEAD per Devin; no product threads open | merge-tree clean | | 2026-08-05 | claude/privacy-notch-safe-area | 0fbb5686ace5368f6a21b07089db886e639c095a | PR #1621 babysit standalone-shell review fixes | supersede: prior row HEAD ef92d628 was unresolvable; tip after main sync is this SHA; product fixes unchanged | ledger:append correction; merge-tree clean vs main | | 2026-08-05 | claude/privacy-notch-safe-area | a3967f8f0ee05b9a3ab922cd4a9feeebdf7efbbc | PR #1621 babysit standalone-shell review fixes | supersede: prior row HEAD ef92d628 was unresolvable; tip after main sync is this SHA; product fixes unchanged | ledger:append correction; merge-tree clean vs main | +| 2026-08-05 | claude/privacy-notch-safe-area | fe352adb18d3260f0646f64ef992376e1449b0de | Run PR sweep | merged main (stale DIRTY); superseded unresolvable ledger HEAD per Devin; no product threads open | merge-tree clean |