←  Caregiver source of truthEnd-to-end caregiver journey · print at 11×17 (tabloid), landscape
Maddy · caregiver surface

The caregiver, end to end.

Every screen a caregiver can reach in the redesigned surface, every happy path and every unhappy one, drawn straight from the built code. The phone on the left is the screen; the card on the right is what is happening and why.

Built states (chapters 1–6) are traced to the shipping caregiver-p1 build, verbatim.
Designed states (chapters 7–8) are the not-yet-built warmth layer, traced to the build spec and marked.
Default palette shown is Dream (each person carries their own; the Things shape stays fixed by role).
Happy path
Unhappy / edge path
Neutral state
Built (P1)
Designed (P2)

The whole journey, at a glance

Eight chapters, 73 screens. Chapters 1 to 6 are the shipping build, traced to code. Chapters 7 and 8 are the designed-but-not-yet-built warmth and graduation layers, drawn from the spec and marked throughout.

01

Getting in

Every door into the caregiver app and every way it can catch you: the chooser, sign-in and its warm error copy, Sign in with Apple, joining by code,.

2 happy5 unhappy5 neutral
02

The daily open

The 'Between You' channel is the whole home. Not tabs, not a dashboard: one warm surface that shows exactly one thing per open. A1 caught-up sends.

4 happy2 unhappy2 neutral
03

Behind the Manage door

Manage is a bottom pull-up handle, never a tab bar. It opens to a 2x2 launcher, then rises to a working hub with a segmented control. Inside: Today.

2 happy6 unhappy8 neutral
04

Rewards and payout

The money surface, held inside the sensory envelope: no '$0' ever renders, the en dash carries the quiet week. The weekly hero, the pay/give.

4 happy2 unhappy2 neutral
05

Your corner

The Care door is the one surface that is only for the caregiver. The same calming tools the subject uses (modeling is the point), a direct line to.

1 happy1 unhappy3 neutral
06

Circle and account

Settings, consolidated. Who you are, who you support, per-person theming, the circle roster and its removes, generating and sharing invites, joining.

1 happy6 unhappy6 neutral
07 · designed

What comes back

Phase 2, designed but not built. The warmth that flows back from them: the felt-feedback card, the earning milestone, the load-relief hatch, and the.

5 happy2 unhappy2 neutral
08 · designed

The long arc

Phase 2, designed but not built. Graduation as a display layer: the home reframes into their growth and your success, and admins get a gentle.

2 happy
01

Getting in

Every door into the caregiver app and every way it can catch you: the chooser, sign-in and its warm error copy, Sign in with Apple, joining by code, the Face ID gate, auto-lock, and the loading shells. This is the threshold, and it is built to never leave a caregiver stranded on a red screen.

Chapter 1  ·  Getting in
Maddy caregiver journey
9:41
💜
How can I find you?
Pick the door that fits. We'll do the rest together.
I'm new hereCreate my circle
I have an invite codeSomeone shared one with me
Already have an account?  Sign in
Three-door entry
NeutralBuilt · P1ChooserScreen.js

Chooser: how can I find you?

The pre-auth entry: one decision per screen, three doors, each routing into the auth flow at the right mode.

What you see

How can I find you?Pick the door that fits. We'll do the rest together.I'm new hereCreate my circleI have an invite codeSomeone shared one with meAlready have an account?  Sign in

What happens

Caregiver-first taps 'I'm new here'; invited taps 'I have an invite code'; returning taps 'Sign in'.

onPick sets chooserPath so AuthScreen renders with that initialMode; the logo breathe loop is skipped under Reduce Motion.

Why it's built this way

The three-door entry is pre-auth and uses the Bubblegum welcome palette, not the caregiver Dream default. The one place the doc shows the un-toned brand gradient.

ChooserScreen.js:25-162

From cold launch, no sessionLeads to Sign in (2)  /  Join by code (5)  /  create circle
9:41
Back
💜
Good to see you.
Sign in to pick up where you left off.
Continue with Apple
or with email
Email
you@example.com
Password
••••••••
Sign in  →
Email + Apple sign-in
NeutralBuilt · P1AuthScreen.js

Sign in: good to see you

The default sign-in plus its in-flight state: loading is a trivial micro-variant folded in here.

What you see

Good to see you.Sign in to pick up where you left off.or with emailEmailyou@example.comPassword••••••••Sign inForgot password?

What happens

Types email and password and taps Sign in; or Apple; or Forgot password.

handleLoginsignInWithPassword; on success saveCredentials() arms biometric; loading swaps the CTA label for a white spinner at opacity 0.7.

Why it's built this way

The default sign-in plus its in-flight state. Loading is a trivial micro-variant folded in here, not a screen of its own.

AuthScreen.js:426-786 · handleLogin 183

From chooserPath 'login'Leads to Face ID gate (7)  /  error family (3)  /  Forgot password
Chapter 1  ·  Getting in
Maddy caregiver journey
9:41
Back
Good to see you.
Email
jeremy@mail.com
Password
••••••••
We couldn't sign you in. Double-check your email and password.
Sign in  →
One calm error box
UnhappyBuilt · P1authError.js

Sign in: the warm error family

Every distinct auth failure is one verbatim line the single red box can carry; only the box chrome is shared.

What you see

Enter your email and password.We couldn't sign you in. Double-check your email and password.We couldn't find an account with that email. Want to create one instead?You haven't confirmed your email yet. Check your inbox for the link we sent (it might be in spam).We couldn't reach our servers. Check your connection and try again.A lot of attempts in a short time. Wait a minute and try again.Something went wrong on our end. Try again in a moment.

What happens

Fixes the field, re-enters the password, or routes to Forgot / create account.

setError with the mapped literal; it never leaks stack text. The generic is the last resort.

Why it's built this way

Every distinct auth failure copy survives as one verbatim line; only the shared error-box chrome is merged. No red flash beyond the calm box.

AuthScreen.js:184-190 · authError.js:13-77

From handleLogin validation or GoTrue errorLeads to corrected sign-in  /  Forgot password  /  create account
9:41
Back
Good to see you.
Continue with Apple
Apple didn't return a sign-in token. Try again, or sign in with email.
or with email
Email
you@example.com
Sign in  →
Apple token gap
UnhappyBuilt · P1AuthScreen.js

Sign in with Apple: token and session gaps

Apple's three outcomes share the form and differ only by one line; silent cancel changes nothing.

What you see

Apple didn't return a sign-in token. Try again, or sign in with email.Sign in succeeded but no session came back. Try again, or sign in with email.(user cancelled, no error shown, returns silently)

What happens

Retries Apple or falls back to email.

setLoading(false) + setError literal; cancel is an early return with no setError; the no-session case clears auth artifacts first.

Why it's built this way

Apple's three distinct outcomes are separate branches with separate handling, kept as one mockup because they share the form and differ only in one line.

AuthScreen.js:133-178

From handleAppleSignIn: no token / no session / cancelLeads to retry Apple  /  email sign-in (2)  /  unchanged form
Chapter 1  ·  Getting in
Maddy caregiver journey
9:41
Back to code
Let's get you connected.
Enter the code someone shared with you.
Joining Jeremy's circle as a support person You'll be able to see what they're working on, leave them notes, and see their photos when they share something. The subscription for the circle covers you.
Set up your sign-in below.
Continue with Apple
or with email
you@example.com
Join circle
Code accepted, green banner
HappyBuilt · P1AuthScreen.js

Join: code accepted, invited as a support person

The invited-caregiver happy path: the green banner with the fully-resolved they/them acceptBody they actually read.

What you see

Let's get you connected.Enter the code someone shared with you.Joining Jeremy's circle as a support personYou'll be able to see what they're working on, leave them notes, and see their photos when they share something. The subscription for the circle covers you.Set up your sign-in below.Continue with Appleor with emailJoin circle← Back to code

What happens

Continues with Apple, or sets email and password and taps Join circle. The Apple button (device-gated by appleAuthAvailable) redeems the invite inline via handleAppleSignIn('join').

Email path: handleJoinSignup writes pending_invite + pending_role, then signUp; redeem is deferred to OnboardingFlow.finish(); banner copy resolves via roleCopy().

Why it's built this way

The invited-caregiver happy path: the green banner with the fully-resolved they/them acceptBody the caregiver actually reads.

AuthScreen.js:427-590 · revenue.js:150

From mode 'join', valid code (role caregiver)Leads to account setup  /  back to code (6)  /  onboarding
9:41
Back
Let's get you connected.
Enter the code someone shared with you.
7GQ4KP2M
We couldn't find that code. Double-check it with whoever sent it.
Continue
Don't have a code yet? Ask whoever invited you to share one.
Code rejected
UnhappyBuilt · P1AuthScreen.js

Join: code validation failures

Six code-rejection copies on one input: five reachable, plus one defensive catch-all. Merged only on the shared box chrome.

What you see

Enter the 8-character code from your invite.We couldn't find that code. Double-check it with whoever sent it.Looks like that code's already been used. Ask whoever sent it for a new one.That code expired. Ask whoever sent it for a new one.You're already in this circle.That code is no longer valid.

What happens

Corrects the code or asks the inviter to re-share.

setError with the mapped reason string; the flow stays on the code step.

Why it's built this way

Five reachable copies: wrong-length, then the four validate_invite reasons (not_found, used, expired, already_member). "No longer valid" is the || catch-all default, unreachable today since the RPC only emits those four, all mapped. It is a safety net for a future reason (revoke, group-full) or a malformed response.

AuthScreen.js:270-288 · validate_invite RPC

From handleValidateCode returns not-validLeads to corrected code → accepted (5)  /  ask inviter for new code
Chapter 1  ·  Getting in
Maddy caregiver journey
9:41
💜
Welcome back
Use Face ID to continue
Open with Face ID  →
App-open lock gate
NeutralBuilt · P1App.js

App-open Face ID gate: welcome back

The one biometric event per session, plus its no-op failure state, merged because failure changes nothing on screen.

What you see

💜Welcome backUse Face ID to continueOpen with Face ID →Sign out

What happens

Taps 'Open with Face ID →', or 'Sign out' as the deliberate escape hatch.

promptBiometric(); ok → onCleared() unlocks. A failed or cancelled prompt does NOT sign out, which prevents the sign-in → Face ID → sign-out loop.

Why it's built this way

