/* app.jsx — CICO app shell */

const APP_DEFAULTS = {
  "scenario": "keeping",
  "showMarkers": true,
  "palette": "cream",
  "cycleLength": 3,
  "markerStyle": "number",
  "connector": "smooth"
};

const CICO_ONBOARDING_KEY = 'cico:user-v1';
const CICO_THEME_KEY = 'cico:theme-v1';

const THEME_OPTIONS = [
  { value: 'cream', label: 'Cream', colors: ['#F4EFE6', '#FAF6EE', '#2A1A10', '#B5421E'] },
  { value: 'bone', label: 'Bone', colors: ['#EFEEEA', '#F8F7F3', '#141413', '#5E8A4A'] },
  { value: 'slate', label: 'Slate', colors: ['#E4E9EE', '#F0F3F6', '#18222E', '#C9853A'] },
  { value: 'carbon', label: 'Carbon', colors: ['#15181E', '#1F232B', '#F0EAE0', '#D4583A'] },
];

function WebApp({
  initialTab = 'today',
  scenario,
  userData,
  showMarkers,
  markerStyle,
  connector,
  cycleLength,
  fullPage = false,
  tdee = TDEE,
  onUpdateDailyIntake,
  theme,
  onThemeChange,
  onSaveUserData,
  onImportUserData,
  onResetUserData,
}) {
  const [tab, setTab] = React.useState(initialTab);
  const unitSystem = userData?.settings?.unitSystem || 'metric';
  return (
    <WebShell activeTab={tab} onTabChange={setTab} cycleLength={cycleLength} fullPage={fullPage}>
      {tab === 'today'    && <WebToday    scenario={scenario} showMarkers={showMarkers} markerStyle={markerStyle} connector={connector} tdee={tdee} windowDays={cycleLength} onUpdateDailyIntake={onUpdateDailyIntake} unitSystem={unitSystem} />}
      {tab === 'log'      && <WebLog      scenario={scenario} tdee={tdee} windowDays={cycleLength} onUpdateDailyIntake={onUpdateDailyIntake} unitSystem={unitSystem} />}
      {tab === 'history'  && <WebHistory  scenario={scenario} tdee={tdee} windowDays={cycleLength} />}
      {tab === 'insights' && <WebInsights scenario={scenario} tdee={tdee} windowDays={cycleLength} userData={userData} unitSystem={unitSystem} />}
      {tab === 'settings' && <WebSettings userData={userData} theme={theme} onThemeChange={onThemeChange} onSave={onSaveUserData} onImport={onImportUserData} onReset={onResetUserData} />}
    </WebShell>
  );
}

