import React, { useMemo, useState } from 'react'; import { View, Text, StyleSheet, ScrollView, TouchableOpacity, Modal, Alert, Share, } from 'react-native'; import { useLocalSearchParams, useRouter } from 'expo-router'; import { Ionicons } from '@expo/vector-icons'; import * as Haptics from 'expo-haptics'; import * as ImagePicker from 'expo-image-picker'; import { useApp } from '../../context/AppContext'; import { useColors } from '../../constants/Colors'; import { requestPermissions, scheduleWateringReminder, cancelReminder } from '../../services/notificationService'; import { SafeImage } from '../../components/SafeImage'; const parseColorToRgb = (input: string) => { const color = input.trim(); if (color.startsWith('#')) { const hex = color.slice(1); const normalized = hex.length === 3 ? hex.split('').map((char) => `${char}${char}`).join('') : hex; if (normalized.length === 6) { return { r: Number.parseInt(normalized.slice(0, 2), 16), g: Number.parseInt(normalized.slice(2, 4), 16), b: Number.parseInt(normalized.slice(4, 6), 16), }; } } const rgbMatch = color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/i); if (rgbMatch) { return { r: Number.parseInt(rgbMatch[1], 10), g: Number.parseInt(rgbMatch[2], 10), b: Number.parseInt(rgbMatch[3], 10), }; } return { r: 127, g: 127, b: 127 }; }; const getReadableTextColor = (background: string, dark: string, light: string) => { const { r, g, b } = parseColorToRgb(background); const luminance = (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255; return luminance > 0.58 ? dark : light; }; const HEALTH_CHECK_CREDIT_COST = 2; const getHealthCopy = (language: 'de' | 'en' | 'es') => { if (language === 'de') { return { title: 'Health Check', action: 'Neues Foto + Health-Check', running: 'Neues Foto wird analysiert...', cost: `Kosten: ${HEALTH_CHECK_CREDIT_COST} Credits`, creditsLabel: 'Credits', managePlan: 'Plan verwalten', noCreditsTitle: 'Nicht genug Credits', noCreditsMessage: `Du brauchst ${HEALTH_CHECK_CREDIT_COST} Credits fuer den Health-Check.`, insufficientInline: 'Nicht genug Credits fuer den Health-Check.', timeoutInline: 'Health-Check Timeout. Bitte erneut versuchen.', providerInline: 'Health-Check ist gerade nicht verfuegbar.', issuesTitle: 'Moegliche Ursachen', actionsTitle: 'Sofortmassnahmen', planTitle: '7-Tage-Plan', scoreLabel: 'Gesundheits-Score', healthy: 'Stabil', watch: 'Beobachten', critical: 'Kritisch', lastCheck: 'Zuletzt geprueft', }; } if (language === 'es') { return { title: 'Health Check', action: 'Foto nuevo + Health-check', running: 'Analizando foto nueva...', cost: `Costo: ${HEALTH_CHECK_CREDIT_COST} creditos`, creditsLabel: 'Creditos', managePlan: 'Gestionar plan', noCreditsTitle: 'Creditos insuficientes', noCreditsMessage: `Necesitas ${HEALTH_CHECK_CREDIT_COST} creditos para el health-check.`, insufficientInline: 'No hay creditos suficientes para el health-check.', timeoutInline: 'Health-check agotado por tiempo. Intenta de nuevo.', providerInline: 'Health-check no disponible ahora.', issuesTitle: 'Posibles causas', actionsTitle: 'Acciones inmediatas', planTitle: 'Plan de 7 dias', scoreLabel: 'Puntaje de salud', healthy: 'Estable', watch: 'Observar', critical: 'Critico', lastCheck: 'Ultima revision', }; } return { title: 'Health Check', action: 'New Photo + Health Check', running: 'Analyzing new photo...', cost: `Cost: ${HEALTH_CHECK_CREDIT_COST} credits`, creditsLabel: 'Credits', managePlan: 'Manage plan', noCreditsTitle: 'Not enough credits', noCreditsMessage: `You need ${HEALTH_CHECK_CREDIT_COST} credits for the health check.`, insufficientInline: 'Not enough credits for the health check.', timeoutInline: 'Health check timed out. Please try again.', providerInline: 'Health check is unavailable right now.', issuesTitle: 'Likely issues', actionsTitle: 'Actions now', planTitle: '7-day plan', scoreLabel: 'Health score', healthy: 'Stable', watch: 'Watch', critical: 'Critical', lastCheck: 'Last checked', }; }; export default function PlantDetailScreen() { const { id } = useLocalSearchParams<{ id: string }>(); const { plants, isDarkMode, colorPalette, language, t, deletePlant, updatePlant, billingSummary, } = useApp(); const colors = useColors(isDarkMode, colorPalette); const router = useRouter(); const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); const [fullscreenImage, setFullscreenImage] = useState(null); const [healthStatus, setHealthStatus] = useState<'idle' | 'insufficient_credits'>('idle'); const [paywallVisible, setPaywallVisible] = useState(false); const plant = plants.find((item) => item.id === id); if (!plant) { return ( Plant not found ); } const localeMap: Record = { de: 'de-DE', en: 'en-US', es: 'es-ES' }; const locale = localeMap[language] || 'de-DE'; const healthCopy = getHealthCopy(language); const availableCredits = billingSummary?.credits.available ?? 0; const formattedWateredDate = new Date(plant.lastWatered).toLocaleDateString(locale); const lastWateredObj = new Date(plant.lastWatered); const nextWateringDate = new Date(lastWateredObj); nextWateringDate.setDate(lastWateredObj.getDate() + plant.careInfo.waterIntervalDays); const formattedNextWatering = nextWateringDate.toLocaleDateString(locale, { weekday: 'short', day: 'numeric', month: 'short', }); const lastWateredText = t.lastWateredDate.replace('{0}', formattedWateredDate); const isWateredToday = new Date(plant.lastWatered).toDateString() === new Date().toDateString(); const daysSinceWatered = Math.max( 0, Math.floor((Date.now() - lastWateredObj.getTime()) / (1000 * 60 * 60 * 24)) ); const nextWaterLabel = language === 'de' ? 'Nächstes Gießen' : language === 'es' ? 'Proximo riego' : 'Next water'; const wateredAgoText = language === 'de' ? `Zuletzt vor ${daysSinceWatered} ${daysSinceWatered === 1 ? 'Tag' : 'Tagen'}` : language === 'es' ? `Ultimo riego hace ${daysSinceWatered} ${daysSinceWatered === 1 ? 'dia' : 'dias'}` : `Last watered ${daysSinceWatered} ${daysSinceWatered === 1 ? 'day' : 'days'} ago`; const textOnPage = getReadableTextColor(colors.pageBase, colors.primaryDark, colors.textOnImage); const textOnSurface = getReadableTextColor(colors.surface, colors.primaryDark, colors.textOnImage); const textOnHeroButton = getReadableTextColor(colors.heroButton, colors.primaryDark, colors.textOnImage); const textOnPrimaryAction = getReadableTextColor(colors.primaryDark, '#ffffff', '#111111'); const textOnAiBadge = getReadableTextColor(colors.primarySoft, colors.primaryDark, colors.textOnImage); const latestHealthCheck = useMemo(() => { if (!plant.healthChecks || plant.healthChecks.length === 0) return null; return plant.healthChecks[0]; }, [plant.healthChecks]); const healthStatusInlineText = (() => { if (healthStatus === 'insufficient_credits') return healthCopy.insufficientInline; if (healthStatus === 'idle' && availableCredits < HEALTH_CHECK_CREDIT_COST) return healthCopy.insufficientInline; return ''; })(); const healthStateColor = (() => { if (healthStatus === 'insufficient_credits') return colors.warning; return colors.textMuted; })(); const latestStatusText = latestHealthCheck ? ( latestHealthCheck.status === 'healthy' ? healthCopy.healthy : latestHealthCheck.status === 'watch' ? healthCopy.watch : healthCopy.critical ) : ''; const latestStatusBg = latestHealthCheck ? ( latestHealthCheck.status === 'healthy' ? colors.successSoft : latestHealthCheck.status === 'watch' ? colors.warningSoft : colors.dangerSoft ) : colors.surfaceMuted; const latestStatusColor = latestHealthCheck ? ( latestHealthCheck.status === 'healthy' ? colors.success : latestHealthCheck.status === 'watch' ? colors.warning : colors.danger ) : colors.textMuted; const timelineEntries = useMemo(() => { const history = plant.wateringHistory && plant.wateringHistory.length > 0 ? plant.wateringHistory : [plant.lastWatered]; return history.slice(0, 5); }, [plant.lastWatered, plant.wateringHistory]); const hasEnoughHealthCredits = availableCredits >= HEALTH_CHECK_CREDIT_COST; const canRunHealthCheck = true; const handleWater = async () => { await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium); const now = new Date().toISOString(); const currentHistory = plant.wateringHistory || []; const newHistory = [now, ...currentHistory].slice(0, 10); updatePlant({ ...plant, lastWatered: now, wateringHistory: newHistory }); }; const handleShare = async () => { try { await Share.share({ message: `Check out my plant: ${plant.name} (${plant.botanicalName}) - identified with GreenLens!`, }); } catch (error: any) { Alert.alert('Error', error.message); } }; const handleDelete = () => { deletePlant(plant.id); router.back(); }; const handleAddPhoto = async () => { const result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ['images'], quality: 0.8, }); if (!result.canceled && result.assets[0]) { const currentGallery = plant.gallery || []; updatePlant({ ...plant, gallery: [...currentGallery, result.assets[0].uri] }); } }; const handleHealthCheck = async () => { if (availableCredits < HEALTH_CHECK_CREDIT_COST) { setPaywallVisible(true); return; } if (healthStatus !== 'idle') { setHealthStatus('idle'); } router.push({ pathname: '/scanner', params: { mode: 'health', plantId: plant.id }, }); }; return ( router.back()} > {plant.name} {plant.botanicalName} {nextWaterLabel} {formattedNextWatering} {isWateredToday ? lastWateredText : wateredAgoText} {isWateredToday ? t.watered : t.waterNow} {[ { icon: 'water' as const, label: t.water, value: `${plant.careInfo.waterIntervalDays} ${t.days}`, iconColor: colors.info, iconBg: colors.infoSoft, }, { icon: 'sunny' as const, label: t.light, value: plant.careInfo.light, iconColor: colors.warning, iconBg: colors.warningSoft, }, { icon: 'thermometer' as const, label: t.temp, value: plant.careInfo.temp, iconColor: colors.success, iconBg: colors.successSoft, }, ].map((item) => ( {item.label} {item.value} ))} Smart Reminders {t.reminderDesc} { if (!plant.notificationsEnabled) { const granted = await requestPermissions(); if (!granted) { Alert.alert(t.reminder, t.reminderPermissionNeeded); return; } await scheduleWateringReminder(plant); } else { await cancelReminder(plant.id); } updatePlant({ ...plant, notificationsEnabled: !plant.notificationsEnabled }); }} > {t.wateringHistory} {timelineEntries.length === 0 ? ( {t.noHistory} ) : ( timelineEntries.map((dateStr, index) => ( {index < timelineEntries.length - 1 && ( )} {new Date(dateStr).toLocaleDateString(locale, { day: '2-digit', month: 'short', year: 'numeric', })} {index === 0 ? t.watered : t.waterNow} {new Date(dateStr).toLocaleTimeString(locale, { hour: '2-digit', minute: '2-digit', })} )) )} {t.galleryTitle} {t.addPhoto} {(!plant.gallery || plant.gallery.length === 0) ? ( {t.noPhotos} ) : ( {plant.gallery.map((uri, index) => ( setFullscreenImage(uri)}> ))} )} {t.aboutPlant} AI {plant.description || t.noDescription} {healthCopy.title} {healthCopy.cost} {healthCopy.action} {healthCopy.creditsLabel}: {availableCredits} {healthCopy.cost} {healthStatusInlineText ? ( {healthStatusInlineText} {healthStatus === 'insufficient_credits' || (healthStatus === 'idle' && !hasEnoughHealthCredits) ? ( router.push('/(tabs)/profile')} > {healthCopy.managePlan} ) : null} ) : null} {latestHealthCheck ? ( {healthCopy.scoreLabel} {latestHealthCheck.overallHealthScore}/100 {latestStatusText} {healthCopy.lastCheck}: {new Date(latestHealthCheck.generatedAt).toLocaleString(locale)} {healthCopy.issuesTitle} {latestHealthCheck.likelyIssues.map((issue, index) => ( {issue.title} {Math.round(issue.confidence * 100)}% {issue.details} ))} {healthCopy.actionsTitle} {latestHealthCheck.actionsNow.map((item, index) => ( {item} ))} {healthCopy.planTitle} {latestHealthCheck.plan7Days.map((item, index) => ( {item} ))} ) : null} setShowDeleteConfirm(true)}> {t.delete} {t.deleteConfirmTitle} {t.deleteConfirmMessage} setShowDeleteConfirm(false)} > {t.cancel} {t.confirm} {fullscreenImage && } setFullscreenImage(null)}> {/* Health Check Paywall Modal */} setPaywallVisible(false)}> setPaywallVisible(false)} style={styles.paywallClose}> KI-Pflanzendoktor freischalten Nutze fortschrittliche KI, um Probleme zu erkennen, bevor sie deine Pflanze gefährden. {[ { icon: 'search', title: 'Präzise Diagnose', desc: 'Erkennt Schädlinge, Krankheiten und Nährstoffmangel.' }, { icon: 'medkit', title: 'Rettungspläne', desc: 'Schritt-für-Schritt Anleitungen zur Genesung.' }, { icon: 'infinite', title: 'Mehr Scans', desc: 'Erhalte 120 Credits jeden Monat mit GreenLens Pro.' }, ].map((feat, i) => ( {feat.title} {feat.desc} ))} { setPaywallVisible(false); router.push('/profile/billing'); }} > Jetzt upgraden setPaywallVisible(false)} > Vielleicht später ); } const styles = StyleSheet.create({ container: { flex: 1 }, scrollContent: { paddingBottom: 120 }, hero: { height: 450, position: 'relative', }, heroImage: { width: '100%', height: '100%', resizeMode: 'cover', }, heroShade: { ...StyleSheet.absoluteFillObject, opacity: 0.18, }, heroBaseFade: { position: 'absolute', left: 0, right: 0, bottom: 0, height: 172, opacity: 0.95, borderTopLeftRadius: 28, borderTopRightRadius: 28, }, topActions: { position: 'absolute', top: 58, left: 22, right: 22, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', }, floatingAction: { width: 44, height: 44, borderRadius: 14, borderWidth: 1, justifyContent: 'center', alignItems: 'center', shadowColor: '#000', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.15, shadowRadius: 10, elevation: 5, }, heroTitleWrap: { position: 'absolute', left: 24, right: 24, bottom: 74, }, heroName: { fontSize: 36, lineHeight: 40, fontWeight: '700', marginBottom: 6, }, heroBotanical: { fontSize: 16, fontStyle: 'italic', fontWeight: '600', opacity: 0.72, }, content: { marginTop: -46, paddingHorizontal: 16, gap: 20, }, waterCard: { borderRadius: 28, borderWidth: 1, paddingHorizontal: 16, paddingVertical: 14, flexDirection: 'row', alignItems: 'center', gap: 12, shadowColor: '#000', shadowOffset: { width: 0, height: 8 }, shadowOpacity: 0.08, shadowRadius: 16, elevation: 4, }, waterInfo: { flex: 1, gap: 2, }, waterHeadline: { flexDirection: 'row', alignItems: 'center', gap: 6, }, waterLabel: { fontSize: 11, textTransform: 'uppercase', letterSpacing: 0.8, fontWeight: '700', }, waterValue: { fontSize: 15, lineHeight: 19, fontWeight: '700', }, waterMeta: { fontSize: 12, fontWeight: '500', }, waterButton: { borderRadius: 16, paddingHorizontal: 20, paddingVertical: 13, flexDirection: 'row', alignItems: 'center', gap: 6, }, waterButtonText: { fontSize: 16, fontWeight: '700', }, careGrid: { flexDirection: 'row', gap: 10, }, careCard: { flex: 1, borderRadius: 22, borderWidth: 1, alignItems: 'center', justifyContent: 'center', paddingHorizontal: 8, paddingVertical: 16, gap: 6, }, careIconWrap: { width: 34, height: 34, borderRadius: 17, alignItems: 'center', justifyContent: 'center', }, careLabel: { fontSize: 10, fontWeight: '700', textTransform: 'uppercase', letterSpacing: 0.5, }, careValue: { width: '100%', fontSize: 12, lineHeight: 15, fontWeight: '700', textAlign: 'center', flexShrink: 1, }, reminderCard: { borderRadius: 24, borderWidth: 1, paddingHorizontal: 14, paddingVertical: 12, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', gap: 12, }, reminderInfoRow: { flexDirection: 'row', alignItems: 'center', gap: 10, flex: 1, }, reminderIconWrap: { width: 40, height: 40, borderRadius: 13, justifyContent: 'center', alignItems: 'center', }, reminderTitle: { fontSize: 14, fontWeight: '700', }, reminderSubtitle: { fontSize: 10, marginTop: 1, }, toggleTrack: { width: 54, height: 30, borderRadius: 999, justifyContent: 'center', position: 'relative', }, toggleThumb: { position: 'absolute', width: 22, height: 22, borderRadius: 11, backgroundColor: '#ffffff', }, summarySection: { gap: 8, }, summaryHeader: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', }, aiBadge: { borderRadius: 999, paddingHorizontal: 10, paddingVertical: 4, }, aiBadgeText: { fontSize: 10, fontWeight: '700', letterSpacing: 0.6, }, summaryCard: { borderRadius: 22, borderWidth: 1, padding: 16, }, summaryText: { fontSize: 14, lineHeight: 22, }, healthActionCard: { borderRadius: 22, borderWidth: 1, padding: 14, gap: 10, }, healthActionRow: { flexDirection: 'row', gap: 10, alignItems: 'center', }, healthActionInfo: { flex: 1, }, healthActionTitle: { fontSize: 14, fontWeight: '700', }, healthActionMeta: { fontSize: 11, marginTop: 2, }, healthActionBtn: { borderRadius: 14, borderWidth: 1, paddingHorizontal: 12, paddingVertical: 10, flexDirection: 'row', alignItems: 'center', gap: 6, }, healthActionBtnText: { fontSize: 12, fontWeight: '700', }, healthMetaRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', }, healthMetaText: { fontSize: 12, fontWeight: '500', }, healthInlineWrap: { gap: 8, }, healthInlineText: { fontSize: 12, fontWeight: '600', }, healthPlanButton: { alignSelf: 'flex-start', borderWidth: 1, borderRadius: 12, paddingHorizontal: 10, paddingVertical: 7, }, healthPlanButtonText: { fontSize: 12, fontWeight: '700', }, healthResultCard: { borderRadius: 22, borderWidth: 1, padding: 14, gap: 12, }, healthResultHead: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', }, healthResultTitle: { fontSize: 11, textTransform: 'uppercase', letterSpacing: 0.5, fontWeight: '700', }, healthResultScore: { fontSize: 26, lineHeight: 30, fontWeight: '800', marginTop: 2, }, healthStatusBadge: { borderRadius: 999, paddingHorizontal: 10, paddingVertical: 5, }, healthStatusBadgeText: { fontSize: 11, fontWeight: '700', }, healthTimestamp: { fontSize: 11, }, healthListBlock: { gap: 8, }, healthListTitle: { fontSize: 13, fontWeight: '700', }, healthIssueWrap: { gap: 3, }, healthIssueHead: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', gap: 8, }, healthIssueTitle: { fontSize: 13, fontWeight: '600', flex: 1, }, healthIssueConfidence: { fontSize: 11, fontWeight: '700', }, healthIssueDetails: { fontSize: 12, lineHeight: 18, }, healthBulletRow: { flexDirection: 'row', alignItems: 'flex-start', gap: 8, }, healthBulletDot: { width: 7, height: 7, borderRadius: 4, marginTop: 5, }, healthBulletText: { flex: 1, fontSize: 12, lineHeight: 18, }, sectionHeaderRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10, marginTop: 4, }, sectionTitle: { fontSize: 18, fontWeight: '700', }, timelineCard: { borderRadius: 22, borderWidth: 1, paddingHorizontal: 14, paddingTop: 16, paddingBottom: 10, gap: 4, }, historyEmpty: { width: '100%', alignItems: 'center', justifyContent: 'center', gap: 8, minHeight: 96, paddingVertical: 20, }, historyEmptyText: { fontSize: 13, textAlign: 'center', }, timelineRow: { flexDirection: 'row', alignItems: 'flex-start', gap: 10, }, timelineMarkerColumn: { width: 20, alignItems: 'center', }, timelineDot: { width: 11, height: 11, borderRadius: 6, borderWidth: 2, }, timelineLine: { width: 2, flex: 1, marginTop: 3, minHeight: 26, borderRadius: 2, }, timelineContent: { flex: 1, paddingBottom: 14, }, timelineTopRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', gap: 10, }, timelineDate: { fontSize: 14, fontWeight: '700', }, timelineType: { fontSize: 10, textTransform: 'uppercase', letterSpacing: 0.45, fontWeight: '600', }, timelineTime: { fontSize: 12, marginTop: 3, }, galleryHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12, }, addPhotoBtn: { flexDirection: 'row', alignItems: 'center', gap: 5, borderWidth: 1, borderRadius: 18, paddingHorizontal: 10, paddingVertical: 6, }, addPhotoBtnText: { fontSize: 13, fontWeight: '600', }, galleryEmpty: { borderRadius: 18, borderWidth: 1, paddingVertical: 22, alignItems: 'center', justifyContent: 'center', gap: 8, }, galleryScroll: { marginBottom: 2, }, galleryContent: { gap: 12, paddingRight: 20, }, galleryThumb: { width: 96, height: 96, borderRadius: 18, }, deleteAction: { alignSelf: 'center', marginTop: 4, marginBottom: 12, flexDirection: 'row', alignItems: 'center', gap: 6, paddingHorizontal: 8, paddingVertical: 6, }, deleteActionText: { fontSize: 13, fontWeight: '600', }, modalOverlay: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 24, }, modalCard: { width: '100%', maxWidth: 340, borderRadius: 24, padding: 22, }, modalIcon: { width: 48, height: 48, borderRadius: 24, justifyContent: 'center', alignItems: 'center', alignSelf: 'center', marginBottom: 14, }, modalTitle: { fontSize: 18, fontWeight: '700', textAlign: 'center', marginBottom: 8, }, modalMessage: { fontSize: 13, lineHeight: 20, textAlign: 'center', marginBottom: 20, }, modalActions: { flexDirection: 'row', gap: 10, }, modalButton: { flex: 1, paddingVertical: 14, borderRadius: 14, alignItems: 'center', }, modalButtonText: { fontSize: 13, fontWeight: '700', }, fullscreenOverlay: { flex: 1, backgroundColor: '#000000ee', justifyContent: 'center', alignItems: 'center', }, fullscreenImage: { width: '90%', height: '72%', resizeMode: 'contain', }, fullscreenClose: { position: 'absolute', top: 56, right: 20, backgroundColor: '#00000088', borderRadius: 20, padding: 8, }, paywallOverlay: { flex: 1, backgroundColor: '#000000aa', justifyContent: 'flex-end', }, paywallContent: { borderTopLeftRadius: 32, borderTopRightRadius: 32, padding: 24, paddingBottom: 48, borderTopWidth: 1, }, paywallHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 20, }, paywallIconWrap: { width: 64, height: 64, borderRadius: 20, justifyContent: 'center', alignItems: 'center', }, paywallClose: { padding: 4, }, paywallTitle: { fontSize: 24, fontWeight: '800', marginBottom: 8, letterSpacing: 0.2, }, paywallSubtitle: { fontSize: 15, lineHeight: 22, marginBottom: 28, }, paywallFeatures: { gap: 18, marginBottom: 32, }, paywallFeatureRow: { flexDirection: 'row', gap: 14, alignItems: 'center', }, paywallFeatureIcon: { width: 38, height: 38, borderRadius: 12, justifyContent: 'center', alignItems: 'center', }, paywallFeatureTitle: { fontSize: 15, fontWeight: '700', marginBottom: 2, }, paywallFeatureDesc: { fontSize: 13, lineHeight: 18, }, paywallPrimaryBtn: { borderRadius: 18, paddingVertical: 16, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 8, shadowOpacity: 0.2, shadowRadius: 10, shadowOffset: { width: 0, height: 4 }, elevation: 4, }, paywallPrimaryBtnText: { fontSize: 17, fontWeight: '700', }, paywallSecondaryBtn: { marginTop: 12, paddingVertical: 12, alignItems: 'center', }, paywallSecondaryBtnText: { fontSize: 14, fontWeight: '600', }, });