The one biometric event per session, plus its no-op failure state. On failure nothing changes on screen, so the two states merge.

App.js:2464-2493

From session 'biometric_pending'Leads to caregiver home (11)  /  gate persists on fail  /  sign out
9:41
Maddy's circle
no gate rendered, flows straight to the caregiver home
Maddy
🦊
Good today
Manage
Gate auto-bypassed
UnhappyBuilt · P1useSession.js

Biometric unavailable: the gate never shows

A real device state (Expo Go, no enrolled Face ID) where no lock surface renders at all.

What you see

(no gate rendered, flows straight to the caregiver home)

What happens

None. The caregiver lands on the home with no gate.

The lock gate is auto-bypassed and never shown, a reachable permission branch the pending-gate / fail / lock trio doesn't cover.

Why it's built this way

The no-gate path is a real device state, notably Expo Go and QA, worth documenting so the absence of a lock is understood as intended, not a bug.

useSession.js:144 · biometrics.js:20

From isBiometricAvailable() returns falseLeads to caregiver home (11), no gate
Chapter 1  ·  Getting in
Maddy caregiver journey
9:41
Caregiver locked
Unlock
10-min auto-lock
NeutralBuilt · P1App.js

Caregiver auto-lock: locked

A separate mechanism from the app-open gate, with its own Dream-toned surface.

What you see

(lock-closed-outline icon)Caregiver lockedUnlock

What happens

Taps Unlock.

On tap, if biometric is available promptBiometric(); on ok setCaregiverLocked(false) → dashboard. In Expo Go it unlocks directly. Distinct from the app-open gate.

Why it's built this way

The 10-minute re-lock is a separate mechanism from the app-open gate and has its own Dream-toned surface.

App.js:2925-2947

From caregiver + caregiverLocked, 10-min backgroundLeads to caregiver home (11)
9:41
Loading caregiver profile…
Spinner + 5s escape
NeutralBuilt · P1App.js

Loading shells: profile and view

Two loading shells share one spinner and status line and the same 5s escape; merged as one mockup.

What you see

Loading caregiver profile…Loading caregiver view…Sign out

What happens

Waits; after 5s can sign out if wedged.

Spins until loggedInCaregiver populates; loadingStuck flips true at 5000ms to surface the escape hatch.

Why it's built this way

Two loading shells share one spinner plus status-line pattern and the same 5s escape; merged as one loading mockup per the consolidation rule.

App.js:2875-2922 · loadingStuck 617

From post-auth, caregiver profile still nullLeads to caregiver home (11)  /  sign out if wedged
Chapter 1  ·  Getting in
Maddy caregiver journey
9:41
Maddy's circle
Maddy
🦊
Good today
Nothing needs you right nowYou'll get a notification the moment anything does.
ManageDone
TodayRecognize & notes
ThingsTasks & budget
RewardsEarnings & payout
SettingsCircle & account
Channel + Manage launcher
HappyBuilt · P1CaregiverDashboard.js

The caregiver shell: channel + Manage handle

The shell chrome is identical admin vs non-admin; the difference is capability, not text, so both collapse to one frame.

What you see

TodayRecognize & notesThingsTasks & budgetRewardsEarnings & payoutSettingsCircle & account

What happens

Admin: recognize, note, add / edit / delete tasks, full Settings. Non-admin: everything but task mutation and Settings config.

isAdmin gates the two task-mutation modals and the edit affordances, progressive enhancement inline, never a separate admin window.

Why it's built this way

The shell chrome is identical admin vs non-admin; the difference is capability not text, so both dashboard states collapse to one framed mockup.

CaregiverDashboard.js:92,970

From unlocked caregiver, loggedInCaregiver presentLeads to the daily open (Ch. 2)  /  Manage sections (Ch. 3)
9:41
💜
Caregiver tools
Hand the phone to your support person. They can sign in with their own account to use these tools.
Close
Wrong-person recovery
UnhappyBuilt · P1CaregiverView.js

Caregiver tools: hand-off recovery

The wrong-person-in-the-seat recovery: a real dead-end that needs its own warm framing.

What you see

💜Caregiver toolsHand the phone to your support person. They can sign in with their own account to use these tools.CloseSign out of this account

What happens

Hands the phone to the caregiver, or taps Close; a solo subject can sign out here.

onLock closes; onSignOut signs out. PIN auth was retired in Session 3, so there is no PIN picker.

Why it's built this way

The wrong-person-in-the-seat recovery; a real dead-end that needs its own warm framing.

CaregiverView.js:52-83

From CaregiverView opened with loggedInCaregiver falsyLeads to Close → subject home  /  caregiver signs in  /  sign out
02

The daily open

The 'Between You' channel is the whole home. Not tabs, not a dashboard: one warm surface that shows exactly one thing per open. A1 caught-up sends you away, A2 recognize is the only job, A3 look-closer is where you settle one thing, A4 is the one composer. Everything else lives behind the Manage door.

Chapter 2  ·  The daily open
Maddy caregiver journey
9:41
Maddy's circle
MADDY
🦊 readysince 8:14am
Nothing needs you right nowYou'll get a notification the moment anything does.
Maddy has earned $49.00this week, 3 things recognized
Send Maddy a note
Manage
A1 · caught-up home
HappyBuilt · P1ChannelHome.js

A1 · caught up (the common open)

The everyday open: nothing is pending, so the home reads their state, states the contract, and sends the caregiver away.

What you see

MADDYreadysince 8:14amNothing needs you right now. You'll get a notification the moment anything does.Maddy has earned $49.00this week, 3 things recognizedSend Maddy a note

What happens

Reads their state and the week's tally; the home sends you away. Optionally taps 'Send Maddy a note'.

stateOpt from STATE_OPTIONS; earnedWeekly > 0 renders the tabular earned line plus recognizedCount; the whole card is the reliability contract that lets them close the app.

Why it's built this way

The centerpiece calm open, authored verbatim from ChannelHome: their state read-only, the contract, the earned row, and the one offer.

ChannelHome.js:298 · earned row tappable to Rewards.

From app unlock (nothing pending)Leads to Manage · Rewards (earned row)  /  A4 composer (note)
9:41
Maddy's circle
MADDY
🦊 With Maddy todayno check-in yet, and that's okay
Nothing needs you right nowYou'll get a notification the moment anything does.
Nothing to settle upthis week
Send Maddy a note
Manage
A1 · no check-in yet
UnhappyBuilt · P1ChannelHome.js

A1 · caught up, no check-in yet

The same calm card before they have checked in: the title falls back and the earned row carries the no-zero framing.

What you see

MADDYWith Maddy todayno check-in yet, and that's okayNothing needs you right now. You'll get a notification the moment anything does.Nothing to settle upthis weekSend Maddy a note

What happens

Reads the gentle not-yet cue; nothing to do.

stateOpt null so the title becomes 'With Maddy today' and the well caption reads "no check-in yet, and that's okay"; earned falls to the no-zero 'Nothing to settle up'.

Why it's built this way

The 'hasn't checked in' variant the matrix promises to preserve, distinct from A1-caughtup which assumes a state, carrying the no-verdict framing for a quiet start.

ChannelHome.js:308 · no-zero earned branch ChannelHome.js:348.

From first open of the day (no daily_state)Leads to A2 recognize (as they complete things)  /  A4 composer
Chapter 2  ·  The daily open
Maddy caregiver journey
9:41
Maddy's circle
MADDY
🦊 readysince 8:14am
Maddy reached out today. A little support would mean a lot.
Maddy said the shower felt rough adjust
Maddy asked about 2 things see
Manage
A1 · the seam rows
UnhappyBuilt · P1ChannelHome.js

A1 · the seam rows (feedback, asked-you, reached-out)

Three quiet hairline rows that ride inside the A1 card, the P1 seam of Phase 2's warmth signals, never alarming.

What you see

Maddy reached out today. A little support would mean a lot.Maddy said the shower felt rough · adjustMaddy asked about 2 things · see

What happens

Taps a seam row and routes into Today to act on the friction signal.

Each row is a quiet cue that survives P1 restyled; the full B1/F5 cards are Phase 2. Never alarming, never red.

Why it's built this way

The friction signals that keep a visible cue on the calm home; each is unhappy because it needs the caregiver, restyled as the warmest possible rows.

ChannelHome.js:140 · reached-out on stateData.reached_out_at.

From reached-out / unseen feedback / task-change requestsLeads to Manage · Today (The After, asked-you)
9:41
Maddy's circle
YOUR CIRCLE
Invite someone to your circle to begin.
Manage
Empty · no one yet
NeutralBuilt · P1ChannelHome.js

Channel · no one in the circle yet

The home before anyone is in it: no state, no earned row, no note button, because the channel has no one to be between.

What you see

YOUR CIRCLEInvite someone to your circle to begin.

What happens

Nothing here beyond the CirclePill and heart chrome; the invite lives in Settings.

The !subjectId branch of ChannelHome renders a single card; seamRows null, no composer offered.

Why it's built this way

The empty-channel first-run, the home before anyone is in it, kept plain so the one next step (invite) is obvious.

ChannelHome.js:219 · !subjectId branch.

From circle created, no subject joinedLeads to Settings · invite  /  A1 (once a subject joins)
Chapter 2  ·  The daily open
Maddy caregiver journey
9:41
Maddy's circle
MADDY
A few things to recognize
💧
💊
🪥
Recognize · $16.00
Tap any one to look closer
Send Maddy a note
Manage
A2 · a few to recognize
HappyBuilt · P1ChannelHome.js

A2 · a few things to recognize

When they've done things they cluster (capped at three, never a scrolling feed); one button blesses them all.

What you see

MADDYA few things to recognize[three subject-hue chips]Recognize · $16.00Tap any one to look closerRunning low? Quiet things downSend Maddy a note

What happens

Taps one chip to look closer, or one batch 'Recognize · $X' to approve everything at once, or the off-ramp to quiet things down.

cluster is the first 3 of the queue, oldest first; countless and capped, recognizing reloads and refills so items 4+ get their turn with no count ever shown; pendingTotal sums partials at half.

Why it's built this way

The centerpiece A2: the queue is the job, the hue dot is their mark on their work, one batch wave of warmth.

ChannelHome.js:227 · hue dot in hueDot, not the caregiver accent.

