import React, { useEffect, useMemo, useRef, useState } from 'react'; import { Alert, KeyboardAvoidingView, Modal, Platform, Pressable, SafeAreaView, ScrollView, StyleSheet, Text, TextInput, View, Image, } from 'react-native'; import AsyncStorage from '@react-native-async-storage/async-storage'; import * as ImagePicker from 'expo-image-picker'; import { StatusBar } from 'expo-status-bar'; const STORAGE_KEY = 'luggage-list:v2'; const TAB_BAR_HEIGHT = 72; const ITEM_STATUSES = ['packed', 'unpacked', 'lost', 'left-behind', 'lent-to']; const ITEM_PLACEMENTS = ['suitcase', 'backpack', 'with-user', 'other']; const STATUS_COLORS = { packed: '#22c55e', unpacked: '#64748b', lost: '#ef4444', 'left-behind': '#f59e0b', 'lent-to': '#8b5cf6', }; const emptyData = { trips: [], itemsByTrip: {}, checkupsByTrip: {}, defaultTemplateTripId: null, }; const emptyTripForm = () => ({ name: '', location: '', startDate: todayYMD(), endDate: todayYMD(), imageUri: '', copyDefaultTemplate: true, setAsDefaultTemplate: false, }); const emptyItemForm = () => ({ id: null, name: '', description: '', category: '', status: 'unpacked', placement: 'suitcase', lentTo: '', imageUri: '', }); function makeId(prefix) { return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`; } function todayYMD() { const now = new Date(); return `${now.getFullYear()}-${`${now.getMonth() + 1}`.padStart(2, '0')}-${`${now.getDate()}`.padStart(2, '0')}`; } function parseYMD(value) { if (!value || !/^\d{4}-\d{2}-\d{2}$/.test(value)) return null; const d = new Date(`${value}T00:00:00`); return Number.isNaN(d.getTime()) ? null : d; } function findActiveTripId(trips) { const today = parseYMD(todayYMD()); if (!today) return null; const active = trips.find((trip) => { const start = parseYMD(trip.startDate); const end = parseYMD(trip.endDate); return !!start && !!end && today >= start && today <= end; }); return active?.id || null; } function Field({ label, children }) { return ( {label} {children} ); } function ChipGroup({ options, value, onChange }) { return ( {options.map((option) => { const active = value === option; return ( onChange(option)}> {option} ); })} ); } function BottomTab({ current, onChange }) { const tabs = [ { key: 'trips', label: 'Trips' }, { key: 'items', label: 'Items' }, { key: 'checkup', label: 'Check-Up' }, { key: 'history', label: 'History' }, ]; return ( {tabs.map((tab) => { const active = current === tab.key; return ( onChange(tab.key)} style={styles.tabItem}> {tab.label} ); })} ); } export default function App() { const scrollRef = useRef(null); const [loaded, setLoaded] = useState(false); const [tab, setTab] = useState('trips'); const [data, setData] = useState(emptyData); const [selectedTripId, setSelectedTripId] = useState(null); const [tripForm, setTripForm] = useState(emptyTripForm()); const [itemModalVisible, setItemModalVisible] = useState(false); const [itemForm, setItemForm] = useState(emptyItemForm()); const [checkupSession, setCheckupSession] = useState([]); const [checkupFixModalVisible, setCheckupFixModalVisible] = useState(false); const [checkupFixTargetId, setCheckupFixTargetId] = useState(null); const [checkupFixForm, setCheckupFixForm] = useState({ status: 'unpacked', placement: 'suitcase', lentTo: '', updateMasterList: false, }); const [selectedCheckupId, setSelectedCheckupId] = useState(null); const selectedTrip = useMemo(() => data.trips.find((trip) => trip.id === selectedTripId) || null, [data.trips, selectedTripId]); const selectedTripItems = useMemo(() => { if (!selectedTripId) return []; return data.itemsByTrip[selectedTripId] || []; }, [data.itemsByTrip, selectedTripId]); const selectedTripCheckups = useMemo(() => { if (!selectedTripId) return []; return (data.checkupsByTrip[selectedTripId] || []).slice().sort((a, b) => b.createdAt - a.createdAt); }, [data.checkupsByTrip, selectedTripId]); const templateTrip = useMemo( () => data.trips.find((trip) => trip.id === data.defaultTemplateTripId) || null, [data.trips, data.defaultTemplateTripId] ); useEffect(() => { (async () => { try { const raw = await AsyncStorage.getItem(STORAGE_KEY); if (raw) { const parsed = JSON.parse(raw); setData({ ...emptyData, ...parsed }); } } catch { Alert.alert('Error', 'Could not load local data.'); } finally { setLoaded(true); } })(); }, []); useEffect(() => { if (!loaded) return; AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(data)).catch(() => { Alert.alert('Error', 'Could not save local data.'); }); }, [data, loaded]); useEffect(() => { if (!loaded) return; if (!data.trips.length) { setSelectedTripId(null); return; } if (selectedTripId && data.trips.some((trip) => trip.id === selectedTripId)) { return; } const activeTripId = findActiveTripId(data.trips); setSelectedTripId(activeTripId || data.trips[0].id); }, [data.trips, selectedTripId, loaded]); useEffect(() => { if (tab !== 'checkup') return; createFreshCheckupSession(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedTripId, selectedTripItems.length]); function chooseTrip(tripId) { setSelectedTripId(tripId); } function updateTripForm(field, value) { setTripForm((prev) => ({ ...prev, [field]: value })); } function updateItemForm(field, value) { setItemForm((prev) => ({ ...prev, [field]: value })); } function openAddItemModal() { setItemForm(emptyItemForm()); setItemModalVisible(true); } function openEditItemModal(item) { setItemForm({ id: item.id, name: item.name || '', description: item.description || '', category: item.category || '', status: item.status || 'unpacked', placement: item.placement || 'suitcase', lentTo: item.lentTo || '', imageUri: item.imageUri || '', }); setItemModalVisible(true); } async function pickImage(setter) { const perm = await ImagePicker.requestMediaLibraryPermissionsAsync(); if (!perm.granted) { Alert.alert('Permission needed', 'Allow gallery access to select images.'); return; } const result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ImagePicker.MediaTypeOptions.Images, allowsEditing: false, quality: 0.85, }); if (!result.canceled && result.assets?.[0]?.uri) { setter(result.assets[0].uri); } } function createTrip() { if (!tripForm.name.trim()) { Alert.alert('Missing name', 'Trip name is required.'); return; } const start = parseYMD(tripForm.startDate); const end = parseYMD(tripForm.endDate); if (!start || !end) { Alert.alert('Invalid dates', 'Use YYYY-MM-DD format.'); return; } if (start > end) { Alert.alert('Invalid dates', 'Start date cannot be after end date.'); return; } const now = Date.now(); const tripId = makeId('trip'); setData((prev) => { const next = { ...prev, trips: [ ...prev.trips, { id: tripId, name: tripForm.name.trim(), location: tripForm.location.trim(), startDate: tripForm.startDate, endDate: tripForm.endDate, imageUri: tripForm.imageUri, createdAt: now, updatedAt: now, }, ], itemsByTrip: { ...prev.itemsByTrip, [tripId]: [] }, checkupsByTrip: { ...prev.checkupsByTrip, [tripId]: [] }, }; if (tripForm.copyDefaultTemplate && prev.defaultTemplateTripId) { const templateItems = prev.itemsByTrip[prev.defaultTemplateTripId] || []; next.itemsByTrip[tripId] = templateItems.map((item) => ({ ...item, id: makeId('item'), createdAt: now, updatedAt: now, })); } if (tripForm.setAsDefaultTemplate) { next.defaultTemplateTripId = tripId; } return next; }); setSelectedTripId(tripId); setTripForm(emptyTripForm()); } function setTripAsTemplate(tripId) { setData((prev) => ({ ...prev, defaultTemplateTripId: tripId })); } function deleteTrip(tripId) { Alert.alert('Delete trip?', 'Trip items and check-up history will also be deleted.', [ { text: 'Cancel', style: 'cancel' }, { text: 'Delete', style: 'destructive', onPress: () => { setData((prev) => { const trips = prev.trips.filter((trip) => trip.id !== tripId); const itemsByTrip = { ...prev.itemsByTrip }; const checkupsByTrip = { ...prev.checkupsByTrip }; delete itemsByTrip[tripId]; delete checkupsByTrip[tripId]; return { ...prev, trips, itemsByTrip, checkupsByTrip, defaultTemplateTripId: prev.defaultTemplateTripId === tripId ? null : prev.defaultTemplateTripId, }; }); }, }, ]); } function saveItemFromModal() { if (!selectedTripId) { Alert.alert('No trip selected', 'Please select or create a trip first.'); return; } if (!itemForm.name.trim()) { Alert.alert('Missing name', 'Item name is required.'); return; } const now = Date.now(); setData((prev) => { const items = prev.itemsByTrip[selectedTripId] || []; const existingCreatedAt = items.find((item) => item.id === itemForm.id)?.createdAt || now; const nextItem = { id: itemForm.id || makeId('item'), name: itemForm.name.trim(), description: itemForm.description.trim(), category: itemForm.category.trim(), status: itemForm.status, placement: itemForm.placement, lentTo: itemForm.status === 'lent-to' ? itemForm.lentTo.trim() : '', imageUri: itemForm.imageUri, createdAt: existingCreatedAt, updatedAt: now, }; const nextItems = itemForm.id ? items.map((item) => (item.id === itemForm.id ? nextItem : item)) : [...items, nextItem]; return { ...prev, itemsByTrip: { ...prev.itemsByTrip, [selectedTripId]: nextItems, }, }; }); setItemModalVisible(false); setItemForm(emptyItemForm()); } function deleteItem(itemId) { setData((prev) => { const items = prev.itemsByTrip[selectedTripId] || []; return { ...prev, itemsByTrip: { ...prev.itemsByTrip, [selectedTripId]: items.filter((item) => item.id !== itemId), }, }; }); } function createFreshCheckupSession() { if (!selectedTripItems.length) { setCheckupSession([]); return; } const fresh = selectedTripItems.map((item) => ({ itemId: item.id, name: item.name, category: item.category, current: { status: item.status, placement: item.placement, lentTo: item.lentTo || '', }, confirmed: false, })); setCheckupSession(fresh); } function answerCheckupYes(itemId) { setCheckupSession((prev) => prev.map((entry) => (entry.itemId === itemId ? { ...entry, confirmed: true } : entry))); } function openFixModal(itemId) { const entry = checkupSession.find((x) => x.itemId === itemId); if (!entry) return; setCheckupFixTargetId(itemId); setCheckupFixForm({ status: entry.current.status || 'unpacked', placement: entry.current.placement || 'suitcase', lentTo: entry.current.lentTo || '', updateMasterList: false, }); setCheckupFixModalVisible(true); } function saveFixModal() { if (!checkupFixTargetId) return; const targetId = checkupFixTargetId; const patch = { status: checkupFixForm.status, placement: checkupFixForm.placement, lentTo: checkupFixForm.status === 'lent-to' ? checkupFixForm.lentTo.trim() : '', }; setCheckupSession((prev) => prev.map((entry) => entry.itemId === targetId ? { ...entry, current: patch, confirmed: true, } : entry ) ); if (checkupFixForm.updateMasterList && selectedTripId) { setData((prev) => { const items = prev.itemsByTrip[selectedTripId] || []; return { ...prev, itemsByTrip: { ...prev.itemsByTrip, [selectedTripId]: items.map((item) => item.id === targetId ? { ...item, status: patch.status, placement: patch.placement, lentTo: patch.lentTo, updatedAt: Date.now(), } : item ), }, }; }); } setCheckupFixModalVisible(false); setCheckupFixTargetId(null); } function saveCheckup() { if (!selectedTripId) { Alert.alert('No trip selected', 'Please select a trip first.'); return; } if (!checkupSession.length) { Alert.alert('No items', 'Add items before creating a check-up.'); return; } const pending = checkupSession.filter((entry) => !entry.confirmed).length; if (pending > 0) { Alert.alert('Incomplete', `Please confirm all items first (${pending} remaining).`); return; } const snapshot = checkupSession.map((entry) => ({ itemId: entry.itemId, name: entry.name, category: entry.category, status: entry.current.status, placement: entry.current.placement, lentTo: entry.current.status === 'lent-to' ? entry.current.lentTo : '', })); setData((prev) => { const existing = prev.checkupsByTrip[selectedTripId] || []; return { ...prev, checkupsByTrip: { ...prev.checkupsByTrip, [selectedTripId]: [ ...existing, { id: makeId('checkup'), createdAt: Date.now(), snapshot, }, ], }, }; }); Alert.alert('Saved', 'Check-up snapshot saved.'); createFreshCheckupSession(); } function statusAccent(status) { return STATUS_COLORS[status] || '#64748b'; } function focusToEnd() { setTimeout(() => { scrollRef.current?.scrollToEnd?.({ animated: true }); }, 80); } if (!loaded) { return ( Loading local data... ); } return ( {data.trips.length ? ( data.trips .slice() .sort((a, b) => b.startDate.localeCompare(a.startDate)) .map((trip) => { const active = selectedTripId === trip.id; return ( chooseTrip(trip.id)}> {trip.name} {trip.startDate} ); }) ) : ( Create your first trip to start. )} {tab === 'trips' && ( Trips updateTripForm('name', v)} placeholder="Summer Weekend" placeholderTextColor="#6b7280" onFocus={focusToEnd} /> updateTripForm('location', v)} placeholder="Berlin" placeholderTextColor="#6b7280" onFocus={focusToEnd} /> updateTripForm('startDate', v)} placeholderTextColor="#6b7280" onFocus={focusToEnd} /> updateTripForm('endDate', v)} placeholderTextColor="#6b7280" onFocus={focusToEnd} /> pickImage((uri) => updateTripForm('imageUri', uri))}> {tripForm.imageUri ? 'Change trip image' : 'Add trip image'} {tripForm.imageUri ? : null} {templateTrip ? ( updateTripForm('copyDefaultTemplate', !tripForm.copyDefaultTemplate)}> {tripForm.copyDefaultTemplate ? '☑' : '☐'} Copy items from template ({templateTrip.name}) ) : null} updateTripForm('setAsDefaultTemplate', !tripForm.setAsDefaultTemplate)}> {tripForm.setAsDefaultTemplate ? '☑' : '☐'} Set as default template Create Trip {data.trips .slice() .sort((a, b) => b.startDate.localeCompare(a.startDate)) .map((trip) => ( {trip.name} {trip.location || 'No location'} · {trip.startDate} → {trip.endDate} {data.defaultTemplateTripId === trip.id ? 'Default template' : ' '} chooseTrip(trip.id)}> Select setTripAsTemplate(trip.id)}> Template deleteTrip(trip.id)}> Delete {trip.imageUri ? : null} ))} )} {tab === 'items' && ( Luggage Items + Add {!selectedTrip ? ( Select a trip first. ) : null} {selectedTripItems.length === 0 && selectedTrip ? No items yet. : null} {selectedTripItems.map((item) => ( {item.name} {item.category || 'uncategorized'} · {item.status} Location: {item.placement} {item.status === 'lent-to' && !!item.lentTo ? Lent to: {item.lentTo} : null} {!!item.description ? {item.description} : null} openEditItemModal(item)}> Edit deleteItem(item.id)}> Delete {item.imageUri ? : null} ))} )} {tab === 'checkup' && ( Check-Up Restart {checkupSession.length === 0 ? No items for this trip yet. : null} {checkupSession.map((entry) => ( {entry.name} {entry.category || 'uncategorized'} {entry.current.status} · {entry.current.placement} {entry.current.status === 'lent-to' && entry.current.lentTo ? ` · ${entry.current.lentTo}` : ''} answerCheckupYes(entry.itemId)}> Yes openFixModal(entry.itemId)}> No ))} {!!checkupSession.length && ( Save Check-Up Snapshot )} )} {tab === 'history' && ( History {selectedTripCheckups.length === 0 ? No check-ups saved yet. : null} {selectedTripCheckups.map((checkup) => ( setSelectedCheckupId((prev) => (prev === checkup.id ? null : checkup.id))}> {new Date(checkup.createdAt).toLocaleString()} {checkup.snapshot.length} items {selectedCheckupId === checkup.id ? 'Tap to collapse' : 'Tap to open'} {selectedCheckupId === checkup.id ? ( {checkup.snapshot.map((entry) => ( {entry.name} {entry.status} · {entry.placement} {entry.status === 'lent-to' && entry.lentTo ? ` · ${entry.lentTo}` : ''} ))} ) : null} ))} )} {itemForm.id ? 'Update Item' : 'Add Item'} setItemModalVisible(false)}> Close updateItemForm('name', v)} placeholder="Toothbrush" placeholderTextColor="#6b7280" /> updateItemForm('description', v)} placeholder="Optional" placeholderTextColor="#6b7280" /> updateItemForm('category', v)} placeholder="toiletries" placeholderTextColor="#6b7280" /> updateItemForm('status', v)} /> updateItemForm('placement', v)} /> {itemForm.status === 'lent-to' ? ( updateItemForm('lentTo', v)} placeholder="Person name" placeholderTextColor="#6b7280" /> ) : null} pickImage((uri) => updateItemForm('imageUri', uri))}> {itemForm.imageUri ? 'Change image' : 'Add image'} {!!itemForm.imageUri && } {itemForm.id ? 'Save Changes' : 'Add Item'} Update for this Check-Up setCheckupFixModalVisible(false)}> Close setCheckupFixForm((prev) => ({ ...prev, status: v }))} /> setCheckupFixForm((prev) => ({ ...prev, placement: v }))} /> {checkupFixForm.status === 'lent-to' ? ( setCheckupFixForm((prev) => ({ ...prev, lentTo: v }))} placeholder="Person name" placeholderTextColor="#6b7280" /> ) : null} setCheckupFixForm((prev) => ({ ...prev, updateMasterList: !prev.updateMasterList }))} > {checkupFixForm.updateMasterList ? '☑' : '☐'} Also update item in trip list Save ); } const styles = StyleSheet.create({ safe: { flex: 1, backgroundColor: '#090d12', }, flex: { flex: 1, }, content: { paddingHorizontal: 14, paddingTop: 12, paddingBottom: TAB_BAR_HEIGHT + 18, gap: 12, }, center: { flex: 1, alignItems: 'center', justifyContent: 'center', }, muted: { color: '#8793a5', }, tripPickerWrap: { marginBottom: 6, }, tripChipScroll: { gap: 8, paddingRight: 12, }, tripChip: { paddingHorizontal: 12, paddingVertical: 9, borderRadius: 12, backgroundColor: '#121923', borderWidth: 1, borderColor: '#1f2937', minWidth: 120, }, tripChipActive: { backgroundColor: '#1d2a3a', borderColor: '#60a5fa', }, tripChipText: { color: '#e2e8f0', fontWeight: '700', }, tripChipTextActive: { color: '#bfdbfe', }, tripChipSub: { color: '#64748b', fontSize: 12, marginTop: 2, }, tripChipSubActive: { color: '#93c5fd', }, section: { gap: 10, }, sectionTitle: { color: '#f1f5f9', fontSize: 18, fontWeight: '700', }, sectionRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', gap: 10, }, card: { backgroundColor: '#111827', borderRadius: 14, borderWidth: 1, borderColor: '#1f2937', padding: 12, gap: 8, }, cardActive: { borderColor: '#60a5fa', }, cardSoft: { backgroundColor: '#0f172a', borderRadius: 14, borderWidth: 1, borderColor: '#1e293b', padding: 12, gap: 10, }, cardRow: { flexDirection: 'row', gap: 10, }, cardTitle: { color: '#f8fafc', fontWeight: '700', fontSize: 15, }, cardMeta: { color: '#94a3b8', marginTop: 3, fontSize: 13, }, fieldWrap: { gap: 6, }, label: { color: '#cbd5e1', fontWeight: '600', }, input: { borderWidth: 1, borderColor: '#243244', borderRadius: 10, backgroundColor: '#0b1220', color: '#e5e7eb', paddingHorizontal: 10, paddingVertical: 11, }, chipGroup: { flexDirection: 'row', flexWrap: 'wrap', gap: 7, }, chip: { borderRadius: 999, paddingHorizontal: 11, paddingVertical: 7, borderWidth: 1, borderColor: '#273449', backgroundColor: '#0b1220', }, chipActive: { borderColor: '#60a5fa', backgroundColor: '#172338', }, chipText: { color: '#cbd5e1', fontWeight: '600', fontSize: 12, }, chipTextActive: { color: '#bfdbfe', }, primaryBtn: { marginTop: 4, borderRadius: 10, backgroundColor: '#2563eb', alignItems: 'center', paddingVertical: 11, paddingHorizontal: 12, }, primaryBtnTight: { borderRadius: 10, backgroundColor: '#2563eb', alignItems: 'center', paddingVertical: 8, paddingHorizontal: 12, }, primaryBtnText: { color: '#fff', fontWeight: '700', }, secondaryBtn: { marginTop: 4, borderRadius: 10, backgroundColor: '#1f2937', alignItems: 'center', paddingVertical: 11, paddingHorizontal: 12, }, secondaryBtnTight: { borderRadius: 10, backgroundColor: '#1f2937', alignItems: 'center', paddingVertical: 8, paddingHorizontal: 12, }, secondaryBtnText: { color: '#dbeafe', fontWeight: '700', }, inlineToggle: { marginTop: 2, }, inlineToggleText: { color: '#cbd5e1', }, stackButtons: { gap: 7, }, miniBtn: { backgroundColor: '#1e293b', borderRadius: 8, paddingVertical: 7, paddingHorizontal: 10, }, miniBtnDanger: { backgroundColor: '#3b1d22', borderRadius: 8, paddingVertical: 7, paddingHorizontal: 10, }, miniBtnText: { color: '#e2e8f0', fontWeight: '700', fontSize: 12, }, itemCard: { borderRadius: 14, backgroundColor: '#111827', borderWidth: 1, borderColor: '#1f2937', overflow: 'hidden', flexDirection: 'row', }, itemAccent: { width: 5, }, itemMain: { flex: 1, padding: 12, gap: 8, }, itemTitle: { color: '#f8fafc', fontWeight: '700', fontSize: 15, }, itemMeta: { color: '#94a3b8', marginTop: 2, fontSize: 13, }, answerRow: { marginTop: 8, flexDirection: 'row', alignItems: 'center', gap: 8, }, answerYes: { backgroundColor: '#163223', borderWidth: 1, borderColor: '#1f7a4e', borderRadius: 9, paddingHorizontal: 16, paddingVertical: 8, }, answerNo: { backgroundColor: '#3b1d22', borderWidth: 1, borderColor: '#7f1d1d', borderRadius: 9, paddingHorizontal: 16, paddingVertical: 8, }, answerText: { color: '#f8fafc', fontWeight: '700', }, answerStateDot: { width: 10, height: 10, borderRadius: 99, backgroundColor: '#475569', }, answerStateDotOn: { backgroundColor: '#22c55e', }, snapshotWrap: { marginTop: 8, borderTopWidth: 1, borderTopColor: '#1e293b', paddingTop: 8, gap: 7, }, snapshotRow: { paddingVertical: 3, }, snapshotTitle: { color: '#e2e8f0', fontWeight: '600', }, previewImage: { width: '100%', height: 150, borderRadius: 10, backgroundColor: '#111827', }, previewImageSmall: { width: '100%', height: 120, borderRadius: 10, backgroundColor: '#111827', }, tabBarWrap: { position: 'absolute', bottom: 0, left: 0, right: 0, paddingHorizontal: 10, paddingBottom: Platform.OS === 'ios' ? 14 : 8, backgroundColor: 'transparent', }, tabBar: { height: TAB_BAR_HEIGHT, borderRadius: 16, backgroundColor: '#0b1220', borderWidth: 1, borderColor: '#1f2937', flexDirection: 'row', alignItems: 'center', justifyContent: 'space-around', }, tabItem: { alignItems: 'center', gap: 4, }, tabDot: { width: 6, height: 6, borderRadius: 99, backgroundColor: '#334155', }, tabDotActive: { backgroundColor: '#60a5fa', }, tabLabel: { color: '#94a3b8', fontSize: 12, fontWeight: '600', }, tabLabelActive: { color: '#dbeafe', }, modalBackdrop: { flex: 1, backgroundColor: 'rgba(2,6,23,0.72)', justifyContent: 'flex-end', }, modalKeyboardWrap: { width: '100%', }, modalCard: { maxHeight: '87%', backgroundColor: '#0f172a', borderTopLeftRadius: 18, borderTopRightRadius: 18, borderWidth: 1, borderColor: '#1e293b', padding: 14, gap: 8, }, closeText: { color: '#93c5fd', fontWeight: '700', }, });