import React from 'react'; import { ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useRouter } from 'expo-router'; import { Ionicons } from '@expo/vector-icons'; import { useSafeAnalytics } from '../../services/analytics'; import { ThemeBackdrop } from '../../components/ThemeBackdrop'; import { useColors } from '../../constants/Colors'; import { useApp } from '../../context/AppContext'; const ONBOARDING_BACKGROUND = { light: '#fbfaf3', dark: '#0a110b', }; const getHealthOnboardingCopy = (language: 'de' | 'en' | 'es') => { if (language === 'de') { return { title: 'So sieht eine Antwort aus', subtitle: 'Ein echtes Beispiel - damit du weißt, was du bekommst, bevor du etwas entscheidest.', exampleSymptomLabel: 'Symptom', exampleSymptom: 'Untere Blätter gelb, Erde feucht', causeLabel: 'Wahrscheinlichste Ursache', // Bewusst ein Fall MITTLERER Sicherheit, kein Vorzeige-Treffer - das ist // der Beweis fuer Belief 3. Die Werte summieren sich absichtlich NICHT auf // 100 %: der Backend-Prompt (server/lib/openai.js, likelyIssues) liefert je // Ursache eine unabhaengige Sicherheit, keine Wahrscheinlichkeitsverteilung. // Ein Beispiel, das sich auf 100 addiert, wuerde ein Verhalten zeigen, das // das Produkt nicht hat. causes: [ { name: 'Überwässerung', level: '64 %', tone: 'high' as const }, { name: 'Nährstoffmangel', level: '41 %', tone: 'mid' as const }, { name: 'Lichtmangel', level: '22 %', tone: 'low' as const }, ], // Schwelle 65 % entspricht der Kalibrierung im Backend-Prompt: // 0.65-0.84 "sehr wahrscheinlich", 0.40-0.64 "mehrdeutig". confidenceNote: 'Unter 65 % sagen wir dir das deutlich - dann sind die Prüfschritte wichtiger als die Ursache.', checkLabel: 'Prüfe zuerst', checks: ['Erde 3 cm tief anfassen', 'Hat der Topf Abzugslöcher?', 'Blattunterseiten ansehen'], actionLabel: 'Tu jetzt', action: 'Nicht gießen. In 5 Tagen erneut prüfen.', followUpLabel: 'In 7 Tagen', followUp: 'Wir fragen nach, ob es besser wird - und passen den Plan an, wenn nicht.', limitNote: 'Ein Foto zeigt keine Wurzeln, keine Erdfeuchte und keine Vorgeschichte. Deshalb bekommst du Wahrscheinlichkeiten und Prüfschritte - keine Diagnose.', guidanceNote: 'Tipp: Fotografiere die ganze Pflanze, die Blattunterseiten und die Erde. Je klarer das Foto, desto präziser der Plan.', cta: 'Weiter', skip: 'Später', }; } if (language === 'es') { return { title: 'Así se ve una respuesta', subtitle: 'Un ejemplo real - para que sepas qué recibes antes de decidir nada.', exampleSymptomLabel: 'Síntoma', exampleSymptom: 'Hojas inferiores amarillas, sustrato húmedo', causeLabel: 'Causa más probable', causes: [ { name: 'Exceso de riego', level: '64 %', tone: 'high' as const }, { name: 'Falta de nutrientes', level: '41 %', tone: 'mid' as const }, { name: 'Falta de luz', level: '22 %', tone: 'low' as const }, ], confidenceNote: 'Por debajo del 65 % te lo decimos claramente - entonces los pasos de revisión importan más que la causa.', checkLabel: 'Revisa primero', checks: ['Tocar el sustrato a 3 cm', '¿La maceta tiene drenaje?', 'Mirar el reverso de las hojas'], actionLabel: 'Haz ahora', action: 'No regar. Volver a revisar en 5 días.', followUpLabel: 'En 7 días', followUp: 'Preguntamos si va mejor - y ajustamos el plan si no.', limitNote: 'Una foto no muestra las raíces, la humedad del sustrato ni el historial. Por eso recibes probabilidades y pasos de revisión - no un diagnóstico.', guidanceNote: 'Consejo: fotografía la planta completa, el reverso de las hojas y el sustrato. Cuanto más clara sea la foto, más preciso será el plan.', cta: 'Continuar', skip: 'Más tarde', }; } return { title: 'This is what an answer looks like', subtitle: 'A real example - so you know what you get before you decide anything.', exampleSymptomLabel: 'Symptom', exampleSymptom: 'Lower leaves yellow, soil damp', causeLabel: 'Most likely cause', causes: [ { name: 'Overwatering', level: '64%', tone: 'high' as const }, { name: 'Nutrient deficiency', level: '41%', tone: 'mid' as const }, { name: 'Too little light', level: '22%', tone: 'low' as const }, ], confidenceNote: 'Below 65% we say so plainly - then the checks matter more than the cause.', checkLabel: 'Check first', checks: ['Feel the soil 3 cm down', 'Does the pot have drainage holes?', 'Look at the leaf undersides'], actionLabel: 'Do now', action: 'Do not water. Check again in 5 days.', followUpLabel: 'In 7 days', followUp: 'We ask whether it is improving - and adjust the plan if it is not.', limitNote: 'A photo cannot show roots, soil moisture or history. So you get probabilities and things to check - not a diagnosis.', guidanceNote: 'Tip: photograph the full plant, leaf undersides, and the soil. The clearer the photo, the more precise the plan.', cta: 'Continue', skip: 'Later', }; }; export default function HealthCheckOnboardingScreen() { const router = useRouter(); const posthog = useSafeAnalytics(); const { isDarkMode, colorPalette, language, billingSummary } = useApp(); const colors = useColors(isDarkMode, colorPalette); const screenBackground = isDarkMode ? ONBOARDING_BACKGROUND.dark : ONBOARDING_BACKGROUND.light; const copy = getHealthOnboardingCopy(language); const finish = (skipped = false) => { posthog.capture('onboarding_health_check_explained', { skipped, plan: billingSummary?.entitlement?.plan ?? 'free', }); router.replace('/onboarding/personalizing'); }; return ( {isDarkMode ? : null} router.back()} style={[styles.backBtn, { backgroundColor: colors.surface }]}> {/* Header liegt im Scrollbereich: bei grosser Systemschrift wuchs er sonst nach unten und hat den Scrollbereich auf Null gequetscht. */} {copy.title} {copy.subtitle} {/* Worked Example statt Wegbeschreibung: ein echtes Ergebnis schlaegt jede Behauptung ueber die Qualitaet der Ergebnisse. */} {copy.exampleSymptomLabel} {copy.exampleSymptom} {copy.causeLabel} {copy.causes.map((cause) => ( {cause.name} {cause.level} ))} {copy.confidenceNote} {copy.checkLabel} {copy.checks.map((check) => ( {check} ))} {copy.actionLabel} {copy.action} {copy.followUpLabel} {copy.followUp} {/* Die Grenze des Verfahrens steht bewusst direkt unter dem Beispiel, nicht im Kleingedruckten. Sie ist Teil des Verkaufsarguments. */} {copy.limitNote} {copy.guidanceNote} finish(true)} > {copy.skip} finish(false)}> {copy.cta} ); } const styles = StyleSheet.create({ container: { flex: 1 }, safeArea: { flex: 1, paddingHorizontal: 20, paddingTop: 24, paddingBottom: 20 }, scroll: { flex: 1 }, topBar: { flexDirection: 'row', alignItems: 'center', gap: 14, paddingVertical: 10 }, backBtn: { width: 40, height: 40, borderRadius: 20, alignItems: 'center', justifyContent: 'center' }, progressTrack: { flex: 1, height: 6, borderRadius: 3, overflow: 'hidden' }, progressFill: { height: 6, borderRadius: 3 }, header: { gap: 9, marginTop: 8, marginBottom: 4 }, title: { fontSize: 30, lineHeight: 34, fontWeight: '900' }, subtitle: { fontSize: 14, lineHeight: 20 }, content: { gap: 14, paddingBottom: 12 }, phone: { width: 178, minHeight: 156, borderRadius: 26, borderWidth: 1, padding: 12, gap: 10, marginLeft: 16 }, phoneTitle: { fontSize: 13, fontWeight: '800' }, phoneRows: { gap: 8 }, phoneRowLong: { height: 8, borderRadius: 999 }, phoneRowShort: { width: '66%', height: 8, borderRadius: 999 }, healthButtonText: { fontSize: 10, fontWeight: '800' }, scanScore: { fontSize: 25, lineHeight: 29, fontWeight: '900' }, scanLine: { height: 8, borderRadius: 999 }, scanLineShort: { width: '68%', height: 8, borderRadius: 999 }, outputRow: { flexDirection: 'row', alignItems: 'flex-start', gap: 9, marginBottom: 6 }, exampleCard: { borderRadius: 20, borderWidth: 1, padding: 18, gap: 4 }, exampleEyebrow: { fontSize: 10.5, fontWeight: '900', letterSpacing: 0.9, textTransform: 'uppercase', marginBottom: 6 }, exampleSymptom: { fontSize: 16, fontWeight: '700', lineHeight: 22 }, divider: { height: 1, marginVertical: 14 }, causeRow: { flexDirection: 'row', alignItems: 'center', gap: 9, marginBottom: 8 }, causeDot: { width: 8, height: 8, borderRadius: 4 }, causeName: { flex: 1, fontSize: 14.5, fontWeight: '600' }, causeNamePrimary: { fontWeight: '800' }, causeLevel: { fontSize: 12.5, fontWeight: '800', fontVariant: ['tabular-nums'] }, confidenceNote: { flexDirection: 'row', alignItems: 'flex-start', gap: 7, borderRadius: 10, padding: 10, marginTop: 4 }, confidenceNoteText: { flex: 1, fontSize: 12, lineHeight: 16.5, fontWeight: '500' }, actionBox: { borderRadius: 14, padding: 13, marginTop: 12, gap: 3 }, actionLabel: { fontSize: 10.5, fontWeight: '900', letterSpacing: 0.9, textTransform: 'uppercase' }, actionText: { fontSize: 14.5, fontWeight: '700', lineHeight: 20 }, followUpRow: { flexDirection: 'row', alignItems: 'flex-start', gap: 9, marginTop: 14 }, followUpCopy: { flex: 1, gap: 2 }, followUpLabel: { fontSize: 13, fontWeight: '800' }, followUpText: { fontSize: 13, lineHeight: 18, fontWeight: '500' }, limitCard: { borderRadius: 16, borderWidth: 1, padding: 14, flexDirection: 'row', alignItems: 'flex-start', gap: 10 }, limitText: { flex: 1, fontSize: 12.5, lineHeight: 18, fontWeight: '500' }, outputText: { flex: 1, fontSize: 13, lineHeight: 18 }, guidanceCard: { borderRadius: 18, borderWidth: 1, padding: 14, flexDirection: 'row', alignItems: 'flex-start', gap: 10 }, guidanceText: { flex: 1, fontSize: 12, lineHeight: 18, fontWeight: '600' }, footer: { flexDirection: 'row', gap: 12, marginTop: 12 }, // minHeight statt height: sonst wird der Label-Text bei groesserer // Systemschrift oben und unten abgeschnitten. secondaryBtn: { flex: 1, minHeight: 52, paddingVertical: 14, paddingHorizontal: 8, borderRadius: 16, borderWidth: 1.5, alignItems: 'center', justifyContent: 'center' }, secondaryBtnText: { fontSize: 15, fontWeight: '600', textAlign: 'center' }, primaryBtn: { flex: 1.3, minHeight: 52, paddingVertical: 14, paddingHorizontal: 8, borderRadius: 16, alignItems: 'center', justifyContent: 'center' }, primaryBtnText: { fontSize: 15, fontWeight: '700', textAlign: 'center' }, });