diff --git a/app/(tabs)/CLAUDE.md b/app/(tabs)/CLAUDE.md
new file mode 100644
index 0000000..bd4937a
--- /dev/null
+++ b/app/(tabs)/CLAUDE.md
@@ -0,0 +1,32 @@
+
+# Recent Activity
+
+
+
+### Feb 20, 2026
+
+| ID | Time | T | Title | Read |
+|----|------|---|-------|------|
+| #5056 | 8:24 AM | ✅ | Completed Host wrapper restoration in home screen | ~258 |
+| #5055 | " | ✅ | Re-added Host wrapper to home screen JSX | ~187 |
+| #5054 | " | ✅ | Re-added Host import to home screen | ~184 |
+| #5043 | 8:22 AM | ✅ | Removed closing Host tag from profile screen | ~210 |
+| #5042 | " | ✅ | Removed opening Host tag from profile screen | ~164 |
+| #5041 | " | ✅ | Removed closing Host tag from browse screen | ~187 |
+| #5040 | " | ✅ | Removed opening Host tag from browse screen | ~159 |
+| #5039 | 8:21 AM | ✅ | Removed closing Host tag from activity screen | ~193 |
+| #5038 | " | ✅ | Removed opening Host tag from activity screen | ~154 |
+| #5037 | " | ✅ | Removed closing Host tag from workouts screen | ~195 |
+| #5036 | " | ✅ | Removed opening Host tag from workouts screen | ~164 |
+| #5035 | " | ✅ | Removed closing Host tag from home screen JSX | ~197 |
+| #5034 | " | ✅ | Removed Host wrapper from home screen JSX | ~139 |
+| #5031 | 8:19 AM | ✅ | Removed Host import from profile screen | ~184 |
+| #5030 | " | ✅ | Removed Host import from browse screen | ~190 |
+| #5029 | 8:18 AM | ✅ | Removed Host import from activity screen | ~183 |
+| #5028 | " | ✅ | Removed Host import from workouts screen | ~189 |
+| #5027 | " | ✅ | Removed Host import from home screen index.tsx | ~180 |
+| #5024 | " | 🔵 | Activity screen properly wraps content with Host component | ~237 |
+| #5023 | " | 🔵 | Profile screen properly wraps content with Host component | ~246 |
+| #5022 | 8:14 AM | 🔵 | Browse screen properly wraps content with Host component | ~217 |
+| #5021 | " | 🔵 | Workouts screen properly wraps content with Host component | ~228 |
+
\ No newline at end of file
diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx
index 8efaea6..41ec091 100644
--- a/app/(tabs)/_layout.tsx
+++ b/app/(tabs)/_layout.tsx
@@ -1,41 +1,41 @@
-import { Tabs } from 'expo-router'
-import Ionicons from '@expo/vector-icons/Ionicons'
+/**
+ * TabataFit Tab Layout
+ * Native iOS tabs with liquid glass effect
+ * 5 tabs: Home, Workouts, Activity, Browse, Profile
+ */
-import { HapticTab } from '@/components/haptic-tab'
-import { BRAND, SURFACE, TEXT, BORDER } from '@/src/shared/constants/colors'
+import { NativeTabs, Icon, Label } from 'expo-router/unstable-native-tabs'
+import { BRAND } from '@/src/shared/constants/colors'
export default function TabLayout() {
return (
-
- (
-
- ),
- }}
- />
- (
-
- ),
- }}
- />
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
)
}
diff --git a/app/(tabs)/activity.tsx b/app/(tabs)/activity.tsx
new file mode 100644
index 0000000..21cefae
--- /dev/null
+++ b/app/(tabs)/activity.tsx
@@ -0,0 +1,306 @@
+/**
+ * TabataFit Activity Screen
+ * React Native + SwiftUI Islands — wired to shared data
+ */
+
+import { View, StyleSheet, ScrollView, Dimensions, Text as RNText } from 'react-native'
+import { useSafeAreaInsets } from 'react-native-safe-area-context'
+import { LinearGradient } from 'expo-linear-gradient'
+import { BlurView } from 'expo-blur'
+import Ionicons from '@expo/vector-icons/Ionicons'
+import {
+ Host,
+ Gauge,
+ Text,
+ HStack,
+ VStack,
+ Chart,
+ List,
+} from '@expo/ui/swift-ui'
+
+import { useMemo } from 'react'
+import { useActivityStore, getWeeklyActivity } from '@/src/shared/stores'
+import { getWorkoutById } from '@/src/shared/data'
+import { ACHIEVEMENTS } from '@/src/shared/data'
+import { StyledText } from '@/src/shared/components/StyledText'
+
+import {
+ BRAND,
+ DARK,
+ TEXT as TEXT_COLORS,
+ GLASS,
+ GRADIENTS,
+} from '@/src/shared/constants/colors'
+import { SPACING, LAYOUT } from '@/src/shared/constants/spacing'
+import { RADIUS } from '@/src/shared/constants/borderRadius'
+
+const { width: SCREEN_WIDTH } = Dimensions.get('window')
+
+const FONTS = {
+ LARGE_TITLE: 34,
+ TITLE_2: 22,
+ SUBHEADLINE: 15,
+ CAPTION_1: 12,
+}
+
+// ═══════════════════════════════════════════════════════════════════════════
+// MAIN SCREEN
+// ═══════════════════════════════════════════════════════════════════════════
+
+export default function ActivityScreen() {
+ const insets = useSafeAreaInsets()
+ const streak = useActivityStore((s) => s.streak)
+ const history = useActivityStore((s) => s.history)
+ const totalWorkouts = history.length
+ const totalMinutes = useMemo(() => history.reduce((sum, r) => sum + r.durationMinutes, 0), [history])
+ const totalCalories = useMemo(() => history.reduce((sum, r) => sum + r.calories, 0), [history])
+ const recentWorkouts = useMemo(() => history.slice(0, 3), [history])
+ const weeklyActivity = useMemo(() => getWeeklyActivity(history), [history])
+
+ // Check achievements
+ const unlockedAchievements = ACHIEVEMENTS.filter(a => {
+ switch (a.type) {
+ case 'workouts': return totalWorkouts >= a.requirement
+ case 'streak': return streak.longest >= a.requirement
+ case 'minutes': return totalMinutes >= a.requirement
+ case 'calories': return totalCalories >= a.requirement
+ default: return false
+ }
+ })
+ const displayAchievements = ACHIEVEMENTS.slice(0, 5).map(a => ({
+ ...a,
+ unlocked: unlockedAchievements.some(u => u.id === a.id),
+ }))
+
+ // Format recent workout dates
+ const formatDate = (timestamp: number) => {
+ const now = Date.now()
+ const diff = now - timestamp
+ if (diff < 86400000) return 'Today'
+ if (diff < 172800000) return 'Yesterday'
+ return Math.floor(diff / 86400000) + ' days ago'
+ }
+
+ return (
+
+
+ {/* Header */}
+ Activity
+
+ {/* Streak Banner */}
+
+
+
+
+
+
+ {(streak.current || 0) + ' Day Streak'}
+
+
+ {streak.current > 0 ? 'Keep it going!' : 'Start your streak today!'}
+
+
+
+
+
+ {/* SwiftUI Island: Stats Gauges */}
+
+
+
+
+ Workouts
+
+
+
+ Minutes
+
+
+
+ Calories
+
+
+
+ Best Streak
+
+
+
+
+ {/* SwiftUI Island: This Week Chart */}
+
+ This Week
+
+ ({
+ x: d.date,
+ y: d.completed ? 1 : 0,
+ color: d.completed ? BRAND.PRIMARY : '#333333',
+ }))}
+ barStyle={{ cornerRadius: 4 }}
+ style={{ height: 160 }}
+ />
+
+
+
+ {/* SwiftUI Island: Recent Workouts */}
+ {recentWorkouts.length > 0 && (
+
+ Recent
+
+
+ {recentWorkouts.map((result) => {
+ const workout = getWorkoutById(result.workoutId)
+ return (
+
+ {workout?.title ?? 'Workout'}
+ {formatDate(result.completedAt)}
+ {result.durationMinutes + ' min'}
+ {result.calories + ' cal'}
+
+ )
+ })}
+
+
+
+ )}
+
+ {/* Achievements */}
+
+ Achievements
+
+ {displayAchievements.map((achievement) => (
+
+
+
+
+
+
+ {achievement.title}
+
+
+ ))}
+
+
+
+
+ )
+}
+
+// ═══════════════════════════════════════════════════════════════════════════
+// STYLES
+// ═══════════════════════════════════════════════════════════════════════════
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: DARK.BASE,
+ },
+ scrollView: {
+ flex: 1,
+ },
+ scrollContent: {
+ paddingHorizontal: LAYOUT.SCREEN_PADDING,
+ },
+
+ // Streak Banner
+ streakBanner: {
+ height: 80,
+ borderRadius: RADIUS.GLASS_CARD,
+ overflow: 'hidden',
+ marginBottom: SPACING[6],
+ marginTop: SPACING[4],
+ },
+ streakContent: {
+ flex: 1,
+ flexDirection: 'row',
+ alignItems: 'center',
+ paddingHorizontal: SPACING[5],
+ gap: SPACING[4],
+ },
+ streakText: {
+ flex: 1,
+ },
+
+ // Stats Island
+ statsIsland: {
+ marginBottom: SPACING[8],
+ },
+
+ // Chart Island
+ chartIsland: {
+ marginTop: SPACING[2],
+ },
+
+ // Section
+ section: {
+ marginBottom: SPACING[6],
+ },
+
+ // Achievements
+ achievementsGrid: {
+ flexDirection: 'row',
+ flexWrap: 'wrap',
+ gap: SPACING[3],
+ },
+ achievementBadge: {
+ width: (SCREEN_WIDTH - LAYOUT.SCREEN_PADDING * 2 - SPACING[3] * 2) / 3,
+ aspectRatio: 1,
+ borderRadius: RADIUS.LG,
+ alignItems: 'center',
+ justifyContent: 'center',
+ borderWidth: 1,
+ borderColor: 'rgba(255, 255, 255, 0.1)',
+ overflow: 'hidden',
+ },
+ achievementLocked: {
+ opacity: 0.5,
+ },
+ achievementIcon: {
+ width: 48,
+ height: 48,
+ borderRadius: 24,
+ backgroundColor: 'rgba(255, 107, 53, 0.15)',
+ alignItems: 'center',
+ justifyContent: 'center',
+ marginBottom: SPACING[2],
+ },
+ achievementIconLocked: {
+ backgroundColor: 'rgba(255, 255, 255, 0.05)',
+ },
+})
diff --git a/app/(tabs)/browse.tsx b/app/(tabs)/browse.tsx
new file mode 100644
index 0000000..2ff124f
--- /dev/null
+++ b/app/(tabs)/browse.tsx
@@ -0,0 +1,379 @@
+/**
+ * TabataFit Browse Screen
+ * React Native UI — wired to shared data
+ */
+
+import { View, StyleSheet, ScrollView, Pressable, Dimensions, Text as RNText } from 'react-native'
+import { useRouter } from 'expo-router'
+import { useSafeAreaInsets } from 'react-native-safe-area-context'
+import { LinearGradient } from 'expo-linear-gradient'
+import { BlurView } from 'expo-blur'
+import Ionicons from '@expo/vector-icons/Ionicons'
+
+import { useHaptics } from '@/src/shared/hooks'
+import {
+ COLLECTIONS,
+ PROGRAMS,
+ getFeaturedCollection,
+ COLLECTION_COLORS,
+ WORKOUTS,
+ getTrainerById,
+} from '@/src/shared/data'
+import { StyledText } from '@/src/shared/components/StyledText'
+
+import {
+ BRAND,
+ DARK,
+ TEXT,
+ GLASS,
+ SHADOW,
+} from '@/src/shared/constants/colors'
+import { SPACING, LAYOUT } from '@/src/shared/constants/spacing'
+import { RADIUS } from '@/src/shared/constants/borderRadius'
+
+const { width: SCREEN_WIDTH } = Dimensions.get('window')
+
+const FONTS = {
+ LARGE_TITLE: 34,
+ TITLE: 28,
+ TITLE_2: 22,
+ HEADLINE: 17,
+ SUBHEADLINE: 15,
+ CAPTION_1: 12,
+ CAPTION_2: 11,
+}
+
+function TextButton({ children, onPress }: { children: string; onPress?: () => void }) {
+ return (
+
+
+ {children}
+
+
+ )
+}
+
+// New Releases: last 4 workouts
+const NEW_RELEASES = WORKOUTS.slice(-4)
+
+// ═══════════════════════════════════════════════════════════════════════════
+// MAIN SCREEN
+// ═══════════════════════════════════════════════════════════════════════════
+
+export default function BrowseScreen() {
+ const insets = useSafeAreaInsets()
+ const router = useRouter()
+ const haptics = useHaptics()
+
+ const featuredCollection = getFeaturedCollection()
+
+ const handleWorkoutPress = (id: string) => {
+ haptics.buttonTap()
+ router.push(`/workout/${id}`)
+ }
+
+ const handleCollectionPress = (id: string) => {
+ haptics.buttonTap()
+ router.push(`/collection/${id}`)
+ }
+
+ return (
+
+
+ {/* Header */}
+ Browse
+
+ {/* Featured Collection */}
+ {featuredCollection && (
+ handleCollectionPress(featuredCollection.id)}>
+
+
+
+
+ FEATURED
+
+
+
+ {featuredCollection.title}
+ {featuredCollection.description}
+
+
+
+
+ {featuredCollection.workoutIds.length + ' workouts'}
+
+
+
+
+ )}
+
+ {/* Collections Grid */}
+
+ Collections
+
+ {COLLECTIONS.map((collection) => {
+ const color = COLLECTION_COLORS[collection.id] ?? BRAND.PRIMARY
+ return (
+ handleCollectionPress(collection.id)}
+ >
+
+
+ {collection.icon}
+
+
+ {collection.title}
+
+
+ {collection.workoutIds.length + ' workouts'}
+
+
+ )
+ })}
+
+
+
+ {/* Programs */}
+
+
+ Programs
+ See All
+
+
+ {PROGRAMS.map((program) => (
+
+
+
+
+
+ {program.level}
+
+
+
+ {program.title}
+
+
+
+
+ {program.weeks + ' weeks'}
+
+
+
+ {program.workoutsPerWeek + 'x /week'}
+
+
+
+ ))}
+
+
+
+ {/* New Releases */}
+
+
+ New Releases
+
+ {NEW_RELEASES.map((workout) => {
+ const trainer = getTrainerById(workout.trainerId)
+ return (
+ handleWorkoutPress(workout.id)}
+ >
+
+ {trainer?.name[0] ?? 'T'}
+
+
+ {workout.title}
+
+ {(trainer?.name ?? '') + ' \u00B7 ' + workout.duration + ' min \u00B7 ' + workout.level}
+
+
+
+
+ )
+ })}
+
+
+
+ )
+}
+
+// ═══════════════════════════════════════════════════════════════════════════
+// STYLES
+// ═══════════════════════════════════════════════════════════════════════════
+
+const COLLECTION_CARD_WIDTH = (SCREEN_WIDTH - LAYOUT.SCREEN_PADDING * 2 - SPACING[3]) / 2
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: DARK.BASE,
+ },
+ scrollView: {
+ flex: 1,
+ },
+ scrollContent: {
+ paddingHorizontal: LAYOUT.SCREEN_PADDING,
+ },
+
+ // Featured Collection
+ featuredCard: {
+ height: 200,
+ borderRadius: RADIUS.GLASS_CARD,
+ overflow: 'hidden',
+ marginBottom: SPACING[8],
+ marginTop: SPACING[4],
+ ...SHADOW.lg,
+ },
+ featuredBadge: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ backgroundColor: 'rgba(0, 0, 0, 0.3)',
+ paddingHorizontal: SPACING[2],
+ paddingVertical: SPACING[1],
+ borderRadius: RADIUS.SM,
+ alignSelf: 'flex-start',
+ margin: SPACING[4],
+ gap: SPACING[1],
+ },
+ featuredBadgeText: {
+ fontSize: 11,
+ fontWeight: 'bold',
+ color: TEXT.PRIMARY,
+ },
+ featuredInfo: {
+ position: 'absolute',
+ bottom: SPACING[5],
+ left: SPACING[5],
+ right: SPACING[5],
+ },
+ featuredStats: {
+ flexDirection: 'row',
+ gap: SPACING[4],
+ marginTop: SPACING[3],
+ },
+ featuredStat: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ gap: SPACING[1],
+ },
+
+ // Section
+ section: {
+ marginBottom: SPACING[6],
+ },
+ sectionHeader: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ marginBottom: SPACING[4],
+ },
+
+ // Collections Grid
+ collectionsGrid: {
+ flexDirection: 'row',
+ flexWrap: 'wrap',
+ gap: SPACING[3],
+ marginTop: SPACING[3],
+ },
+ collectionCard: {
+ width: COLLECTION_CARD_WIDTH,
+ paddingVertical: SPACING[4],
+ paddingHorizontal: SPACING[3],
+ borderRadius: RADIUS.LG,
+ overflow: 'hidden',
+ borderWidth: 1,
+ borderColor: 'rgba(255, 255, 255, 0.1)',
+ gap: SPACING[1],
+ },
+ collectionIconBg: {
+ width: 44,
+ height: 44,
+ borderRadius: 12,
+ alignItems: 'center',
+ justifyContent: 'center',
+ marginBottom: SPACING[2],
+ },
+ collectionEmoji: {
+ fontSize: 22,
+ },
+
+ // Programs
+ programsScroll: {
+ gap: SPACING[3],
+ },
+ programCard: {
+ width: 200,
+ height: 140,
+ borderRadius: RADIUS.LG,
+ overflow: 'hidden',
+ padding: SPACING[4],
+ borderWidth: 1,
+ borderColor: 'rgba(255, 255, 255, 0.1)',
+ },
+ programHeader: {
+ flexDirection: 'row',
+ justifyContent: 'flex-end',
+ marginBottom: SPACING[2],
+ },
+ programLevelBadge: {
+ backgroundColor: 'rgba(255, 107, 53, 0.2)',
+ paddingHorizontal: SPACING[2],
+ paddingVertical: SPACING[1],
+ borderRadius: RADIUS.SM,
+ },
+ programMeta: {
+ flexDirection: 'row',
+ gap: SPACING[3],
+ marginTop: SPACING[3],
+ },
+ programMetaItem: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ gap: SPACING[1],
+ },
+
+ // New Releases
+ releaseRow: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ paddingVertical: SPACING[3],
+ paddingHorizontal: SPACING[4],
+ backgroundColor: DARK.SURFACE,
+ borderRadius: RADIUS.LG,
+ marginBottom: SPACING[2],
+ gap: SPACING[3],
+ },
+ releaseAvatar: {
+ width: 44,
+ height: 44,
+ borderRadius: 22,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ releaseInitial: {
+ fontSize: 18,
+ fontWeight: '700',
+ color: TEXT.PRIMARY,
+ },
+ releaseInfo: {
+ flex: 1,
+ gap: 2,
+ },
+})
diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx
index 9ff99ad..fb0ea7a 100644
--- a/app/(tabs)/index.tsx
+++ b/app/(tabs)/index.tsx
@@ -1,156 +1,416 @@
-import { useEffect, useRef } from 'react'
-import {
- Animated,
- Pressable,
- StyleSheet,
- Text,
- View,
-} from 'react-native'
-import { useRouter, Redirect } from 'expo-router'
-import { StatusBar } from 'expo-status-bar'
-import { LinearGradient } from 'expo-linear-gradient'
+/**
+ * TabataFit Home Screen
+ * React Native UI — wired to shared data
+ */
+
+import { View, StyleSheet, ScrollView, Pressable, Dimensions, Text as RNText, Image as RNImage } from 'react-native'
+import { useRouter } from 'expo-router'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
+import { LinearGradient } from 'expo-linear-gradient'
+import { BlurView } from 'expo-blur'
+import Ionicons from '@expo/vector-icons/Ionicons'
-import { BRAND, TEXT, APP_GRADIENTS, ACCENT } from '@/src/shared/constants/colors'
-import { TYPOGRAPHY } from '@/src/shared/constants/typography'
-import { SHADOW, TEXT_SHADOW } from '@/src/shared/constants/shadows'
-import { DURATION, EASING } from '@/src/shared/constants/animations'
-import { useIsOnboardingComplete } from '@/src/features/onboarding/hooks/useOnboarding'
+import { useMemo } from 'react'
+import { useHaptics } from '@/src/shared/hooks'
+import { useUserStore, useActivityStore } from '@/src/shared/stores'
+import {
+ getFeaturedWorkouts,
+ getPopularWorkouts,
+ getTrainerById,
+ COLLECTIONS,
+ WORKOUTS,
+} from '@/src/shared/data'
+import { StyledText } from '@/src/shared/components/StyledText'
-export default function HomeScreen() {
- const router = useRouter()
- const insets = useSafeAreaInsets()
- const isOnboardingComplete = useIsOnboardingComplete()
+import {
+ BRAND,
+ DARK,
+ TEXT,
+ GLASS,
+ SHADOW,
+ GRADIENTS,
+} from '@/src/shared/constants/colors'
+import { SPACING, LAYOUT } from '@/src/shared/constants/spacing'
+import { RADIUS } from '@/src/shared/constants/borderRadius'
- const glowAnim = useRef(new Animated.Value(0)).current
+const { width: SCREEN_WIDTH } = Dimensions.get('window')
+const CARD_WIDTH = SCREEN_WIDTH - LAYOUT.SCREEN_PADDING * 2
- // Show nothing while Zustand hydrates
- if (isOnboardingComplete === undefined) {
- return null
- }
+const FONTS = {
+ LARGE_TITLE: 34,
+ TITLE: 28,
+ TITLE_2: 22,
+ HEADLINE: 17,
+ BODY: 17,
+ SUBHEADLINE: 15,
+ CAPTION_1: 12,
+ CAPTION_2: 11,
+}
- // Redirect to onboarding if not complete
- if (isOnboardingComplete === false) {
- return
- }
+// ═══════════════════════════════════════════════════════════════════════════
+// HELPERS
+// ═══════════════════════════════════════════════════════════════════════════
- useEffect(() => {
- Animated.loop(
- Animated.sequence([
- Animated.timing(glowAnim, {
- toValue: 1,
- duration: DURATION.BREATH,
- easing: EASING.STANDARD,
- useNativeDriver: false,
- }),
- Animated.timing(glowAnim, {
- toValue: 0,
- duration: DURATION.BREATH,
- easing: EASING.STANDARD,
- useNativeDriver: false,
- }),
- ])
- ).start()
- }, [])
-
- const glowOpacity = glowAnim.interpolate({
- inputRange: [0, 1],
- outputRange: [0.15, 0.4],
- })
-
- const glowScale = glowAnim.interpolate({
- inputRange: [0, 1],
- outputRange: [1, 1.12],
- })
+function getGreeting() {
+ const hour = new Date().getHours()
+ if (hour < 12) return 'Good morning'
+ if (hour < 18) return 'Good afternoon'
+ return 'Good evening'
+}
+function PrimaryButton({ children, onPress }: { children: string; onPress?: () => void }) {
return (
-
-
-
-
- TABATA
- GO
- 4 minutes. Tout donner.
-
-
-
-
- [
- styles.startButton,
- pressed && styles.startButtonPressed,
- ]}
- onPress={() => router.push('/timer')}
- >
- START
-
-
-
+
+
+ {children}
+
)
}
+function PlainButton({ children, onPress }: { children: string; onPress?: () => void }) {
+ return (
+
+ {children}
+
+ )
+}
+
+// ═══════════════════════════════════════════════════════════════════════════
+// MAIN SCREEN
+// ═══════════════════════════════════════════════════════════════════════════
+
+export default function HomeScreen() {
+ const insets = useSafeAreaInsets()
+ const router = useRouter()
+ const haptics = useHaptics()
+ const userName = useUserStore((s) => s.profile.name)
+ const history = useActivityStore((s) => s.history)
+ const recentWorkouts = useMemo(() => history.slice(0, 3), [history])
+
+ const featured = getFeaturedWorkouts()[0] ?? WORKOUTS[0]
+ const featuredTrainer = getTrainerById(featured.trainerId)
+ const popular = getPopularWorkouts(4)
+
+ const handleWorkoutPress = (id: string) => {
+ haptics.buttonTap()
+ router.push(`/workout/${id}`)
+ }
+
+ return (
+
+
+ {/* Header */}
+
+
+ {getGreeting() + ', ' + userName}
+
+
+
+
+
+
+ {/* Featured */}
+ handleWorkoutPress(featured.id)}
+ >
+ {featured.thumbnailUrl ? (
+
+ ) : (
+
+ )}
+
+
+
+ 🔥 FEATURED
+
+
+
+
+ {featured.title}
+
+
+ {featured.duration + ' min • ' + featured.level + ' • ' + (featuredTrainer?.name ?? '')}
+
+
+
+ handleWorkoutPress(featured.id)}>START
+
+
+
+
+
+
+
+ {/* Continue Watching — from activity store */}
+ {recentWorkouts.length > 0 && (
+
+
+ Recent
+ See All
+
+
+ {recentWorkouts.map((result) => {
+ const workout = WORKOUTS.find(w => w.id === result.workoutId)
+ if (!workout) return null
+ const trainer = getTrainerById(workout.trainerId)
+ return (
+ handleWorkoutPress(result.workoutId)}
+ >
+
+
+
+ {trainer?.name[0] ?? 'T'}
+
+
+ {workout.title}
+
+ {result.calories + ' cal • ' + result.durationMinutes + ' min'}
+
+
+ )
+ })}
+
+
+ )}
+
+ {/* Popular This Week */}
+
+ Popular This Week
+
+ {popular.map((item) => (
+ handleWorkoutPress(item.id)}
+ >
+
+
+
+ {item.title}
+ {item.duration + ' min'}
+
+ ))}
+
+
+
+ {/* Collections */}
+
+ Collections
+ {COLLECTIONS.map((item) => (
+ { haptics.buttonTap(); router.push(`/collection/${item.id}`) }}>
+
+
+ {item.icon}
+
+ {item.title}
+
+ {item.workoutIds.length + ' workouts • ' + item.description}
+
+
+
+
+
+ ))}
+
+
+
+ )
+}
+
+// ═══════════════════════════════════════════════════════════════════════════
+// STYLES
+// ═══════════════════════════════════════════════════════════════════════════
+
const styles = StyleSheet.create({
container: {
flex: 1,
+ backgroundColor: DARK.BASE,
+ },
+ scrollView: {
+ flex: 1,
+ },
+ scrollContent: {
+ paddingHorizontal: LAYOUT.SCREEN_PADDING,
+ },
+
+ // Header
+ header: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ marginBottom: SPACING[6],
+ },
+ profileButton: {
+ width: 40,
+ height: 40,
alignItems: 'center',
justifyContent: 'center',
},
- brandArea: {
+
+ // Buttons
+ primaryButton: {
+ flexDirection: 'row',
alignItems: 'center',
- },
- title: {
- ...TYPOGRAPHY.brandTitle,
- color: BRAND.PRIMARY,
- ...TEXT_SHADOW.BRAND,
- },
- subtitle: {
- ...TYPOGRAPHY.displaySmall,
- color: TEXT.PRIMARY,
- marginTop: -6,
- },
- tagline: {
- ...TYPOGRAPHY.caption,
- color: TEXT.HINT,
- fontStyle: 'italic',
- marginTop: 12,
- },
- buttonArea: {
- marginTop: 72,
- alignItems: 'center',
- justifyContent: 'center',
- },
- buttonGlow: {
- position: 'absolute',
- width: 172,
- height: 172,
- borderRadius: 86,
- backgroundColor: ACCENT.ORANGE,
- },
- startButton: {
- width: 160,
- height: 160,
- borderRadius: 80,
backgroundColor: BRAND.PRIMARY,
+ paddingHorizontal: SPACING[4],
+ paddingVertical: SPACING[3],
+ borderRadius: RADIUS.SM,
+ },
+ buttonIcon: {
+ marginRight: SPACING[2],
+ },
+ primaryButtonText: {
+ fontSize: 14,
+ fontWeight: '600',
+ color: TEXT.PRIMARY,
+ },
+ plainButtonText: {
+ fontSize: FONTS.BODY,
+ color: BRAND.PRIMARY,
+ },
+
+ // Featured
+ featuredCard: {
+ width: CARD_WIDTH,
+ height: 220,
+ borderRadius: RADIUS.GLASS_CARD,
+ overflow: 'hidden',
+ marginBottom: SPACING[8],
+ ...SHADOW.lg,
+ },
+ featuredBadge: {
+ position: 'absolute',
+ top: SPACING[4],
+ left: SPACING[4],
+ backgroundColor: 'rgba(255, 255, 255, 0.15)',
+ paddingHorizontal: SPACING[3],
+ paddingVertical: SPACING[1],
+ borderRadius: RADIUS.SM,
+ borderWidth: 1,
+ borderColor: 'rgba(255, 255, 255, 0.2)',
+ },
+ featuredBadgeText: {
+ fontSize: 11,
+ fontWeight: 'bold',
+ color: TEXT.PRIMARY,
+ },
+ featuredContent: {
+ position: 'absolute',
+ bottom: 0,
+ left: 0,
+ right: 0,
+ padding: SPACING[5],
+ },
+ featuredButtons: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ gap: SPACING[3],
+ marginTop: SPACING[4],
+ },
+ saveButton: {
+ width: 44,
+ height: 44,
alignItems: 'center',
justifyContent: 'center',
- ...SHADOW.BRAND_GLOW,
+ backgroundColor: 'rgba(255, 255, 255, 0.1)',
+ borderRadius: 22,
+ borderWidth: 1,
+ borderColor: 'rgba(255, 255, 255, 0.15)',
},
- startButtonPressed: {
- transform: [{ scale: 0.95 }],
+
+ // Sections
+ section: {
+ marginBottom: SPACING[8],
},
- startButtonText: {
- ...TYPOGRAPHY.buttonHero,
- color: TEXT.PRIMARY,
- letterSpacing: 4,
+ sectionHeader: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ marginBottom: SPACING[4],
+ },
+ horizontalScroll: {
+ gap: SPACING[3],
+ },
+
+ // Continue Card
+ continueCard: {
+ width: 140,
+ },
+ continueThumb: {
+ width: 140,
+ height: 200,
+ borderRadius: RADIUS.LG,
+ overflow: 'hidden',
+ alignItems: 'center',
+ justifyContent: 'center',
+ marginBottom: SPACING[2],
+ },
+
+ // Popular Card
+ popularCard: {
+ width: 120,
+ },
+ popularThumb: {
+ width: 120,
+ height: 120,
+ borderRadius: RADIUS.LG,
+ alignItems: 'center',
+ justifyContent: 'center',
+ marginBottom: SPACING[2],
+ borderWidth: 1,
+ borderColor: 'rgba(255, 255, 255, 0.1)',
+ backgroundColor: 'rgba(255, 255, 255, 0.05)',
+ },
+
+ // Collection Card
+ collectionCard: {
+ height: 80,
+ borderRadius: RADIUS.LG,
+ overflow: 'hidden',
+ marginBottom: SPACING[3],
+ borderWidth: 1,
+ borderColor: 'rgba(255, 255, 255, 0.1)',
+ },
+ collectionContent: {
+ flex: 1,
+ flexDirection: 'row',
+ alignItems: 'center',
+ paddingHorizontal: SPACING[5],
+ },
+ collectionIcon: {
+ fontSize: 28,
+ marginRight: SPACING[4],
+ },
+ collectionText: {
+ flex: 1,
},
})
diff --git a/app/(tabs)/profile.tsx b/app/(tabs)/profile.tsx
new file mode 100644
index 0000000..0a61855
--- /dev/null
+++ b/app/(tabs)/profile.tsx
@@ -0,0 +1,236 @@
+/**
+ * TabataFit Profile Screen
+ * React Native + SwiftUI Islands — wired to shared data
+ */
+
+import { View, StyleSheet, ScrollView, Text as RNText } from 'react-native'
+import { useSafeAreaInsets } from 'react-native-safe-area-context'
+import { LinearGradient } from 'expo-linear-gradient'
+import { BlurView } from 'expo-blur'
+import Ionicons from '@expo/vector-icons/Ionicons'
+import {
+ Host,
+ List,
+ Section,
+ Switch,
+ Text,
+ LabeledContent,
+} from '@expo/ui/swift-ui'
+
+import { useUserStore } from '@/src/shared/stores'
+import { StyledText } from '@/src/shared/components/StyledText'
+
+import {
+ BRAND,
+ DARK,
+ TEXT,
+ GLASS,
+ SHADOW,
+ GRADIENTS,
+} from '@/src/shared/constants/colors'
+import { SPACING, LAYOUT } from '@/src/shared/constants/spacing'
+import { RADIUS } from '@/src/shared/constants/borderRadius'
+
+const FONTS = {
+ LARGE_TITLE: 34,
+ TITLE_2: 22,
+ HEADLINE: 17,
+ SUBHEADLINE: 15,
+ CAPTION_1: 12,
+}
+
+// ═══════════════════════════════════════════════════════════════════════════
+// MAIN SCREEN
+// ═══════════════════════════════════════════════════════════════════════════
+
+export default function ProfileScreen() {
+ const insets = useSafeAreaInsets()
+ const profile = useUserStore((s) => s.profile)
+ const settings = useUserStore((s) => s.settings)
+ const updateSettings = useUserStore((s) => s.updateSettings)
+
+ const isPremium = profile.subscription !== 'free'
+ const planLabel = isPremium ? 'TabataFit+' : 'Free'
+
+ return (
+
+
+ {/* Header */}
+
+ Profile
+
+
+ {/* Profile Card */}
+
+
+
+
+
+ {profile.name[0]}
+
+
+
+ {profile.name}
+
+
+ {profile.email}
+
+ {isPremium && (
+
+
+
+ {planLabel}
+
+
+ )}
+
+
+ {/* Subscription */}
+ {isPremium && (
+
+
+
+
+
+
+ {planLabel}
+
+
+ {'Member since ' + profile.joinDate}
+
+
+
+
+ )}
+
+ {/* SwiftUI Island: Settings */}
+
+
+
+ updateSettings({ haptics: v })}
+ color={BRAND.PRIMARY}
+ />
+ updateSettings({ soundEffects: v })}
+ color={BRAND.PRIMARY}
+ />
+ updateSettings({ voiceCoaching: v })}
+ color={BRAND.PRIMARY}
+ />
+
+
+ updateSettings({ reminders: v })}
+ color={BRAND.PRIMARY}
+ />
+
+ {settings.reminderTime.replace(':00', ':00 AM')}
+
+
+
+
+
+ {/* Version */}
+
+ TabataFit v1.0.0
+
+
+
+ )
+}
+
+// ═══════════════════════════════════════════════════════════════════════════
+// STYLES
+// ═══════════════════════════════════════════════════════════════════════════
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: DARK.BASE,
+ },
+ scrollView: {
+ flex: 1,
+ },
+ scrollContent: {
+ paddingHorizontal: LAYOUT.SCREEN_PADDING,
+ },
+
+ // Profile Card
+ profileCard: {
+ borderRadius: RADIUS.GLASS_CARD,
+ overflow: 'hidden',
+ marginBottom: SPACING[6],
+ marginTop: SPACING[4],
+ alignItems: 'center',
+ paddingVertical: SPACING[6],
+ borderWidth: 1,
+ borderColor: 'rgba(255, 255, 255, 0.1)',
+ ...SHADOW.md,
+ },
+ avatarContainer: {
+ width: 80,
+ height: 80,
+ borderRadius: 40,
+ alignItems: 'center',
+ justifyContent: 'center',
+ marginBottom: SPACING[3],
+ overflow: 'hidden',
+ },
+ premiumBadge: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ backgroundColor: 'rgba(255, 107, 53, 0.15)',
+ paddingHorizontal: SPACING[3],
+ paddingVertical: SPACING[1],
+ borderRadius: RADIUS.FULL,
+ marginTop: SPACING[3],
+ gap: SPACING[1],
+ },
+
+ // Subscription Card
+ subscriptionCard: {
+ height: 80,
+ borderRadius: RADIUS.LG,
+ overflow: 'hidden',
+ marginBottom: SPACING[6],
+ ...SHADOW.BRAND_GLOW,
+ },
+ subscriptionContent: {
+ flex: 1,
+ flexDirection: 'row',
+ alignItems: 'center',
+ paddingHorizontal: SPACING[4],
+ gap: SPACING[3],
+ },
+ subscriptionInfo: {
+ flex: 1,
+ },
+
+ // Version Text
+ versionText: {
+ textAlign: 'center',
+ marginTop: SPACING[6],
+ },
+})
diff --git a/app/(tabs)/workouts.tsx b/app/(tabs)/workouts.tsx
new file mode 100644
index 0000000..bcace97
--- /dev/null
+++ b/app/(tabs)/workouts.tsx
@@ -0,0 +1,279 @@
+/**
+ * TabataFit Workouts Screen
+ * React Native + SwiftUI Islands — wired to shared data
+ */
+
+import { useState } from 'react'
+import { View, StyleSheet, ScrollView, Pressable, Dimensions, Text as RNText } from 'react-native'
+import { useRouter } from 'expo-router'
+import { useSafeAreaInsets } from 'react-native-safe-area-context'
+import { BlurView } from 'expo-blur'
+import Ionicons from '@expo/vector-icons/Ionicons'
+import { Host, Picker } from '@expo/ui/swift-ui'
+
+import { useHaptics } from '@/src/shared/hooks'
+import { WORKOUTS, TRAINERS, CATEGORIES, getTrainerById } from '@/src/shared/data'
+import { StyledText } from '@/src/shared/components/StyledText'
+
+import {
+ BRAND,
+ DARK,
+ TEXT,
+ GLASS,
+ SHADOW,
+} from '@/src/shared/constants/colors'
+import { SPACING, LAYOUT } from '@/src/shared/constants/spacing'
+import { RADIUS } from '@/src/shared/constants/borderRadius'
+
+const { width: SCREEN_WIDTH } = Dimensions.get('window')
+const CARD_WIDTH = (SCREEN_WIDTH - LAYOUT.SCREEN_PADDING * 2 - SPACING[3]) / 2
+
+const FONTS = {
+ LARGE_TITLE: 34,
+ TITLE_2: 22,
+ HEADLINE: 17,
+ SUBHEADLINE: 15,
+ CAPTION_1: 12,
+ CAPTION_2: 11,
+}
+
+// ═══════════════════════════════════════════════════════════════════════════
+// MAIN SCREEN
+// ═══════════════════════════════════════════════════════════════════════════
+
+export default function WorkoutsScreen() {
+ const insets = useSafeAreaInsets()
+ const router = useRouter()
+ const haptics = useHaptics()
+ const [selectedCategoryIndex, setSelectedCategoryIndex] = useState(0)
+
+ const selectedCategory = CATEGORIES[selectedCategoryIndex].id
+
+ const filteredWorkouts = selectedCategory === 'all'
+ ? WORKOUTS
+ : WORKOUTS.filter(w => w.category === selectedCategory)
+
+ const handleWorkoutPress = (id: string) => {
+ haptics.buttonTap()
+ router.push(`/workout/${id}`)
+ }
+
+ return (
+
+
+ {/* Header */}
+
+ Workouts
+ {WORKOUTS.length + ' workouts available'}
+
+
+ {/* SwiftUI Island: Category Picker */}
+
+ c.label)}
+ selectedIndex={selectedCategoryIndex}
+ onOptionSelected={(e) => {
+ haptics.selection()
+ setSelectedCategoryIndex(e.nativeEvent.index)
+ }}
+ color={BRAND.PRIMARY}
+ />
+
+
+ {/* Trainers */}
+
+ Trainers
+
+ {TRAINERS.map((trainer) => (
+
+
+
+ {trainer.name[0]}
+
+ {trainer.name}
+ {trainer.specialty}
+
+ ))}
+
+
+
+ {/* Workouts Grid */}
+
+
+
+ {selectedCategory === 'all' ? 'All Workouts' : (CATEGORIES.find(c => c.id === selectedCategory)?.label ?? 'All Workouts')}
+
+ {selectedCategory !== 'all' && (
+ { haptics.buttonTap(); router.push(`/workout/category/${selectedCategory}`) }}>
+ See All
+
+ )}
+
+
+ {filteredWorkouts.map((workout) => {
+ const trainer = getTrainerById(workout.trainerId)
+ return (
+ handleWorkoutPress(workout.id)}
+ >
+
+
+
+ {workout.duration + ' min'}
+
+
+
+ {trainer?.name[0] ?? 'T'}
+
+
+
+
+
+
+
+
+
+ {workout.title}
+ {workout.level}
+
+
+ )
+ })}
+
+
+
+
+ )
+}
+
+// ═══════════════════════════════════════════════════════════════════════════
+// STYLES
+// ═══════════════════════════════════════════════════════════════════════════
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: DARK.BASE,
+ },
+ scrollView: {
+ flex: 1,
+ },
+ scrollContent: {
+ paddingHorizontal: LAYOUT.SCREEN_PADDING,
+ },
+
+ // Header
+ header: {
+ marginBottom: SPACING[6],
+ },
+
+ // Picker Island
+ pickerIsland: {
+ marginBottom: SPACING[6],
+ },
+
+ // Section
+ section: {
+ marginBottom: SPACING[8],
+ },
+ sectionHeader: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ marginBottom: SPACING[2],
+ },
+
+ // Trainers
+ trainersScroll: {
+ gap: SPACING[3],
+ },
+ trainerCard: {
+ width: 100,
+ alignItems: 'center',
+ paddingVertical: SPACING[4],
+ borderRadius: RADIUS.LG,
+ borderWidth: 1,
+ borderColor: 'rgba(255, 255, 255, 0.1)',
+ overflow: 'hidden',
+ },
+ trainerAvatar: {
+ width: 48,
+ height: 48,
+ borderRadius: 24,
+ alignItems: 'center',
+ justifyContent: 'center',
+ marginBottom: SPACING[2],
+ },
+
+ // Workouts Grid
+ workoutsGrid: {
+ flexDirection: 'row',
+ flexWrap: 'wrap',
+ gap: SPACING[3],
+ },
+ workoutCard: {
+ width: CARD_WIDTH,
+ height: 180,
+ borderRadius: RADIUS.GLASS_CARD,
+ overflow: 'hidden',
+ ...SHADOW.md,
+ borderWidth: 1,
+ borderColor: 'rgba(255, 255, 255, 0.1)',
+ },
+ durationBadge: {
+ position: 'absolute',
+ top: SPACING[2],
+ right: SPACING[2],
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
+ paddingHorizontal: SPACING[2],
+ paddingVertical: SPACING[1],
+ borderRadius: RADIUS.SM,
+ },
+ workoutTrainerBadge: {
+ position: 'absolute',
+ top: SPACING[2],
+ left: SPACING[2],
+ width: 28,
+ height: 28,
+ borderRadius: 14,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ playOverlay: {
+ position: 'absolute',
+ top: 0,
+ left: 0,
+ right: 0,
+ bottom: 60,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ playCircle: {
+ width: 44,
+ height: 44,
+ borderRadius: 22,
+ backgroundColor: 'rgba(255, 255, 255, 0.2)',
+ alignItems: 'center',
+ justifyContent: 'center',
+ borderWidth: 1,
+ borderColor: 'rgba(255, 255, 255, 0.3)',
+ },
+ workoutInfo: {
+ position: 'absolute',
+ bottom: 0,
+ left: 0,
+ right: 0,
+ padding: SPACING[3],
+ },
+})