# Add Months

Local-first Flutter app that identifies the single behavioral change most likely to extend lifespan using hazard-based modeling.

## Quick Start

```bash
flutter pub get
flutter test test/risk_engine/    # 23 unit tests
flutter run                       # Debug mode
flutter run --release -d <device> # Release build
```

## Architecture

```
lib/
├── main.dart                 # App entry, routing logic
├── theme.dart                # Muted clinical color palette
├── models/
│   ├── enums.dart            # Sex, SmokingStatus, AlcoholLevel, etc.
│   ├── user_profile.dart     # Age, sex, country, height, weight, diagnoses
│   ├── behavioral_inputs.dart # Modifiable behaviors
│   └── result.dart           # LifespanDelta, RankedFactor, CalculationResult
├── risk_engine/
│   ├── hazard_ratios.dart    # HR constants from meta-analyses
│   ├── mortality_tables.dart # 50 countries → 4 mortality groups
│   └── calculator.dart       # Core ranking algorithm
├── screens/
│   ├── welcome_screen.dart   # Onboarding
│   ├── baseline_screen.dart  # Demographics, BMI, conditions
│   ├── behavioral_screen.dart # Modifiable factors input
│   └── results_screen.dart   # Dominant challenge display
└── storage/
    └── local_storage.dart    # SQLite persistence
```

## Core Principles

1. **Local-first**: All data on device, no cloud, no accounts, no analytics
2. **Evidence-based**: Hazard ratios from peer-reviewed meta-analyses
3. **Privacy**: Delete All Data = full wipe including encryption keys
4. **Neutral tone**: "Exposure", "Factor", "Estimated gain" — no moral language

## Risk Engine

### Hazard Ratio Model

Combined HR = Smoking × Alcohol × Sleep × Activity × BMI × Driving × WorkHours

Capped at 4.0 to prevent unrealistic compounding.

### Key Hazard Ratios

| Factor | Level | HR |
|--------|-------|-----|
| Smoking | Never | 1.0 |
| | Former | 1.3 |
| | Current (<10/day) | 1.8 |
| | Current (10-20/day) | 2.2 |
| | Current (>20/day) | 2.8 |
| Alcohol | None/Light | 1.0 |
| | Moderate (8-14/wk) | 1.1 |
| | Heavy (15-21/wk) | 1.3 |
| | Very Heavy (21+/wk) | 1.6 |
| Sleep | 7-8 hrs | 1.0 |
| | 6-7 hrs | 1.05 |
| | <6 hrs | 1.15 |
| | >8 hrs | 1.10 |
| | + Inconsistent | ×1.05 |
| Activity | High | 1.0 |
| | Moderate | 1.05 |
| | Light | 1.15 |
| | Sedentary | 1.4 |
| BMI | 18.5-25 | 1.0 |
| | 25-30 | 1.1 |
| | 30-35 | 1.2 |
| | 35-40 | 1.4 |
| | 40+ | 1.8 |
| Driving | <50 mi/wk | 1.0 |
| | 50-150 | 1.02 |
| | 150-300 | 1.04 |
| | 300+ | 1.08 |
| Work Hours | <40 | 1.0 |
| | 40-55 | 1.05 |
| | 55-70 | 1.15 |
| | 70+ | 1.3 |

### Existing Conditions (Non-modifiable)

| Condition | HR Multiplier |
|-----------|---------------|
| Cardiovascular | 1.5 |
| Diabetes | 1.4 |
| Cancer (active) | 2.0 |
| COPD | 1.6 |
| Hypertension | 1.2 |

### Delta Calculation

```dart
// Simplified Gompertz-style approximation
rawDeltaYears = baselineYears × (1 - modifiedHR/currentHR) × 0.3

// Convert to months with uncertainty range
lowMonths = rawDeltaYears × 12 × 0.6
highMonths = rawDeltaYears × 12 × 1.4
```

### Ranking Algorithm

1. For each modifiable behavior:
   - Compute HR with behavior set to optimal
   - Calculate delta months gained
2. Sort by midpoint delta descending
3. Filter out behaviors already at optimal
4. Return ranked list with confidence levels

### Confidence Levels

