PLAT-6338 · build handoff

Kelbin iOS — what to build, and what to delete.

The design pack is the source of truth for layout and copy: the clickable pack. This page is what a developer needs that the pack cannot show — the token map, the navigation delta against the current RootNavigator, and the API gaps the design assumes.

22 screens, clickable Repo conversite-ios → rename Branch cole/ios-companion-phase1-staging-build-20260721

1 · What is there today

The shipped app is Conversite-era. Three tabs, one of them a person's name, and a screen no tab can reach:

TodayFileBecomes
Trevor tabHomeScreen.tsx Home — Needs You queue + handled-today receipts + today's bookings glance
Activity tabInboxScreen.tsx Chats — conversations list, with a live marker
Site tabSettingsScreen.tsx Split: Settings (behind the gear) and Your site (a glance, inside Settings)
ThreadThreadScreen.tsx Thread — plus Join & take over, the takeover banner, and long-thread collapse
Leads (orphaned)LeadsScreen.tsx Delete. Leads were derived from thread profileFacts; the honest surface is Customers, which is a real list of people, not an inferred score
Sign inSignInScreen.tsx Sign in + a new First run (calendar, cutoff, notifications)
Nothing named Trevor or Conversite may survive.

That includes the tab label, the app name, bundle identifier strings shown to users, push copy, and the sign-in screen. The pack's check suite fails if either word appears anywhere in the rendered app.

2 · Navigation delta

Current: a 3-tab createBottomTabNavigator with a native stack above it. Target keeps that shape and changes the contents.

LevelScreensNotes
Auth stackSignIn → FirstRun FirstRun is skippable and re-enterable from Settings
Tabs (4)Home · Bookings · Chats · Customers Bookings and Customers are new. Order is deliberate: it matches how often an owner opens them
Pushed (no tab bar) BookingDetail, Thread, Customer, Notifications, Settings, Calendar, Site, Conflict tabBarStyle:{display:'none'} on these routes — the pack hides the tab bar on every pushed screen so the bottom edge belongs to that screen's one action
Modal sheetsAsk Kelbin, Business switcher Presented as native sheets; the ask sheet is reachable from Home, Bookings, Chats, Customers

Two rules that are easy to get wrong

3 · Token map

Ported from the marketing site's marketing.css. Same values, new names for the two roles a website never needed.

TokenLightDarkUse
--bg#FBFAF6 #101725Screen ground
--card#FFFEFB #18202FCards, tab bar, fields
--line#E9E3DA #283243Hairlines, borders
--ink#152033 #F3F1ECText only
--slate#697386 #9AA4B8Secondary text
--indigo#5B5CEB #8B8CFFThe one accent
--indigo-soft#ECEBFF #242B4AKelbin's chat bubbles
--strong#152033 #2B3550 Inverse surface — ask bar, your bubbles, count pills
--on-strong#FFFEFB #F5F3EEText on --strong
--on-accent#FFFFFF #101725Text on --indigo
--green / --terra / --amber #5E8C68 #C97855 #D5A249 same Fills only, never text. None clears 4.5:1 as type on either ground
The trap that cost me two rounds: --ink is a text token.

Anything that fills with it needs its own pair. Filling with --ink made dark mode flip the ask bar to near-white with near-white text — measured at 1.12:1. That is why --strong / --on-strong exist, and why in dark the strong surface raises to #2B3550 rather than inverting.

Second trap, native and web alike: color inherits a computed value. The themed container must re-declare color from the token or text that only inherits from the root never repaints in dark.

Type

RoleFaceSize / weightWhere
DisplayNewsreader 50025–27 / 1.15 Greeting, screen titles, sheet titles. Never inside a list row
BodyFigtree 400–65013.5–15Everything else
LabelFigtree 80011 / .10em, uppercaseSection labels, bylines
NumeralsFigtree, tabular-nums Times, prices, counts — anything that lines up in a column

Spacing is a 4px grid; radii are 11 (buttons) / 14–15 (cards) / 22 (sheets) / 99 (pills). Minimum tap target is 44px — the first draft shipped 36px and the suite now fails under 40.

4 · Screen notes

Full rationale sits beside each screen in the pack. The decisions a developer needs to preserve:

5 · API gaps this design assumes

Written against what conversite-api exposes today. Each of these is a build dependency, not a design nicety.

NeedsStatus todayGap
Needs You queueDoes not exist New. A typed list of pending owner decisions (reschedule inside cutoff, unanswered question, staged site change, conflict) with a reason string, and approve/decline endpoints. The reason string is required — the UI has nowhere to invent it
Handled-today receiptsDoes not exist New. An append-only activity feed of completed agent actions with timestamps, scoped to the business and the day
BookingsNot in the mobile client List by day, booking detail, reschedule/cancel, and free-gap computation (the agenda renders gaps, so either the API returns them or the client needs bookable hours + buffer)
Booking ↔ conversation linkUnknown Both directions. A booking must carry the thread that produced it
CustomersOnly derived profileFacts via agentSetting A real customer entity with visit history and next booking, searchable server-side, plus CSV export (emailed, not downloaded — the viewer cannot save files)
Multi-businessSingle site assumed List of businesses for the signed-in owner, and every call scoped by business. This is the biggest structural gap
CalendarNot exposed to mobile Connection status, calendar list, which calendar receives bookings, bookable hours, buffer, and the cutoff value
Pushdevice_tokens + notifyVisitorArrived exist Extend to the Needs You classes; the design promises "roughly twice a week", so arrival-of-a-visitor must not be a push by default
Site statusNot exposed to mobile Live URL, last published, and the list of staged changes with a publish action
TakeoverPartially exists Owner join, owner send, and release-back-to-Kelbin, with the socket emitting on owner replies (today it only emits on host replies)
OfflineNone A last-sync timestamp and a queue for decisions made offline
Auth is already solved — do not redo it.

Track B of the 2026-07-07 launch added POST /auth/mobile/signin (bearer in body) and an AuthGuard bearer fallback, because the web flow is cookie-only. The redesign changes none of that.

6 · Verification

The pack ships with a Playwright suite (acheck.mjs, 39 checks) covering the four marketing promises, shell-surface parity, tab set, tap targets, sideways scroll on every screen, dark-mode contrast ratios computed in-page, and distinct Kelbin portraits. Key checks were mutation-tested — reintroducing the flipping fill, the 36px tap target and a wrong-but-loadable image path each failed the suite as they should.

Assets: kelbin-avatar.webp and kelbin-greet.webp are portraits 3 and 1 of the supplied set — deliberately not any of the four used on the marketing site.