From app unlock (they completed things, plural)Leads to A3 look-closer (chip)  /  quiet things down (Care)
9:41
Maddy's circle
MADDY
Something to recognize
💧
Recognize · $6.00
Tap any one to look closer
Send Maddy a note
Manage
A2 · single item
HappyBuilt · P1ChannelHome.js

A2 · something to recognize (single item)

The same A2 card with exactly one chip and the singular title; everything else stays put.

What you see

MADDYSomething to recognizeRecognize · $6.00Tap any one to look closerRunning low? Quiet things down

What happens

Taps the one chip to look closer, or 'Recognize · $X'.

pending.length === 1 flips the title to the singular 'Something to recognize'.

Why it's built this way

The singular-queue copy branch is a separate reachable state from the plural cluster, one distinct verbatim string worth its own frame.

ChannelHome.js:232 · pending.length === 1.

From app unlock (one pending thing)Leads to A3 look-closer (chip)  /  quiet things down (Care)
Chapter 2  ·  The daily open
Maddy caregiver journey
9:41
Maddy's circle
MADDY
A few things to recognize
💧
💊
🪥
Recognize this
💧 WaterTuesday
Recognize · $6.00
A3 · look closer
NeutralBuilt · P1ChannelHome.js

A3 · look closer (one submission)

Opened from a chip tap: the settle-one-thing surface where a caregiver recognizes a single item or asks for a change.

What you see

Recognize thisAsk for a changeRecognize · $6.00

What happens

Recognizes the one submission, or asks for a change (warm redo / needs proof).

Routes through the shipped atomic idempotent approve path; 'Ask for a change' writes needs_redo/needs_proof back to the task rather than approving.

Why it's built this way

The look-closer surface is the third of the channel's A1–A4 spine, where a caregiver settles one thing rather than batch-approving, and the warm-redo path lives.

ChannelHome.js:242 chip → onOpenApproval · ApprovalModal.

From A2 single chip tapLeads to recognized (back to home)  /  warm redo (needs_redo / needs_proof)
9:41
Maddy's circle
MADDY
🦊 readysince 8:14am
🦊Maddy
Send Maddy a note
They're going to see it when they're ready. No pressure to reply.
So proud of how you showed up today.
💛
🌸
🫶
☀️
Send
It lands on their home as a note from you.
A4 · the one composer
HappyBuilt · P1NoteComposer.js

A4 · the one composer (compose / sending / sent-error)

The single note composer: compose, the 'Sending…' in-flight, and the send-error, folded into one sheet since they differ only by button label and one line.

What you see

MaddySend Maddy a noteThey're going to see it when they're ready. No pressure to reply.So proud of how you showed up today.💛 ✨ 🌸 🫶 ☀️Send · Sending…That note did not go through. Try again in a moment.It lands on their home as a note from you.

What happens

Types a note, optionally taps emoji quick-picks appended at the cursor, taps Send; on error re-taps Send to retry.

send() guards message && !sending && subjectId; insertCaregiverNote writes to caregiver_notes (bidirectional inner-ring); success runs successComplete() + onClose; catch sets the error line and re-enables. No read-receipt, no sent log either direction. Field resets fresh every open.

Why it's built this way

The centerpiece A4, authored verbatim: compose, the 'Sending…' in-flight, and the send-error, all one composer since they differ only by button label and one error line.

NoteComposer.js:26 · insertCaregiverNote().

From 'Send Maddy a note' on any home stateLeads to sent (lands on their home)  /  send-error (retry)
03

Behind the Manage door

Manage is a bottom pull-up handle, never a tab bar. It opens to a 2x2 launcher, then rises to a working hub with a segmented control. Inside: Today (the state line, progress, the approval queue, what they told you, their messages, the note composer) and Things (the budget hero and the task list, admin and read-only).

Chapter 3  ·  Behind the Manage door
Maddy caregiver journey
9:41
Maddy's circle
Maddy
🦊
Good today
Manage
The door, resting
NeutralBuilt · P1ManageSheet.js

The Manage handle, teaching then receded

The one door to everything caregiver-configurable, drawn as quietly as the iOS home indicator.

What you see