function App() {
  const [theme, setTheme] = React.useState(() => loadThemePreference() || APP_DEFAULTS.palette);
  const params = new URLSearchParams(window.location.search);
  const shouldReset = params.has('reset');
  const didReset = React.useRef(false);
  if (shouldReset && !didReset.current) {
    localStorage.removeItem(CICO_ONBOARDING_KEY);
    localStorage.removeItem(CICO_THEME_KEY);
    didReset.current = true;
    window.history.replaceState(null, '', window.location.pathname);
  }
  const [userData, setUserData] = React.useState(() => loadOnboardingData());
  const settings = userData?.settings || buildDefaultSettings(theme);
  const N = normalizeWindowDays(settings.readingWindowDays);
  const profile = SCENARIO_PROFILES[APP_DEFAULTS.scenario] || SCENARIO_PROFILES.keeping;
  const demoScenario = React.useMemo(
    () => ({ label: profile.label, days: getScenarioDays(APP_DEFAULTS.scenario, N), dailyTotalOnly: false }),
    [profile.label, N]
  );
  const userScenario = React.useMemo(
    () => userData ? buildOnboardingScenario(userData) : null,
    [userData]
  );
  const scenario = userScenario || demoScenario;
  const activeTdee = userData?.energy?.tdee || TDEE;
  const activeTheme = settings.theme || theme;

  React.useEffect(() => {
    document.documentElement.dataset.theme = activeTheme;
  }, [activeTheme]);

  const handleThemeChange = React.useCallback((nextTheme) => {
    setTheme(nextTheme);
    localStorage.setItem(CICO_THEME_KEY, nextTheme);
    setUserData((current) => {
      if (!current) return current;
      const next = {
        ...current,
        settings: { ...buildDefaultSettings(nextTheme), ...(current.settings || {}), theme: nextTheme },
        updatedAt: new Date().toISOString(),
      };
      localStorage.setItem(CICO_ONBOARDING_KEY, JSON.stringify(next));
      return next;
    });
  }, []);

  const sharedProps = {
    scenario,
    userData,
    showMarkers: APP_DEFAULTS.showMarkers,
    markerStyle: APP_DEFAULTS.markerStyle,
    connector: APP_DEFAULTS.connector,
    cycleLength: N,
    tdee: activeTdee,
    onUpdateDailyIntake: updateDailyIntake,
    theme: activeTheme,
    onThemeChange: handleThemeChange,
    onSaveUserData: saveUserData,
    onImportUserData: importUserData,
    onResetUserData: resetUserData,
  };

  const handleOnboardingComplete = (nextData) => {
    saveUserData(normalizeUserData(nextData, theme));
  };

  function updateDailyIntake(indexOrDateKey, intake) {
    if (!userData) return;
    const nextData = updateStoredDailyIntake(userData, indexOrDateKey, intake);
    localStorage.setItem(CICO_ONBOARDING_KEY, JSON.stringify(nextData));
    setUserData(nextData);
  }

  function saveUserData(nextData) {
    const normalized = normalizeUserData(nextData, activeTheme);
    localStorage.setItem(CICO_ONBOARDING_KEY, JSON.stringify(normalized));
    localStorage.setItem(CICO_THEME_KEY, normalized.settings.theme);
    setTheme(normalized.settings.theme);
    setUserData(normalized);
  }

  function importUserData(nextData) {
    const normalized = normalizeUserData(nextData, activeTheme);
    saveUserData(normalized);
  }

  function resetUserData() {
    localStorage.removeItem(CICO_ONBOARDING_KEY);
    localStorage.removeItem(CICO_THEME_KEY);
    setUserData(null);
    setTheme(APP_DEFAULTS.palette);
  }

  if (!userData) {
    return <OnboardingFlow onComplete={handleOnboardingComplete} theme={activeTheme} onThemeChange={handleThemeChange} />;
  }
  return <WebApp initialTab="today" {...sharedProps} fullPage />;
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);

function loadOnboardingData() {
  try {
    const raw = localStorage.getItem(CICO_ONBOARDING_KEY);
    return raw ? normalizeUserData(JSON.parse(raw), loadThemePreference() || APP_DEFAULTS.palette) : null;
  } catch {
    return null;
  }
}

function loadThemePreference() {
  try {
    const theme = localStorage.getItem(CICO_THEME_KEY);
    return THEME_OPTIONS.some((option) => option.value === theme) ? theme : null;
  } catch {
    return null;
  }
}

function buildDefaultSettings(theme = APP_DEFAULTS.palette) {
  return {
    unitSystem: 'metric',
    readingWindowDays: APP_DEFAULTS.cycleLength,
    theme: THEME_OPTIONS.some((option) => option.value === theme) ? theme : APP_DEFAULTS.palette,
  };
}

function normalizeWindowDays(value) {
  return Math.max(3, Math.min(30, Math.round(Number(value) || APP_DEFAULTS.cycleLength)));
}

function normalizeProfile(profile = PROFILE) {
  const normalized = {
    age: Math.max(1, Math.round(Number(profile.age) || PROFILE.age)),
    sex: normalizeSex(profile.sex),
    heightCm: Math.max(1, Number(profile.heightCm ?? profile.height) || PROFILE.height),
    weightKg: Math.max(1, Number(profile.weightKg ?? profile.weight) || PROFILE.weight),
    activityLevel: ACTIVITY_MULTIPLIERS[profile.activityLevel] ? profile.activityLevel : PROFILE.activityLevel,
    bodyFatPercent: profile.bodyFatPercent === null || profile.bodyFatPercent === '' || !Number.isFinite(Number(profile.bodyFatPercent))
      ? null
      : Math.max(1, Math.min(99, Number(profile.bodyFatPercent))),
    tdeeOverride: profile.tdeeOverride === null || profile.tdeeOverride === '' || !Number.isFinite(Number(profile.tdeeOverride))
      ? null
      : Math.max(1, Math.round(Number(profile.tdeeOverride))),
  };
  return normalized;
}

