Parent: #504. This is a frontend-only hardening child. It does not satisfy or claim #507.\n\nOfficer impact: The future My Account photo/discoverability controls and guarded People finder will communicate name eligibility, file errors, and completed searches more clearly to keyboard and screen-reader users. The production preview remains disabled and the backend remains NOT AVAILABLE YET.\n\nOfficer documentation: Update SYSTEM_DESIGN.md, IMPLEMENTATION_PLAN.md, SECURITY.md, and the optional-profile/People-finder source-review section of docs/officers/EVENTS_SHOP_MEMBERS.md.\n\nDeployment evidence: None at issue creation. Closure requires reviewed source, focused and full frontend checks, a diagnostic production build, lint-baseline verification, independent privacy/accessibility/backup-officer review, exact-main CI, and explicit proof that the availability constant remains false and Firebase, providers, accounts, sign-in, and production data were unchanged. A merge is not a website publication.\n\n## Atomic outcome\n\nHarden the already-preserved connected frontend before the later protected backend release:\n\n- derive the UI name prerequisite from the same bounded Unicode display-name eligibility contract used by the directory projection, instead of treating every non-empty Profile name as searchable;\n- keep an existing opt-in removable even when the current name is ineligible;\n- give no-photo placeholders image semantics;\n- link photo and validation errors to the control that needs attention without leaking provider details;\n- announce a successful non-empty finder response without exposing a result total;\n- give the search field accurate validation state and descriptions;\n- improve checkbox, input, and narrow-screen interaction geometry;\n- add stale FileReader/account-change and display-name/update coverage using generated synthetic inputs only.\n\n## Exact ownership\n\nRuntime and tests:\n- src/pages/account/Account.tsx — directory display-name prop only.\n- src/pages/account/Account.test.tsx — separately named current-name/child-prop coverage only.\n- src/pages/account/MemberDirectoryProfile.tsx — pure display-name eligibility, control semantics/copy, placeholder semantics, and stale file-read containment only.\n- src/pages/account/MemberDirectoryProfile.test.tsx — separately named eligibility/accessibility/stale-file blocks and minimum expectation updates only.\n- src/pages/account/Account.css — member-directory control/touch/narrow-screen selectors only.\n- src/pages/admin/members/AdminMemberDirectory.tsx — search field semantics, non-counting completion status, and minimum narrow-control classes only.\n- src/pages/admin/members/AdminMemberDirectory.test.tsx — separately named accessibility/completion/narrow-state blocks and minimum expectation updates only.\n\nDocumentation:\n- SYSTEM_DESIGN.md — one separately named source-only frontend-hardening paragraph.\n- IMPLEMENTATION_PLAN.md — one separately named current optional-directory boundary update.\n- SECURITY.md — one separately named source-only privacy/accessibility ledger row; preserve every unrelated risk and #616 hunk.\n- docs/officers/EVENTS_SHOP_MEMBERS.md — one separately named source-review/procedure update inside the existing optional profile-photo and People-finder section.\n\n## Invariants\n\n- src/services/account/memberDirectoryAvailability.ts remains byte-for-byte false.\n- The default Account and admin branches remain inert: no file/name accepted, no request ID, no directory service/context initialization, no read/upload/search/save, and no result cards.\n- AdminGuard remains the route boundary.\n- Search remains explicit-submit name-prefix only with voluntary thumbnails. No photo query, face recognition, image matching, similarity score, embedding, biometric processing, roster inference, export, total, or pagination.\n- No real name/photo/account/data appears in tests or evidence.\n- No Functions, Rules, indexes, services, packages, workflows, release control, provider configuration, or production data changes.\n- Preserve #616 Strava paths and its separately named SECURITY row byte-for-byte.\n- #507 retains privacy approval, scoped authorization, staging, backend-first deployment/readback, availability flip, connected publication, and live proof.\n\n## Acceptance criteria\n\n- [ ] Names that cannot enter the bounded directory projection are described as ineligible and cannot newly opt in through the connected UI.\n- [ ] Existing opt-in remains turn-off-able after a name becomes missing or ineligible.\n- [ ] Profile placeholders expose image semantics and action errors are associated with the relevant control.\n- [ ] Search validation is programmatically tied to the input; non-empty result completion is announced without a count.\n- [ ] Native controls remain keyboard-operable, readable, and contained at 320 pixels in the available test/evidence harness.\n- [ ] A deferred file read cannot submit or render after the app/account identity changes.\n- [ ] Focused and relevant full tests, typecheck/build, lint baseline, and diff checks pass.\n- [ ] Independent privacy/security, frontend/accessibility, and backup-officer reviews return GO.\n- [ ] The pull request reports Officer impact, Officer documentation, and Deployment evidence separately.\n\n## Stop conditions\n\nStop on an availability flip, enabled production-preview control, backend/provider/data mutation, real personal data, a biometric request, an authorization change, an unbounded result, raw error/detail exposure, an exact active claim collision, or any attempt to publish the accumulated main artifact.\n\n## Claim protocol\n\nAssign this issue and post CLAIMED by /root at an exact UTC timestamp with branch name and exact base before editing. The claim remains active through reviewed merge, exact-main CI/deployment audit, closure, and an explicit COMPLETE/CLOSED/CLAIM RELEASED marker.
Parent: #504. This is a frontend-only hardening child. It does not satisfy or claim #507.\n\nOfficer impact: The future My Account photo/discoverability controls and guarded People finder will communicate name eligibility, file errors, and completed searches more clearly to keyboard and screen-reader users. The production preview remains disabled and the backend remains NOT AVAILABLE YET.\n\nOfficer documentation: Update SYSTEM_DESIGN.md, IMPLEMENTATION_PLAN.md, SECURITY.md, and the optional-profile/People-finder source-review section of docs/officers/EVENTS_SHOP_MEMBERS.md.\n\nDeployment evidence: None at issue creation. Closure requires reviewed source, focused and full frontend checks, a diagnostic production build, lint-baseline verification, independent privacy/accessibility/backup-officer review, exact-main CI, and explicit proof that the availability constant remains false and Firebase, providers, accounts, sign-in, and production data were unchanged. A merge is not a website publication.\n\n## Atomic outcome\n\nHarden the already-preserved connected frontend before the later protected backend release:\n\n- derive the UI name prerequisite from the same bounded Unicode display-name eligibility contract used by the directory projection, instead of treating every non-empty Profile name as searchable;\n- keep an existing opt-in removable even when the current name is ineligible;\n- give no-photo placeholders image semantics;\n- link photo and validation errors to the control that needs attention without leaking provider details;\n- announce a successful non-empty finder response without exposing a result total;\n- give the search field accurate validation state and descriptions;\n- improve checkbox, input, and narrow-screen interaction geometry;\n- add stale FileReader/account-change and display-name/update coverage using generated synthetic inputs only.\n\n## Exact ownership\n\nRuntime and tests:\n- src/pages/account/Account.tsx — directory display-name prop only.\n- src/pages/account/Account.test.tsx — separately named current-name/child-prop coverage only.\n- src/pages/account/MemberDirectoryProfile.tsx — pure display-name eligibility, control semantics/copy, placeholder semantics, and stale file-read containment only.\n- src/pages/account/MemberDirectoryProfile.test.tsx — separately named eligibility/accessibility/stale-file blocks and minimum expectation updates only.\n- src/pages/account/Account.css — member-directory control/touch/narrow-screen selectors only.\n- src/pages/admin/members/AdminMemberDirectory.tsx — search field semantics, non-counting completion status, and minimum narrow-control classes only.\n- src/pages/admin/members/AdminMemberDirectory.test.tsx — separately named accessibility/completion/narrow-state blocks and minimum expectation updates only.\n\nDocumentation:\n- SYSTEM_DESIGN.md — one separately named source-only frontend-hardening paragraph.\n- IMPLEMENTATION_PLAN.md — one separately named current optional-directory boundary update.\n- SECURITY.md — one separately named source-only privacy/accessibility ledger row; preserve every unrelated risk and #616 hunk.\n- docs/officers/EVENTS_SHOP_MEMBERS.md — one separately named source-review/procedure update inside the existing optional profile-photo and People-finder section.\n\n## Invariants\n\n- src/services/account/memberDirectoryAvailability.ts remains byte-for-byte false.\n- The default Account and admin branches remain inert: no file/name accepted, no request ID, no directory service/context initialization, no read/upload/search/save, and no result cards.\n- AdminGuard remains the route boundary.\n- Search remains explicit-submit name-prefix only with voluntary thumbnails. No photo query, face recognition, image matching, similarity score, embedding, biometric processing, roster inference, export, total, or pagination.\n- No real name/photo/account/data appears in tests or evidence.\n- No Functions, Rules, indexes, services, packages, workflows, release control, provider configuration, or production data changes.\n- Preserve #616 Strava paths and its separately named SECURITY row byte-for-byte.\n- #507 retains privacy approval, scoped authorization, staging, backend-first deployment/readback, availability flip, connected publication, and live proof.\n\n## Acceptance criteria\n\n- [ ] Names that cannot enter the bounded directory projection are described as ineligible and cannot newly opt in through the connected UI.\n- [ ] Existing opt-in remains turn-off-able after a name becomes missing or ineligible.\n- [ ] Profile placeholders expose image semantics and action errors are associated with the relevant control.\n- [ ] Search validation is programmatically tied to the input; non-empty result completion is announced without a count.\n- [ ] Native controls remain keyboard-operable, readable, and contained at 320 pixels in the available test/evidence harness.\n- [ ] A deferred file read cannot submit or render after the app/account identity changes.\n- [ ] Focused and relevant full tests, typecheck/build, lint baseline, and diff checks pass.\n- [ ] Independent privacy/security, frontend/accessibility, and backup-officer reviews return GO.\n- [ ] The pull request reports Officer impact, Officer documentation, and Deployment evidence separately.\n\n## Stop conditions\n\nStop on an availability flip, enabled production-preview control, backend/provider/data mutation, real personal data, a biometric request, an authorization change, an unbounded result, raw error/detail exposure, an exact active claim collision, or any attempt to publish the accumulated main artifact.\n\n## Claim protocol\n\nAssign this issue and post CLAIMED by /root at an exact UTC timestamp with branch name and exact base before editing. The claim remains active through reviewed merge, exact-main CI/deployment audit, closure, and an explicit COMPLETE/CLOSED/CLAIM RELEASED marker.