Files
metabloom/App.tsx
T
AI Company ea8b5be058
MetaBloom CI / app (push) Canceled after 0s
MetaBloom CI / api (push) Canceled after 0s
feat: evolve MetaBloom into full product foundation
2026-07-21 19:24:59 +00:00

67 lines
26 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import AsyncStorage from '@react-native-async-storage/async-storage';
import { LinearGradient } from 'expo-linear-gradient';
import { StatusBar } from 'expo-status-bar';
import React, { useEffect, useMemo, useState } from 'react';
import { Alert, Pressable, SafeAreaView, ScrollView, Share, StyleSheet, Switch, Text, TextInput, View, useWindowDimensions } from 'react-native';
type Tab = 'today' | 'journal' | 'progress' | 'coach' | 'profile';
type Entry = { date: string; iso?: string; weight: number; water: number; steps: number; glucose?: number; sleep?: number; meals?: number; mood?: string };
type ProfileData = { name:string; goal:string; journey:string; glucoseUnit:'mg/dL'|'mmol/L'; reminders:boolean; complete:boolean };
const C = { ink:'#15211c', muted:'#637169', cream:'#f6f7f2', white:'#fff', green:'#1d6b50', mint:'#dcefe5', coral:'#ef835f', yellow:'#f4d58d', line:'#e5e9e3' };
const seed: Entry[] = [
{date:'Lun',weight:91.4,water:1500,steps:6100,glucose:112},{date:'Mar',weight:91.2,water:1800,steps:7400,glucose:108},
{date:'Mer',weight:91.1,water:1600,steps:6900,glucose:110},{date:'Jeu',weight:90.9,water:2000,steps:8200,glucose:105},
{date:'Ven',weight:90.8,water:2100,steps:8600,glucose:104},{date:'Sam',weight:90.7,water:1750,steps:7200,glucose:107},{date:'Auj.',weight:90.6,water:1250,steps:6240,glucose:103},
];
function Card({children, style}: any){return <View style={[s.card,style]}>{children}</View>}
function Pill({children, tone='green'}:any){return <View style={[s.pill,{backgroundColor:tone==='coral'?'#fff0eb':C.mint}]}><Text style={[s.pillText,{color:tone==='coral'?'#9b432a':C.green}]}>{children}</Text></View>}
function Metric({emoji,label,value,sub,onPress}:any){return <Pressable onPress={onPress} style={({pressed})=>[s.metric,pressed&&{opacity:.7}]}><Text style={s.metricEmoji}>{emoji}</Text><Text style={s.metricLabel}>{label}</Text><Text style={s.metricValue}>{value}</Text><Text style={s.metricSub}>{sub}</Text></Pressable>}
function ProgressBar({value,color=C.green}:any){return <View style={s.track}><View style={[s.fill,{width:`${Math.min(value,100)}%`,backgroundColor:color}]}/></View>}
function Nav({tab,setTab}:any){const items:[Tab,string,string][]=[['today','⌂','Aujourdhui'],['journal','','Journal'],['progress','↗','Progrès'],['coach','✦','Coach'],['profile','○','Profil']];return <View style={s.nav}>{items.map(([id,icon,label])=><Pressable accessibilityLabel={`Ouvrir ${label}`} accessibilityRole="button" key={id} onPress={()=>setTab(id)} style={s.navItem}><Text style={[s.navIcon,tab===id&&s.active]}>{icon}</Text><Text style={[s.navLabel,tab===id&&s.active]}>{label}</Text></Pressable>)}</View>}
export default function App(){
const [tab,setTab]=useState<Tab>('today'); const [entries,setEntries]=useState(seed); const [water,setWater]=useState(1250); const [steps,setSteps]=useState(6240);
const [weight,setWeight]=useState('90.6'); const [glucose,setGlucose]=useState('103'); const [sleep,setSleep]=useState('7.2'); const [meals,setMeals]=useState(2); const [mood,setMood]=useState('🙂'); const [saved,setSaved]=useState(false); const [loaded,setLoaded]=useState(false); const [profile,setProfile]=useState<ProfileData>({name:'',goal:'Retrouver mon équilibre',journey:'Prévention',glucoseUnit:'mg/dL',reminders:false,complete:false}); const {width}=useWindowDimensions();
useEffect(()=>{AsyncStorage.getItem('metabloom-v2').then(v=>{if(v){const d=JSON.parse(v);setEntries(d.entries||seed);setWater(d.water||1250);setSteps(d.steps||6240);setProfile(d.profile||profile)}}).finally(()=>setLoaded(true))},[]);
useEffect(()=>{if(loaded)AsyncStorage.setItem('metabloom-v2',JSON.stringify({entries,water,steps,profile})).catch(()=>{})},[entries,water,steps,profile,loaded]);
const score=Math.round((Math.min(water/2000,1)+Math.min(steps/8000,1)+.82+.75)*25);
const save=()=>{const w=parseFloat(weight.replace(',','.')); const g=parseFloat(glucose.replace(',','.')); const sl=parseFloat(sleep.replace(',','.')); if(!w||w<30||w>300){Alert.alert('Valeur à vérifier','Saisis un poids compris entre 30 et 300 kg.');return} const glucoseBounds=profile.glucoseUnit==='mmol/L'?[1.1,33.3]:[20,600]; if(g && (g<glucoseBounds[0]||g>glucoseBounds[1])){Alert.alert('Valeur à vérifier','La glycémie saisie semble hors de la plage de saisie autorisée. Vérifie lunité et la valeur.');return} if(!sl||sl>24){Alert.alert('Valeur à vérifier','Le sommeil doit être compris entre 0 et 24 heures.');return} const now=new Date();setEntries(x=>[...x.slice(-29),{date:'Auj.',iso:now.toISOString(),weight:w,water,steps,glucose:g||undefined,sleep:sl,meals,mood}]);setSaved(true);setTimeout(()=>setSaved(false),2200)};
const reset=()=>{setEntries(seed);setWater(1250);setSteps(6240);setProfile({...profile,complete:false});AsyncStorage.removeItem('metabloom-v2')};
const content=useMemo(()=>tab==='today'?<Today name={profile.name} score={score} water={water} setWater={setWater} steps={steps} setSteps={setSteps} go={setTab}/>:tab==='journal'?<Journal weight={weight} setWeight={setWeight} glucose={glucose} setGlucose={setGlucose} glucoseUnit={profile.glucoseUnit} water={water} setWater={setWater} steps={steps} setSteps={setSteps} sleep={sleep} setSleep={setSleep} meals={meals} setMeals={setMeals} mood={mood} setMood={setMood} save={save} saved={saved}/>:tab==='progress'?<Progress entries={entries}/>:tab==='coach'?<Coach journey={profile.journey}/>:<Profile profile={profile} setProfile={setProfile} entries={entries} reset={reset}/>,[tab,score,water,steps,weight,glucose,sleep,meals,mood,saved,entries,profile]);
if(!loaded)return <SafeAreaView style={s.safe}/>;
if(!profile.complete)return <Onboarding profile={profile} setProfile={setProfile}/>;
return <SafeAreaView style={s.safe}><StatusBar style="dark"/><View style={[s.shell,{maxWidth:width>900?1120:680}]}>{content}<Nav tab={tab} setTab={setTab}/></View></SafeAreaView>
}
function Onboarding({profile,setProfile}:any){const [step,setStep]=useState(0);const journeys=['Prévention','Perte de poids','Diabète type 2','Traitement GLP-1'];return <SafeAreaView style={s.safe}><StatusBar style="dark"/><ScrollView contentContainerStyle={s.onboard}><View style={s.logo}><Text style={s.logoText}>M</Text></View><Text style={s.eyebrow}>BIENVENUE SUR METABLOOM</Text><Text style={s.onboardTitle}>{step===0?'Prenons soin de toi, simplement.':'Quel parcours te ressemble ?'}</Text><Text style={s.lead}>{step===0?'Quelques informations suffisent pour personnaliser ton expérience. Tu gardes le contrôle de tes données.':'Tu pourras modifier ce choix à tout moment. MetaBloom ne pose aucun diagnostic.'}</Text>{step===0?<><Text style={s.inputLabel}>Comment souhaites-tu être appelé·e ?</Text><View style={s.inputRow}><TextInput accessibilityLabel="Prénom ou pseudonyme" placeholder="Prénom ou pseudonyme" value={profile.name} onChangeText={(name)=>setProfile({...profile,name})} style={s.input}/></View><Text style={s.inputLabel}>Ton objectif principal</Text><View style={s.inputRow}><TextInput accessibilityLabel="Objectif principal" value={profile.goal} onChangeText={(goal)=>setProfile({...profile,goal})} style={s.input}/></View></>:<View style={{gap:10}}>{journeys.map(j=><Pressable key={j} accessibilityRole="radio" accessibilityState={{selected:profile.journey===j}} onPress={()=>setProfile({...profile,journey:j})} style={[s.choice,profile.journey===j&&s.choiceActive]}><Text style={s.choiceEmoji}>{j==='Prévention'?'🌱':j==='Perte de poids'?'⚖️':j==='Diabète type 2'?'💙':'✨'}</Text><View style={{flex:1}}><Text style={s.tipTitle}>{j}</Text><Text style={s.body}>Conseils éducatifs et habitudes adaptés</Text></View><Text style={s.check}>{profile.journey===j?'✓':''}</Text></Pressable>)}</View>}<Card style={s.safety}><Text style={s.tipTag}>À SAVOIR</Text><Text style={s.body}>MetaBloom est un outil de bien-être. Il ne remplace pas un professionnel de santé et ne modifie jamais un traitement.</Text></Card><Pressable disabled={step===0&&!profile.name.trim()} onPress={()=>step===0?setStep(1):setProfile({...profile,complete:true})} style={[s.primary,step===0&&!profile.name.trim()&&{opacity:.4}]}><Text style={s.primaryText}>{step===0?'Continuer':'Accepter et commencer'}</Text></Pressable>{step===1&&<Pressable onPress={()=>setStep(0)} style={s.secondary}><Text style={s.secondaryText}>Retour</Text></Pressable>}</ScrollView></SafeAreaView>}
function Header({eyebrow,title,right}:any){return <View style={s.header}><View><Text style={s.eyebrow}>{eyebrow}</Text><Text style={s.title}>{title}</Text></View>{right}</View>}
function Today({name,score,water,setWater,steps,setSteps,go}:any){const date=new Intl.DateTimeFormat('fr-FR',{weekday:'long',day:'numeric',month:'long'}).format(new Date()).toUpperCase();return <ScrollView contentContainerStyle={s.page} showsVerticalScrollIndicator={false}>
<Header eyebrow={date} title={`Bonjour ${name} 👋`} right={<View style={s.avatar}><Text style={s.avatarText}>{name[0]?.toUpperCase()}</Text></View>}/>
<LinearGradient colors={['#1d6b50','#2d8063']} style={s.hero}><View style={{flex:1}}><Pill>Aujourdhui</Pill><Text style={s.heroTitle}>Chaque petit pas compte.</Text><Text style={s.heroText}>Tu avances à ton rythme, sans pression. Encore une action douce pour compléter ta journée.</Text><Pressable onPress={()=>go('journal')} style={s.heroButton}><Text style={s.heroButtonText}>Ajouter une donnée </Text></Pressable></View><View style={s.ring}><Text style={s.ringBig}>{score}</Text><Text style={s.ringSmall}>/ 100</Text></View></LinearGradient>
<View style={s.sectionHead}><Text style={s.h2}>Mes essentiels</Text><Text style={s.caption}>Objectifs ajustables</Text></View>
<View style={s.grid}><Metric emoji="💧" label="Hydratation" value={`${water/1000} L`} sub="sur 2 L" onPress={()=>setWater((x:number)=>Math.min(x+250,2500))}/><Metric emoji="👟" label="Mouvement" value={steps.toLocaleString('fr-FR')} sub="sur 8 000 pas" onPress={()=>setSteps((x:number)=>x+500)}/><Metric emoji="🥗" label="Repas équilibrés" value="2 / 3" sub="très bien"/><Metric emoji="🌙" label="Sommeil" value="7 h 12" sub="objectif 8 h"/></View>
<Card style={s.tip}><View style={s.tipIcon}><Text style={{fontSize:22}}>☀️</Text></View><View style={{flex:1}}><Text style={s.tipTag}>PETIT CONSEIL DU JOUR</Text><Text style={s.tipTitle}>Une marche de 10 minutes après le repas ?</Text><Text style={s.body}>Un peu de mouvement aide à se sentir mieux. Adapte toujours leffort à ta situation.</Text></View></Card>
<Text style={s.disclaimer}>MetaBloom accompagne vos habitudes. Il ne remplace ni un diagnostic, ni un professionnel de santé.</Text>
</ScrollView>}
function Journal({weight,setWeight,glucose,setGlucose,glucoseUnit,water,setWater,steps,setSteps,sleep,setSleep,meals,setMeals,mood,setMood,save,saved}:any){return <ScrollView contentContainerStyle={s.page} keyboardShouldPersistTaps="handled"><Header eyebrow="SUIVI COMPLET" title="Mon journal" right={<Pill> Prêt à synchroniser</Pill>}/><Text style={s.lead}>Note ce qui compte pour toi. Rien nest obligatoire.</Text>
<Card><Text style={s.h2}>Mesures du jour</Text><Text style={s.inputLabel}>Poids <Text style={s.optional}>facultatif</Text></Text><View style={s.inputRow}><TextInput accessibilityLabel="Poids en kilogrammes" value={weight} onChangeText={setWeight} keyboardType="decimal-pad" style={s.input}/><Text style={s.unit}>kg</Text></View><Text style={s.inputLabel}>Glycémie <Text style={s.optional}>facultatif · saisie manuelle</Text></Text><View style={s.inputRow}><TextInput accessibilityLabel="Glycémie" value={glucose} onChangeText={setGlucose} keyboardType="decimal-pad" style={s.input}/><Text style={s.unit}>{glucoseUnit}</Text></View><Text style={s.helper}>Ces valeurs servent uniquement à ton suivi personnel. Aucune interprétation médicale nest fournie.</Text></Card>
<Card><Text style={s.h2}>Habitudes</Text><Stepper icon="💧" label="Eau" value={`${water} ml`} minus={()=>setWater(Math.max(0,water-250))} plus={()=>setWater(water+250)}/><Stepper icon="👟" label="Pas" value={steps.toLocaleString('fr-FR')} minus={()=>setSteps(Math.max(0,steps-500))} plus={()=>setSteps(steps+500)}/></Card>
<Card><Text style={s.h2}>Bien-être</Text><Text style={s.inputLabel}>Sommeil</Text><View style={s.inputRow}><TextInput accessibilityLabel="Durée de sommeil" value={sleep} onChangeText={setSleep} keyboardType="decimal-pad" style={s.input}/><Text style={s.unit}>heures</Text></View><Text style={s.inputLabel}>Repas équilibrés</Text><View style={s.segment}>{[0,1,2,3].map(n=><Pressable accessibilityLabel={`${n} repas équilibrés`} key={n} onPress={()=>setMeals(n)} style={[s.segmentItem,meals===n&&s.segmentActive]}><Text style={[s.segmentText,meals===n&&{color:C.white}]}>{n}</Text></Pressable>)}</View><Text style={s.inputLabel}>Humeur</Text><View style={s.segment}>{['😔','😐','🙂','😄'].map(x=><Pressable accessibilityLabel={`Humeur ${x}`} key={x} onPress={()=>setMood(x)} style={[s.segmentItem,mood===x&&s.segmentActive]}><Text style={{fontSize:21}}>{x}</Text></Pressable>)}</View></Card>
<Card style={s.scan}><Text style={s.tipTag}>REPAS</Text><Text style={s.tipTitle}>📷 Scanner ou photographier un repas</Text><Text style={s.body}>La confirmation des aliments restera toujours nécessaire. Lanalyse automatique arrivera avec une base nutritionnelle validée.</Text></Card>
<Pressable onPress={save} style={({pressed})=>[s.primary,pressed&&{opacity:.8}]}><Text style={s.primaryText}>{saved?'✓ Journée enregistrée':'Enregistrer ma journée'}</Text></Pressable>
</ScrollView>}
function Stepper({icon,label,value,minus,plus}:any){return <View style={s.stepper}><Text style={{fontSize:24}}>{icon}</Text><View style={{flex:1}}><Text style={s.metricLabel}>{label}</Text><Text style={s.stepValue}>{value}</Text></View><Pressable onPress={minus} style={s.stepBtn}><Text style={s.stepBtnText}></Text></Pressable><Pressable onPress={plus} style={s.stepBtn}><Text style={s.stepBtnText}>+</Text></Pressable></View>}
function Progress({entries}:any){const recent=entries.slice(-7);const min=Math.min(...recent.map((e:Entry)=>e.weight)); const max=Math.max(...recent.map((e:Entry)=>e.weight));const avg=(key:'water'|'steps')=>Math.round(recent.reduce((a:number,e:Entry)=>a+e[key],0)/recent.length);const delta=(recent.at(-1).weight-recent[0].weight).toFixed(1).replace('.',',');return <ScrollView contentContainerStyle={s.page}><Header eyebrow="7 DERNIERS JOURS" title="Mes progrès" right={<Pill tone="coral">{Number(delta)<=0?'↘':'↗'} {Math.abs(Number(delta))} kg</Pill>}/><Text style={s.lead}>Observe les tendances, pas la perfection.</Text><Card><View style={s.sectionHead}><View><Text style={s.metricLabel}>Tendance de poids</Text><Text style={s.bigNumber}>{recent.at(-1).weight} <Text style={s.bigUnit}>kg</Text></Text></View><Pill>Données dynamiques</Pill></View><View style={s.chart}>{recent.map((e:Entry,i:number)=><View key={`${e.date}-${i}`} style={s.barCol}><Text style={s.barValue}>{e.weight}</Text><View style={[s.bar,{height:58+((max-e.weight)/(max-min||1))*55}]}/><Text style={s.barLabel}>{e.date}</Text></View>)}</View></Card>
<View style={s.grid}><Card style={s.stat}><Text style={s.statIcon}>💧</Text><Text style={s.bigNumber}>{(avg('water')/1000).toFixed(1).replace('.',',')} L</Text><Text style={s.body}>moyenne deau</Text></Card><Card style={s.stat}><Text style={s.statIcon}>👟</Text><Text style={s.bigNumber}>{avg('steps').toLocaleString('fr-FR')}</Text><Text style={s.body}>pas en moyenne</Text></Card></View>
<Card style={s.insight}><Text style={s.tipTag}>À RETENIR</Text><Text style={s.tipTitle}>Une semaine régulière 🌱</Text><Text style={s.body}>Tu as bougé au moins 6 000 pas chaque jour. Cette régularité compte davantage quune journée parfaite.</Text></Card></ScrollView>}
function Coach({journey}:any){const lessons=[['🥗','Composer une assiette rassasiante','4 min'],['🚶','Bouger après un repas, à ton rythme','3 min'],['🧠','Sortir du tout ou rien','5 min']];return <ScrollView contentContainerStyle={s.page}><Header eyebrow="PARCOURS PERSONNALISÉ" title="Mon coach" right={<Pill> {journey}</Pill>}/><LinearGradient colors={['#563c82','#7659a6']} style={s.coachHero}><Text style={s.heroTitle}>Ta semaine, sans pression.</Text><Text style={s.heroText}>3 petites étapes pédagogiques. Aucun conseil ne modifie ton traitement.</Text><ProgressBar value={42} color={C.yellow}/><Text style={s.coachProgress}>42 % du parcours hebdomadaire</Text></LinearGradient><Text style={s.h2}>Micro-leçons</Text>{lessons.map((x,i)=><Card key={x[1]} style={s.lesson}><View style={s.lessonIcon}><Text style={{fontSize:24}}>{x[0]}</Text></View><View style={{flex:1}}><Text style={s.tipTitle}>{x[1]}</Text><Text style={s.body}>{x[2]} · contenu éducatif</Text></View><Text style={s.chevron}></Text></Card>)}<Card style={s.insight}><Text style={s.tipTag}>COACHING DU JOUR</Text><Text style={s.tipTitle}>Choisis une action si petite quelle paraît facile.</Text><Text style={s.body}>Par exemple : préparer une bouteille deau ou marcher cinq minutes. Tu décides selon ton énergie et les recommandations de ton soignant.</Text></Card><Card><Text style={s.tipTag}>CONNEXIONS SANTÉ</Text><Text style={s.tipTitle}>Apple Santé & Health Connect</Text><Text style={s.body}>Architecture prête pour importer pas, activité et sommeil avec permission explicite. Lactivation nécessite un build natif et les comptes développeur Apple/Google.</Text></Card></ScrollView>}
function Profile({profile,setProfile,entries,reset}:any){const exportData=async()=>{const csv=['date,poids,eau_ml,pas,glycemie',...entries.map((e:Entry)=>[e.iso||e.date,e.weight,e.water,e.steps,e.glucose||''].join(','))].join('\n');await Share.share({title:'Export MetaBloom',message:csv})};return <ScrollView contentContainerStyle={s.page}><Header eyebrow="MON ESPACE" title="Profil & sécurité"/><Card style={s.profile}><View style={s.avatarLarge}><Text style={s.avatarLargeText}>{profile.name[0]?.toUpperCase()}</Text></View><View><Text style={s.tipTitle}>{profile.name}</Text><Text style={s.body}>{profile.goal} · {profile.journey}</Text></View></Card><Text style={s.h2}>Préférences</Text><Card style={s.setting}><Text style={{fontSize:22}}>🩸</Text><View style={{flex:1}}><Text style={s.metricLabel}>Unité glycémie</Text><Text style={s.body}>{profile.glucoseUnit}</Text></View><Pressable accessibilityLabel="Changer lunité de glycémie" onPress={()=>setProfile({...profile,glucoseUnit:profile.glucoseUnit==='mg/dL'?'mmol/L':'mg/dL'})} style={s.smallButton}><Text style={s.smallButtonText}>Changer</Text></Pressable></Card><Card style={s.setting}><Text style={{fontSize:22}}>🔔</Text><View style={{flex:1}}><Text style={s.metricLabel}>Rappels doux</Text><Text style={s.body}>{profile.reminders?'Activés à 19 h':'Désactivés'}</Text></View><Switch accessibilityLabel="Activer les rappels" value={profile.reminders} onValueChange={(reminders)=>setProfile({...profile,reminders})} trackColor={{true:C.green}}/></Card><Pressable onPress={exportData} style={s.secondaryCard}><Text style={s.secondaryCardText}> Exporter mes données CSV</Text></Pressable><Card style={s.safety}><Text style={s.tipTag}>SÉCURITÉ & CONFIDENTIALITÉ</Text><Text style={s.tipTitle}>Contrôle, export et effacement.</Text><Text style={s.body}>Le backend auto-hébergeable fournit authentification, consentement, isolation des comptes, export et suppression. Une validation RGPD/HDS/MDR reste obligatoire avant production.</Text></Card><Pressable onPress={()=>Alert.alert('Effacer les données locales ?','Cette action réinitialise lapplication.',[{text:'Annuler'},{text:'Effacer',style:'destructive',onPress:reset}])} style={s.danger}><Text style={s.dangerText}>Effacer mes données locales</Text></Pressable><Text style={s.disclaimer}>En cas de malaise ou durgence, contactez immédiatement les services durgence. MetaBloom ne fournit pas de conseil thérapeutique.</Text></ScrollView>}
const s=StyleSheet.create({safe:{flex:1,backgroundColor:C.cream},shell:{flex:1,width:'100%',alignSelf:'center'},page:{padding:20,paddingTop:26,paddingBottom:120,gap:16},header:{flexDirection:'row',justifyContent:'space-between',alignItems:'center',marginBottom:4},eyebrow:{fontSize:11,fontWeight:'800',letterSpacing:1.4,color:C.green},title:{fontSize:28,fontWeight:'800',color:C.ink,letterSpacing:-.7,marginTop:4},avatar:{width:44,height:44,borderRadius:22,backgroundColor:C.yellow,alignItems:'center',justifyContent:'center'},avatarText:{fontWeight:'800',color:C.ink,fontSize:17},hero:{borderRadius:28,padding:22,minHeight:230,flexDirection:'row',alignItems:'center',gap:14,overflow:'hidden'},heroTitle:{fontSize:27,lineHeight:32,fontWeight:'800',color:C.white,marginTop:18,maxWidth:360},heroText:{fontSize:14,lineHeight:21,color:'#dcebe4',marginTop:8,maxWidth:400},heroButton:{backgroundColor:C.white,borderRadius:14,paddingVertical:13,paddingHorizontal:16,alignSelf:'flex-start',marginTop:18},heroButtonText:{color:C.green,fontWeight:'800'},ring:{width:96,height:96,borderWidth:8,borderColor:C.yellow,borderRadius:50,alignItems:'center',justifyContent:'center',backgroundColor:'#ffffff13'},ringBig:{color:C.white,fontWeight:'900',fontSize:28},ringSmall:{color:'#dcebe4',fontSize:11},pill:{borderRadius:99,paddingHorizontal:11,paddingVertical:6,alignSelf:'flex-start'},pillText:{fontSize:11,fontWeight:'800'},sectionHead:{flexDirection:'row',justifyContent:'space-between',alignItems:'center'},h2:{fontSize:19,fontWeight:'800',color:C.ink},caption:{fontSize:12,color:C.muted},grid:{flexDirection:'row',flexWrap:'wrap',gap:12},metric:{backgroundColor:C.white,borderRadius:20,padding:17,width:'48%',minWidth:150,borderWidth:1,borderColor:C.line},metricEmoji:{fontSize:24,marginBottom:12},metricLabel:{fontSize:12,fontWeight:'700',color:C.muted},metricValue:{fontSize:22,fontWeight:'900',color:C.ink,marginTop:3},metricSub:{fontSize:11,color:C.muted,marginTop:3},card:{backgroundColor:C.white,borderRadius:22,padding:19,borderWidth:1,borderColor:C.line},tip:{flexDirection:'row',gap:14,backgroundColor:'#fffaf0'},tipIcon:{width:46,height:46,borderRadius:14,backgroundColor:C.yellow,alignItems:'center',justifyContent:'center'},tipTag:{fontSize:10,fontWeight:'900',letterSpacing:1.1,color:C.green,marginBottom:6},tipTitle:{fontSize:17,fontWeight:'800',color:C.ink,marginBottom:5},body:{fontSize:13,lineHeight:19,color:C.muted},disclaimer:{fontSize:11,lineHeight:17,textAlign:'center',color:C.muted,paddingHorizontal:16},nav:{position:'absolute',bottom:0,left:0,right:0,height:82,backgroundColor:'#fffffffa',borderTopWidth:1,borderColor:C.line,flexDirection:'row',paddingTop:9},navItem:{flex:1,alignItems:'center',justifyContent:'center',gap:3},navIcon:{fontSize:22,color:'#829087'},navLabel:{fontSize:9,fontWeight:'700',color:'#829087'},active:{color:C.green},lead:{fontSize:15,lineHeight:22,color:C.muted,marginTop:-8},inputLabel:{fontSize:13,fontWeight:'800',color:C.ink,marginTop:17,marginBottom:7},optional:{fontWeight:'500',color:C.muted},inputRow:{height:56,borderWidth:1,borderColor:C.line,borderRadius:14,flexDirection:'row',alignItems:'center',paddingHorizontal:15,backgroundColor:C.cream},input:{flex:1,fontSize:18,fontWeight:'800',color:C.ink,outlineStyle:'none'} as any,unit:{fontWeight:'700',color:C.muted},helper:{fontSize:11,lineHeight:16,color:C.muted,marginTop:12},stepper:{flexDirection:'row',alignItems:'center',gap:12,paddingVertical:13,borderBottomWidth:1,borderBottomColor:C.line},stepValue:{fontSize:17,fontWeight:'800',color:C.ink,marginTop:2},stepBtn:{width:44,height:44,borderRadius:13,backgroundColor:C.mint,alignItems:'center',justifyContent:'center'},stepBtnText:{fontSize:24,color:C.green,fontWeight:'700'},primary:{backgroundColor:C.green,borderRadius:16,padding:17,alignItems:'center'},primaryText:{color:C.white,fontSize:15,fontWeight:'900'},track:{height:8,borderRadius:4,backgroundColor:'#ffffff3d',overflow:'hidden'},fill:{height:8,borderRadius:4},bigNumber:{fontSize:28,fontWeight:'900',color:C.ink,marginTop:4},bigUnit:{fontSize:14,color:C.muted},chart:{height:160,flexDirection:'row',alignItems:'flex-end',justifyContent:'space-between',marginTop:20},barCol:{flex:1,alignItems:'center'},bar:{width:22,backgroundColor:C.green,borderRadius:8,marginVertical:6},barValue:{fontSize:9,color:C.muted},barLabel:{fontSize:10,color:C.muted},stat:{width:'48%',minWidth:150},statIcon:{fontSize:22},insight:{backgroundColor:C.mint},profile:{flexDirection:'row',alignItems:'center',gap:15},avatarLarge:{width:62,height:62,borderRadius:22,backgroundColor:C.yellow,alignItems:'center',justifyContent:'center'},avatarLargeText:{fontSize:23,fontWeight:'900',color:C.ink},setting:{flexDirection:'row',alignItems:'center',gap:13,padding:15},chevron:{fontSize:28,color:C.muted},safety:{backgroundColor:C.mint},danger:{padding:16,alignItems:'center'},dangerText:{color:'#a23d33',fontWeight:'800'},onboard:{padding:24,paddingTop:54,paddingBottom:50,gap:16,maxWidth:560,width:'100%',alignSelf:'center'},logo:{width:58,height:58,borderRadius:20,backgroundColor:C.green,alignItems:'center',justifyContent:'center',marginBottom:8},logoText:{color:C.white,fontSize:28,fontWeight:'900'},onboardTitle:{fontSize:34,lineHeight:40,fontWeight:'900',color:C.ink,letterSpacing:-1},choice:{backgroundColor:C.white,borderWidth:1,borderColor:C.line,borderRadius:18,padding:15,flexDirection:'row',alignItems:'center',gap:12},choiceActive:{borderWidth:2,borderColor:C.green,backgroundColor:C.mint},choiceEmoji:{fontSize:24},check:{fontSize:18,fontWeight:'900',color:C.green},secondary:{padding:14,alignItems:'center'},secondaryText:{color:C.green,fontWeight:'800'},segment:{flexDirection:'row',gap:8},segmentItem:{height:46,flex:1,borderRadius:13,backgroundColor:C.cream,alignItems:'center',justifyContent:'center',borderWidth:1,borderColor:C.line},segmentActive:{backgroundColor:C.green,borderColor:C.green},segmentText:{fontWeight:'800',color:C.ink},scan:{backgroundColor:'#fff8ed',borderStyle:'dashed',borderColor:'#d8a960'},coachHero:{borderRadius:26,padding:22,gap:14},coachProgress:{fontSize:12,fontWeight:'700',color:'#eee8f7'},lesson:{flexDirection:'row',alignItems:'center',gap:12},lessonIcon:{width:48,height:48,borderRadius:15,backgroundColor:C.cream,alignItems:'center',justifyContent:'center'},smallButton:{paddingHorizontal:12,paddingVertical:9,borderRadius:11,backgroundColor:C.mint},smallButtonText:{color:C.green,fontWeight:'800',fontSize:12},secondaryCard:{backgroundColor:C.white,borderRadius:16,borderWidth:1,borderColor:C.green,padding:16,alignItems:'center'},secondaryCardText:{color:C.green,fontWeight:'900'}});