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.
conversite-ios → rename
Branch cole/ios-companion-phase1-staging-build-20260721
The shipped app is Conversite-era. Three tabs, one of them a person's name, and a screen no tab can reach:
| Today | File | Becomes |
|---|---|---|
| Trevor tab | HomeScreen.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 tab | InboxScreen.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 tab | SettingsScreen.tsx |
Split: Settings (behind the gear) and Your site (a glance, inside Settings) |
| Thread | ThreadScreen.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 in | SignInScreen.tsx |
Sign in + a new First run (calendar, cutoff, notifications) |
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.
Current: a 3-tab createBottomTabNavigator with a native stack above it.
Target keeps that shape and changes the contents.
| Level | Screens | Notes |
|---|---|---|
| Auth stack | SignIn → 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 sheets | Ask Kelbin, Business switcher | Presented as native sheets; the ask sheet is reachable from Home, Bookings, Chats, Customers |
Ported from the marketing site's marketing.css. Same values, new names for the
two roles a website never needed.
| Token | Light | Dark | Use |
|---|---|---|---|
--bg | #FDFDFB | #101725 | Screen ground |
--card | #FFFFFF | #18202F | Cards, 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 | #283243 | Hairlines, borders |
--ink | #152033 | #F3F1EC | Text only |
--slate | #697386 | #9AA4B8 | Secondary text |
--indigo | #5B5CEB | #8B8CFF | The one accent |
--indigo-soft | #ECEBFF | #242B4A | Kelbin's chat bubbles |
--strong | #152033 | #2B3550 | Inverse surface — ask bar, your bubbles, count pills |
--on-strong | #FFFEFB | #F5F3EE | Text on --strong |
--on-accent | #FFFFFF | #101725 | Text 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 |
--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.
| Role | Face | Size / weight | Where |
|---|---|---|---|
| Display | Newsreader 500 | 25–27 / 1.15 | Greeting, screen titles, sheet titles. Never inside a list row |
| Body | Figtree 400–650 | 13.5–15 | Everything else |
| Label | Figtree 800 | 11 / .10em, uppercase | Section labels, bylines |
| Numerals | Figtree, 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.
Full rationale sits beside each screen in the pack. The decisions a developer needs to preserve:
prefers-reduced-motion: the poses hold still, they do not vanish.Written against what conversite-api exposes today. Each of these is a build
dependency, not a design nicety.
| Needs | Status today | Gap |
|---|---|---|
| Live presence — who is on the site right now | Does 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 queue | Does 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 receipts | Does not exist | New. An append-only activity feed of completed agent actions with timestamps, scoped to the business and the day |
| Bookings | Not 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 link | Unknown | Both directions. A booking must carry the thread that produced it |
| Customers | Only 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-business | Single site assumed | List of businesses for the signed-in owner, and every call scoped by business. This is the biggest structural gap |
| Calendar | Not exposed to mobile | Connection status, calendar list, which calendar receives bookings, bookable hours, buffer, and the cutoff value |
| Push | device_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 status | Not exposed to mobile | Live URL, last published, and the list of staged changes with a publish action |
| Takeover | Partially exists | Owner join, owner send, and release-back-to-Kelbin, with the socket emitting on owner replies (today it only emits on host replies) |
| Offline | None | A last-sync timestamp and a queue for decisions made offline |
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.
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.
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.
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.