Moatbrain Copy & type pre-build audit T3 · review only staging 7cd604b

No coach has a face — and two of the three copy lines are already gone

Audited live on what is actually deployed. The task named 86b9bd1; staging.ownermind.co reports sourceSha 7cd604b, built 06:12Z. 86b9bd1 is an ancestor — staging is three commits ahead, and those commits touch CoachExperience.tsx, ReviewLanePanel.tsx, app.screener.tsx and app.css: all three of the areas asked about. Auditing the named commit would have reported problems that were already fixed.

Q1Why June Sato has no face

The premise needs one correction, and it makes the fix easier

It is not a June bug, and it is not a code bug. No coach has a portrait. All four coaches are the same silhouette glyph in four tints. The nine specialists do have real photographs — which is why the gap only becomes obvious once a coach is selected and sits in the drawer header directly above them.

The Change research coach dialog showing Nadia Halim, Daniel Osei, June Sato and Arthur Lindqvist — all four rendered as the same generic head-and-shoulders silhouette in four different tints.
All four coaches, as deployed. Tan, green, mauve, blue-grey — one glyph, four colours.

Everything in the rendering path checks out, which is why this looks like a bug and is not one. Verified end to end on the deployed build:

This one is mine. The deployed asset is byte-identical to mocks/ownermind-coaches/sana-coach-sprite.webp — same SHA. I shipped that sheet as a placeholder alongside the avatar CSS on 31 Jul so the frame could be built against something, and it was consumed literally. The real portraits were never commissioned. My own comment in that stylesheet says the frame is sized so "a Tavus still and a talking-head loop are interchangeable without reflow" — the slot was always meant to hold a real face.

ActionWhatWhere
Change Produce four coach portraits at 418×418 into the same 2×2 sheet, same byte layout. No code, no CSS, no class changes — the mapping is already correct and will pick them up. Precedent for the treatment is the CourseVenture companion roster. apps/web-start/public/
sana-coach-sprite.webp
Change Adjacent defect, found while checking the sprites. Specialist av9 (Lena Ortiz) renders from the base sheet at 0% 0% — the same cell as av0. My extension rule is scoped .sana-source .drawer .roster .ava.av9, but the live element is .coach-specialist-face.av9, so it never matches and the ext sprite is never loaded. Rescope the selector to the class actually rendered. sana-avatars.css
(the .roster .ava block)

Q2Redundant and legacy instructional copy

Walked Screener plus all ten assessment stages · 213 distinct strings collected

All three lines named in the brief are already removed on the deployed build. They exist at 86b9bd1 and are gone by 7cd604b. Nothing to do — recorded here so the same items are not re-reported.

ActionLineWas atStatus on staging
Done "Ask the selected agent" ReviewLanePanel.tsx:463
<span class="sr-only">
This was screen-reader-only text, not visible copy. Correctly replaced with aria-label="Message your coach or specialist" — migrated, not dropped.
Done "Drafts arrive as cards you accept, edit or ignore…" ReviewLanePanel.tsx:507Removed.
Done "Research time protected: evaluated…" app.screener.tsx:387Removed.

What is left. The work area is clean — only three instructional strings repeat across stages at all, and each belongs to the stage it is on. The remaining redundancy is in the drawer:

ActionLineRepeatsWhy
Change "I'll stay with what is on this page, question the gaps, and keep every draft separate until you decide what enters your assessment." 12.5px 11 of 11
surfaces
The one genuinely redundant line. It is a promise about how the coach behaves, which is true once and does not change per stage. Show it on first session or until dismissed, not on every navigation.
Keep "Your coach: <name>" 14px 11 of 11 Identity, not instruction. It should persist.
Keep The nine specialist descriptors — "Source coverage, citations, and evidence gaps", "Independent counter-work and falsifiers", … 9.5px, visible 11 of 11 These are the only thing distinguishing nine faces, and they double as the buttons' accessible names. Keep the text. If the roster is ever collapsed to faces alone, move them to title/aria-label — do not delete.
Keep "Why does it matter?" 13px 11 of 11 A control, not a caption.

Q3Font-size inconsistencies

The item named is real, and the reason is not size alone. "Include delisted companies" is label.check at 12.5px, sentence case. Every other field label in the same Screener form is a .flab span at 9.5px, uppercase, letterspaced. It is ~32% larger and in a different case treatment than its immediate siblings — that mismatch is what reads as oversized. .check is declared only in app.css, away from the rest of the form's typography.

The larger finding underneath it: the field-label tier is defined on only 5 of 11 stages. .flab gets the 9.5px uppercase treatment on screener, first-pass, decision, allocation, monitoring. On risk, memo, compound, quality and valuation no font-size rule applies, so labels fall back to the 16px root default — bigger than the 13px panel headings that contain them, and second only to the page title. Measured on Risk: 12 of 12 labels at 16px.

A grep for the selector is misleading here, which is worth flagging to whoever fixes it: .sana-source.sana-page-risk .flab does exist, at sana-pages.css:822 — but it only sets overflow-wrap:anywhere. The element matches the rule and still has no size.

ActionWhatWhere
Change Give .flab one declaration that is not page-scoped — 9.5px, uppercase, .13em tracking, var(--cream-3) — and delete the five per-page copies. Field labels are not a per-stage decision. sana-pages.css:82, 225, 894
+ allocation, monitoring
Change Fold .check into the same tier so a checkbox label matches the labels beside it, and move it out of app.css into the page stylesheet with the rest of the form. app.css
Change Page titles render 32px. The hierarchy verified in July was 27 for page titles and 20 for section headings. Risk currently carries 8 distinct font sizes, Screener 6. stage stylesheets

A11yAccessibility text that must remain

No regression from the copy pass — I checked before assuming one. All seven Screener form controls have a real accessible name; none relies on a placeholder; no orphaned .sr-only nodes are left behind. The items below are the accessible name itself, so they will look like removable instructional copy and are not:

!What this audit did not cover

Audited live against staging.ownermind.co, sourceSha 7cd604b (builtAt 2026-08-03T06:12:35Z), signed in on an isolated QA account. Named ref 86b9bd1 is an ancestor, three commits behind. Review only — no code, no deploy, no routing.