Card Style — Same as Home
All cards
Background: --bg-soft #fafaf7 · Radius: 14px · No borders, no glass, no fancy shadows. Same recipe as Home stat-tile.
Color comes from icon square
Each card uses a small 30×30 rounded square (8px radius) for status color: red for paused/danger · brown for strict · blue for break · green for task done. Icon square is solid-filled with white icon inside. Exact same pattern as Home's .icon-bg.time and .icon-bg.block.
Stat tiles on Complete
Same --bg-soft cream tiles, 14px radius, no borders. Just number + label. Visual identity matches Home stat-tile exactly.
Share buttons
Three flat --bg-soft buttons in a row. 12px radius. Icon + label. Looks like a row of stat-tiles.
Task context (Complete)
Just a passive line: "You worked on Finish v4 mockup". No card, no toggle by default. A small Mark done link sits at the end for the rare case where the task is actually finished. Default behavior: task stays in todos, user manages it from Tasks tab.
Header icons
--bg-soft background, 12px radius. Active = primary-dark color. Locked (Strict) = muted-light (greyed). Same exact treatment as Home header icons.
No glass / no premium effects
Cut all backdrop-filter blurs · cut layered drop shadows · cut inset highlights · cut linear gradients on fills. Visual consistency wins over individual screen polish.
What Stayed the Same
Mascot system
Same 4 face states (studying / worried / relaxed / celebrating) with breathing/bob/float/joy animations. Lock badge for Strict mode (dark-brown circle).
Ring + glow
SVG progress ring with state-colored stroke and glow. Pulsing radial glow behind mascot. This is the only "premium" touch retained — kept because it's the visual centerpiece, not just a card decoration.
Motivation block
Task name (Plus Jakarta 14px 700) + state-colored quote (12px italic). Below the timer.
Strict Mode logic
All Strict differences preserved: locked back/setup, mascot lock badge, dark-brown pause btn, info-card with strict icon, "+50% bonus" pill on Complete, "phone trapped" copy.
Engineering Spec — Focus Screen v1.0 (MVP)
This is the canonical spec for the Focus Screen. Use this alongside the visual mockup above and the focus_screen_prompt.md file for Claude Code implementation. Stack is Compose Multiplatform / Kotlin Multiplatform. UI consistency follows the existing Home screen and Block Screen patterns documented in lockinvibe_docs_v2.html.
1 · Screen Purpose
Goal
Show the user a live focus session: timer, mascot, app blocking status. Acts as the user's "you're locked in" environment for the duration of a Pomodoro-style session. Differentiator vs other Pomodoro apps: mascot reacts emotionally, apps are actually blocked, Strict Mode commits the user to finish.
Entry points
From Home screen Begin button (loads last settings, instant start) · from Setup screen Lock In button (with new settings).
Exit points
Flexible: back arrow → Home (session continues in background) · Setup gear → confirm dialog → end session, no rewards · End early on Paused → confirm dialog → Home · Timer 0:00 → Complete state. Strict: only Timer 0:00 → Complete state.
2 · State Machine
Flexible flow
Active → (Pause → Active)? → 0:00 → Break → 0:00 → Complete
Paused max 1 per session, auto-resumes after 2 min. End early jumps to Home with no Complete screen. Skip on Break jumps directly to next Active or Complete.
Strict flow
Active → 0:00 → Break → 0:00 → Complete
No pause allowed. No skip on break. No end early. Linear, no branches. Phone stays on Focus Screen until Timer 0:00 (enforced via foreground service + accessibility service redirect).
State transitions
Each state change triggers: mascot face swap (4 composables), ring color update, tag pill update, controls update, info chip update, motivation quote rotation.
3 · Mascot States
Studying (Active)
Eyes narrow to horizontal slits · concentration mouth · breathing animation (scale 1 → 1.04, 3.5s loop) · 5s blink interval · gold palette.
Worried (Paused)
Eyes wide and round · flat mouth (thin line) · idle bob animation (4s loop, no scale change) · gold palette · no sparkles.
Relaxed (Break)
Eyes closed (curved arcs facing up) · soft U-smile · float animation (4s, gentle Y-translate) · gold palette stays (visual mode change comes from ring + glow color, not mascot).
Celebrating (Complete)
Eyes squinted up (joy curves) · big open mouth · party hat (orange cone with golden pom-pom) · bounce-joy animation (0.9s, scale + rotate).
Strict lock badge
In Strict mode only: small dark-brown lock badge on bottom-right of mascot, white-bordered. Visible across Active, Paused, Break. Disappears on Complete (replaced by party hat).
4 · Core Components
Header row
Three slots: Back arrow (left, 36×36, returns to Home) · Tag pill (center, shows mode + state) · Action icon (right, 36×36).
Action icon swaps by state: gear (Setup access, Flexible Active/Paused/Break) · disabled lock (Strict, all states) · share (Complete, both modes).
Tag pill colors
Active = gold tint · Paused = soft red · Break = blue · Complete = green · Strict (any state) = dark brown solid. Live pulsing dot on Active and Break (animation: 1.6s opacity pulse).
Ring + Mascot
220×220 SVG progress ring (8px stroke, rounded caps, glow drop-shadow) wraps around 120×120 mascot. Ring color matches state. Background radial glow pulses (4s loop).
Timer
Plus Jakarta Sans 800, 50px, tabular figures (font-variant-numeric: tabular-nums) so digits don't shift. Color matches state (default on-surface, paused red, break blue, complete green/smaller).
Motivation block
Two centered lines below timer:
(1) Task name in Plus Jakarta 14px 700 (only if user set one in Setup)
(2) State-specific quote in 11.5px italic, color matches state. Quote rotates per session per state from a static bank.
Info card (Paused/Strict states)
Cream --bg-soft card, 14px radius. Icon square (30×30, 8px radius, solid color fill) on left + title + sub on right. Used for Strict Mode indicator, Paused banner, Strict Pause banner, Strict Break banner. Same recipe as Home stat-tile pattern.
Bottom info chip
Small pill: "10 apps blocked" / "10 apps locked" (Strict) / "Apps unblocked during break" / "10 apps still blocked" (Paused). 11px font, cream bg, color-tinted variants for Strict and Break.
Single stat card (Complete only)
Cream card, centered "1h 35m" in 32px Plus Jakarta 800, "FOCUSED TODAY" label in 10px caps below. Rolls up all sessions today, not just current session.
Task subtitle row (Complete only)
Tiny checkbox (16×16) + "Done · {task name}" in 12px text. Optional — only shown if user set a task. Tap to mark task done from Tasks tab. Truncates with ellipsis if task name too long.
5 · Color Tokens (CSS variables → Compose equivalent)
Primary
--primary #ffbf00 · --primary-light #ffd45a · --primary-dark #815f00 · --primary-deep #563e00
Surface
--on-surface #1a1a1a · --muted #6b7280 · --muted-light #d1d5db · --border #f0eee8 · --bg-soft #fafaf7
State colors
--success #3d8c40 · --soft-error #f95630 · --break-blue #4a7fb8
Mascot gradient
radial-gradient(circle at 32% 28%, #ffe89a 0%, #ffbf00 55%, #d99e00 100%) with inset and outer shadows for depth. Lock badge gradient: linear-gradient(180deg, #6e4f00, #563e00).
6 · Animation Specs
Mascot breathing
Active state. Scale 1 → 1.04, translateY 0 → -2px. 3.5s ease-in-out, infinite. Use infiniteTransition in Compose.
Mascot blink
Active state. scaleY(1) → scaleY(0.1) → scaleY(1) at 88-92% keyframes. 5s loop, infinite. Eye element only.
Mascot bob (worried)
Paused state. translateY 0 → -3px, rotate -1° → 1°. 4s ease-in-out, infinite.
Mascot float (relaxed)
Break state. translateY 0 → -6px, rotate -1° → 1°. 4s ease-in-out, infinite.
Mascot bounce (celebrating)
Complete state. translateY 0 → -8px, rotate -3° → 3°, scale 1 → 1.05. 0.9s ease-in-out, infinite.
Glow pulse
Active state radial glow behind mascot. opacity 0.5 → 0.85, scale 1 → 1.08. 4s ease-in-out, infinite. Disabled on Paused.
Live dot pulse
Tag pill. opacity 1 → 0.5, scale 1 → 0.8. 1.6s ease-in-out, infinite. Disabled on Paused.
Reduced motion
All loop animations must be disabled when system reduced motion is on. Mascot becomes static, ring still updates per-second but without easing transitions.
7 · Quote Bank (v1.0 starter set)
Active (Flexible)
"You got this." · "One brick at a time." · "Future you says thanks." · "Slow is smooth, smooth is fast." · "Just this. Right now."
Active (Strict)
"You committed. Now finish." · "No exits. Just work." · "The lock is on. So is the focus."
Paused (Flexible)
"Pause is fine. Quitting is the only fail." · "It's okay. Come back when ready." · "Take a breath. The work waits."
Paused (Strict)
"Breathe. Vibe waits with you." · "Quick reset. We're not done yet."
Break (both)
Flexible: "Rest is part of the work." · "Stretch, sip water, look outside."
Strict: "Stretch. Breathe. Apps stay locked." · "Even your break is sacred."
Complete (both)
Flexible: "That's how it's done." · "Streak protected." · "Future you is grinning."
Strict: "Locked in. Logged off. Legend." · "No flinching. That was clean."
8 · App Blocking Behavior
Active state
All blocked apps are blocked. iOS: FamilyControls + Screen Time API shows shield. Android: AccessibilityService intercepts launches and shows shield activity.
Paused state
Blocking remains active. User cannot scroll Instagram during pause. Pause is for thinking/short breaks, not phone time.
Break state (Flexible)
Apps unblocked. User can scroll freely during break.
Break state (Strict)
Apps stay blocked. User cannot scroll. The break is enforced rest, not phone time.
Complete state
Apps unblocked immediately. Blocking releases as soon as Complete state is entered.
Force quit / app kill (Strict)
Foreground service keeps timer running and blocking active. Relaunching app lands on current state. Closing LockInVibe does NOT end a Strict session. Only Timer 0:00 does.
Force quit (Flexible)
Session pauses on force-quit. Lands on Paused state on relaunch with banner: "Welcome back."
9 · Background & Persistence
Timer continuity
Use Android WorkManager + ForegroundService with persistent notification. iOS: background timer + scheduled local notifications + silent push for resync. Timer must continue accurately across app backgrounding, screen locks, and OS reboots.
State persistence
Session state stored in local DB (SQLDelight or equivalent). Schema: session_id, mode (flex/strict), state (active/paused/break/complete), task_name, duration_min, started_at, paused_count, blocked_apps[]. Persists across crashes.
Behavioral data capture
Per session, log: start time, end time, completion status (completed/abandoned), pauses (count + duration), block events (which app the user tried to open), task association. Stored locally, no cloud sync in v1.0. Used for future AI Add-on insights.
10 · Edge Cases
Phone call during session
Auto-pause on incoming call (both modes). Mascot goes to worried. Resume manually after — never auto-resume.
Low battery
Show banner toast: "Low battery — session may end if power runs out." Suggest plugging in. Do not end session automatically.
Permission revoked mid-session
If user disables FamilyControls / Accessibility mid-session, show toast: "App blocking lost. Re-enable in Settings." Session continues but blocking is broken.
Time zone / clock change
Use monotonic clock (SystemClock.elapsedRealtime() on Android, CACurrentMediaTime() on iOS) for timer math. Wall-clock changes don't affect duration.
Mid-session task change
Setup gear tap → confirm dialog "Change settings? Current session won't count" → Yes ends session, lands on Setup with previous values pre-filled. Strict mode: setup gear is disabled, no tap.
Reduced motion / accessibility
All animations disabled when system reduced motion is on. All touch targets ≥48dp. All text meets WCAG AA contrast. VoiceOver / TalkBack labels: timer announces remaining time, mascot has alt text per state.
11 · v1.0 MVP Scope (Ship vs Defer)
SHIP in v1.0
All 4 states (Active, Paused, Break, Complete) · both modes (Flexible, Strict) · timer with tabular digits · ring + glow · 4 mascot face composables · pause/resume/end logic · app blocking continuity across active+paused · break auto-unblock (Flexible only) · session counter via 1h 35m today rollup · streak increment on Complete only · task association (set in Setup, displayed on Focus, mark-done on Complete) · Strict Mode lock enforcement · motivation quote bank (8 categories, 3-5 quotes each).
DEFER to v1.1+
Rive mascot rigging (use static composables for v1.0) · outfit system on focus screen (clean mascot for v1.0) · custom break quotes per session type · share buttons / Story export · streak share image generation · Today's accumulated time visible during Active state.
DO NOT BUILD
Coach voice / audio cues · ambient sound mixer · third-party calendar sync · "focus score" gamification (keep streak only) · session sets / Pomodoro cycles forced upfront (let users build organically via Start another).
12 · Implementation Order (suggested)
Step 1
Build static UI for Active state (Flexible). Hardcoded timer "17:23". Mascot studying composable. Verify visual matches mockup.
Step 2
Add real timer logic with WorkManager (Android) / Background Tasks (iOS). Tabular digits, monotonic clock, persists across app backgrounding.
Step 3
Add 4 mascot face composables. Wire state machine to swap face on state change. Add animations (breathing, bob, float, joy bounce).
Step 4
Add Paused state with banner, oversized resume button, 2-min auto-resume. Test 1-pause-max enforcement.
Step 5
Add Break state with relaxed mascot, blue ring, skip button. Auto-transition from Active 0:00 → Break.
Step 6
Add Complete state with celebrating mascot, single stat card, task subtitle, Start another / Done for now CTAs.
Step 7
Add Strict Mode variants: lock badge on mascot, dark-brown info card, disabled setup gear, disabled back, no skip on break, no end early.
Step 8
Wire up app blocking (FamilyControls iOS / AccessibilityService Android). Foreground service for Android. Test blocking continuity across all states.
Step 9
Add motivation quote bank, randomized per session per state. Add session persistence to local DB.
Step 10
Edge case handling: phone call auto-pause, force-quit recovery, reduced motion, permission revocation, time zone changes.