| Factor | Confidence | Rationale |
|--------|------------|-----------|
| Smoking | High | Extremely well-documented |
| Alcohol (heavy) | High | Strong epidemiological data |
| Physical Activity | High | Large meta-analyses |
| BMI (extreme) | High | Well-established |
| Sleep | Moderate | Growing evidence, some confounding |
| Work Hours | Moderate | Decent studies, cultural variation |
| Driving | Emerging | Harder to isolate, regional variation |

## Mortality Tables

### Country Groups

| Group | LE at Birth (M) | Countries |
|-------|-----------------|-----------|
| A | 81 | Japan, Switzerland, Singapore, Spain, Italy, Australia, Iceland, Israel, Sweden, France, South Korea, Norway |
| B | 77 | USA, UK, Germany, Canada, Netherlands, Belgium, Austria, Finland, Ireland, New Zealand, Denmark, Portugal, Czech Republic, Poland, Chile, Costa Rica, Cuba, UAE, Qatar, Taiwan |
| C | 72 | China, Brazil, Mexico, Russia, Turkey, Argentina, Colombia, Thailand, Vietnam, Malaysia, Iran, Saudi Arabia, Egypt, Ukraine, Romania, Hungary, Peru, Philippines |
| D | 65 | India, Indonesia, South Africa, Pakistan, Bangladesh, Nigeria, Kenya, Ghana, Ethiopia, Myanmar, Nepal, Cambodia |

Female LE = Male LE + 4.5 years

### Remaining Life Expectancy

```dart
// Survivors have higher LE than birth cohort suggests
survivorBonus = currentAge × 0.15  // capped at 5
remainingLE = (leAtBirth - currentAge) + survivorBonus
```

## Storage

### SQLite Schema

```sql
CREATE TABLE user_data (
  key TEXT PRIMARY KEY,
  value TEXT NOT NULL,      -- JSON
  updated_at INTEGER NOT NULL
)
```

### Stored Keys

- `profile`: UserProfile JSON
- `behaviors`: BehavioralInputs JSON
- `lastResult`: CalculationResult JSON

### Delete All Data

```dart
await db.delete('user_data');  // Wipes all rows
```

## UI Theme

### Colors (Muted Clinical)

```dart
primary:       #4A90A4  // Muted teal
primaryDark:   #2D6073
primaryLight:  #7BB8CC
surface:       #F8FAFB
textPrimary:   #1A2B33
textSecondary: #5A6B73
success:       #4A9A7C
warning:       #B8934A
error:         #A45A5A
```

### Typography

- Headlines: SF Pro Display style, tight letter-spacing
- Body: 16px, 1.5 line height
- Labels: 600 weight

## Testing

```bash
# Run all risk engine tests
flutter test test/risk_engine/

# 23 tests covering:
# - Hazard ratios for each behavior
# - Mortality table lookups
# - Combined HR calculation
# - Ranking algorithm
# - Confidence assignments
# - Existing conditions impact
```

Widget tests require SQLite mocking — integration test on device.

## App Icon

Generated programmatically: muted teal tree on white background.

```bash
dart run tool/generate_icon.dart
dart run flutter_launcher_icons
```

## Model Versioning

```dart
const modelVersion = '1.0';
```

Stored with each calculation result. Future updates can show:
"Results updated under model v1.1"

## Screen Flow

```
Welcome → Baseline → Behavioral → Results
              ↑                      ↓
              └────── Recalculate ───┘
```

Results screen shows:
- Dominant Challenge (largest gain)
- Estimated Gain range (e.g., "36-60 months")
- Confidence level (High/Moderate/Emerging)
- Secondary factor
- All other factors (if any)
- Delete All Data button

## Key Design Decisions

1. **BMI is baseline only** — affects calculation but not shown as a "challenge"
2. **Cigarettes/day** — slider with haptic at 20 (one pack), max 40
3. **Country** — full dropdown (50 countries), mapped internally to groups
4. **No gamification** — no streaks, badges, or progress tracking
5. **No notifications** — user controls when to recalculate

## Dependencies

```yaml
dependencies:
  sqflite: ^2.3.0         # Local database
  path: ^1.8.3            # Path utilities
  flutter_secure_storage: # Encryption key storage (future)

dev_dependencies:
  flutter_launcher_icons: ^0.14.1
```

## Future Enhancements (Out of MVP Scope)

- Partner mode (compare two profiles)
- Export PDF summary
- Drug use factor
- Diet quality factor
- Stress/mental health factor
- Location-based mortality refinement
- Longitudinal tracking
