import React, { useState, useEffect, useRef } from 'react'; import { View, Text, StyleSheet, ScrollView, TouchableOpacity, Modal, TextInput, KeyboardAvoidingView, Platform, SafeAreaView, Animated, Linking, Alert, Share, } from 'react-native'; import { LinearGradient } from 'expo-linear-gradient'; import { Ionicons, Feather, MaterialCommunityIcons, FontAwesome5 } from '@expo/vector-icons'; import { captureRef } from 'react-native-view-shot'; import AsyncStorage from '@react-native-async-storage/async-storage'; import * as bip39 from 'bip39'; import { colors, typography, spacing, borderRadius, shadows } from '../theme/colors'; import { VaultAsset, VaultAssetType, Heir } from '../types'; import BiometricModal from '../components/common/BiometricModal'; import { useAuth } from '../context/AuthContext'; import { useVaultAssets } from '../hooks/useVaultAssets'; import { getVaultStorageKeys, DEBUG_MODE } from '../config'; import { mnemonicToEntropy, splitSecret, serializeShare } from '../utils/sss'; import { storageService } from '../services/storage.service'; import { SentinelVault } from '@/utils/crypto_core'; // Asset type configuration with nautical theme const assetTypeConfig: Record = { game_account: { icon: 'account-key', iconType: 'material', label: 'Account Login' }, private_key: { icon: 'key', iconType: 'fontawesome5', label: 'Secret Key' }, document: { icon: 'scroll', iconType: 'fontawesome5', label: 'Document' }, photo: { icon: 'image', iconType: 'ionicons', label: 'Sealed Photo' }, will: { icon: 'file-signature', iconType: 'fontawesome5', label: 'Testament' }, custom: { icon: 'gem', iconType: 'fontawesome5', label: 'Treasure' }, }; const accountProviderOptions = [ { key: 'bank', label: 'Bank', icon: 'bank', iconType: 'material' as const }, { key: 'steam', label: 'Steam', icon: 'steam', iconType: 'fontawesome5' as const }, { key: 'facebook', label: 'Facebook', icon: 'facebook-f', iconType: 'fontawesome5' as const }, { key: 'custom', label: 'Other', icon: 'shield-account', iconType: 'material' as const }, ]; const initialHeirs: Heir[] = [ { id: '1', name: 'John Smith', email: 'john.smith@email.com', phone: '+1 415 555 0132', status: 'confirmed', releaseLevel: 3, releaseOrder: 1, paymentStrategy: 'prepaid', }, { id: '2', name: 'Jane Doe', email: 'jane.doe@email.com', phone: '+1 212 555 0184', status: 'confirmed', releaseLevel: 2, releaseOrder: 2, paymentStrategy: 'pay_on_access', }, { id: '3', name: 'Alice Johnson', email: 'alice.j@email.com', phone: '+1 646 555 0149', status: 'invited', releaseLevel: 1, releaseOrder: 3, paymentStrategy: 'pay_on_access', }, ]; const generateMnemonic = () => bip39.generateMnemonic(128).split(' '); const splitMnemonic = (words: string[]) => [ words.slice(0, 4), words.slice(4, 8), words.slice(8, 12), ]; type HeirAssignment = { asset: VaultAsset; heir: Heir; }; const renderAssetTypeIcon = (config: typeof assetTypeConfig[VaultAssetType], size: number, color: string) => { switch (config.iconType) { case 'ionicons': return ; case 'feather': return ; case 'material': return ; case 'fontawesome5': return ; } }; export default function VaultScreen() { const [isUnlocked, setIsUnlocked] = useState(false); const [showBiometric, setShowBiometric] = useState(false); const { assets, setAssets, refreshAssets, createAsset: createVaultAsset, deleteAsset: deleteVaultAsset, assignAsset: assignVaultAsset, isSealing, createError: addError, clearCreateError: clearAddError, } = useVaultAssets(isUnlocked); const [showAddModal, setShowAddModal] = useState(false); const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); const [isDeleting, setIsDeleting] = useState(false); const [selectedType, setSelectedType] = useState('custom'); const [newLabel, setNewLabel] = useState(''); const [showUploadSuccess, setShowUploadSuccess] = useState(false); const [fadeAnim] = useState(new Animated.Value(0)); const [pulseAnim] = useState(new Animated.Value(1)); const [selectedAsset, setSelectedAsset] = useState(null); const [showDetail, setShowDetail] = useState(false); const [showGuardedBiometric, setShowGuardedBiometric] = useState(false); const [showKeyPreview, setShowKeyPreview] = useState(false); const [addStep, setAddStep] = useState(1); const [addMethod, setAddMethod] = useState<'text' | 'file' | 'scan'>('text'); const [addVerified, setAddVerified] = useState(false); const [rehearsalConfirmed, setRehearsalConfirmed] = useState(false); const [showAddBiometric, setShowAddBiometric] = useState(false); const [accountProvider, setAccountProvider] = useState<'bank' | 'steam' | 'facebook' | 'custom'>('bank'); const [showMnemonic, setShowMnemonic] = useState(false); const [showAssignModal, setShowAssignModal] = useState(false); const [showAssignErrorModal, setShowAssignErrorModal] = useState(false); const [assignErrorMessage, setAssignErrorMessage] = useState(''); const [isAssigning, setIsAssigning] = useState(false); const [heirEmail, setHeirEmail] = useState(''); const [showLegacyAssignCta, setShowLegacyAssignCta] = useState(false); const [hasS0, setHasS0] = useState(null); const [backupContent, setBackupContent] = useState(null); const [isFetchingBackup, setIsFetchingBackup] = useState(false); const [mnemonicWords, setMnemonicWords] = useState([]); const [mnemonicParts, setMnemonicParts] = useState([]); const [mnemonicStep, setMnemonicStep] = useState<1 | 2 | 3 | 4 | 5>(1); const [heirStep, setHeirStep] = useState<'decision' | 'asset' | 'heir' | 'summary'>('decision'); const [selectedHeir, setSelectedHeir] = useState(null); const [selectedHeirAsset, setSelectedHeirAsset] = useState(null); const [assignments, setAssignments] = useState([]); const [replaceIndex, setReplaceIndex] = useState(null); const [replaceQuery, setReplaceQuery] = useState(''); const [progressIndex, setProgressIndex] = useState(0); const [progressAnim] = useState(new Animated.Value(0)); const { user, token } = useAuth(); const vaultKeys = React.useMemo(() => getVaultStorageKeys(user?.id ?? null), [user?.id]); const [isCapturing, setIsCapturing] = useState(false); const [treasureContent, setTreasureContent] = useState(''); const mnemonicRef = useRef(null); const progressTimerRef = useRef | null>(null); // Detect S0 (TEE/SE) for current user: if present, later open shows biometric only; if not, mnemonic flow useEffect(() => { let cancelled = false; AsyncStorage.getItem(vaultKeys.SHARE_DEVICE) .then((v) => { if (!cancelled) setHasS0(!!v); }) .catch(() => { if (!cancelled) setHasS0(false); }); return () => { cancelled = true; }; }, [vaultKeys.SHARE_DEVICE]); // Only when S0 exists and vault not unlocked: show biometric after short delay. // When hasS0 is false or null, never show biometric — go straight to mnemonic flow. useEffect(() => { if (hasS0 !== true) { setShowBiometric(false); return; } if (isUnlocked) return; const timer = setTimeout(() => setShowBiometric(true), 100); return () => clearTimeout(timer); }, [isUnlocked, hasS0]); useEffect(() => { if (isUnlocked) { Animated.timing(fadeAnim, { toValue: 1, duration: 200, useNativeDriver: true, }).start(); } }, [isUnlocked]); useEffect(() => { if (!isUnlocked) { Animated.loop( Animated.sequence([ Animated.timing(pulseAnim, { toValue: 1.05, duration: 500, useNativeDriver: true, }), Animated.timing(pulseAnim, { toValue: 1, duration: 500, useNativeDriver: true, }), ]) ).start(); } }, [isUnlocked]); const handleUnlock = () => { setShowBiometric(false); const words = generateMnemonic(); const parts = splitMnemonic(words); setMnemonicWords(words); setMnemonicParts(parts); setReplaceIndex(null); setReplaceQuery(''); setMnemonicStep(1); setHeirStep('decision'); setSelectedHeir(null); setSelectedHeirAsset(null); setProgressIndex(0); progressAnim.setValue(0); setTimeout(() => setShowMnemonic(true), 200); AsyncStorage.setItem(vaultKeys.MNEMONIC_PART_LOCAL, parts[0].join(' ')).catch(() => { // Best-effort local store; UI remains available }); }; const handleScreenshot = async () => { try { setIsCapturing(true); const uri = await captureRef(mnemonicRef, { format: 'png', quality: 1, result: Platform.OS === 'web' ? 'data-uri' : 'tmpfile', }); if (Platform.OS === 'web') { try { await Linking.openURL(uri); } catch { // Ignore if the browser blocks data-uri navigation. } } else { await Share.share({ url: uri, message: 'Sentinel key backup', }); } setMnemonicStep(4); } catch (error) { Alert.alert('Screenshot failed', 'Please try again or use email backup.'); } finally { setIsCapturing(false); } }; const handleEmailBackup = () => { // Proceed immediately; email delivery is handled separately. setMnemonicStep(4); setShowMnemonic(true); }; const handleReplaceWord = (word: string) => { if (replaceIndex === null) return; const nextWords = [...mnemonicWords]; nextWords[replaceIndex] = word; setMnemonicWords(nextWords); setMnemonicParts(splitMnemonic(nextWords)); setReplaceIndex(null); setReplaceQuery(''); }; useEffect(() => { if (mnemonicStep !== 4) { if (progressTimerRef.current) { clearInterval(progressTimerRef.current); progressTimerRef.current = null; } return; } const messagesCount = 5; let current = 0; setProgressIndex(current); progressAnim.setValue(0); progressTimerRef.current = setInterval(() => { current += 1; if (current >= messagesCount) { if (progressTimerRef.current) { clearInterval(progressTimerRef.current); progressTimerRef.current = null; } setMnemonicStep(5); return; } setProgressIndex(current); Animated.timing(progressAnim, { toValue: current / (messagesCount - 1), duration: 700, useNativeDriver: false, }).start(); }, 1100); Animated.timing(progressAnim, { toValue: 1 / (messagesCount - 1), duration: 700, useNativeDriver: false, }).start(); return () => { if (progressTimerRef.current) { clearInterval(progressTimerRef.current); progressTimerRef.current = null; } }; }, [mnemonicStep, progressAnim]); const handleHeirDecision = (share: boolean) => { // Placeholder for future heir flow finishMnemonicThenShowBiometric(); }; const handleSubmitAssignment = () => { // Placeholder for submitting assignment to API finishMnemonicThenShowBiometric(); }; const handleHeirYes = () => { finishMnemonicThenShowBiometric(); setShowLegacyAssignCta(true); }; const handleHeirNo = () => { finishMnemonicThenShowBiometric(); setShowLegacyAssignCta(true); }; /** After mnemonic flow: persist S0 (simulated TEE/SE via AsyncStorage), then show biometric; unlock on success */ const finishMnemonicThenShowBiometric = async () => { try { const wordList = bip39.wordlists.english; const entropy = mnemonicToEntropy(mnemonicWords, wordList); const shares = splitSecret(entropy); const s0 = shares[0]; // device share (S0) const s1 = shares[1]; // server share (S1) const s2 = shares[2]; // heir share (S2) // S0 is stored in AsyncStorage under user-scoped key — app-level storage, not hardware TEE/SE const vault = new SentinelVault() const aes_key = await vault.deriveKey(mnemonicWords.join(' ')) await AsyncStorage.setItem(vaultKeys.SHARE_DEVICE, serializeShare(s0)); await AsyncStorage.setItem(vaultKeys.SHARE_SERVER, serializeShare(s1)); await AsyncStorage.setItem(vaultKeys.INITIALIZED, '1'); await AsyncStorage.setItem(vaultKeys.AES_KEY, aes_key.toString('hex')); await AsyncStorage.setItem(vaultKeys.SHARE_HEIR, serializeShare(s2)); setHasS0(true); setShowMnemonic(false); setShowBiometric(true); } catch (e) { if (__DEV__) console.warn('finishMnemonicThenShowBiometric', e); setShowMnemonic(false); setShowBiometric(true); } }; const handleBiometricSuccess = () => { setShowBiometric(false); setIsUnlocked(true); }; const handleOpenLegacyAssign = () => { setSelectedHeir(null); setSelectedHeirAsset(null); setHeirStep('asset'); setMnemonicStep(5); setShowMnemonic(true); }; const handleSelectHeirAsset = (asset: VaultAsset) => { setSelectedHeirAsset(asset); setHeirStep('heir'); }; const handleSelectHeir = (heir: Heir) => { setSelectedHeir(heir); setHeirStep('summary'); }; const resetAddFlow = () => { setAddStep(1); setAddMethod('text'); setAddVerified(false); setRehearsalConfirmed(false); setSelectedType('custom'); setNewLabel(''); setAccountProvider('bank'); }; const handleAddAsset = async () => { if (!newLabel.trim() || !treasureContent.trim()) return; if (!addVerified) return; if (selectedType === 'private_key' && !rehearsalConfirmed) return; if (!token) { Alert.alert('Not logged in', 'Please sign in first to add a Treasure.'); return; } const result = await createVaultAsset({ title: newLabel.trim(), content: treasureContent.trim(), }); if (result.success) { setNewLabel(''); setTreasureContent(''); setSelectedType('custom'); setAddVerified(false); setRehearsalConfirmed(false); setShowAddModal(false); clearAddError(); setShowUploadSuccess(true); setTimeout(() => setShowUploadSuccess(false), 2500); if (typeof Alert !== 'undefined' && Alert.alert) { Alert.alert('Success', 'Treasure sealed and saved successfully.'); } return; } if (result.isUnauthorized) { setShowAddModal(false); clearAddError(); if (typeof Alert !== 'undefined' && Alert.alert) { Alert.alert( 'Unauthorized', 'Your session has expired or you are not logged in. Please sign in again.', [{ text: 'OK' }] ); } return; } if (result.error && typeof Alert !== 'undefined' && Alert.alert) { Alert.alert('Failed', result.error); } }; const formatDate = (date: Date) => { return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric', }); }; const handleOpenDetail = (asset: VaultAsset) => { setSelectedAsset(asset); setShowDetail(true); setShowKeyPreview(false); if (DEBUG_MODE) { console.log('[DEBUG] Vault Asset Details:', JSON.stringify(asset.rawData, null, 2)); } }; const handleCloseDetail = () => { setShowDetail(false); setSelectedAsset(null); setShowKeyPreview(false); setShowGuardedBiometric(false); setBackupContent(null); }; const handleFetchBackup = async () => { if (!selectedAsset || !user?.id) return; setIsFetchingBackup(true); try { const content = await storageService.getAssetBackup(Number(selectedAsset.id), user.id); if (content) { setBackupContent(content); } else { if (typeof Alert !== 'undefined' && Alert.alert) { Alert.alert('No Backup Found', 'No local plaintext backup found for this treasure.'); } } } catch (error) { console.error('Fetch backup error:', error); if (typeof Alert !== 'undefined' && Alert.alert) { Alert.alert('Error', 'Failed to retrieve local backup.'); } } finally { setIsFetchingBackup(false); } }; const handleDeleteAsset = async () => { if (!selectedAsset || isDeleting) return; setIsDeleting(true); try { const result = await deleteVaultAsset(Number(selectedAsset.id)); if (result.success) { setShowDeleteConfirm(false); handleCloseDetail(); if (typeof Alert !== 'undefined' && Alert.alert) { Alert.alert('Success', 'Treasure removed from the vault.'); } } else if (result.isUnauthorized) { setShowDeleteConfirm(false); handleCloseDetail(); if (typeof Alert !== 'undefined' && Alert.alert) { Alert.alert('Unauthorized', 'Your session has expired. Please sign in again.'); } } else if (result.error && typeof Alert !== 'undefined' && Alert.alert) { Alert.alert('Failed', result.error); } } catch (error) { console.error('Delete error:', error); if (typeof Alert !== 'undefined' && Alert.alert) { Alert.alert('Error', 'An unexpected error occurred during deletion.'); } } finally { setIsDeleting(false); } }; const handleAssignHeir = async () => { if (!selectedAsset || isAssigning) return; if (!heirEmail.trim() || !heirEmail.includes('@')) { Alert.alert('Invalid Email', 'Please enter a valid email address.'); return; } setIsAssigning(true); try { const result = await assignVaultAsset(Number(selectedAsset.id), heirEmail.trim()); if (result.success) { setShowAssignModal(false); setHeirEmail(''); Alert.alert('Success', `Asset assigned to ${heirEmail.trim()}`); } else if (result.isUnauthorized) { setShowAssignModal(false); if (typeof Alert !== 'undefined' && Alert.alert) { Alert.alert('Unauthorized', 'Your session has expired. Please sign in again.'); } } else if (result.error) { setAssignErrorMessage(result.error); setShowAssignErrorModal(true); } } catch (error) { console.error('Assign error:', error); setAssignErrorMessage('An unexpected error occurred during assignment.'); setShowAssignErrorModal(true); } finally { setIsAssigning(false); } }; const handleGuardedAccess = () => { setShowGuardedBiometric(true); }; const handleGuardedSuccess = () => { setShowGuardedBiometric(false); setShowKeyPreview(true); handleFetchBackup(); }; const handleAddVerification = () => { setShowAddBiometric(true); }; const handleAddVerificationSuccess = () => { setShowAddBiometric(false); setAddVerified(true); }; const openProviderLogin = async () => { if (accountProvider === 'bank') { Alert.alert( 'Bank App Needed', 'Provide the bank app deep link scheme to enable one-tap login.' ); return; } const providerLinks = { steam: { app: 'steam://openurl/https://store.steampowered.com/login/', web: 'https://store.steampowered.com/login/', }, facebook: { app: 'fb://facewebmodal/f?href=https://www.facebook.com/login', web: 'https://www.facebook.com/login', }, custom: { app: '', web: '', }, } as const; const target = providerLinks[accountProvider]; if (!target?.app) { return; } const canOpen = await Linking.canOpenURL(target.app); if (canOpen) { await Linking.openURL(target.app); } else if (target.web) { await Linking.openURL(target.web); } }; const selectedConfig = selectedAsset ? assetTypeConfig[selectedAsset.type] : null; const detailHeading = selectedAsset?.type === 'private_key' ? 'Key Material' : selectedConfig?.label || 'Vault Asset'; const detailMetaLabel = selectedAsset?.type === 'private_key' ? 'KEY MATERIAL' : 'ASSET CLASS'; const detailMetaValue = selectedAsset?.type === 'private_key' ? '12/24 Words' : selectedConfig?.label || '--'; const canSeal = !!newLabel.trim() && !!treasureContent.trim() && addVerified && !isSealing && (selectedType !== 'private_key' || rehearsalConfirmed); const mnemonicModal = ( setShowMnemonic(false)} > setShowMnemonic(false)} activeOpacity={0.85} > Mnemonic Setup {mnemonicStep === 1 ? ( <> Review your 12-word mnemonic. Tap any word to replace it. {mnemonicWords.map((word, index) => ( setReplaceIndex(index)} activeOpacity={0.8} > {index + 1} {word} ))} {replaceIndex !== null ? ( Replace word {replaceIndex + 1} {(replaceQuery ? bip39.wordlists.english.filter((word) => word.startsWith(replaceQuery.toLowerCase()) ) : bip39.wordlists.english ) .slice(0, 24) .map((word) => ( handleReplaceWord(word)} activeOpacity={0.8} > {word} ))} setReplaceIndex(null)} activeOpacity={0.85} > CANCEL ) : null} setMnemonicStep(2)} activeOpacity={0.85} > NEXT ) : null} {mnemonicStep === 2 ? ( <> Confirm your 12-word mnemonic. {mnemonicWords.join(' ')} setMnemonicStep(3)} activeOpacity={0.85} > CONFIRM setMnemonicStep(1)} activeOpacity={0.85} > EDIT SELECTION ) : null} {mnemonicStep === 3 ? ( <> Back up your mnemonic before entering the Vault. {mnemonicWords.join(' ')} {isCapturing ? 'CAPTURING...' : 'PHYSICAL BACKUP (SCREENSHOT)'} EMAIL BACKUP ) : null} {mnemonicStep === 4 ? ( <> Finalizing your vault protection. {progressIndex === 0 && '1. Your key is being processed'} {progressIndex === 1 && '2. Your key has been split'} {progressIndex === 2 && '3. Part one stored on this device'} {progressIndex === 3 && '4. Part two uploaded to the cloud'} {progressIndex >= 4 && '5. Part three inquiry initiated'} ) : null} {mnemonicStep === 5 ? ( <> {heirStep === 'decision' ? ( <> Share Part Three with your legacy handler? YES, SEND NOT NOW ) : null} {heirStep === 'asset' ? ( <> Select the vault item to assign. {assets.map((asset) => { const config = assetTypeConfig[asset.type]; return ( handleSelectHeirAsset(asset)} activeOpacity={0.8} > {renderAssetTypeIcon(config, 18, colors.vault.primary)} {asset.label} {config.label} ); })} setHeirStep('decision')} activeOpacity={0.85} > BACK ) : null} {heirStep === 'heir' ? ( <> Choose a legacy handler. {initialHeirs.map((heir) => ( handleSelectHeir(heir)} activeOpacity={0.8} > {heir.name} {heir.email} ))} setHeirStep('asset')} activeOpacity={0.85} > BACK ) : null} {heirStep === 'summary' ? ( <> Confirm assignment details. Vault Item {selectedHeirAsset?.label} Legacy Handler {selectedHeir?.name} {selectedHeir?.email} Release Tier Tier {selectedHeir?.releaseLevel} SUBMIT setHeirStep('heir')} activeOpacity={0.85} > EDIT ) : null} ) : null} ); // Lock screen const lockScreen = ( THE DEEP VAULT Where treasures rest in silence setShowBiometric(true) : handleUnlock} activeOpacity={0.8} > {hasS0 === true ? "Captain's Verification" : hasS0 === false ? 'Enter Vault' : 'Loading…'} setShowBiometric(false)} title="Enter the Vault" message="Verify your identity to access your treasures" isDark /> ); const vaultScreen = ( {/* Header */} THE VAULT SEALED {assets.length} treasures · Encrypted at rest {showLegacyAssignCta && ( Legacy Assignment Continue assigning a vault item to your legacy handler. Continue )} {/* Asset List */} {assets.map((asset) => { const config = assetTypeConfig[asset.type]; return ( handleOpenDetail(asset)} > {renderAssetTypeIcon(config, 22, colors.vault.primary)} {config.label} {asset.label} Sealed {formatDate(asset.createdAt)} ); })} {/* Add Button */} { resetAddFlow(); clearAddError(); setShowAddModal(true); }} activeOpacity={0.9} > Add Treasure {/* Upload Success Toast */} {showUploadSuccess && ( Treasure sealed successfully )} {/* Add Modal */} setShowAddModal(false)} > Add New Treasure {['Title', 'Content', 'Verify'].map((label, index) => { const stepIndex = index + 1; const isActive = addStep === stepIndex; const isDone = addStep > stepIndex; return ( {stepIndex} {label} ); })} {addStep === 1 && ( <> TREASURE TITLE TREASURE TYPE {(Object.keys(assetTypeConfig) as VaultAssetType[]).map((type) => { const config = assetTypeConfig[type]; const isSelected = selectedType === type; return ( setSelectedType(type)} > {renderAssetTypeIcon(config, 22, isSelected ? colors.nautical.teal : colors.nautical.sage)} {config.label} ); })} )} {addStep === 2 && ( <> {selectedType !== 'game_account' && ( <> CAPTURE METHOD {[ { key: 'text', label: 'Text', icon: 'text' }, { key: 'file', label: 'File', icon: 'file-tray' }, { key: 'scan', label: 'Scan', icon: 'camera' }, ].map((item) => { const isActive = addMethod === item.key; return ( setAddMethod(item.key as typeof addMethod)} > {item.label} ); })} CONTENT Data is encrypted on-device. Plaintext is shredded after sealing. )} {selectedType === 'game_account' && ( <> ACCOUNT PROVIDER {accountProviderOptions.map((option) => { const isSelected = accountProvider === option.key; return ( setAccountProvider(option.key as typeof accountProvider)} > {renderAssetTypeIcon( { icon: option.icon, iconType: option.iconType, label: option.label }, 22, isSelected ? colors.nautical.teal : colors.nautical.sage )} {option.label} ); })} Open App to Login TREASURE NAME )} )} {addStep === 3 && ( <> IDENTITY VERIFICATION Biometric required before sealing. {addVerified ? 'Verified' : 'Verify Now'} {selectedType === 'private_key' && ( setRehearsalConfirmed(!rehearsalConfirmed)} activeOpacity={0.8} > I rehearsed the mnemonic once (required). )} Only a masked shard can be revealed, and access is time-limited. )} {addError ? ( {addError} ) : null} { if (addStep === 1) { setShowAddModal(false); setTreasureContent(''); clearAddError(); } else { setAddStep(addStep - 1); clearAddError(); } }} > {addStep === 1 ? 'Cancel' : 'Back'} {addStep < 3 ? ( setAddStep(addStep + 1)} > Continue ) : ( {isSealing ? 'Sealing...' : 'Seal Treasure'} )} setShowAddBiometric(false)} title="Confirm Identity" message="Verify before sealing this treasure" isDark /> {/* Detail Modal */} {detailHeading} {selectedAsset?.type === 'private_key' && ( HIGH-RISK )} SEALED ASSET {selectedAsset?.label} Encrypted at rest · No plaintext stored CREATED {selectedAsset ? formatDate(selectedAsset.createdAt) : '--'} LAST VERIFIED {selectedAsset ? formatDate(selectedAsset.updatedAt) : '--'} STORAGE Cipher Pack {detailMetaLabel} {detailMetaValue} {selectedAsset?.heirEmail ? ( ASSIGNED HEIR {selectedAsset.heirEmail} ) : null} CONTROLLED ACTIONS View Encrypted Digest Export Cipher Pack Reset Sentinel Timer setShowAssignModal(true)} activeOpacity={0.7} > {selectedAsset?.heirEmail ? 'Change Heir' : 'Assign Heir'} setShowDeleteConfirm(true)} activeOpacity={0.7} > Delete Treasure Guarded Reveal Plaintext access requires biometric verification and a memory rehearsal step. Begin Verification {showKeyPreview && ( LOCAL PLAINTEXT BACKUP {isFetchingBackup ? 'Fetching content...' : (backupContent || 'No local backup found for this treasure')} )} Any reveal attempt is time-limited and logged. Screenshots are blocked by policy. setShowGuardedBiometric(false)} title="Verify Access" message="Confirm to reveal a masked shard of the mnemonic" isDark /> {/* Delete Confirmation Modal */} setShowDeleteConfirm(false)} > setShowDeleteConfirm(false)} /> Remove Treasure? This action cannot be undone. The treasure will be permanently shredded from the deep vault. setShowDeleteConfirm(false)} disabled={isDeleting} > Cancel {isDeleting ? ( Shredding... ) : ( Confirm Delete )} {/* Assign Heir Modal */} setShowAssignModal(false)} > setShowAssignModal(false)} /> Designate Heir Enter the email address of the person who should inherit this treasure. HEIR EMAIL ADDRESS { setShowAssignModal(false); setHeirEmail(''); }} disabled={isAssigning} > Cancel {isAssigning ? ( Assigning... ) : ( Confirm Heir )} {/* Assign Heir Error Modal */} setShowAssignErrorModal(false)} > setShowAssignErrorModal(false)} /> Assignment Failed {assignErrorMessage} setShowAssignErrorModal(false)} > Dismiss ); return ( {isUnlocked ? vaultScreen : lockScreen} {mnemonicModal} ); } const styles = StyleSheet.create({ root: { flex: 1, }, container: { flex: 1, }, gradient: { flex: 1, }, safeArea: { flex: 1, }, content: { flex: 1, }, lockContainer: { flex: 1, }, lockGradient: { flex: 1, }, lockSafeArea: { flex: 1, }, lockContent: { flex: 1, justifyContent: 'center', alignItems: 'center', paddingHorizontal: spacing.xl, }, lockIconContainer: { marginBottom: spacing.lg, }, lockIconGradient: { width: 130, height: 130, borderRadius: 65, justifyContent: 'center', alignItems: 'center', ...shadows.glow, }, lockTitle: { fontSize: typography.fontSize.xxl, fontWeight: '700', color: colors.vault.text, letterSpacing: typography.letterSpacing.widest, marginBottom: spacing.sm, fontFamily: typography.fontFamily.serif, }, lockSubtitle: { fontSize: typography.fontSize.base, color: colors.vault.textSecondary, marginBottom: spacing.xl, textAlign: 'center', fontStyle: 'italic', }, waveContainer: { marginBottom: spacing.xl, }, unlockButton: { borderRadius: borderRadius.lg, overflow: 'hidden', }, unlockButtonGradient: { flexDirection: 'row', alignItems: 'center', paddingVertical: spacing.md, paddingHorizontal: spacing.xl, gap: spacing.sm, }, unlockButtonText: { fontSize: typography.fontSize.base, color: colors.vault.background, fontWeight: '600', }, header: { paddingHorizontal: spacing.lg, paddingTop: spacing.lg, paddingBottom: spacing.md, }, headerTop: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: spacing.xs, }, headerTitleRow: { flexDirection: 'row', alignItems: 'center', gap: spacing.sm, }, title: { fontSize: typography.fontSize.xl, fontWeight: '700', color: colors.vault.text, letterSpacing: typography.letterSpacing.wider, fontFamily: typography.fontFamily.serif, }, securityBadge: { flexDirection: 'row', alignItems: 'center', backgroundColor: `${colors.vault.success}20`, paddingHorizontal: spacing.sm, paddingVertical: spacing.xs, borderRadius: borderRadius.full, gap: spacing.xs, }, securityText: { fontSize: typography.fontSize.xs, color: colors.vault.success, fontWeight: '700', letterSpacing: 1, }, subtitle: { fontSize: typography.fontSize.sm, color: colors.vault.textSecondary, }, legacyCtaCard: { marginHorizontal: spacing.lg, marginBottom: spacing.sm, padding: spacing.md, borderRadius: borderRadius.lg, backgroundColor: colors.vault.cardBackground, borderWidth: 1, borderColor: colors.vault.cardBorder, flexDirection: 'row', alignItems: 'center', gap: spacing.md, }, legacyCtaInfo: { flex: 1, }, legacyCtaTitle: { color: colors.vault.text, fontSize: typography.fontSize.base, fontWeight: '700', }, legacyCtaText: { color: colors.vault.textSecondary, fontSize: typography.fontSize.sm, marginTop: spacing.xs, }, legacyCtaButton: { paddingHorizontal: spacing.md, paddingVertical: spacing.sm, borderRadius: borderRadius.full, backgroundColor: colors.vault.primary, }, legacyCtaButtonText: { color: colors.vault.background, fontWeight: '700', fontSize: typography.fontSize.sm, }, assetList: { flex: 1, }, assetListContent: { padding: spacing.lg, paddingTop: spacing.sm, }, assetCard: { flexDirection: 'row', alignItems: 'center', backgroundColor: colors.vault.cardBackground, borderRadius: borderRadius.xl, padding: spacing.base, marginBottom: spacing.md, borderWidth: 1, borderColor: colors.vault.cardBorder, }, assetIconContainer: { width: 52, height: 52, borderRadius: 26, backgroundColor: `${colors.vault.primary}15`, justifyContent: 'center', alignItems: 'center', marginRight: spacing.base, }, assetInfo: { flex: 1, }, assetType: { fontSize: typography.fontSize.xs, color: colors.vault.textSecondary, textTransform: 'uppercase', letterSpacing: 1, marginBottom: 2, fontWeight: '600', }, assetLabel: { fontSize: typography.fontSize.base, color: colors.vault.text, fontWeight: '600', marginBottom: 4, }, assetMetaRow: { flexDirection: 'row', alignItems: 'center', gap: spacing.xs, }, assetMeta: { fontSize: typography.fontSize.xs, color: colors.vault.textSecondary, }, encryptedBadge: { width: 36, height: 36, borderRadius: 18, backgroundColor: colors.vault.success, justifyContent: 'center', alignItems: 'center', }, addButton: { position: 'absolute', bottom: 100, left: spacing.lg, right: spacing.lg, borderRadius: borderRadius.lg, overflow: 'hidden', }, addButtonGradient: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', paddingVertical: spacing.md, gap: spacing.sm, }, addButtonText: { fontSize: typography.fontSize.base, color: colors.vault.background, fontWeight: '700', }, successToast: { position: 'absolute', bottom: 170, left: spacing.lg, right: spacing.lg, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', backgroundColor: colors.vault.success, paddingVertical: spacing.md, borderRadius: borderRadius.lg, gap: spacing.sm, }, successText: { fontSize: typography.fontSize.base, color: '#fff', fontWeight: '600', }, modalOverlay: { flex: 1, backgroundColor: 'rgba(26, 58, 74, 0.8)', justifyContent: 'flex-end', }, modalOverlayDismiss: { ...StyleSheet.absoluteFillObject, }, deleteConfirmContent: { backgroundColor: colors.vault.cardBackground, marginHorizontal: spacing.lg, borderRadius: borderRadius.xl, padding: spacing.xl, borderWidth: 1, borderColor: colors.vault.cardBorder, alignItems: 'center', marginBottom: spacing.xxl + spacing.lg, }, deleteIconContainer: { width: 64, height: 64, borderRadius: 32, backgroundColor: `${colors.vault.warning}20`, alignItems: 'center', justifyContent: 'center', marginBottom: spacing.lg, }, deleteTitle: { fontSize: typography.fontSize.lg, color: colors.vault.text, fontWeight: '700', marginBottom: spacing.sm, fontFamily: typography.fontFamily.serif, }, deleteMessage: { fontSize: typography.fontSize.sm, color: colors.vault.textSecondary, textAlign: 'center', lineHeight: typography.fontSize.sm * 1.5, marginBottom: spacing.xl, }, deleteButtons: { flexDirection: 'row', gap: spacing.md, width: '100%', }, deleteCancelButton: { flex: 1, paddingVertical: spacing.md, borderRadius: borderRadius.lg, backgroundColor: 'rgba(255, 255, 255, 0.08)', alignItems: 'center', borderWidth: 1, borderColor: 'rgba(255, 255, 255, 0.1)', }, deleteCancelText: { color: colors.vault.textSecondary, fontSize: typography.fontSize.base, fontWeight: '600', }, deleteConfirmButton: { flex: 2, paddingVertical: spacing.md, borderRadius: borderRadius.lg, backgroundColor: colors.vault.warning, alignItems: 'center', }, deleteConfirmText: { color: colors.vault.text, fontSize: typography.fontSize.base, fontWeight: '700', }, modalContent: { backgroundColor: colors.nautical.cream, borderTopLeftRadius: borderRadius.xxl, borderTopRightRadius: borderRadius.xxl, padding: spacing.lg, paddingBottom: spacing.xxl, }, modalHandle: { width: 40, height: 4, backgroundColor: colors.nautical.lightMint, borderRadius: 2, alignSelf: 'center', marginBottom: spacing.lg, }, modalHeader: { flexDirection: 'row', alignItems: 'center', gap: spacing.sm, marginBottom: spacing.lg, }, modalTitle: { fontSize: typography.fontSize.lg, fontWeight: '600', color: colors.nautical.navy, }, stepRow: { flexDirection: 'row', justifyContent: 'space-between', marginBottom: spacing.lg, }, stepItem: { alignItems: 'center', flex: 1, }, stepCircle: { width: 28, height: 28, borderRadius: 14, borderWidth: 1, borderColor: colors.nautical.lightMint, alignItems: 'center', justifyContent: 'center', backgroundColor: colors.nautical.paleAqua, }, stepCircleActive: { borderColor: colors.nautical.teal, backgroundColor: colors.nautical.lightMint, }, stepCircleDone: { borderColor: colors.nautical.teal, backgroundColor: colors.nautical.teal, }, stepNumber: { fontSize: typography.fontSize.xs, color: colors.nautical.sage, fontWeight: '600', }, stepNumberActive: { color: colors.nautical.teal, }, stepNumberDone: { color: colors.nautical.cream, }, stepLabel: { fontSize: typography.fontSize.xs, color: colors.nautical.sage, marginTop: spacing.xs, }, stepLabelActive: { color: colors.nautical.teal, fontWeight: '600', }, modalLabel: { fontSize: typography.fontSize.xs, color: colors.nautical.sage, marginBottom: spacing.sm, letterSpacing: typography.letterSpacing.wider, fontWeight: '600', }, methodRow: { flexDirection: 'row', gap: spacing.sm, marginBottom: spacing.lg, }, methodButton: { flex: 1, paddingVertical: spacing.sm, borderRadius: borderRadius.lg, alignItems: 'center', gap: spacing.xs, backgroundColor: colors.nautical.paleAqua, borderWidth: 1, borderColor: colors.nautical.lightMint, }, methodButtonActive: { borderColor: colors.nautical.teal, backgroundColor: colors.nautical.lightMint, }, methodLabel: { fontSize: typography.fontSize.sm, color: colors.nautical.sage, fontWeight: '600', }, methodLabelActive: { color: colors.nautical.teal, }, loginButton: { marginTop: spacing.sm, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: spacing.sm, backgroundColor: colors.nautical.teal, paddingVertical: spacing.sm, borderRadius: borderRadius.full, }, loginButtonText: { color: colors.nautical.cream, fontSize: typography.fontSize.sm, fontWeight: '700', letterSpacing: 0.6, }, typeScroll: { marginBottom: spacing.lg, }, typeScrollContent: { gap: spacing.sm, }, typeButton: { alignItems: 'center', paddingVertical: spacing.sm, paddingHorizontal: spacing.base, borderRadius: borderRadius.lg, backgroundColor: colors.nautical.paleAqua, borderWidth: 2, borderColor: 'transparent', minWidth: 100, }, typeButtonActive: { borderColor: colors.nautical.teal, backgroundColor: colors.nautical.lightMint, }, typeIconContainer: { width: 44, height: 44, borderRadius: 22, backgroundColor: colors.nautical.cream, justifyContent: 'center', alignItems: 'center', marginBottom: spacing.xs, }, typeIconContainerActive: { backgroundColor: `${colors.nautical.teal}15`, }, typeButtonLabel: { fontSize: typography.fontSize.xs, color: colors.nautical.sage, textAlign: 'center', fontWeight: '500', }, typeButtonLabelActive: { color: colors.nautical.teal, fontWeight: '600', }, input: { backgroundColor: colors.nautical.paleAqua, borderRadius: borderRadius.lg, padding: spacing.base, fontSize: typography.fontSize.base, color: colors.nautical.navy, marginBottom: spacing.md, borderWidth: 1, borderColor: colors.nautical.lightMint, }, inputMultiline: { minHeight: 120, paddingTop: spacing.base, }, encryptionNote: { flexDirection: 'row', alignItems: 'center', backgroundColor: colors.nautical.lightMint, borderRadius: borderRadius.lg, padding: spacing.md, marginBottom: spacing.lg, gap: spacing.sm, }, encryptionNoteText: { flex: 1, fontSize: typography.fontSize.sm, color: colors.nautical.teal, lineHeight: typography.fontSize.sm * 1.4, }, addErrorBox: { flexDirection: 'row', alignItems: 'center', backgroundColor: 'rgba(194, 65, 12, 0.12)', borderRadius: borderRadius.lg, padding: spacing.md, marginBottom: spacing.md, gap: spacing.sm, borderWidth: 1, borderColor: 'rgba(194, 65, 12, 0.3)', }, addErrorText: { flex: 1, fontSize: typography.fontSize.sm, color: '#c2410c', lineHeight: typography.fontSize.sm * 1.4, }, modalButtons: { flexDirection: 'row', gap: spacing.md, }, cancelButton: { flex: 1, paddingVertical: spacing.md, borderRadius: borderRadius.lg, backgroundColor: colors.nautical.paleAqua, alignItems: 'center', borderWidth: 1, borderColor: colors.nautical.lightMint, }, cancelButtonText: { fontSize: typography.fontSize.base, color: colors.nautical.sage, fontWeight: '600', }, confirmButton: { flex: 1, borderRadius: borderRadius.lg, overflow: 'hidden', }, confirmButtonGradient: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', paddingVertical: spacing.md, gap: spacing.sm, }, confirmButtonGradientDisabled: { opacity: 0.5, }, confirmButtonText: { fontSize: typography.fontSize.base, color: '#fff', fontWeight: '600', }, verifyCard: { backgroundColor: colors.nautical.paleAqua, borderRadius: borderRadius.lg, padding: spacing.base, marginBottom: spacing.md, borderWidth: 1, borderColor: colors.nautical.lightMint, gap: spacing.sm, }, verifyRow: { flexDirection: 'row', alignItems: 'center', gap: spacing.sm, }, verifyText: { color: colors.nautical.navy, fontSize: typography.fontSize.sm, }, verifyButton: { alignSelf: 'flex-start', paddingHorizontal: spacing.md, paddingVertical: spacing.sm, borderRadius: borderRadius.full, backgroundColor: colors.nautical.teal, }, verifyButtonDone: { backgroundColor: colors.nautical.sage, }, verifyButtonText: { color: colors.nautical.cream, fontWeight: '700', }, rehearsalRow: { flexDirection: 'row', alignItems: 'center', gap: spacing.sm, marginBottom: spacing.md, }, rehearsalText: { color: colors.nautical.navy, fontSize: typography.fontSize.sm, flex: 1, }, detailContainer: { flex: 1, backgroundColor: colors.vault.background, }, detailGradient: { flex: 1, }, detailSafeArea: { flex: 1, }, detailHeader: { paddingHorizontal: spacing.lg, paddingTop: spacing.lg, paddingBottom: spacing.md, flexDirection: 'row', alignItems: 'center', gap: spacing.sm, }, detailBackButton: { width: 36, height: 36, borderRadius: 18, backgroundColor: 'rgba(255, 255, 255, 0.08)', alignItems: 'center', justifyContent: 'center', }, detailTitle: { flex: 1, fontSize: typography.fontSize.lg, color: colors.vault.text, fontWeight: '700', letterSpacing: typography.letterSpacing.wide, }, riskBadge: { backgroundColor: `${colors.vault.warning}25`, borderRadius: borderRadius.full, paddingHorizontal: spacing.sm, paddingVertical: 4, }, riskBadgeText: { fontSize: typography.fontSize.xs, color: colors.vault.warning, fontWeight: '700', letterSpacing: 1, }, detailScroll: { flex: 1, }, detailContent: { padding: spacing.lg, paddingTop: spacing.sm, gap: spacing.lg, }, detailHeroCard: { backgroundColor: colors.vault.cardBackground, borderRadius: borderRadius.xl, padding: spacing.lg, borderWidth: 1, borderColor: colors.vault.cardBorder, flexDirection: 'row', gap: spacing.base, alignItems: 'center', }, detailHeroIcon: { width: 54, height: 54, borderRadius: 27, backgroundColor: 'rgba(184, 224, 229, 0.15)', alignItems: 'center', justifyContent: 'center', }, detailHeroInfo: { flex: 1, }, detailHeroLabel: { fontSize: typography.fontSize.xs, color: colors.vault.textSecondary, letterSpacing: 1, fontWeight: '600', marginBottom: 4, }, detailHeroTitle: { fontSize: typography.fontSize.lg, color: colors.vault.text, fontWeight: '700', marginBottom: 4, }, detailHeroSubtitle: { fontSize: typography.fontSize.sm, color: colors.vault.textSecondary, }, metaGrid: { flexDirection: 'row', flexWrap: 'wrap', gap: spacing.md, }, metaCard: { width: '48%', backgroundColor: 'rgba(255, 255, 255, 0.05)', borderRadius: borderRadius.lg, padding: spacing.base, borderWidth: 1, borderColor: 'rgba(255, 255, 255, 0.08)', }, metaLabel: { fontSize: typography.fontSize.xs, color: colors.vault.textSecondary, letterSpacing: 1, marginBottom: spacing.xs, fontWeight: '600', }, metaValue: { fontSize: typography.fontSize.sm, color: colors.vault.text, fontWeight: '600', }, actionGroup: { gap: spacing.sm, }, sectionTitle: { fontSize: typography.fontSize.xs, color: colors.vault.textSecondary, letterSpacing: 1, fontWeight: '700', }, actionRow: { flexDirection: 'row', alignItems: 'center', gap: spacing.sm, paddingVertical: spacing.sm, paddingHorizontal: spacing.md, borderRadius: borderRadius.lg, backgroundColor: 'rgba(255, 255, 255, 0.06)', borderWidth: 1, borderColor: 'rgba(255, 255, 255, 0.08)', }, actionText: { color: colors.vault.text, fontSize: typography.fontSize.base, fontWeight: '600', }, guardCard: { backgroundColor: 'rgba(229, 115, 115, 0.12)', borderRadius: borderRadius.lg, padding: spacing.base, gap: spacing.sm, borderWidth: 1, borderColor: 'rgba(229, 115, 115, 0.3)', }, guardHeader: { flexDirection: 'row', alignItems: 'center', gap: spacing.sm, }, guardTitle: { fontSize: typography.fontSize.base, color: colors.vault.warning, fontWeight: '700', }, guardText: { fontSize: typography.fontSize.sm, color: colors.vault.textSecondary, lineHeight: typography.fontSize.sm * 1.5, }, guardButton: { alignSelf: 'flex-start', backgroundColor: colors.vault.warning, paddingHorizontal: spacing.md, paddingVertical: spacing.sm, borderRadius: borderRadius.full, }, guardButtonText: { color: colors.vault.text, fontWeight: '700', letterSpacing: 0.6, }, previewCard: { backgroundColor: colors.vault.cardBackground, borderRadius: borderRadius.lg, padding: spacing.base, borderWidth: 1, borderColor: colors.vault.cardBorder, }, previewLabel: { fontSize: typography.fontSize.xs, color: colors.vault.textSecondary, letterSpacing: 1, marginBottom: spacing.xs, fontWeight: '600', }, previewValue: { fontFamily: typography.fontFamily.mono, color: colors.vault.text, fontSize: typography.fontSize.base, letterSpacing: 0.6, }, warningCard: { flexDirection: 'row', alignItems: 'flex-start', gap: spacing.sm, backgroundColor: 'rgba(26, 58, 74, 0.6)', borderRadius: borderRadius.lg, padding: spacing.base, borderWidth: 1, borderColor: 'rgba(229, 115, 115, 0.35)', }, warningText: { flex: 1, color: colors.vault.textSecondary, fontSize: typography.fontSize.sm, lineHeight: typography.fontSize.sm * 1.5, }, mnemonicOverlay: { flex: 1, backgroundColor: 'rgba(11, 20, 24, 0.72)', justifyContent: 'center', padding: spacing.lg, }, mnemonicCard: { borderRadius: borderRadius.xl, padding: spacing.lg, borderWidth: 1, borderColor: colors.sentinel.cardBorder, ...shadows.glow, }, mnemonicHeader: { flexDirection: 'row', alignItems: 'center', gap: spacing.sm, marginBottom: spacing.sm, }, stepDots: { flexDirection: 'row', gap: spacing.sm, marginTop: spacing.sm, justifyContent: 'center', }, stepDot: { width: 8, height: 8, borderRadius: 4, backgroundColor: colors.sentinel.cardBorder, }, stepDotActive: { backgroundColor: colors.sentinel.primary, }, mnemonicClose: { position: 'absolute', top: spacing.sm, right: spacing.sm, width: 32, height: 32, borderRadius: 16, alignItems: 'center', justifyContent: 'center', backgroundColor: 'rgba(26, 58, 74, 0.35)', }, mnemonicTitle: { fontSize: typography.fontSize.lg, fontWeight: '700', color: colors.sentinel.text, letterSpacing: typography.letterSpacing.wide, }, mnemonicSubtitle: { fontSize: typography.fontSize.sm, color: colors.sentinel.textSecondary, marginBottom: spacing.md, }, mnemonicBlock: { backgroundColor: colors.sentinel.cardBackground, borderRadius: borderRadius.lg, paddingVertical: spacing.md, paddingHorizontal: spacing.md, borderWidth: 1, borderColor: colors.sentinel.cardBorder, marginBottom: spacing.lg, }, mnemonicBlockText: { fontSize: typography.fontSize.sm, color: colors.sentinel.text, fontFamily: typography.fontFamily.mono, fontWeight: '600', lineHeight: 22, textAlign: 'center', }, progressContainer: { marginTop: spacing.sm, marginBottom: spacing.md, }, progressTrack: { height: 6, borderRadius: 999, backgroundColor: colors.sentinel.cardBorder, overflow: 'hidden', }, progressFill: { height: '100%', backgroundColor: colors.sentinel.primary, }, progressSteps: { minHeight: 44, justifyContent: 'center', marginBottom: spacing.md, }, progressText: { fontSize: typography.fontSize.sm, color: colors.sentinel.text, textAlign: 'center', lineHeight: typography.fontSize.sm * 1.4, }, wordGrid: { flexDirection: 'row', flexWrap: 'wrap', gap: spacing.sm, marginBottom: spacing.lg, }, wordChip: { minWidth: '30%', paddingHorizontal: spacing.sm, paddingVertical: spacing.sm, borderRadius: borderRadius.lg, borderWidth: 1, borderColor: colors.sentinel.cardBorder, backgroundColor: 'transparent', }, wordChipSelected: { backgroundColor: colors.sentinel.primary, borderColor: colors.sentinel.primary, }, wordChipIndex: { fontSize: typography.fontSize.xs, color: colors.sentinel.textSecondary, marginBottom: 2, }, wordChipText: { color: colors.sentinel.text, fontSize: typography.fontSize.xs, fontWeight: '600', }, wordChipTextSelected: { color: colors.nautical.cream, }, replacePanel: { backgroundColor: colors.sentinel.cardBackground, borderRadius: borderRadius.lg, padding: spacing.md, borderWidth: 1, borderColor: colors.sentinel.cardBorder, marginBottom: spacing.lg, }, replaceTitle: { fontSize: typography.fontSize.sm, color: colors.sentinel.text, fontWeight: '600', marginBottom: spacing.sm, }, replaceInput: { height: 40, borderRadius: borderRadius.full, borderWidth: 1, borderColor: colors.sentinel.cardBorder, paddingHorizontal: spacing.md, color: colors.sentinel.text, fontSize: typography.fontSize.sm, backgroundColor: 'rgba(255, 255, 255, 0.02)', marginBottom: spacing.sm, }, replaceList: { maxHeight: 160, marginBottom: spacing.sm, }, replaceOption: { paddingVertical: spacing.xs, paddingHorizontal: spacing.sm, borderRadius: borderRadius.full, borderWidth: 1, borderColor: colors.sentinel.cardBorder, marginBottom: spacing.xs, }, replaceOptionText: { fontSize: typography.fontSize.xs, color: colors.sentinel.text, fontWeight: '600', }, replaceCancel: { alignSelf: 'flex-end', paddingHorizontal: spacing.md, paddingVertical: spacing.xs, borderRadius: borderRadius.full, borderWidth: 1, borderColor: colors.sentinel.cardBorder, }, replaceCancelText: { fontSize: typography.fontSize.xs, color: colors.sentinel.textSecondary, fontWeight: '600', letterSpacing: typography.letterSpacing.wide, }, selectorList: { maxHeight: 260, marginBottom: spacing.md, }, selectorRow: { flexDirection: 'row', alignItems: 'center', gap: spacing.sm, paddingVertical: spacing.sm, paddingHorizontal: spacing.sm, borderRadius: borderRadius.lg, borderWidth: 1, borderColor: colors.sentinel.cardBorder, backgroundColor: colors.sentinel.cardBackground, marginBottom: spacing.sm, }, selectorIcon: { width: 32, height: 32, borderRadius: 16, alignItems: 'center', justifyContent: 'center', backgroundColor: `${colors.sentinel.primary}1A`, }, selectorContent: { flex: 1, }, selectorTitle: { fontSize: typography.fontSize.sm, color: colors.sentinel.text, fontWeight: '600', }, selectorSubtitle: { fontSize: typography.fontSize.xs, color: colors.sentinel.textSecondary, marginTop: 2, }, summaryCard: { backgroundColor: colors.sentinel.cardBackground, borderRadius: borderRadius.lg, padding: spacing.md, borderWidth: 1, borderColor: colors.sentinel.cardBorder, marginBottom: spacing.md, gap: spacing.xs, }, summaryLabel: { fontSize: typography.fontSize.xs, color: colors.sentinel.textSecondary, letterSpacing: typography.letterSpacing.wide, marginTop: spacing.xs, }, summaryValue: { fontSize: typography.fontSize.sm, color: colors.sentinel.text, fontWeight: '600', }, partGrid: { gap: spacing.sm, marginBottom: spacing.lg, }, partCard: { backgroundColor: colors.sentinel.cardBackground, borderRadius: borderRadius.lg, paddingVertical: spacing.sm, paddingHorizontal: spacing.md, borderWidth: 1, borderColor: colors.sentinel.cardBorder, }, partCardStored: { borderColor: colors.sentinel.primary, }, partLabel: { fontSize: typography.fontSize.xs, color: colors.sentinel.textSecondary, letterSpacing: typography.letterSpacing.wide, marginBottom: 4, fontWeight: '600', }, partValue: { fontSize: typography.fontSize.md, color: colors.sentinel.text, fontFamily: typography.fontFamily.mono, fontWeight: '700', marginBottom: 2, }, partHint: { fontSize: typography.fontSize.xs, color: colors.sentinel.textSecondary, }, mnemonicPrimaryButton: { backgroundColor: colors.sentinel.primary, paddingVertical: spacing.sm, borderRadius: borderRadius.full, alignItems: 'center', marginBottom: spacing.sm, }, mnemonicButtonDisabled: { opacity: 0.6, }, mnemonicPrimaryText: { color: colors.nautical.cream, fontWeight: '700', letterSpacing: typography.letterSpacing.wide, }, mnemonicSecondaryButton: { backgroundColor: 'transparent', paddingVertical: spacing.sm, borderRadius: borderRadius.full, alignItems: 'center', borderWidth: 1, borderColor: colors.sentinel.cardBorder, }, mnemonicSecondaryText: { fontWeight: '700', letterSpacing: typography.letterSpacing.wide, }, deleteActionRow: { backgroundColor: 'rgba(229, 115, 115, 0.08)', borderColor: 'rgba(229, 115, 115, 0.2)', marginTop: spacing.sm, }, deleteActionText: { color: colors.vault.warning, }, confirmModalOverlay: { flex: 1, backgroundColor: 'rgba(11, 20, 24, 0.85)', justifyContent: 'center', alignItems: 'center', padding: spacing.xl, }, confirmModalContent: { width: '100%', backgroundColor: colors.vault.cardBackground, borderRadius: borderRadius.xl, padding: spacing.lg, borderWidth: 1, borderColor: colors.vault.cardBorder, gap: spacing.md, }, confirmModalTitle: { fontSize: typography.fontSize.lg, color: colors.vault.text, fontWeight: '700', textAlign: 'center', }, confirmModalText: { fontSize: typography.fontSize.base, color: colors.vault.textSecondary, textAlign: 'center', lineHeight: 22, }, confirmModalButtons: { flexDirection: 'row', gap: spacing.md, marginTop: spacing.sm, }, confirmModalButton: { flex: 1, paddingVertical: spacing.md, borderRadius: borderRadius.lg, alignItems: 'center', justifyContent: 'center', }, confirmCancelButton: { backgroundColor: 'rgba(255, 255, 255, 0.05)', borderWidth: 1, borderColor: 'rgba(255, 255, 255, 0.1)', }, confirmDeleteButton: { backgroundColor: colors.vault.warning, }, confirmCancelText: { color: colors.vault.textSecondary, }, assignActionRow: { // Optional specific styling }, assignModalContainer: { backgroundColor: colors.vault.cardBackground, marginHorizontal: spacing.lg, borderRadius: borderRadius.xl, borderWidth: 1, borderColor: colors.vault.cardBorder, overflow: 'hidden', marginBottom: spacing.xxl, }, assignModalContent: { padding: spacing.xl, }, assignModalHeader: { alignItems: 'center', marginBottom: spacing.xl, }, assignIconGlow: { width: 64, height: 64, borderRadius: 32, backgroundColor: `${colors.vault.primary}15`, alignItems: 'center', justifyContent: 'center', marginBottom: spacing.md, }, assignTitle: { fontSize: typography.fontSize.lg, color: colors.vault.text, fontWeight: '700', marginBottom: spacing.xs, fontFamily: typography.fontFamily.serif, }, assignSubtitle: { fontSize: typography.fontSize.sm, color: colors.vault.textSecondary, textAlign: 'center', lineHeight: typography.fontSize.sm * 1.5, }, assignInputContainer: { marginBottom: spacing.xl, }, inputLabel: { fontSize: typography.fontSize.xs, color: colors.vault.textSecondary, fontWeight: '700', letterSpacing: 1, marginBottom: spacing.sm, }, assignInput: { backgroundColor: 'rgba(255, 255, 255, 0.05)', borderRadius: borderRadius.lg, padding: spacing.md, fontSize: typography.fontSize.base, color: colors.vault.text, borderWidth: 1, borderColor: 'rgba(255, 255, 255, 0.1)', }, assignButtons: { flexDirection: 'row', gap: spacing.md, }, assignCancelButton: { flex: 1, paddingVertical: spacing.md, borderRadius: borderRadius.lg, backgroundColor: 'rgba(255, 255, 255, 0.08)', alignItems: 'center', borderWidth: 1, borderColor: 'rgba(255, 255, 255, 0.1)', }, assignCancelText: { color: colors.vault.textSecondary, fontSize: typography.fontSize.base, fontWeight: '600', }, assignConfirmButton: { flex: 2, borderRadius: borderRadius.lg, overflow: 'hidden', }, assignConfirmGradient: { paddingVertical: spacing.md, alignItems: 'center', justifyContent: 'center', }, assignConfirmText: { color: colors.vault.text, fontSize: typography.fontSize.base, fontWeight: '700', }, errorModalContent: { backgroundColor: colors.vault.cardBackground, marginHorizontal: spacing.lg, borderRadius: borderRadius.xl, padding: spacing.xl, borderWidth: 1, borderColor: colors.vault.cardBorder, alignItems: 'center', marginBottom: spacing.xxl, }, errorIconContainer: { width: 64, height: 64, borderRadius: 32, backgroundColor: `${colors.vault.warning}15`, alignItems: 'center', justifyContent: 'center', marginBottom: spacing.lg, }, errorTitle: { fontSize: typography.fontSize.lg, color: colors.vault.text, fontWeight: '700', marginBottom: spacing.sm, fontFamily: typography.fontFamily.serif, }, errorMessage: { fontSize: typography.fontSize.sm, color: colors.vault.textSecondary, textAlign: 'center', lineHeight: typography.fontSize.sm * 1.5, marginBottom: spacing.xl, }, errorCloseButton: { width: '100%', paddingVertical: spacing.md, borderRadius: borderRadius.lg, backgroundColor: colors.vault.warning, alignItems: 'center', }, errorCloseButtonText: { color: colors.vault.text, fontSize: typography.fontSize.base, fontWeight: '700', }, });