function normalizeUserData(userData, fallbackTheme = APP_DEFAULTS.palette) {
  const profile = normalizeProfile(userData?.profile || PROFILE);
  const energy = getProfileEnergy(profile);
  const settings = {
    ...buildDefaultSettings(fallbackTheme),
    ...(userData?.settings || {}),
  };
  settings.readingWindowDays = normalizeWindowDays(settings.readingWindowDays);
  settings.theme = THEME_OPTIONS.some((option) => option.value === settings.theme) ? settings.theme : fallbackTheme;
  const sourceEntries = userData?.entries || userData?.intakes || [];
  const seenDates = new Set();
  const intakes = sourceEntries
    .filter((entry) => Number.isFinite(Number(entry?.intake)))
    .map((entry) => {
      const dateKey = toDateKey(entry.dateKey || entry.date || entry.label || new Date());
      return normalizeDailyEntry({
        ...entry,
        date: dateKey,
        dateKey,
        intake: Math.max(0, Math.round(Number(entry.intake))),
        intakeSource: 'logged',
      }, energy.tdee);
    })
    .filter((entry) => {
      if (seenDates.has(entry.dateKey)) return false;
      seenDates.add(entry.dateKey);
      return true;
    })
    .sort((a, b) => dateFromKey(a.dateKey) - dateFromKey(b.dateKey));
  return {
    version: 1,
    profile,
    energy,
    settings,
    intakes,
    entries: intakes,
    createdAt: userData?.createdAt || new Date().toISOString(),
    updatedAt: new Date().toISOString(),
  };
}

function buildOnboardingScenario(userData) {
  const normalized = normalizeUserData(userData);
  const tdee = normalized.energy.tdee;
  const todayKey = toDateKey(new Date());
  const firstLoggedKey = normalized.intakes[0]?.dateKey || addDaysToKey(todayKey, -2);
  const startKey = dateFromKey(firstLoggedKey) < dateFromKey(addDaysToKey(todayKey, -2))
    ? firstLoggedKey
    : addDaysToKey(todayKey, -2);
  const byDate = new Map(normalized.intakes.map((entry, index) => [entry.dateKey, { entry, index }]));
  const days = [];
  for (let key = startKey; dateFromKey(key) <= dateFromKey(todayKey); key = addDaysToKey(key, 1)) {
    const meta = dateMetaFromKey(key);
    const logged = byDate.get(key);
    if (logged) {
      days.push({
        ...meta,
        ...normalizeDailyEntry(logged.entry, tdee),
        date: key,
        dateKey: key,
        label: meta.label,
        dow: meta.dow,
        d: meta.d,
        dateObj: meta.dateObj,
        entryIndex: logged.index,
        dayStatus: 'logged',
      });
    } else {
      days.push({
        ...meta,
        intake: null,
        balance: null,
        tdee,
        intakeSource: 'empty',
        dayStatus: 'empty',
        entryIndex: null,
      });
    }
  }
  return {
    label: 'Your storage reading',
    days,
    dailyTotalOnly: true,
  };
}

function updateStoredDailyIntake(userData, indexOrDateKey, intake) {
  const normalizedData = normalizeUserData(userData);
  const tdee = normalizedData.energy.tdee;
  const numericIntake = Math.max(0, Math.round(Number(intake) || 0));
  const targetKey = typeof indexOrDateKey === 'string'
    ? toDateKey(indexOrDateKey)
    : normalizedData.intakes[indexOrDateKey]?.dateKey;
  if (!targetKey) return userData;
  const currentIndex = normalizedData.intakes.findIndex((entry) => entry.dateKey === targetKey);
  const meta = dateMetaFromKey(targetKey);
  const currentEntry = currentIndex >= 0 ? normalizedData.intakes[currentIndex] : meta;
  const normalized = normalizeDailyEntry({
    ...currentEntry,
    ...meta,
    intake: numericIntake,
    intakeSource: 'logged',
  }, tdee);
  const intakes = currentIndex >= 0
    ? normalizedData.intakes.map((entry, i) => i === currentIndex ? normalized : entry)
    : [...normalizedData.intakes, normalized].sort((a, b) => dateFromKey(a.dateKey) - dateFromKey(b.dateKey));
  return {
    ...normalizedData,
    intakes,
    entries: intakes,
    updatedAt: new Date().toISOString(),
  };
}

function getRecentDateLabels() {
  const today = new Date();
  return [2, 1, 0].map((daysAgo) => {
    const date = new Date(today.getTime() - daysAgo * 86400000);
    return dateMetaFromKey(toDateKey(date));
  });
}