A 40×5 grab pill above the safe areaManage(caption shows only while it's still teaching)

What happens

Taps the pill to open Manage at the mid launcher detent.

On press it increments cg_manage_handle_opens then calls onPress(); taught defaults true to avoid a flash for a settled caregiver.

Why it's built this way

The door is identical no matter what's behind it. The two handle states differ only by whether the teaching caption shows, so they merge into one quiet-chrome frame.

Never a badge, count, dot, or preview. HANDLE_TEACH_OPENS=4.

From idle on the caregiver homeLeads to Manage · mid launcher detent
9:41
Maddy's circle
Maddy
🦊
Good today
ManageDone
TodayRecognize & notes
ThingsTasks & budget
RewardsEarnings & payout
SettingsCircle & account
Mid detent · 2×2 launcher
NeutralBuilt · P1ManageSheet.js

The 2×2 launcher

The map behind the door: four tiles over a flat scrim, one soft shadow, non-numeric subtitles.

What you see

Manage · DoneToday · Recognize & notesThings · Tasks & budgetRewards · Earnings & payoutSettings · Circle & account

What happens

Taps one of four tiles, taps Done, or taps the scrim to dismiss.

Tile tap: haptic + onSelectSection(key); the sheet rises to the tall detent with that section (tap and rise are one motion). Done or scrim returns to the calm home.

Why it's built this way

The launcher is the map behind the door; subtitles are non-numeric by canon.

No counts live here. The one allowed soft shadow sits over a flat scrim.

From the handle (springs to mid)Leads to tall detent working hub · or back to the calm home
Chapter 3  ·  Behind the Manage door
Maddy caregiver journey
9:41
ManageDone
Today
Things
Rewards
Settings
Maddy's state
8:14am · 🌟 Ready
Ready for you (3)
Section content scrolls hereToday / Things / Rewards / Settings each render inside this frame
Tall detent · working hub
NeutralBuilt · P1ManageSheet.js

The working hub

The full-height sheet every Manage section renders inside, switched by a segmented control, never bottom tabs.

What you see

Manage · DoneTodayThingsRewardsSettings

What happens

Works inside the section; switches sections via the segmented control; drags the header down to return to launcher or close.

Header pan governs detents (fling up to tall, down to next detent, past mid + 0.5 to close). renderSection mounts each tab.

Why it's built this way

The working hub is the shared frame every Manage section renders inside; the segmented-control chrome is documented once.

Sub-sections use this control, TodayDetailTab and friends, never bottom tabs.

From a launcher tile, or a fling past midLeads to Today / Things / Rewards / Settings sections
9:41
ManageDone
Fades in placeNo spring, no slide between detents
Reduce Motion rendering
UnhappyBuilt · P1ManageSheet.js

The same door, under Reduce Motion

The accessibility rendering of the Manage sheet: it fades and snaps rather than springs and slides.

What you see

Manage · Done(fades in place. No spring, no slide between detents)

What happens

Same taps; the transitions are fades and snaps instead of springs.

Required accessibility rendering per spec §10 under useReducedMotion: every new-surface animation freezes.

Why it's built this way

A required accessibility state of the sheet that the two detent frames don't capture. Motion is a first-class part of this design system.

From OS Reduce Motion onLeads to the same launcher / hub, faded not sprung
Chapter 3  ·  Behind the Manage door
Maddy caregiver journey
9:41
Maddy's circle
Today
Things
Rewards
Settings
Maddy's state
8:14am · 🌟 Ready
Maddy hasn't checked in yet
Checked in this morning
Today · the state line
UnhappyBuilt · P1TodayDetailTab.js

The state line, three branches

One row that reads whether and how they checked in, with a protective mask over a hard morning.

What you see

Maddy's state8:14am · 🌟 ReadyMaddy hasn't checked in yetChecked in this morning

What happens

Glances at whether and how the subject checked in today.

Branches: no stateData shows hasn't-checked-in; entry_state 'shutdown' shows 'Checked in this morning'; else time · emoji · shortLabel.

Why it's built this way

Three distinct branches on one line, and the shutdown-masking one is sensitive, so they stay together but each verbatim branch is preserved.

stateLine protects a hard morning; the section title drops the name when none is set.

From Today section open with a subjectIdLeads to the rest of Today · progress, queue, seams
9:41
Maddy's circle
Today
Things
Rewards
Settings
Progress by category
🧼 Care6 / week8.00 / 20.00
🍽️ Meals6 / week·
Today · progress by category
NeutralBuilt · P1TodayDetailTab.js

Progress by category

A per-group read of where the day stands, no verdict, with the middle-dot no-zero placeholder.

What you see

Progress by categoryearned / 20.00 per group6 / week· (a group earning nothing yet)

What happens

Sees where the day stands per category, no verdict.

groupEarned counts full completions at value, partials at half; each group carries a 4px progress bar and a 'X / week' caption.

Why it's built this way

The per-category read; the middle-dot no-zero is a specific design token worth showing.

A group earning 0 shows a centered middle dot '·', never '$0', distinct from the Rewards en dash.

From subjectId present with a task active todayLeads to Ready-for-you queue below
Chapter 3  ·  Behind the Manage door
Maddy caregiver journey
9:41
Maddy's circle
Today
Things
Rewards
Settings
Ready for you
Nothing waiting. Maddy is all caught up
Ready for you
No one's joined your circle yet
Ready for you · empty
UnhappyBuilt · P1TodayDetailTab.js

Ready for you, empty

The Manage-side approval queue with nothing in it, in both its caught-up and no-circle forms.

What you see

Ready for youNothing waiting. Maddy is all caught upNo one's joined your circle yet

What happens

Caught-up: moves on. No-circle: needs to invite a subject.

The empty branch's ternary falls to the no-name string; the no-circle case is a first-run and permission dead-end.

Why it's built this way

Manage · Today's approval queue is a different screen from the channel home; its two empty variants both survive, merged on shared chrome.

No count suffix and no Select action when empty.

From pending queue length 0Leads to onward (caught-up) · or Settings to invite (no-circle)
9:41
Maddy's circle
Today
Things
Rewards
Settings
Ready for you (3)Select
💧 WaterTuesday$6.00
🚿 ShowerTuesday · partial$3.00
Recognize 3
Ready for you · pending + batch
HappyBuilt · P1TodayDetailTab.js

The approval queue, with batch

The detailed Manage-side counterpart to the channel's A2 chips, with a Select-to-recognize-many mode.

What you see

Ready for you (3)Select / DoneTuesdayTuesday · partialRecognize 3

What happens

Taps a row to open one day's approval, or Select then 'Recognize N' to approve many at once.

Row tap: selectMode ? onToggleSelect : onOpenApproval; batch fires onApproveSelected() + successComplete().

Why it's built this way

The full Manage-side approval queue with its multi-select batch, the detailed counterpart to the channel's A2 chips.

The section title carries a count; the batch bar appears only with selectedIds.size > 0.

From pending queue length > 0Leads to one-day approval (row) · or batch recognize
Chapter 3  ·  Behind the Manage door
Maddy caregiver journey
9:41
Maddy's circle
Today
Things
Rewards
Settings
Maddy asked you
🧺 Laundrywants this removed
🚿 Showerwants a change“Can this one be optional?”
RemoveEditKeep as is
Today · Maddy asked you
UnhappyBuilt · P1TodayDetailTab.js

Maddy asked you

Their voice reaching into task config: a request to remove or change a caregiver-authored task, with an admin decision.

What you see

Maddy asked youwants this removedwants a change“Can this one be optional?”Remove · Edit · Keep as is

What happens

Admin taps Edit or Remove to act, or Keep as is to dismiss; non-admin sees the ask with no buttons.

onRequestEdit / onRequestRemove / onRequestDismiss; isAdmin gates the action row.

Why it's built this way

A friction signal with an open decision, the subject's voice reaching into task config (JER-571).

The action row is admin-only; the ask itself is visible to all.

From taskRequests length > 0Leads to TaskEditModal (Edit) · confirm remove · or dismiss
9:41
Maddy's circle
Today
Things
Rewards
Settings
What Maddy told youClear all
🚿 Showerfelt rough“The shower felt like a lot today.”
💧 Water✨ felt good ·2 · Tuesday
Adjust this task
Today · what they told you
UnhappyBuilt · P1TodayDetailTab.js

What Maddy told you (The After)

Their per-task felt feedback; 'felt rough' is the actionable friction that routes an admin to a fix.

What you see

What Maddy told you · Clear allfelt rough✨ felt good ·2“The shower felt like a lot today.”Adjust this task

What happens

Reads how a task felt; for rough, admin taps 'Adjust this task'; dismisses durably via X or Clear all.

handleDismissFeedback is durable; 'Adjust this task' fires onSetEditingTask + dismiss + haptic; unseen auto-marked seen after 2.5s.

Why it's built this way

The shipped P1 The After surface; 'felt rough' is the actionable friction that routes to a fix, the seam of the P2 B1 card.

Inner-ring RLS: the outer ring never sees this.

From feedbackToShow length > 0Leads to TaskEditModal (Adjust) · or durable dismiss
Chapter 3  ·  Behind the Manage door
Maddy caregiver journey
9:41
Maddy's circle
Today
Things
Rewards
Settings
Pip passed along from Maddy
Can we do ramen this week?8:22am
Thank you for the note 💛Yesterday
Mark all as read
Today · messages from them
NeutralBuilt · P1TodayDetailTab.js

Messages from the subject

Inbound notes from them, framed through their companion, a distinct Today surface from The After.

What you see

Pip passed along from MaddyMaddy saysMark all as read

What happens

Reads each; taps X to clear one, or 'Mark all as read'.

Single X: markMaddyMessageSeen(id) + local filter (no haptic); mark all: markMaddyMessagesSeen(subjectId).

Why it's built this way

Inbound messages from them, framed through their companion, a distinct Today surface from The After.

The section title uses the companion frame when one exists, else 'Maddy says'.

From maddyMessages length > 0Leads to messages cleared · write-to-subject composer below
9:41
Maddy's circle
Today
Things
Rewards
Settings
Write to Maddy
Thinking of you 💙
I see you showing up. 🌸
Rooting for you today. 🌱
Here if you need anything. 💜
Something kind for Maddy…
Send noteVoice
Today · write to them
NeutralBuilt · P1TodayDetailTab.js

The write-to-subject composer

The Manage-side composer with its quick-send presets, its voice-memo path, and its empty state.

What you see

Write to MaddyQuick sendThinking of you 💙Send note · VoiceRecording · tap to sendNote sent · Send anotherNo one to write to yet

What happens

Picks a preset or types, taps Send note; or taps Voice to record; an empty circle blocks it.

Send: insertCaregiverNote then setNoteSent(true) + haptic; Voice: onStartRecording / onStopAndSendRecording; recordingError surfaces calmly in red text, not a flash.

Why it's built this way

The Manage-side composer, distinct from the A4 sheet, with its voice-memo path and empty state.

The many transient sub-states (recording, uploading, sent) merge into this one composer per the consolidation rule.

From subjectId present · noteSent falseLeads to Note sent + Send another · or the no-subject empty
Chapter 3  ·  Behind the Manage door
Maddy caregiver journey
9:41
Maddy's circle
Today
Things
Rewards
Settings
Still outstanding
🪥 Brush teethevening$2.00
Recent completions
💧 WaterTuesday+$6.00
🚿 ShowerTuesday · partial+$3.00
Today · outstanding & recent
HappyBuilt · P1TodayDetailTab.js

Outstanding and recent completions

The bottom of Today: what's left, read-only, and what recently landed, with the daily-reset empty line.

What you see

Still outstandingRecent completionsTuesdayTuesday · partialNothing completed yet

What happens

Glances at what's left and what's recently done, read-only.

Recent merges dated ledger approvals + direct completions, newest first, deduped; empty is a no-verdict line honoring daily reset.

Why it's built this way

The bottom of Today, what's left and what landed; the empty completions line carries the daily-reset canon.

Outstanding shows values in tone.dim (muted); recent uses formatRewardDelta.

From outstanding > 0 · Recent always rendersLeads to Things section · or the calm home on Done
9:41
Maddy's circle
Today
Things
Rewards
Settings
Weekly budget
$49.00
from 6 things across 3 groups
Add a thing
💧 WaterDaily · $6.00
🚿 ShowerDaily · $6.00
Things · budget hero + admin list
NeutralBuilt · P1TasksTab.js

Weekly budget hero + admin task list

The admin task-editing surface with the live budget hero, the same weekly number that becomes the Rewards goal denominator.

What you see

Weekly budgetfrom 6 things across 3 groupsFROM DAILY THINGSFROM WEEKLY THINGSAdd a thingTap to edit · Hold and drag to reorder · Swipe right to move group · Swipe left to edit or delete

What happens

Watches the weekly total move; taps a task to edit, holds and drags to reorder, swipes to move-group/edit/delete, taps Add a thing.

total = weeklyPossible(tasks); split = weeklyBreakdown; SortableTaskList wires the edit and reorder handlers.

Why it's built this way

The admin task-editing surface with the live budget hero, the same weekly number that becomes the Rewards goal denominator.

Grouped by non-empty group; gesture legend as the section footer.

From Things open · isAdmin, hasSubject, tasks > 0Leads to TaskEditModal / AddTaskModal · Rewards denominator
Chapter 3  ·  Behind the Manage door
Maddy caregiver journey
9:41
Maddy's circle
Today
Things
Rewards
Settings
Things
Add a thing
Things live with the person you support. Generate an invite first.
Things
Add a thing
Nothing here yet. Tap above to add the first one.
Things · admin empties
UnhappyBuilt · P1TasksTab.js

Admin empties: no subject, no tasks

The two admin dead-ends before a populated list: a blocked no-circle case and a neutral onboarding empty.

What you see

ThingsAdd a thingThings live with the person you support. Generate an invite first.Nothing here yet. Tap above to add the first one.

What happens

No-subject: must generate an invite in Settings first. No-tasks: taps Add a thing.

Add onPress is undefined when !hasSubject, guarding the orphan insertTask(subjectId=null) bug; the empty-list branch keeps Add enabled.

Why it's built this way

The no-subject case is a real dead-end for a fresh caregiver; the no-tasks case is a neutral onboarding empty, both distinct from the populated list.

Blocked 'Add a thing' dims to 0.5 with no chevron.

From isAdmin · !hasSubject · or tasks length 0Leads to Settings invite (blocked) · AddTaskModal (empty)
9:41
Maddy's circle
Today
Things
Rewards
Settings
All things
💧 WaterDaily$6.00
🚿 ShowerDaily$6.00
Tap anything not done yet to mark it done
Things · non-admin list
NeutralBuilt · P1TasksTab.js

Non-admin tap-to-complete list

The progressive-enhancement stripped view: a read-only caregiver gets a checkable list, no editing.

What you see

All thingsTap anything not done yet to mark it done(empty day: footer only, no rows, no empty message)

What happens

Taps an unfinished task to mark it done on the subject's behalf.

Row onPress: !completed && markComplete(id); completed rows dim and strike, non-tappable; a zero-active-task day is a bare-footer empty gap.

Why it's built this way

The progressive-enhancement stripped view for non-admin caregivers, including the uncovered zero-active-tasks empty day.

No hero, no gesture legend, no Add, just a checkable list.

From isAdmin falseLeads to a task marked complete · or a bare empty day
04

Rewards and payout

The money surface, held inside the sensory envelope: no '$0' ever renders, the en dash carries the quiet week. The weekly hero, the pay/give affordances with an undo window, the recognized tally, and the reward-style config where an abstract reward must bind to something real before it saves.

Chapter 4  ·  Rewards and payout
Maddy caregiver journey
9:41
Maddy's circle
Rewards
Maddy has earned this week
$49.00
of $49.00 possible
"We'll go to the ramen place Friday night!"
Quiet so farWorking toward $49.00
Manage
E2 · weekly hero
HappyBuilt · P1RewardsTab.js

Rewards: the weekly hero

The week-to-date read, held so it never renders a '$0' verdict: a quiet week reshapes rather than reports zero.

What you see

REWARDSMaddy has earned this week$49.00of $49.00 possibleQuiet so farWorking toward $49.00"We'll go to the ramen place Friday night!"

What happens

Passive read of week-to-date against the weekly possible. The bar fills to the earned percentage.

The quiet-week hero suppresses the bar and percent so no '$0' propagates to the subject through tone.

Why it's built this way

The earned, quiet, and goal-quote branches share one hero and merge cleanly; the quote is the goal backing made visible.

pct = min(earnedWeekly/possible,1) · RewardsTab.js:191-227

From Manage · Rewards opensLeads to payout owed (E2)  /  recognized tally (E2)
9:41
Maddy's circle
Payout
$12.00 unpaid
Balance owed to Maddy
Pay Maddy $12.00
Payout history
Rewards
30 min to give
Screen Time earned by Maddy
Give Maddy 30 min
Rewards given
E2 · pay / give
HappyBuilt · P1RewardsTab.js

Rewards: payout owed

The core settle affordance, shown in both currency and non-currency wording: every Pay flips to Give when the reward style is not money.

What you see

Payout · $12.00 · unpaidBalance owed to MaddyPay Maddy $12.00Pay part of thisPayout historyRewards · 30 min · to giveScreen Time earned by MaddyGive Maddy 30 minGive part of this · Rewards given

What happens

Taps to settle the full balance, 'part of this', or opens history.

isCurrency flips every Pay to Give and Payout to Rewards. PayoutButton records the amount with an idempotency token.

Why it's built this way

Money is one form, not the only one; the 'Give' flip is a full copy re-skin worth showing side by side.

recordPayout(balanceOwed) · RewardsTab.js:231-282

From a balance is owedLeads to recording / undo (E2)  /  partial composer (E2)  /  history
Chapter 4  ·  Rewards and payout
Maddy caregiver journey
9:41
Maddy's circle
Payout
Nothing to settle up
Payout history
Payout
No one to pay out to in this circle
Manage
E2 · settled / no subject
UnhappyBuilt · P1RewardsTab.js

Rewards: settled / no subject

The two quiet ends of payout: a fully-settled week, and a circle with no subject to pay. Neither ever renders '$0'.

What you see

Nothing to settle upPayout historyNo one to pay out to in this circle

What happens

Settled: no Pay button; history stays so a mistaken payout can be voided.

No-subject: the label swaps and the history and recognized sections are suppressed.

Why it's built this way

No-zero is locked: settled weeks render the en dash '–', never '$0'. The label carries the meaning.

!subjectId hard guard blocks recordPayout(..., null) · RewardsTab.js:251-266

From balance settled, or no subject joinedLeads to history soft-void  /  blocked until a subject joins
9:41
Maddy's circle
Payout
$12.00 unpaid
Couldn't record the payout. Try again in a moment.
Recording…
Marked $12.00 as paid.Undo just voids it gently. Nothing is taken from them. Undo
Manage
E2 · recording · undo · error
HappyBuilt · P1RewardsTab.js

Rewards: recording, undo, and error

The full payout lifecycle after the tap: the in-flight label, the 8-second soft-void undo, and the deliberately-not-red error.

What you see

Recording…Marked $12.00 as paid.UndoUndoing…Undo just voids it gently. Nothing is taken from them.Couldn't record the payout. Try again in a moment.

What happens

Waits; optionally taps Undo within 8s; on error re-taps the CTA.

Error keeps the idempotency token so a same-amount retry dedupes; a changed amount mints a fresh token.

Why it's built this way

The 8s undo and the not-red error live inside the sensory envelope: no startling flash, ever.

voidPayout() · justPaid clears at 8000ms · RewardsTab.js:287-313

From Pay/Give tappedLeads to Undo restores balance  /  retry the CTA
Chapter 4  ·  Rewards and payout
Maddy caregiver journey
9:41
Maddy's circle
Payout
$12.00 unpaid
Pay Maddy $12.00
Amount to pay
$0.00
Pay
Up to $12.00 Cancel
$4.00 will remain
Manage
E2 · partial composer
NeutralBuilt · P1RewardsTab.js

Rewards: partial payout composer

A collapsed 'Pay part of this' link that expands into an inline composer for settling a chunk against a larger running balance.

What you see

Pay part of thisAmount to pay$0.00Pay$4.00 will remainUp to $12.00Cancel

What happens

Types an amount, taps Pay; or Cancel.

The amount is clamped to the balance so the running balance never goes negative; record_payout clamps server-side too.

Why it's built this way

The collapsed link and expanded form are one affordance, with live-remaining math.

parsedPartial = min(parseFloat, balanceOwed) · RewardsTab.js:600-739

From 'Pay part of this' tappedLeads to recording / undo (E2)  /  collapse via Cancel
9:41
Maddy's circle
Recognized
🚿Showerpartial $6.00
Maddy's whole day
Recognized
Quiet so far
🌙 A rest day
Manage
E2 · recognized tally
HappyBuilt · P1RewardsTab.js

Rewards: recognized tally

A loose tally of what was recognized, never a timestamped log, with both no-zero placeholders: quiet, and a rest day that names itself.

What you see

Recognized🚿 Shower · partial · $6.00Maddy's whole dayQuiet so far · –🌙 A rest day · –

What happens

Reads the loose tally (never WHEN); taps 'whole day' to drill into the dated ledger.

The tally is filtered to active-today, completed or partial; partials count at half.

Why it's built this way

No-zero uses the en dash '–', never '$0'; the rest-day variant is a valid state that names itself rather than reading as failure.

tally.filter(...) · RewardsTab.js:335-377

From Rewards scrolled below payoutLeads to the whole day (dated ledger)  /  quiet-day placeholder
Chapter 4  ·  Rewards and payout
Maddy caregiver journey
9:41
Maddy's circle
Reward style
$
Money
🎮
Screen Time
Custom
Choose what Maddy earns. Money and screen time pay out as themselves. Custom lets you name any reward: a treat, an activity, or a goal to save toward.
Goal name Movie Night, Game Time…
WHAT IT'S FOR (OPTIONAL)We'll go to the ramen place Friday night!
Manage
E2 · reward style + goal
NeutralBuilt · P1RewardsTab.js

Rewards: reward style picker + self-backing goal

The admin-only config where a caregiver chooses what the subject earns, and money is only one of three co-equal forms.

What you see

Reward styleMoney · Screen Time · CustomChoose what Maddy earns. Money and screen time pay out as themselves. Custom lets you name any reward: a treat, an activity, or a goal to save toward.Goal name · Movie Night, Game Time…WHAT IT'S FOR (OPTIONAL)We'll go to the ramen place Friday night!

What happens

Taps Money or Screen Time (self-backing, applies inline) or Custom (opens the gated modal); optionally names a goal and backing.

Money and time write reward_type inline; Custom always commits through the modal. Non-admins never see this section.

Why it's built this way

Money is one form, not the only one; the self-backing goal fields are co-equal.

updateSubject(...) · RewardsTab.js:392-512

From admin scrolls to Reward styleLeads to self-backing goal fields  /  Custom backing (E2)
9:41
Maddy's circle
Reward style
Custom rewardTap to name what these are worth
✨ Custom reward
We'll go to the ramen place Friday night!
Money
Screen Time
Treat
Save toward
Maybe later
Manage
E2 · custom must bind
UnhappyBuilt · P1RewardsTab.js

Rewards: custom reward backing

The reward-canon guardrail made visible: an abstract reward that binds to nothing gets a nudge and a one-tap backing before it saves.

What you see

✨ Custom rewardWe'll go to the ramen place Friday night!Tap to name what these are worthMoney / Screen Time / Treat / Save toward · Maybe later

What happens

Taps to open the gated modal, or picks a real backing from the prompt; 'Maybe later' defers for the session.

The nudge and prompt route toward a real backing before save, and never block earning or payout.

Why it's built this way

An empty backing is a terminal abstract reward, forbidden by canon decision 1; binding it to something real satisfies the rule.

RewardBackingPrompt · RewardsTab.js:513-550

From Custom selected, no backing namedLeads to a real backing saved  /  deferred via Maybe later
05

Your corner

The Care door is the one surface that is only for the caregiver. The same calming tools the subject uses (modeling is the point), a direct line to Jeremy, ambient sound, and the short honest education cards, including what graduation really means: the app is built to need you less.

Chapter 5  ·  Your corner
Maddy caregiver journey
9:41
Maddy's circle
For you, Jeremy.
Supporting is heavy work. This part is yours.
Laughs100 jokes, no pressure
RelaxA few slow breaths
Reach JeremyThey read every note
SoundsSomething soft while you work
What you do for them lands harder when
you take care of yourself too.
Manage
C1 · the Care door
NeutralBuilt · P1CareTab.js

For you, Jeremy

The one caregiver-only surface: a warm door into the caregiver's corner, never a self-report prompt.

What you see

For you, Jeremy.Supporting is heavy work. This part is yours.What you do for them lands harder whenyou take care of yourself too.

What happens

Scrolls; taps any tile to route to a tool, Reach Jeremy for the note modal, an education card to expand.

Static render; the Sounds row reactively reflects the global sound player; no writes on view. The soft note uses p.obj() so it reads "them" for they/them.

Why it's built this way

The one caregiver-only surface's lead, warm, no self-report ask, the caregiver's corner. No badge, no count, no "did you self-care?" prompt, a door never a prompt.

CareTab.js:101-199

From heart into the Care tab (C1)Leads to a tool  /  Reach Jeremy modal  /  an education card
9:41
Maddy's circle
Laughs100 jokes, no pressure
RelaxA few slow breaths
LearnA small fact
Ground5-4-3-2-1
SqueezeTense, then release
BreatheA slow breath out
HumA soft sound to feel
Manage
C1 · the tools (Laughs to Hum)
HappyBuilt · P1CareTab.js

The same tools, modeling the point

Seven co-regulation rows, the exact screens the subject uses, in the caregiver's own corner.

What you see

Laughs · 100 jokes, no pressureRelax · A few slow breathsLearn · A small factGround · 5-4-3-2-1Squeeze · Tense, then releaseBreathe · A slow breath outHum · A soft sound to feel

What happens

Taps a row, haptic, routes via the matching onOpen* handler.

Each onPress fires selectPrimary() then the guarded handler; an undefined handler is a no-op.

Why it's built this way

The co-regulation toolkit, a caregiver using the same tools they'd hand the subject; each tile's exact label and subtitle preserved.

CareTab.js:121-133

From always rendered in the Care doorLeads to Laughs / Relax / Learn / Ground / Squeeze / Breathe / Hum
Chapter 5  ·  Your corner
Maddy caregiver journey
9:41
Maddy's circle
Reach JeremyThey read every note
Ask for a tool, flag something, or just say hi.
SoundsRain is playing
Manage
C1 · Reach Jeremy + Sounds
NeutralBuilt · P1CareTab.js

A direct line, and a soft room

Two own-inset sections: the note channel to Jeremy, and the one passive tool reflecting the live sound player.

What you see

Reach Jeremy · They read every noteAsk for a tool, flag something, or just say hi.SoundsSomething soft while you workRain is playing

What happens

Taps Reach Jeremy opens CareNoteModal; taps Sounds opens onOpenSounds.

Reach Jeremy opens a second doorway to the same founder_notes channel as Settings; Sounds subscribes to the global soundPlayer store.

Why it's built this way

The direct line to Jeremy and the ambient-sound banner. Idle (headset, "Something soft while you work") and playing ("Rain is playing") are one reactive banner.

CareTab.js:138-160

From always render; Sounds follows the live playerLeads to Reach Jeremy modal  /  Sounds picker
9:41
Good to know
💡  How the reward system works
Why immediate, real rewards fit their wiring.
🌱  What graduation means
As Maddy builds the habit, this app is built to let the prompts and approvals thin out over time. Doing less isn't losing the thread, it's the whole point. You'll always be one tap away if they need more support again.
🤍  Rewarding the need, not the look
Why we never reward masking.
Manage
C1 · Good to know (graduation open)
NeutralBuilt · P1CareTab.js

The honest graduation card

Three tap-to-expand micro-doses; card two is the emotional core, the app built to need you less.

What you see

GOOD TO KNOWHow the reward system works · Why immediate, real rewards fit their wiring.What graduation means · This app is built to need you less over time.Rewarding the need, not the look · Why we never reward masking.As Maddy builds the habit, this app is built to let the prompts and approvals thin out over time. Doing less isn't losing the thread, it's the whole point. You'll always be one tap away if they need more support again.

What happens

Default eduOpen null (collapsed); tapping a card sets eduOpen to its index.

Single-open accordion; card two is the honest-graduation copy, the app built to need the caregiver less, framed as the whole point not loss.

Why it's built this way

The three education cards, expanded, especially graduation, the emotional core of the whole product's arc, in the caregiver's own words.

CareTab.js:92-192

From default collapsed; a card tap expands itLeads to the micro-dose explainer  /  tap again to collapse
Chapter 5  ·  Your corner
Maddy caregiver journey
9:41
Maddy's circle
Reach JeremyThey read every note
SoundsSomething soft while you work
Reach JeremyAsk for a tool, flag something, or just say hi. They read every note.
What would help?
Cancel Send
C1 · Reach Jeremy modal (compose)
UnhappyBuilt · P1CareNoteModal.js

The outreach, and when it can't land

The one Care surface that writes to Supabase, plus its calm sent and error branches.

What you see

Reach JeremyAsk for a tool, flag something, or just say hi. They read every note.What would help?Cancel   SendSent. Thank you 💛Couldn't send just now. Give it another try in a moment.

What happens

Types, taps Send; on error re-taps Send; success auto-dismisses. Sending swaps the send icon for a spinner; success shows a checkmark and "Sent. Thank you 💛" (auto-closes at 2200ms).

sendFounderNote writes founder_notes then notes-watch.js iMessage; the error is inline text, not a startling flash (sensory canon).

Why it's built this way

The one Care surface that writes to Supabase and its failure branch, the caregiver's outreach failing to land is a real unhappy path. The error keeps the composer and retains the text to retry.

CareNoteModal.js:30-83 · FounderNoteComposer.js

From taps Reach Jeremy on the Care doorLeads to sent (auto-close)  /  error (retain + retry)  /  back to Care
06

Circle and account

Settings, consolidated. Who you are, who you support, per-person theming, the circle roster and its removes, generating and sharing invites, joining another circle, the OTP password change, and the account edges: delete, lock, sign out. Every destructive path keeps its confirm.

Chapter 6  ·  Circle and account
Maddy caregiver journey
9:41
Settings
You
You
Who you support
Your circle
Sensory
Companion
Notifications
Got a code?
Tools
Help
Reach Jeremy
Account
Lock caregiver view
Settings · the scroll
NeutralBuilt · P1SettingsTab.js

The Settings map

One scroll of inset-grouped sections; admin sees more of them, never a separate window.

What you see

YouWho you supportYour circleSensoryCompanionNotificationsGot a code?ToolsHelp · Reach JeremyAccount · Lock · Sign out

What happens

Scrolls; taps any row to edit or expand.

circleMembers loads via loadCircleWithProfiles plus a realtime group_members subscription; subject copy renders live through the pronoun engine.

Why it's built this way

The Settings map and the admin/non-admin difference in one frame; the two full-scroll states differ only by which sections render.

Admin-only: Who you support, Invite, Tools, Account. Non-admin simply doesn't render them, and per-member Remove disappears.

From Manage · Settings tileLeads to any section (You, Circle, Sensory, Tools, Account)
9:41
Settings
You
NameYour name
Emoji👤
Pronounsthey/them
Settings · You
NeutralBuilt · P1SettingsTab.js

Who you are

The caregiver's own three identity fields, each an inline micro-editor.

What you see

YouName · Your nameEmojiPronouns · they/them

What happens

Types a name, picks an emoji, sets pronouns.

Name writes onUpdateCaregiver({name}) per keystroke; emoji and pronouns persist on select via saveMyPronouns.

Why it's built this way

The caregiver's own identity fields; the three micro-editors merge into one You mockup per the consolidation rule.

Emoji taps expand an EmojiPicker grid inline; Pronouns taps expand a PronounPicker inline.

From Settings · You rowLeads to inline EmojiPicker / PronounPicker
Chapter 6  ·  Circle and account
Maddy caregiver journey
9:41
Settings
Who you support
Unnamed primary
NamePre-fills when they sign up with your invite code.
Emoji👤
PronounsSet this once they join.
Tap a name to switch. The dashboard reloads against the chosen primary.
Settings · Who you support
NeutralBuilt · P1SettingsTab.js

Who you support

The admin switcher plus the pre-signup placeholder, sharing one set of editor rows.

What you see

Who you supportTap a name to switch. The dashboard reloads against the chosen primary.Unnamed primaryName · Pre-fills when they sign up with your invite code.EmojiPronouns · Set this once they join.

What happens

Switches active primary, or edits the subject's name, emoji, and pronouns.

onSwitchSubject reloads the dashboard against the chosen subject; pre-signup name persists via saveGroupPendingSubjectName; the pronoun picker is guarded by subjectId.

Why it's built this way

The multi-subject switcher and the pre-signup placeholder are distinct admin states of the same section, merged on shared editor rows.

From Settings · Who you supportLeads to dashboard reload / inline editors
9:41
Settings
Your circle
MaddyPrimary
Jeremy · YouCaregiver
Aunt RoSupporter Remove
Joining…Peer
Settings · Your circle
NeutralBuilt · P1SettingsTab.js

The roster

Every member with their role label, including the transient placeholder for someone still joining.

What you see

Your circleMaddy · PrimaryJeremy · You · CaregiverAunt Ro · Supporter · RemoveJoining… · Peer

What happens

Reads the roster; admin may tap Remove.

Live via the realtime group_members channel; a member with no name yet renders the literal "Joining…" (deliberately not "Someone").

Why it's built this way

The roster with role labels and the transient "Joining…" placeholder; a real in-progress state distinct from a resolved member.

Admin sees a red Remove on removable rows (non-self, non-subject).

From Settings · Your circleLeads to remove-member confirm (Remove)
Chapter 6  ·  Circle and account
Maddy caregiver journey
9:41
Settings
Your circle
MaddyPrimary
Remove Maddy from the circle?
They'll lose access to this circle. You can invite them back with the correct role anytime.
Couldn't remove them. Try again in a moment.
Cancel
Removing…
Settings · remove member
UnhappyBuilt · P1SettingsTab.js

Removing someone

A destructive, access-revoking action with its confirm, in-flight, and error folded into one card.

What you see

Remove Maddy from the circle?They'll lose access to this circle. You can invite them back with the correct role anytime.CancelRemoveRemoving…Couldn't remove them. Try again in a moment.

What happens

Taps Remove to confirm, or Cancel; retries on error.

removeGroupMember(user_id, groupId) then reload; catch sets removeError.

Why it's built this way

A destructive, irreversible-access action; its confirm, loading, and error are one destructive path with distinct states.

In flight, Remove swaps to "Removing…" with both disabled; on failure a red line sits above the buttons and the card stays open.

From Settings · roster (Remove)Leads to reloaded roster / retry
9:41
Settings
Sensory
🌈Bubblegum
Dream Space
🍃Mint Fresh
🌊Ocean Blue
🦉Night Owl
Pick the room your day fits in. Each one has its own surface, gradient, contrast, and motion.
Settings · Sensory (Bubblegum)
NeutralBuilt · P1SettingsTab.js

Per-person theming

Shown in Bubblegum to prove the same shape re-hues per person; theme is palette, tone is fixed by role.

What you see

SensoryPick the room your day fits in. Each one has its own surface, gradient, contrast, and motion.

What happens

Taps a theme.

setTheme(t) plus saveProfile({theme: t.key}) writes the caregiver's own theme; per-person theming means each person carries their own hue inside a role-fixed shape.

Why it's built this way

Rendered in Bubblegum specifically so the doc demonstrates that the same shape re-hues per person.

Each swatch keeps the theme's own hue, the one exemption from the Things-room flatten; the active theme shows a checkmark.

From Settings · SensoryLeads to re-themed caregiver surface
Chapter 6  ·  Circle and account
Maddy caregiver journey
9:41
Settings
Companion
Reset Maddy companionWalks them through choosing a new one next time they open the app
Reset Maddy companion?
Maddy'll choose a new companion the next time they open the app.
Could not reset companion. Try again in a moment.
Cancel
Reset
Settings · Companion (Night Owl)
UnhappyBuilt · P1SettingsTab.js

Resetting their companion

A destructive companion reset with confirm and error, rendered in Night Owl to prove dark is a real environment.

What you see

CompanionReset Maddy companionWalks them through choosing a new one next time they open the appReset Maddy companion?Maddy'll choose a new companion the next time they open the app.Cancel · ResetCould not reset companion. Try again in a moment.

What happens

Taps to open confirm, then Reset or Cancel; retries on error.

resetSubjectCompanionRpc(subject.id); catch sets companionResetError; the card stays open.

Why it's built this way

Rendered in Night Owl so the doc proves the dark theme is a real sensory environment, not just an inverted palette.

From Settings · Companion rowLeads to their companion re-picker on next open / retry
9:41
Settings
Invite Maddy
A one-time code Maddy can use to join your circle.
7XR4P2QK
expires in 7 days
Share code
Invite more people
Pick the role. Caregivers help day-to-day. Supporters and peers cheer from the outer ring. They're always free.
Settings · invite
HappyBuilt · P1SettingsTab.js

The invite lifecycle

Generate, code, share, revoke, and the generic error, for the subject invite and the additional-role invites.

What you see

Invite MaddyA one-time code Maddy can use to join your circle.Generate invite code · Generating…expires in 7 daysShare code · Generate a new codeInvite more people · CAREGIVERCancel this code · Generate a different inviteCould not generate invite code. Try again in a moment.

What happens

Generates a code, shares it via the native sheet, regenerates or revokes.

generateInviteCode(groupId, role); Share.share(buildInviteMessage); revoke/regenerate reset the code; catch sets inviteError.

Why it's built this way

The full invite lifecycle merged into one flow; the generic error also covers the over-cap trip.

Loading swaps labels to "Generating…"; failure shows a red line.

From Settings · Invite sectionLeads to native share sheet / revoke / regenerate
Chapter 6  ·  Circle and account
Maddy caregiver journey
9:41
Join another circle
🤝
Enter the 8-character code someone shared with you.
XXXXXXXX
Joining Jeremy's circle
Join circle
You already have a circle
You're already set up in Maddy's circle. If Aunt Ro wants to support you, they should join your circle, not create a separate one.
Settings · join another circle
UnhappyBuilt · P1JoinCircleSheet.js

Joining another circle

The full code flow with its error family and the one-circle-per-subject hard block.

What you see

Got a code?Enter a code someone shared with you to join their circle.Join another circleEnter the 8-character code someone shared with you.ContinueWe couldn't find that code. Double-check it with whoever sent it.Joining Jeremy's circle · Join circleCouldn't join the circle. Try again in a moment.You already have a circleGot it

What happens

Types a code, Continues, confirms Join circle; on the subject-conflict block taps "Got it".

validateInviteRpc then redeemInviteRpc; a subject can belong to only one circle, so a second subject code is hard-blocked.

Why it's built this way

Joining another circle with its full error family and the one-circle-per-subject hard block; each distinct verbatim rejection preserved.

From Settings · Got a code?Leads to joined circle / hard-block dismiss
9:41
Settings
Tools
Preview Maddy's viewSee what they see (read only)
Restore starter thingsAdd back any default things that were deleted
Reset today's thingsMark everything incomplete so it can be redone
Reset today's things?
Marks all things incomplete so they can be done again today.
Cancel
Reset
Settings · Tools
UnhappyBuilt · P1SettingsTab.js

Admin utilities

Preview, an additive restore, and a genuinely destructive reset, told apart by confirm-button colour.

What you see

ToolsPreview Maddy's view · See what they see (read only)Restore starter things · Add back any default things that were deletedReset today's things · Mark everything incomplete so it can be redoneRestore starter things?Reset today's things?Marks all things incomplete so they can be done again today.Cancel · Restore · Reset

What happens

Previews the subject's view, restores deleted starters (additive), or resets today's completion state (destructive).

Restore upserts STARTER_TASKs; Reset is guarded on subjectId and UPDATEs completed/pending/redo flags off.

Why it's built this way

Reset-today is genuinely destructive (wipes today's state) and restore is safely additive; the two confirms differ in button colour by intent.

From Settings · ToolsLeads to preview view / restore / reset confirm
Chapter 6  ·  Circle and account
Maddy caregiver journey
9:41
Change password
Enter your reset code
We'll email a code to jerrunge@gmail.com.
••••••
Verify
Resend in 30s
Settings · change password (OTP)
UnhappyBuilt · P1PasswordResetFlow.js

Change password by code

The three-step OTP flow, request then code then set, each with its own loading and error, in the AuthFrame palette.

What you see

Change passwordReset your password · We'll email a code to jerrunge@gmail.com.Send codeEnter your reset code · 6-digit codeVerify · Resend in 30sDidn't get it? Resend itSet a new password · At least 8 charactersConfirm new password · Save new passwordThose don't match. Try again.

What happens

Taps Send code, enters or pastes the 6-digit code, sets and confirms a new password.

resetPasswordForEmailverifyOtp(recovery)updateUser({password}); friendlyAuthError maps server errors; validation guards empty/length/mismatch in order.

Why it's built this way

The whole OTP password change consolidated to one flow mockup, rendered in the AuthFrame palette.

The code input auto-submits at 6 digits; a "Resend in 30s" countdown gates the resend.

From Settings · Account · Change passwordLeads to new password saved / step error
9:41
Settings
Help
How the app worksOpen the quick guide
Reach Jeremy
Goes straight to Jeremy as a message. They read every one.
Send Jeremy a noteBugs, ideas, or just hi. It reaches them.
Sent. Thank you 💛
Settings · Help + Reach Jeremy
NeutralBuilt · P1FounderNoteSection.js

Help and a line to Jeremy

The always-present quick guide and a second doorway to the same founder-note pipe as Care.

What you see

HelpHow the app works · Open the quick guideReach JeremyGoes straight to Jeremy as a message. They read every one.Send Jeremy a note · Bugs, ideas, or just hi. It reaches them.Sent. Thank you 💛

What happens

Opens the quick guide, or sends Jeremy a founder note.

GuideModal at the bottom of the tree; FounderNoteSectionfounder_notesnotes-watch.js.

Why it's built this way

The always-present help and the Settings-side founder note channel, a second doorway to the same pipe as Care.

On success a checkmark and "Sent. Thank you" row auto-clears at 4s.

From Settings · Help / Reach JeremyLeads to GuideModal / founder note sent
Chapter 6  ·  Circle and account
Maddy caregiver journey
9:41
Settings
Account
Delete accountPermanently removes all data. Cannot be undone.
Delete your account?
This permanently removes your account, all subjects, things, earnings, and photos. There is no undo.
Something went wrong. Try again, or contact support if this keeps happening.
Cancel
Delete everything
Lock caregiver view
Sign out?
You'll need your email and password to sign back in.
Stay
Sign out
Settings · account edges
UnhappyBuilt · P1SettingsTab.js

The account edges

Delete, lock, and sign out at the bottom of Settings, each destructive path keeping its confirm.

What you see

Delete accountPermanently removes all data. Cannot be undone.Delete your account?This permanently removes your account, all subjects, things, earnings, and photos. There is no undo.Delete everythingSomething went wrong. Try again, or contact support if this keeps happening.Lock caregiver viewSign out · Sign out? · StayYou'll need your email and password to sign back in.

What happens

Deletes the account (permanent), locks the caregiver view, or signs out (confirmed).

Delete → delete-account edge function + signOut; lock → onLock re-gates biometric; sign out → onSignOut after confirm.

Why it's built this way

Delete is permanent data destruction; sign-out is confirmed to avoid accidental session loss; each keeps its confirm.

Delete's confirm card is red-bordered; a failure appends a red line.

From Settings · Account (bottom)Leads to account deleted / re-locked gate / signed out
07

What comes back

Phase 2, designed but not built. The warmth that flows back from them: the felt-feedback card, the earning milestone, the load-relief hatch, and the Group F signals they initiate, all governed by an arbiter that surfaces exactly one warm thing per open, or calm silence.

Chapter 7  ·  What comes back
Maddy caregiver journey
Designed · not built
9:41
Maddy's circle
In their words
The shower felt like a lot today.
too much sensory
🎧 They left a voice note · 0:08
Soften this task
Suggested: make it optional on tough days
Adjust this task
Manage
B1 · in their words
NeutralDesigned · P2CAREGIVER-BUILD-SPEC.md

B1 – The After: in their words

The dedicated card behind the P1 rough-line seam, where their own words about a task lead and the fix is one reversible tap.

What you see

IN THEIR WORDS"The shower felt like a lot today."too much · sensory🎧 They left a voice note · 0:08Soften this taskSuggested: make it optional on tough daysOr just send them a note

What happens

"Soften this task" writes task config only, fully reversible; the lean fix auto-applies with an undo. "Or just send them a note" routes to A4. Admin "Adjust this task" opens TaskEditModal.

Reads task_feedback under inner-ring RLS, dedupes by task|valence, auto-marks-seen after 2.5s.

Why it's designed this way

The dedicated in-their-words card is never folded into the composer, and softening never touches earned or future money. Their quote keeps their own hue.

feedbackCadence.js · not built. No B1 surface renders in src/.

From the P1 The After rough lineLeads to softened task config (undo)  /  A4 note  /  TaskEditModal (admin)
Designed · not built
9:41
Maddy's circle
Maddy passed $100 they earned themselves.
You made the room for that. Quietly, every day.
Close
Manage
B2 · in their corner
HappyDesigned · P2CAREGIVER-BUILD-SPEC.md

B2 – in their corner (milestone)

A cream medallion at arbiter rank 4, shown once when they cross an earning threshold they reached themselves.

What you see

[96px cream medallion, heart, one fade-in]Maddy passed $100 they earned themselves.You made the room for that. Quietly, every day.Close

What happens

Fires when the subject crosses a monotonic earning milestone (7 / 30 / 100 / 365), shown once per milestone and dismissable.

Tapping "Close" writes the warm_acks ack so it never resurfaces.

Why it's designed this way

A descriptive figure only, never a tier, never a preview of the next threshold, and no streak count. It renders inside the arbiter's single warm slot.

caregiverMilestones.js exists; the medallion and arbiter wiring are not built.

From a caught-up open at a crossed milestoneLeads to dismiss (acked)  /  back to the calm home
Chapter 7  ·  What comes back
Maddy caregiver journey
Designed · not built
9:41
Maddy's circle
🐢
Let's quiet things down.
I'll carry today. Nothing here will pull at you.
Maddy won't be left waiting. They'll see a warm note: “Seen. I'll get to these when there's room.”
Take a breath
Manage
C2 · quiet things down
UnhappyDesigned · P2CAREGIVER-BUILD-SPEC.md

C2 – running low, quiet things down

The strained-caregiver hatch behind the A2 off-ramp: the app takes today and the subject is never left waiting.

What you see

Let's quiet things down.I'll carry today. Nothing here will pull at you.Maddy won't be left waiting. They'll see a warm note: "Seen. I'll get to these when there's room."Take a breath

What happens

Reached by tapping the load off-ramp "Running low? Quiet things down" from A2. "Take a breath" sends the subject a warm holding note, then routes the caregiver to a breath. Zero self-report is asked.

Why it's designed this way

On hold, defaults carry and the subject is never stranded. There is no "how are you feeling?" prompt anywhere.

NEW running-low hold + holding-note pipe, not built.

From the A2 "Quiet things down" linkLeads to holding note to subject  /  a caregiver breath
Designed · not built
9:41
Maddy's circle
🐢
Maddy left you something
Thanks for not giving up on me. Today was a better one.
From Maddy
Say something back
Manage
F1 · gratitude
HappyDesigned · P2CAREGIVER-BUILD-SPEC.md

F1 – Maddy left you something (gratitude)

The #1 evidence-backed warmth lever: a gratitude message from them, arriving quiet, never as a stat.

What you see

MADDY LEFT YOU SOMETHING"Thanks for not giving up on me. Today was a better one."From MaddySay something back

What happens

Fires when the subject sends the caregiver a gratitude message; arbiter precedence rank 2, just below F5. "Say something back" routes to A4 to reply, and tapping acks the F1 item.

Reads maddy_messages kind='gratitude' OR encouragement_notes.to_user_id, pushed content-free.

Why it's designed this way

It renders as a quiet hero moment: never a stat, no confetti. The reply is optional.

NEW kind='gratitude' column + F1 card not built.

From a subject gratitude message (rank 2)Leads to A4 reply (optional)  /  acked on tap
Chapter 7  ·  What comes back
Maddy caregiver journey
Designed · not built
9:41
Maddy's circle
They sent a little something back.
So proud of how you showed up today.
💛
Manage
F2 · reaction back
HappyDesigned · P2CAREGIVER-BUILD-SPEC.md

F2 – they sent a little something back (reaction)

Their chosen reaction to a note the caregiver sent, paired with their own outbubble. Never a seen-at, never a count.

What you see

They sent a little something back.[your outbubble + their reaction chip]

What happens

Fires when the subject reacts to a note the caregiver previously sent them; arbiter precedence rank 3. A passive view of their chosen reaction, acked on interaction or dismiss.

Reads caregiver_notes.reactions + NEW from_user_id for routing.

Why it's designed this way

F2 never pushes, shows only their chosen reaction, never a "seen at 3:14" or a reaction count. A removed reaction is silent.

NEW from_user_id column + F2 card not built.

From a subject reaction to the caregiver's note (rank 3)Leads to passive view  /  acked on interaction or dismiss
Designed · not built
9:41
Maddy's circle
👵
🧑
🧓
Maddy's circle was in their corner today.
Nana and two others sent them warmth.
You are not carrying this alone.
Manage
F3 · the circle showed up
HappyDesigned · P2CAREGIVER-BUILD-SPEC.md

F3 – the circle showed up

Warmth toward them from the wider circle, spelled not counted, so a caregiver feels they are not alone.

What you see

Maddy's circle was in their corner today.Nana and two others sent them warmth.You are not carrying this alone.

What happens

Fires when 3 or more distinct supporters sent warmth toward the subject in one subject-local day; consent-gated (default off); arbiter rank 5. A passive warmth view, acked on interaction or dismiss.

Reads milestone_shares / encouragement_notes + NEW consent column + F3-crossing evaluator.

Why it's designed this way

Non-numeric framing spells the warmth ("Nana and two others"), never the raw count, never "who didn't show." At most one F3 per subject-local day.

NEW consent column + evaluator + card not built.

From 3+ supporters' warmth in one local day (rank 5)Leads to passive view  /  acked on interaction or dismiss
Chapter 7  ·  What comes back
Maddy caregiver journey
Designed · not built
9:41
Maddy's circle
🐢
Maddy sent Pip over with a joke.
Tap for the rest 👀
Manage
F4 · levity
HappyDesigned · P2CAREGIVER-BUILD-SPEC.md

F4 – Maddy sent Pip over with a joke

The decoupled levity fallback at the lowest rank, returned only on a truly-empty open so higher warmth never starves it.

What you see

Maddy sent Pip over with a joke.Tap for the rest 👀

What happens

Fires when the subject sends the caregiver a laugh from their Laughs screen; arbiter rank 6, lowest. Tapping "Tap for the rest 👀" reveals the punchline (content-surprise); reacts; acked on interaction.

Reads NEW levity_sends table + private bucket.

Why it's designed this way

Content-surprise only, never sensory. Send-cap 2/day 5/week silently coalesced, photos EXIF-stripped with short-TTL signed reads; the punchline stays out of the accessibility tree until revealed.

NEW levity_sends table + card + subject-side send flow not built.

From a subject laugh on a truly-empty open (rank 6)Leads to punchline reveal  /  react  /  acked on interaction
Designed · not built
9:41
Maddy's circle
Maddy reached out today.
Today felt heavy. They wanted you to know.
Be in their corner
Their wins are here when you're ready.
Manage
F5 · hard-day reach-out
UnhappyDesigned · P2CAREGIVER-BUILD-SPEC.md

F5 – Maddy reached out today (hard day)

The single highest-precedence warm signal, the one card that owns the whole screen, even above a pending Recognize.

What you see

Maddy reached out today.Today felt heavy. They wanted you to know.Be in their cornerTheir wins are here when you're ready.

What happens

Fires when the subject signals a hard day (writes daily_state.reached_out_at). "Be in their corner" routes to warmth, never asks "are you okay?"; the tap writes a subject-unreadable warm_acks ack.

Why it's designed this way

Soft tint and heart, never red / banner / modal. It owns the whole screen with no cluster and no competing CTA, the one arbiter exception to pending-Recognize suppression. Silence still means okay.

reached_out_at may be shipped; the F5 surface + warm_acks ack are not built.

From a subject hard-day signal (highest precedence)Leads to warmth  /  subject-unreadable ack  /  wins still available
Chapter 7  ·  What comes back
Maddy caregiver journey
Designed · not built
9:41
Maddy's circle
🐢
All quiet with Maddy.
I'll find you if anything changes.
Manage
Arbiter · all quiet
NeutralDesigned · P2CAREGIVER-BUILD-SPEC.md

The warm-slot arbiter + all-quiet default

The precedence engine that fills the home's one warm slot, and its most-common outcome: calm silence, framed as calm, never a deficit.

What you see

All quiet with Maddy.I'll find you if anything changes.Their wins are here when you're ready.precedence: F5 > F1 > F2 > milestone > F3 > F4

What happens

Runs once per home focus and returns exactly one unacked warm item or null (the common case). The one offer on the calm home is warmth, never a chore prompt: reach into Self-Care or reach them first.

One batched debounced read, LEFT JOIN to warm_acks.

Why it's designed this way

Acked only on a real action or a completed VoiceOver read, never on bare render. Returns null while Recognize is pending (except F5); on read error it renders the plain calm home.

warmInbound.js verified absent, not built. The P1 A1 caught-up home is today's analog.

From every home focusLeads to one warm card (F5/F1/F2/milestone/F3/F4)  /  calm silence
08

The long arc

Phase 2, designed but not built. Graduation as a display layer: the home reframes into their growth and your success, and admins get a gentle step-down suggestion to offer them more room. The fade is framed as a win the caregiver built, never as loss.

Chapter 8  ·  The long arc
Maddy caregiver journey
Designed · not built
9:41
Maddy's circle
Maddy
🦊 Maddy's mostly got this now.
For you
More and more, this part is just yours.
They're doing more on their own, because of the foundation you built.
Your cornerThe calming tools, and a line to Jeremy.
Send Maddy a noteStill here whenever you want to reach them.
Manage
D1 · the fade
HappyDesigned · P2CAREGIVER-BUILD-SPEC.md

D1 · the fade (months later)

The same home, reframed into two zones: a smaller them, a larger you, so the surface becomes the caregiver's own.

What you see

MADDYMaddy's mostly got this now.FOR YOUMore and more, this part is just yours.They're doing more on their own, because of the foundation you built.

What happens

Passive read; access to the condensed Self-Care rows in the YOU zone. No decision required.

Display layer; ships dormant (cannot compute a level at v1.0 and must not show a fabricated one); framed as THEIR growth + YOUR success, never emptiness, no downward metric.

Why it's designed this way

The graduation reframe of the home. The them/you distinction is carried by LABEL + SIZE only, never by tint. It ships dormant and never activates in P1.

CAREGIVER-BUILD-SPEC.md §5 D1 · §12 (two-zone them / you layer)

From the regulation arc, months in (dormant in P1)Leads to Your corner / Send a note  /  step-down (D2)
Designed · not built
9:41
Maddy's circle
🦊
Maddy's been steady for a while.
A gentle idea, just for you
Want to offer them a little more room?
You could let some tasks run without a check from you. They can take it or leave it.
Offer it to Maddy
They decide. Nothing changes unless they say yes.
Manage
D2 · soft step-down
HappyDesigned · P2CAREGIVER-BUILD-SPEC.md

D2 · soft step-down (admin only)

Admins get a gentle suggestion to offer them more room. The caregiver suggests; the subject decides.

What you see

Maddy's been steady for a while.A gentle idea, just for youWant to offer them a little more room?You could let some tasks run without a check from you. They can take it or leave it.Offer it to MaddyThey decide. Nothing changes unless they say yes.

What happens

Admin taps 'Offer it to Maddy', which sends the offer to the subject, who accepts or declines. The caregiver NEVER moves their level directly.

Admin-only gate (can_access_settings); asymmetric authority, suggest never decide; nothing changes unless they say yes; never guilt.

Why it's designed this way

The designed graduation step-down suggestion. It closes the long-arc chapter on the subject's agency: the admin step-down surface doesn't exist yet.

CAREGIVER-BUILD-SPEC.md §5 D2 · can_access_settings

From admin caregiver, subject steady a whileLeads to subject accepts / declines (nothing changes unless yes)