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 — LIVE NOW (who is on the site) first, then Needs You, then handled-today receipts, then today's bookings as a glance
Activity tabInboxScreen.tsx Chats — conversations list, opening with who is on the site NOW. Promoted to the SECOND tab: this is the app's core loop
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 · Chats · Bookings · Customers Chats is second, deliberately. Khoa's ruling of 2026-08-18: bookings will be very few, and the real loop is notified someone is on the site → open the app → read the conversation → jump in. The Chats tab carries a live-count badge that is visible from every screen
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#FDFDFB #101725Screen ground
--card#FFFFFF #18202FCards, tab bar, fields
--tint#F5F5F8 #1E2637 Small fills — avatars, search, chips, skeletons. Cool, not sand: the app previously used #F4F2EC here and read beige against the marketing site
--line#EAE9E4 #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 live count sits on terracotta and therefore takes a literal #152033 ink — white on terracotta measures 3.33:1. The literal is deliberate: the fill does not change between themes, so its text must not either
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
Live presence — who is on the site right nowDoes not exist New, and now the app's core dependency. A live list of active visitors per business: how many, what page, their last message, and whether Kelbin is holding or answering. Needs a socket or short-poll the app can hold open, plus a push when a visitor arrives that matters. The whole Home screen and the Chats tab badge are built on this — if it lands late, the app has no reason to be on a phone
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 Two distinct classes now: (a) someone is live and Kelbin is holding — the one push that makes the app worth opening, and (b) Needs You decisions. The design promises "roughly twice a week" for (b); an ordinary visitor arriving must not push. Deep-link each push straight to its thread
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 · Assets and the avatar seam

Eight Kelbin assets ship with the pack — two faces used in-app, two more for Notifications and Settings, and four full-body poses. None of them repeat a portrait used on the marketing site, so the character has range across surfaces rather than one stock pose.

Settings → "Kelbin's appearance" is a deliberate seam, not a finished screen.

Khoa wants owners to swap Kelbin for a different avatar and customise him later. The row exists so the navigation does not have to change when that lands; the picker behind it is not designed yet. Build the row, route it to a stub.

7 · Deliberately not designed — ask before inventing

8 · Verification

The pack ships with a Playwright suite (acheck.mjs, 56 checks) covering the four marketing promises, shell-surface parity, tab set, tap targets, sideways scroll on every screen (including the pack opened AT 390×844), dark-mode contrast ratios computed in-page, the Live-now ordering, the Chats-second tab order, and that every Kelbin asset decodes. 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.