function OnboardingFlow({ onComplete, theme, onThemeChange }) {
  const isNarrow = useIsNarrow();
  const dateLabels = React.useMemo(() => getRecentDateLabels(), []);
  const demoDays = React.useMemo(() => getScenarioDays('keeping', 3), []);
  const [form, setForm] = React.useState({
    age: String(PROFILE.age),
    sex: PROFILE.sex,
    height: String(PROFILE.height),
    weight: String(PROFILE.weight),
    activityLevel: PROFILE.activityLevel,
    bodyFatPercent: '',
    tdeeOverride: '',
    intakes: demoDays.map((d) => String(d.intake)),
  });

  const profile = React.useMemo(() => ({
    age: Number(form.age),
    sex: form.sex,
    heightCm: Number(form.height),
    weightKg: Number(form.weight),
    activityLevel: form.activityLevel,
    bodyFatPercent: form.bodyFatPercent === '' ? null : Number(form.bodyFatPercent),
    tdeeOverride: form.tdeeOverride === '' ? null : Number(form.tdeeOverride),
  }), [form]);

  const energy = React.useMemo(() => getProfileEnergy(profile), [profile]);
  const previewDays = React.useMemo(() => (
    dateLabels.map((dateMeta, index) => normalizeDailyEntry({
      ...dateMeta,
      intake: Number(form.intakes[index]) || energy.tdee,
      intakeSource: 'logged',
    }, energy.tdee))
  ), [dateLabels, form.intakes, energy.tdee]);
  const reading = React.useMemo(() => getBalanceReading(previewDays, 3, energy.tdee), [previewDays, energy.tdee]);

  const setField = (key, value) => setForm((current) => ({ ...current, [key]: value }));
  const setIntake = (index, value) => setForm((current) => ({
    ...current,
    intakes: current.intakes.map((item, i) => i === index ? value : item),
  }));

  const complete = () => {
    const settings = buildDefaultSettings(theme);
    const nextData = {
      version: 1,
      profile,
      energy,
      settings,
      intakes: previewDays,
      entries: previewDays,
      createdAt: new Date().toISOString(),
      updatedAt: new Date().toISOString(),
    };
    onComplete(nextData);
  };

  return (
    <main style={{
      minHeight: '100vh',
      background: 'var(--paper)',
      color: 'var(--ink)',
      fontFamily: "'Geist', system-ui, sans-serif",
      position: 'relative',
      overflow: 'auto',
    }}>
      <div style={{
        position: 'absolute', inset: 0,
        backgroundImage: 'radial-gradient(circle at 1px 1px, var(--grid) 1px, transparent 0)',
        backgroundSize: '20px 20px',
        pointerEvents: 'none',
      }} />
      <div style={{
        position: 'relative', zIndex: 1,
        minHeight: '100vh',
        display: 'grid',
        gridTemplateColumns: isNarrow ? '1fr' : 'minmax(520px, 1fr) 430px',
        gap: isNarrow ? 24 : 48,
        padding: isNarrow ? '22px 18px' : '44px 56px',
        boxSizing: 'border-box',
      }}>
        <section style={{ display: 'flex', flexDirection: 'column', gap: 26 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 18 }}>
            <Wordmark size={34} />
            <ThemePicker value={theme} onChange={onThemeChange} />
          </div>
          <div>
            <div className="mono" style={{ fontSize: 11, color: 'var(--ink-3)', letterSpacing: '0.12em', textTransform: 'uppercase' }}>
              first reading
            </div>
            <h1 style={{
              margin: '10px 0 0',
              fontFamily: "'Instrument Serif', serif",
              fontSize: isNarrow ? 46 : 68,
              lineHeight: 0.94,
              fontWeight: 400,
              maxWidth: 620,
            }}>
              set your maintenance.
            </h1>
          </div>

          <div style={{ display: 'grid', gridTemplateColumns: isNarrow ? '1fr' : 'repeat(2, minmax(0, 1fr))', gap: 14, maxWidth: 720 }}>
            <Field label="age" value={form.age} onChange={(v) => setField('age', v)} />
            <SelectField label="sex" value={form.sex} onChange={(v) => setField('sex', v)}
              options={[['male', 'Male'], ['female', 'Female']]} />
            <Field label="height" value={form.height} unit="cm" onChange={(v) => setField('height', v)} />
            <Field label="weight" value={form.weight} unit="kg" onChange={(v) => setField('weight', v)} />
            <SelectField label="activity" value={form.activityLevel} onChange={(v) => setField('activityLevel', v)}
              options={[
                ['sedentary', 'Sedentary'],
                ['lightly_active', 'Lightly active'],
                ['moderately_active', 'Moderately active'],
                ['very_active', 'Very active'],
                ['athlete', 'Athlete'],
              ]} />
            <Field label="body fat" value={form.bodyFatPercent} unit="%" placeholder="optional" onChange={(v) => setField('bodyFatPercent', v)} />
            <Field label="manual TDEE" value={form.tdeeOverride} unit="kcal" placeholder="optional" onChange={(v) => setField('tdeeOverride', v)} wide />
          </div>

          <div style={{ maxWidth: 720 }}>
            <SectionHead title="last three days" right="daily intake" />
            <div style={{ display: 'grid', gridTemplateColumns: isNarrow ? '1fr' : 'repeat(3, minmax(0, 1fr))', gap: 12, marginTop: 10 }}>
              {dateLabels.map((dateMeta, index) => (
                <div key={dateMeta.label} style={{
                  background: 'var(--paper-2)', border: '1px solid var(--hair)',
                  borderRadius: 12, padding: 14,
                }}>
                  <div className="mono" style={{ fontSize: 10.5, color: 'var(--ink-3)', letterSpacing: '0.08em', textTransform: 'uppercase' }}>
                    {dateMeta.label}
                  </div>
                  <input
                    inputMode="numeric"
                    value={form.intakes[index]}
                    onChange={(e) => setIntake(index, e.target.value)}
                    style={onboardingInputStyle({ fontSize: 26, marginTop: 8 })}
                  />
                  <div className="mono" style={{ marginTop: 3, fontSize: 10.5, color: 'var(--ink-3)' }}>kcal</div>
                </div>
              ))}
            </div>
          </div>
        </section>

        <aside style={{ alignSelf: isNarrow ? 'stretch' : 'center', display: 'flex', flexDirection: 'column', gap: 16 }}>
          <div style={{
            background: 'var(--paper-2)', border: '1px solid var(--hair)',
            borderRadius: 14, padding: 22,
          }}>
            <SectionHead title="calculated maintenance" right={form.tdeeOverride ? 'manual' : 'formula'} />
            <div className="mono num-tab" style={{ fontSize: 56, lineHeight: 1, marginTop: 18, color: 'var(--ink)' }}>
              {energy.tdee.toLocaleString()}
            </div>
            <div className="mono" style={{ marginTop: 4, fontSize: 12, color: 'var(--ink-3)' }}>kcal / day TDEE</div>
            <div style={{ marginTop: 16, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
              <OnboardingStat label="BMR" value={energy.bmr} unit="kcal" />
              <OnboardingStat label="Band" value={`+/-${Math.round(reading.threshold)}`} unit="kcal" />
            </div>
          </div>

          <div style={{
            background: 'var(--paper-2)', border: '1px solid var(--hair)',
            borderRadius: 14, padding: 22,
          }}>
            <SectionHead title="preview" right={reading.confidence} />
            <div style={{
              fontFamily: "'Instrument Serif', serif",
              fontSize: 44, lineHeight: 1,
              color: ZONES[reading.state].hex,
              fontStyle: 'italic',
              marginTop: 14,
            }}>
              {reading.state}.
            </div>
            <p style={{ margin: '12px 0 0', color: 'var(--ink-2)', fontSize: 14, lineHeight: 1.45 }}>
              {todayStatusBody(reading)}
            </p>
          </div>

          <button onClick={complete} style={{
            width: '100%',
            background: 'var(--ink)', color: 'var(--paper-2)',
            border: 'none', borderRadius: 12, padding: '15px 18px',
            fontFamily: "'Geist', system-ui, sans-serif",
            fontSize: 14, fontWeight: 600, cursor: 'pointer',
            boxShadow: '0 1px 0 var(--btn-shine) inset, 0 8px 20px var(--shadow-cta)',
          }}>
            enter today
          </button>
        </aside>
      </div>
    </main>
  );
}

function Field({ label, value, onChange, unit, placeholder, wide = false }) {
  return (
    <label style={{
      gridColumn: wide ? 'span 2' : undefined,
      background: 'var(--paper-2)', border: '1px solid var(--hair)',
      borderRadius: 12, padding: '11px 13px',
      display: 'flex', flexDirection: 'column', gap: 7,
    }}>
      <span className="mono" style={{ fontSize: 10.5, color: 'var(--ink-3)', letterSpacing: '0.08em', textTransform: 'uppercase' }}>{label}</span>
      <div style={{ display: 'flex', alignItems: 'baseline', gap: 8 }}>
        <input inputMode="numeric" value={value} placeholder={placeholder || ''}
          onChange={(e) => onChange(e.target.value)}
          style={onboardingInputStyle()} />
        {unit && <span className="mono" style={{ fontSize: 12, color: 'var(--ink-3)' }}>{unit}</span>}
      </div>
    </label>
  );
}

function SelectField({ label, value, onChange, options }) {
  return (
    <label style={{
      background: 'var(--paper-2)', border: '1px solid var(--hair)',
      borderRadius: 12, padding: '11px 13px',
      display: 'flex', flexDirection: 'column', gap: 7,
    }}>
      <span className="mono" style={{ fontSize: 10.5, color: 'var(--ink-3)', letterSpacing: '0.08em', textTransform: 'uppercase' }}>{label}</span>
      <select value={value} onChange={(e) => onChange(e.target.value)}
        style={{ ...onboardingInputStyle(), appearance: 'none', cursor: 'pointer' }}>
        {options.map(([v, l]) => <option key={v} value={v}>{l}</option>)}
      </select>
    </label>
  );
}

function OnboardingStat({ label, value, unit }) {
  return (
    <div style={{ borderTop: '1px solid var(--hair)', paddingTop: 10 }}>
      <div className="mono" style={{ fontSize: 10, color: 'var(--ink-3)', letterSpacing: '0.08em', textTransform: 'uppercase' }}>{label}</div>
      <div className="mono num-tab" style={{ marginTop: 4, fontSize: 20, color: 'var(--ink)' }}>{value}</div>
      <div className="mono" style={{ fontSize: 10, color: 'var(--ink-3)' }}>{unit}</div>
    </div>
  );
}

function WebSettings({ userData, theme, onThemeChange, onSave, onImport, onReset }) {
  const isNarrow = useIsNarrow();
  const data = React.useMemo(() => normalizeUserData(userData, theme), [userData, theme]);
  const [form, setForm] = React.useState(() => settingsFormFromData(data));
  const [importMessage, setImportMessage] = React.useState('');
  const [saveMessage, setSaveMessage] = React.useState('');
  const fileRef = React.useRef(null);

  React.useEffect(() => {
    setForm(settingsFormFromData(data));
  }, [data.updatedAt, theme]);

  const profile = React.useMemo(() => normalizeProfile({
    age: form.age,
    sex: form.sex,
    heightCm: form.unitSystem === 'imperial' ? inToCm(Number(form.height)) : Number(form.height),
    weightKg: form.unitSystem === 'imperial' ? lbToKg(Number(form.weight)) : Number(form.weight),
    activityLevel: form.activityLevel,
    bodyFatPercent: form.bodyFatPercent,
    tdeeOverride: form.tdeeOverride,
  }), [form]);
  const energy = React.useMemo(() => getProfileEnergy(profile), [profile]);
  const setField = (key, value) => setForm((current) => {
    setSaveMessage('');
    if (key === 'unitSystem' && value !== current.unitSystem) {
      const heightCm = current.unitSystem === 'imperial' ? inToCm(Number(current.height)) : Number(current.height);
      const weightKg = current.unitSystem === 'imperial' ? lbToKg(Number(current.weight)) : Number(current.weight);
      return {
        ...current,
        unitSystem: value,
        height: String(displayHeightValue(heightCm, value)),
        weight: String(displayWeightValue(weightKg, value)),
      };
    }
    return { ...current, [key]: value };
  });

  const save = () => {
    const settings = {
      unitSystem: form.unitSystem,
      readingWindowDays: normalizeWindowDays(form.readingWindowDays),
      theme,
    };
    const intakes = data.intakes.map((entry) => normalizeDailyEntry(entry, energy.tdee));
    onSave({
      ...data,
      profile,
      energy,
      settings,
      intakes,
      entries: intakes,
      updatedAt: new Date().toISOString(),
    });
    setImportMessage('');
    setSaveMessage('saved');
  };

  const exportData = () => {
    const settings = {
      unitSystem: form.unitSystem,
      readingWindowDays: normalizeWindowDays(form.readingWindowDays),
      theme,
    };
    const payload = JSON.stringify(normalizeUserData({ ...data, profile, energy, settings }, theme), null, 2);
    const blob = new Blob([payload], { type: 'application/json' });
    const url = URL.createObjectURL(blob);
    const anchor = document.createElement('a');
    anchor.href = url;
    anchor.download = `cico-local-${new Date().toISOString().slice(0, 10)}.json`;
    document.body.appendChild(anchor);
    anchor.click();
    anchor.remove();
    URL.revokeObjectURL(url);
  };

  const importFile = (file) => {
    if (!file) return;
    const reader = new FileReader();
    reader.onload = () => {
      try {
        const parsed = JSON.parse(String(reader.result || ''));
        const normalized = normalizeUserData(parsed, theme);
        if (!normalized.intakes.length) throw new Error('No daily entries found.');
        onImport(normalized);
        setImportMessage('imported');
      } catch (error) {
        setImportMessage('invalid file');
      }
    };
    reader.readAsText(file);
  };

  const reset = () => {
    if (window.confirm('Reset local CICO data on this device?')) {
      onReset();
    }
  };

  return (
    <div style={{ height: isNarrow ? 'auto' : '100%', overflow: 'auto', padding: isNarrow ? '18px' : '20px 40px 28px', boxSizing: 'border-box' }}>
      <SectionEyebrow left="local settings" right="no account · no sync" />
      <div style={{ display: 'grid', gridTemplateColumns: isNarrow ? '1fr' : 'minmax(0, 1fr) 380px', gap: 24, marginTop: 18 }}>
        <section style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
          <SettingsPanel title="profile" right={form.tdeeOverride ? 'manual tdee' : 'formula'}>
            <div style={{ display: 'grid', gridTemplateColumns: isNarrow ? '1fr' : 'repeat(2, minmax(0, 1fr))', gap: 12 }}>
              <Field label="age" value={form.age} onChange={(v) => setField('age', v)} />
              <SelectField label="sex" value={form.sex} onChange={(v) => setField('sex', v)}
                options={[['male', 'Male'], ['female', 'Female']]} />
              <Field label="height" value={form.height} unit={heightUnit(form.unitSystem)} onChange={(v) => setField('height', v)} />
              <Field label="weight" value={form.weight} unit={weightUnit(form.unitSystem)} onChange={(v) => setField('weight', v)} />
              <SelectField label="activity" value={form.activityLevel} onChange={(v) => setField('activityLevel', v)}
                options={[
                  ['sedentary', 'Sedentary'],
                  ['lightly_active', 'Lightly active'],
                  ['moderately_active', 'Moderately active'],
                  ['very_active', 'Very active'],
                  ['athlete', 'Athlete'],
                ]} />
              <Field label="body fat" value={form.bodyFatPercent} unit="%" placeholder="optional" onChange={(v) => setField('bodyFatPercent', v)} />
              <Field label="manual TDEE" value={form.tdeeOverride} unit="kcal" placeholder="optional" wide onChange={(v) => setField('tdeeOverride', v)} />
            </div>
          </SettingsPanel>

          <SettingsPanel title="reading" right="minimum 3 days">
            <div style={{ display: 'grid', gridTemplateColumns: isNarrow ? '1fr' : 'repeat(2, minmax(0, 1fr))', gap: 12 }}>
              <Field label="window" value={form.readingWindowDays} unit="days" onChange={(v) => setField('readingWindowDays', v)} />
              <SelectField label="units" value={form.unitSystem} onChange={(v) => setField('unitSystem', v)}
                options={[['metric', 'Metric'], ['imperial', 'Imperial']]} />
            </div>
          </SettingsPanel>

          <SettingsPanel title="theme">
            <ThemePicker value={theme} onChange={onThemeChange} />
          </SettingsPanel>
        </section>

        <aside style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
          <SettingsPanel title="maintenance preview" right={form.tdeeOverride ? 'manual' : 'formula'}>
            <div className="mono num-tab" style={{ fontSize: 52, lineHeight: 1, color: 'var(--ink)', marginTop: 12 }}>
              {energy.tdee.toLocaleString()}
            </div>
            <div className="mono" style={{ marginTop: 4, fontSize: 12, color: 'var(--ink-3)' }}>kcal / day TDEE</div>
            <div style={{ marginTop: 16, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
              <OnboardingStat label="BMR" value={energy.bmr} unit="kcal" />
              <OnboardingStat label="Band" value={`+/-${energy.threshold}`} unit="kcal" />
            </div>
          </SettingsPanel>

          <SettingsPanel title="local data" right="this device">
            <div style={{ display: 'grid', gap: 10 }}>
              <button onClick={save} style={settingsButtonStyle()}>save settings</button>
              {saveMessage && <div className="mono" style={{ fontSize: 11, color: 'var(--moss)' }}>{saveMessage}</div>}
              <button onClick={exportData} style={settingsButtonStyle(true)}>export JSON</button>
              <button onClick={() => fileRef.current?.click()} style={settingsButtonStyle(true)}>import JSON</button>
              <input
                ref={fileRef}
                type="file"
                accept="application/json,.json"
                style={{ display: 'none' }}
                onChange={(e) => importFile(e.target.files?.[0])}
              />
              <button onClick={reset} style={{ ...settingsButtonStyle(true), color: 'var(--rust)' }}>reset local data</button>
              {importMessage && <div className="mono" style={{ fontSize: 11, color: 'var(--ink-3)' }}>{importMessage}</div>}
            </div>
          </SettingsPanel>
        </aside>
      </div>
    </div>
  );
}

function settingsFormFromData(data) {
  const unitSystem = data.settings.unitSystem || 'metric';
  return {
    age: String(data.profile.age),
    sex: data.profile.sex,
    height: String(displayHeightValue(data.profile.heightCm, unitSystem)),
    weight: String(displayWeightValue(data.profile.weightKg, unitSystem)),
    activityLevel: data.profile.activityLevel,
    bodyFatPercent: data.profile.bodyFatPercent == null ? '' : String(data.profile.bodyFatPercent),
    tdeeOverride: data.profile.tdeeOverride == null ? '' : String(data.profile.tdeeOverride),
    readingWindowDays: String(data.settings.readingWindowDays),
    unitSystem,
  };
}

function SettingsPanel({ title, right, children }) {
  return (
    <div style={{
      background: 'var(--paper-2)', border: '1px solid var(--hair)',
      borderRadius: 14, padding: 18,
    }}>
      <SectionHead title={title} right={right} />
      <div style={{ marginTop: 12 }}>{children}</div>
    </div>
  );
}

function settingsButtonStyle(secondary = false) {
  return {
    width: '100%',
    border: secondary ? '1px solid var(--hair)' : 'none',
    background: secondary ? 'var(--paper-2)' : 'var(--ink)',
    color: secondary ? 'var(--ink)' : 'var(--paper-2)',
    borderRadius: 10,
    padding: '12px 14px',
    fontFamily: "'Geist', system-ui, sans-serif",
    fontSize: 13,
    fontWeight: 600,
    cursor: 'pointer',
  };
}

function Wordmark({ size = 34 }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: size * 0.16 }}>
      <span style={{ fontFamily: "'Instrument Serif', serif", fontSize: size, lineHeight: 1, color: 'var(--ink)' }}>cico</span>
      <span style={{ width: size * 0.22, height: size * 0.22, borderRadius: 99, background: 'var(--rust)', transform: 'translateY(-3px)' }} />
    </div>
  );
}

function ThemePicker({ value = 'cream', onChange }) {
  return (
    <div role="radiogroup" aria-label="Theme" style={{
      display: 'flex', alignItems: 'center', gap: 6,
      background: 'var(--paper-2)',
      border: '1px solid var(--hair)',
      borderRadius: 999,
      padding: 5,
      boxShadow: '0 8px 22px var(--hair-faint)',
    }}>
      <span className="mono" style={{
        fontSize: 10,
        color: 'var(--ink-3)',
        letterSpacing: '0.08em',
        textTransform: 'uppercase',
        padding: '0 4px 0 7px',
      }}>
        theme
      </span>
      {THEME_OPTIONS.map((option) => {
        const active = option.value === value;
        return (
          <button
            key={option.value}
            type="button"
            role="radio"
            aria-checked={active}
            aria-label={`${option.label} theme`}
            title={option.label}
            onClick={() => onChange && onChange(option.value)}
            style={{
              width: 31,
              height: 31,
              border: active ? '2px solid var(--ink)' : '1px solid var(--hair-strong)',
              borderRadius: 999,
              padding: 3,
              background: 'var(--paper-2)',
              cursor: 'pointer',
              display: 'grid',
              gridTemplateColumns: '1fr 1fr',
              gridTemplateRows: '1fr 1fr',
              gap: 1,
              boxShadow: active ? '0 0 0 2px var(--paper-2), 0 0 0 3px var(--ink)' : 'none',
            }}
          >
            {option.colors.map((color, index) => (
              <span
                key={color}
                style={{
                  background: color,
                  borderRadius: index === 0 ? '99px 2px 2px 2px'
                    : index === 1 ? '2px 99px 2px 2px'
                      : index === 2 ? '2px 2px 2px 99px'
                        : '2px 2px 99px 2px',
                }}
              />
            ))}
          </button>
        );
      })}
    </div>
  );
}

function onboardingInputStyle(extra = {}) {
  return {
    width: '100%',
    border: 'none',
    outline: 'none',
    background: 'transparent',
    color: 'var(--ink)',
    fontFamily: "'JetBrains Mono', monospace",
    fontSize: 18,
    fontVariantNumeric: 'tabular-nums',
    minWidth: 0,
    ...extra,
  };
}
