// Core screens: Login, Home, Goal, Accounts.

// ═══ LOGIN ═══════════════════════════════════════
function LoginScreen({ dark, onLogin }) {
  const t = T(dark);
  const [step, setStep] = React.useState('email');
  const [email, setEmail] = React.useState(() => {
    try { return localStorage.getItem('money:email') || 'alex@mail.ru'; } catch { return 'alex@mail.ru'; }
  });
  const [code, setCode] = React.useState('');
  const codeInputRef = React.useRef(null);

  const next = () => {
    if (step === 'email') {
      if (!email || !email.includes('@')) return;
      try { localStorage.setItem('money:email', email); } catch {}
      setStep('code');
      setTimeout(() => codeInputRef.current && codeInputRef.current.focus(), 200);
    } else {
      if (code.length < 6) return;
      try {
        localStorage.setItem('money:loggedIn', 'true');
        const cur = JSON.parse(localStorage.getItem('money:settings') || '{}');
        if (!cur.displayName) {
          cur.displayName = (email.split('@')[0] || 'Саша').replace(/[._-]/g, ' ').replace(/\b\w/g, m => m.toUpperCase());
          localStorage.setItem('money:settings', JSON.stringify(cur));
        }
      } catch {}
      onLogin && onLogin();
    }
  };

  return (
    <div style={{ position: 'absolute', inset: 0, top: 44, padding: '40px 28px 30px', display: 'flex', flexDirection: 'column' }}>
      <div style={{
        width: 64, height: 64, borderRadius: 22,
        background: `linear-gradient(135deg, ${t.accent}, ${dark ? '#2A8C66' : '#0B6B47'})`,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        boxShadow: `0 14px 32px ${t.accent}55`,
      }}>
        <div style={{ fontFamily: TOK.fontDisplay, fontSize: 32, fontWeight: 800, color: '#fff', letterSpacing: -1 }}>M</div>
      </div>

      <div style={{ marginTop: 32 }}>
        <div style={{ fontFamily: TOK.fontDisplay, fontSize: 38, fontWeight: 800, color: t.ink, lineHeight: 1.05, letterSpacing: -1.2 }}>
          {step === 'email' ? <>С возвращением.<br/><span style={{ color: t.accent }}>Money.</span></> : <>Проверьте<br/>почту.</>}
        </div>
        <div style={{ marginTop: 14, fontSize: 15, color: t.muted, lineHeight: 1.5, maxWidth: 320 }}>
          {step === 'email'
            ? 'Войдите по email — пришлём одноразовый код. Никаких паролей.'
            : <>Код отправлен на <b style={{ color: t.ink }}>{email}</b>. Введите 6 цифр.</>}
        </div>
      </div>

      <div style={{ marginTop: 40 }}>
        {step === 'email' ? (
          <>
            <div style={{ fontSize: 12, fontWeight: 700, color: t.muted, textTransform: 'uppercase', letterSpacing: 0.6, marginBottom: 8 }}>Email</div>
            <Card dark={dark} pad={0} radius={16} flat style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '14px 16px', border: `1.5px solid ${t.accent}55`, background: t.surface }}>
              <Icon d={ICONS.mail} size={20} stroke={t.muted} sw={1.8} />
              <input value={email} onChange={e => setEmail(e.target.value)} type="email" autoComplete="email"
                onKeyDown={e => { if (e.key === 'Enter') next(); }}
                style={{
                  flex: 1, border: 'none', outline: 'none', fontFamily: 'inherit', fontSize: 16, fontWeight: 600,
                  color: t.ink, background: 'transparent', width: '100%',
                }} />
            </Card>
          </>
        ) : (
          <>
            <div style={{ fontSize: 12, fontWeight: 700, color: t.muted, textTransform: 'uppercase', letterSpacing: 0.6, marginBottom: 8 }}>Код из письма</div>
            <div style={{ position: 'relative' }}>
              <div style={{ display: 'flex', gap: 8 }}>
                {[0,1,2,3,4,5].map(i => (
                  <div key={i} style={{
                    flex: 1, height: 56, borderRadius: 14,
                    background: t.surface, border: `1.5px solid ${i === code.length ? t.accent : t.line}`,
                    display: 'flex', alignItems: 'center', justifyContent: 'center',
                    fontFamily: TOK.fontDisplay, fontSize: 24, fontWeight: 800, color: t.ink,
                  }}>{code[i] || ''}</div>
                ))}
              </div>
              <input ref={codeInputRef} value={code} inputMode="numeric" pattern="[0-9]*" autoFocus
                onChange={e => setCode(e.target.value.replace(/\D/g,'').slice(0, 6))}
                onKeyDown={e => { if (e.key === 'Enter') next(); }}
                style={{ position: 'absolute', inset: 0, opacity: 0, fontSize: 24, padding: 0, border: 'none' }} />
            </div>
            <div style={{ marginTop: 12, fontSize: 13, color: t.muted, textAlign: 'center' }}>
              Не пришло? <span style={{ color: t.accent, fontWeight: 700, cursor: 'pointer' }} onClick={() => setCode('')}>Отправить снова</span>
            </div>
            <div style={{ marginTop: 8, fontSize: 11, color: t.faint, textAlign: 'center' }}>
              (демо: введите любые 6 цифр)
            </div>
          </>
        )}
      </div>

      <div style={{ flex: 1 }} />

      <Btn dark={dark} primary full onClick={next} icon="arrowRight">
        {step === 'email' ? 'Получить код' : 'Войти'}
      </Btn>

      <div style={{ marginTop: 18, textAlign: 'center', fontSize: 12, color: t.muted }}>
        Нажимая «Войти», вы соглашаетесь с условиями.
      </div>
    </div>
  );
}

// 3D-ish rotating sphere of category bubbles.
function CategorySphere({ dark, onNav, cats }) {
  const t = T(dark);
  const all = (cats || DATA.categories).filter(c => !c.archived && c.spent > 0);
  const rotRef = React.useRef({ rx: -0.25, ry: 0.4 });
  const dragRef = React.useRef(null);
  const lastTick = React.useRef(Date.now());
  const [, force] = React.useReducer(x => x + 1, 0);

  React.useEffect(() => {
    let raf;
    const loop = () => {
      const now = Date.now();
      const dt = Math.min(50, now - lastTick.current);
      lastTick.current = now;
      if (!dragRef.current) rotRef.current.ry += dt * 0.0003;
      force();
      raf = requestAnimationFrame(loop);
    };
    raf = requestAnimationFrame(loop);
    return () => cancelAnimationFrame(raf);
  }, []);

  const W = 372, H = 240, R = 96;
  const cx = W / 2, cy = H / 2;

  if (!all.length) {
    return (
      <div style={{ padding: '0 20px', marginBottom: 4 }}>
        <Card dark={dark} pad={28} radius={20} style={{ textAlign: 'center' }}>
          <div style={{ fontSize: 32, marginBottom: 8 }}>🪐</div>
          <div style={{ fontSize: 14, fontWeight: 700, color: t.ink }}>Пока нет трат в этом месяце</div>
          <div style={{ fontSize: 12, color: t.muted, marginTop: 6 }}>Добавьте первую — она появится здесь.</div>
        </Card>
      </div>
    );
  }

  const maxSpent = Math.max(...all.map(c => c.spent));
  const positions = all.map((c, i) => {
    const phi = Math.acos(1 - 2 * (i + 0.5) / all.length);
    const theta = Math.PI * (1 + Math.sqrt(5)) * (i + 0.5);
    const x0 = Math.sin(phi) * Math.cos(theta);
    const y0 = Math.sin(phi) * Math.sin(theta);
    const z0 = Math.cos(phi);
    const { rx, ry } = rotRef.current;
    let x1 = x0 * Math.cos(ry) - z0 * Math.sin(ry);
    let z1 = x0 * Math.sin(ry) + z0 * Math.cos(ry);
    let y1 = y0;
    let y2 = y1 * Math.cos(rx) - z1 * Math.sin(rx);
    let z2 = y1 * Math.sin(rx) + z1 * Math.cos(rx);
    return { cat: c, x: x1, y: y2, z: z2 };
  });
  positions.sort((a, b) => a.z - b.z);

  const onPointerDown = (e) => {
    dragRef.current = { sx: e.clientX, sy: e.clientY, ry: rotRef.current.ry, rx: rotRef.current.rx };
    e.currentTarget.setPointerCapture(e.pointerId);
  };
  const onPointerMove = (e) => {
    if (!dragRef.current) return;
    const dx = e.clientX - dragRef.current.sx;
    const dy = e.clientY - dragRef.current.sy;
    rotRef.current = {
      ry: dragRef.current.ry + dx * 0.01,
      rx: Math.max(-1.2, Math.min(1.2, dragRef.current.rx - dy * 0.01)),
    };
  };
  const onPointerUp = (e) => {
    dragRef.current = null;
    try { e.currentTarget.releasePointerCapture(e.pointerId); } catch {}
  };

  const sizeOf = (spent) => 38 + (spent / maxSpent) * 42;

  return (
    <div style={{ padding: '0 20px', marginBottom: 4 }}>
      <div onPointerDown={onPointerDown} onPointerMove={onPointerMove} onPointerUp={onPointerUp} onPointerCancel={onPointerUp}
        style={{
          position: 'relative', width: '100%', height: H,
          touchAction: 'none', cursor: dragRef.current ? 'grabbing' : 'grab',
          userSelect: 'none', overflow: 'visible',
        }}>
        <div style={{
          position: 'absolute', left: cx - R, top: cy - R, width: R * 2, height: R * 2,
          borderRadius: '50%',
          background: `radial-gradient(circle at 35% 30%, ${t.accent}11, transparent 70%)`,
          pointerEvents: 'none',
        }} />
        {positions.map(({ cat, x, y, z }) => {
          const depth = (z + 1) / 2;
          const scale = 0.55 + depth * 0.55;
          const size = sizeOf(cat.spent) * scale;
          const px = cx + x * R - size / 2;
          const py = cy + y * R - size / 2;
          const opacity = 0.32 + depth * 0.68;
          return (
            <button key={cat.id}
              onClick={(e) => { e.stopPropagation(); onNav && onNav('analytics'); }}
              style={{
                position: 'absolute', left: px, top: py, width: size, height: size,
                borderRadius: '50%', background: cat.color, border: 'none', cursor: 'pointer',
                color: '#fff', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
                boxShadow: `0 ${2 + depth * 14}px ${8 + depth * 20}px rgba(0,0,0,${0.05 + depth * 0.25})`,
                padding: 2, fontFamily: 'inherit', opacity,
                zIndex: Math.round(depth * 100) + 10,
                transition: 'none',
              }} title={`${cat.title} · ${fmtR(cat.spent)} ₽`}>
              {cat.emoji
                ? <div style={{ fontSize: size * 0.4, lineHeight: 1 }}>{cat.emoji}</div>
                : <Icon d={ICONS[cat.icon]} size={size * 0.32} stroke="#fff" sw={2} />}
              {size > 48 && (
                <div style={{
                  fontSize: Math.max(9, size * 0.14), fontWeight: 800, marginTop: 1,
                  fontFamily: TOK.fontDisplay, fontVariantNumeric: 'tabular-nums', lineHeight: 1,
                }}>{fmtCompact(cat.spent)}</div>
              )}
            </button>
          );
        })}
        <div style={{ position: 'absolute', left: 4, bottom: 4, fontSize: 10, color: t.muted, fontWeight: 700, letterSpacing: 0.3, pointerEvents: 'none' }}>
          {all.length} КАТЕГОРИЙ · КРУТИ
        </div>
        <div style={{ position: 'absolute', right: 4, bottom: 4, fontSize: 10, color: t.muted, fontWeight: 700, fontVariantNumeric: 'tabular-nums', pointerEvents: 'none' }}>
          {fmtR(DATA.expense.actual)} ₽
        </div>
      </div>
    </div>
  );
}

// ═══ HOME ════════════════════════════════════════
function HomeScreen({ dark, onNav, settings, txs }) {
  const t = T(dark);
  const inc = DATA.income.actual, exp = DATA.expense.actual;

  const DOW = ['Воскресенье','Понедельник','Вторник','Среда','Четверг','Пятница','Суббота'];
  const MONTHS_GEN = ['января','февраля','марта','апреля','мая','июня','июля','августа','сентября','октября','ноября','декабря'];
  const now = new Date();
  const todayLabel = `${DOW[now.getDay()]}, ${now.getDate()} ${MONTHS_GEN[now.getMonth()]} ${now.getFullYear()}`;

  const displayName = (settings && settings.displayName) || 'Саша';
  const homeAcctId = (settings && settings.homeAccountId) || (DATA.accounts.find(a => a.main) || DATA.accounts[0]).id;
  const homeAcct = DATA.accounts.find(a => a.id === homeAcctId) || DATA.accounts[0];

  // Recent txs from store, first 4.
  const recentTxs = (txs || []).slice(0, 4);
  const upcoming = DATA.upcoming.filter(u => u.day - now.getDate() >= 0 && u.day - now.getDate() <= 7).slice(0, 4);

  const unreadNotifs = ((settings && settings.notifFeed) || []).filter(n => !n.read).length;

  return (
    <Screen dark={dark}>
      <div style={{ padding: '4px 20px 14px', display: 'flex', alignItems: 'center', gap: 12 }}>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontSize: 13, color: t.muted, fontWeight: 600, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{todayLabel}</div>
          <div style={{ fontFamily: TOK.fontDisplay, fontSize: 20, fontWeight: 700, color: t.ink, marginTop: 2 }}>Привет, {displayName}</div>
        </div>
        <button onClick={() => onNav('notifications')} style={{
          width: 40, height: 40, borderRadius: 20, background: t.surface, border: `1px solid ${t.line}`,
          display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', position: 'relative',
        }}>
          <Icon d={ICONS.bell} size={19} stroke={t.ink} sw={1.8} />
          {unreadNotifs > 0 && (
            <div style={{ position: 'absolute', top: 7, right: 7, minWidth: 14, height: 14, padding: '0 3px', borderRadius: 7, background: t.danger, color: '#fff', fontSize: 9, fontWeight: 800, display: 'flex', alignItems: 'center', justifyContent: 'center', border: `2px solid ${t.surface}` }}>{unreadNotifs}</div>
          )}
        </button>
        <button onClick={() => onNav('me')} style={{
          width: 40, height: 40, borderRadius: 20, border: 'none', cursor: 'pointer',
          background: `linear-gradient(135deg, ${t.accent}, ${t.accent2})`, color: '#fff',
          fontFamily: TOK.fontDisplay, fontWeight: 800, fontSize: 16,
        }}>{displayName.slice(0,1).toUpperCase()}</button>
      </div>

      {/* hero balance */}
      <div style={{ padding: '0 20px' }}>
        <Card dark={dark} pad={22} radius={24} style={{ background: dark ? t.surface : '#1A1814', color: '#F5F1EA', position: 'relative', overflow: 'hidden' }}>
          <div style={{ position: 'absolute', top: -60, right: -40, width: 220, height: 220, borderRadius: '50%', background: `radial-gradient(circle, ${t.accent}33, transparent 70%)` }} />
          <div style={{ position: 'relative' }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
              <div style={{ fontSize: 12, opacity: 0.65, fontWeight: 600, letterSpacing: 0.3, display: 'flex', alignItems: 'center', gap: 6 }}>
                <div style={{ width: 8, height: 8, borderRadius: 4, background: homeAcct.color }} />
                {homeAcct.bank.toUpperCase()} · {homeAcct.title.toUpperCase()}
              </div>
              <button onClick={() => onNav('accounts')} style={{
                border: 'none', background: 'rgba(255,255,255,0.1)', color: 'inherit', cursor: 'pointer',
                padding: '5px 10px', borderRadius: 10, fontFamily: 'inherit', fontSize: 11, fontWeight: 700,
                display: 'flex', alignItems: 'center', gap: 4,
              }}>
                Все счета
                <Icon d={ICONS.chevronR} size={11} stroke="currentColor" sw={2.2} />
              </button>
            </div>
            <div style={{ display: 'flex', alignItems: 'baseline', gap: 6, marginTop: 12 }}>
              <div style={{ fontFamily: TOK.fontDisplay, fontSize: 44, fontWeight: 800, letterSpacing: -1.5, lineHeight: 1, fontVariantNumeric: 'tabular-nums' }}>
                {fmtR(homeAcct.balance)}
              </div>
              <div style={{ fontSize: 22, opacity: 0.7, fontWeight: 700 }}>₽</div>
            </div>
            <div style={{ fontSize: 12, opacity: 0.6, marginTop: 6, fontVariantNumeric: 'tabular-nums' }}>
              ≈ {fmtUSD(homeAcct.balance / DATA.rates.usd)} · курс {DATA.rates.usd} · доступно к тратам
            </div>
          </div>
        </Card>
      </div>

      {/* flow stats */}
      <div style={{ padding: '12px 20px 0', display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
        <Card dark={dark} pad={14} radius={18}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
            <IconCircle icon="arrowDownRight" color={t.positive} size={28} dark={dark} />
            <div style={{ fontSize: 12, color: t.muted, fontWeight: 600 }}>Доход</div>
          </div>
          <div style={{ fontFamily: TOK.fontDisplay, fontSize: 22, fontWeight: 800, color: t.ink, letterSpacing: -0.5, fontVariantNumeric: 'tabular-nums' }}>+{fmtCompact(inc)}</div>
          <div style={{ fontSize: 11, color: t.muted, marginTop: 2 }}>из {fmtCompact(DATA.income.planned)} план</div>
        </Card>
        <Card dark={dark} pad={14} radius={18}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
            <IconCircle icon="arrowUpRight" color={t.danger} size={28} dark={dark} />
            <div style={{ fontSize: 12, color: t.muted, fontWeight: 600 }}>Расход</div>
          </div>
          <div style={{ fontFamily: TOK.fontDisplay, fontSize: 22, fontWeight: 800, color: t.ink, letterSpacing: -0.5, fontVariantNumeric: 'tabular-nums' }}>−{fmtCompact(exp)}</div>
          <div style={{ fontSize: 11, color: t.muted, marginTop: 2 }}>{Math.round((exp/(DATA.expense.planned||1))*100)}% от плана</div>
        </Card>
      </div>

      <SectionLabel dark={dark} action="Все →" onAction={() => onNav('analytics')}>Куда уходят</SectionLabel>
      <CategorySphere dark={dark} onNav={onNav} cats={DATA.categories} />

      {/* upcoming */}
      <SectionLabel dark={dark} action="Календарь →" onAction={() => onNav('calendar')}>Скоро</SectionLabel>
      <div style={{ padding: '0 20px', display: 'flex', flexDirection: 'column', gap: 10 }}>
        {upcoming.length === 0 && (
          <Card dark={dark} pad={20} radius={16} style={{ textAlign: 'center', color: t.muted, fontSize: 13 }}>
            На ближайшую неделю нет запланированных платежей.
          </Card>
        )}
        {upcoming.map(u => (
          <Card key={u.id} dark={dark} pad={0} radius={16}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: 14 }}>
              <div style={{ width: 44, padding: '6px 0', borderRadius: 12,
                background: u.income ? t.accentSoft : (u.isCredit ? t.warnSoft : t.dangerSoft),
                color: u.income ? t.accent : (u.isCredit ? t.warn : t.danger),
                textAlign: 'center', flexShrink: 0,
              }}>
                <div style={{ fontFamily: TOK.fontDisplay, fontSize: 15, fontWeight: 800, lineHeight: 1 }}>{u.date.split(' ')[0]}</div>
                <div style={{ fontSize: 9, marginTop: 2, fontWeight: 700, textTransform: 'uppercase', opacity: 0.8 }}>{u.date.split(' ')[1]}</div>
              </div>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontSize: 14.5, fontWeight: 600, color: t.ink }}>{u.title}</div>
                <div style={{ fontSize: 12, color: t.muted, marginTop: 2, fontWeight: 500 }}>{u.tag}</div>
              </div>
              <div style={{ fontFamily: TOK.fontDisplay, fontSize: 16, fontWeight: 800, color: u.income ? t.positive : t.ink, fontVariantNumeric: 'tabular-nums' }}>
                {u.income ? '+' : '−'}{fmtR(u.amount)}
              </div>
            </div>
          </Card>
        ))}
      </div>

      {/* recent transactions */}
      {recentTxs.length > 0 && (
        <>
          <SectionLabel dark={dark} action="Все →" onAction={() => onNav('txs')}>Последние операции</SectionLabel>
          <div style={{ padding: '0 20px' }}>
            <Card dark={dark} pad={0} radius={20}>
              {recentTxs.map((tx, i) => {
                const c = DATA.categories.find(c => c.id === tx.categoryId) || { title: 'Прочее', color: '#9A938A', icon: 'plus' };
                const acct = DATA.accounts.find(a => a.id === tx.accountId);
                return (
                  <ListRow key={tx.id} dark={dark} icon={c.icon} color={c.color}
                    title={tx.note || c.title}
                    sub={`${c.title}${acct ? ' · ' + acct.bank : ''}`}
                    value={(tx.type === 'income' ? '+' : '−') + fmtR(tx.amount) + ' ₽'}
                    valueColor={tx.type === 'income' ? t.positive : t.ink}
                    onClick={() => onNav('txs', { editId: tx.id })}
                    last={i === recentTxs.length - 1} />
                );
              })}
            </Card>
          </div>
        </>
      )}
    </Screen>
  );
}

// ═══ ACCOUNTS ════════════════════════════════════
function AccountsScreen({ dark, onBack, onNav }) {
  const t = T(dark);
  const [editing, setEditing] = React.useState(null);
  const [, force] = React.useReducer(x => x + 1, 0);

  const onSave = (a) => {
    if (a.__delete) { deleteAccount(a.id); }
    else if (a.id === '__new') { addAccount({ ...a, id: undefined }); }
    else { updateAccount(a.id, a); }
    setEditing(null); force();
  };

  return (
    <Screen dark={dark}>
      <PageHeader dark={dark} title="Счета" subtitle={`${DATA.accounts.length} карты · ${fmtR(DATA.totalBalance)} ₽`}
        leading={<BackBtn dark={dark} onClick={onBack} />}
        trailing={
          <button onClick={() => setEditing('__new')} style={{ width: 36, height: 36, borderRadius: 18, border: `1px solid ${t.line}`, background: t.surface, display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer' }}>
            <Icon d={ICONS.plus} size={18} stroke={t.ink} sw={2.2} />
          </button>
        } />

      <div style={{ padding: '0 20px', display: 'flex', flexDirection: 'column', gap: 12 }}>
        {DATA.accounts.map(a => (
          <Card key={a.id} dark={dark} pad={18} radius={20} onClick={() => setEditing(a.id)}
            style={{ background: dark ? t.surface : '#1A1814', color: '#F5F1EA', position: 'relative', overflow: 'hidden', cursor: 'pointer' }}>
            <div style={{ position: 'absolute', top: -40, right: -30, width: 160, height: 160, borderRadius: '50%', background: a.color + '40' }} />
            <div style={{ position: 'relative' }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
                <div>
                  <div style={{ fontSize: 11, opacity: 0.6, fontWeight: 700, letterSpacing: 0.5 }}>{a.bank.toUpperCase()} · •• {a.last4}</div>
                  <div style={{ fontFamily: TOK.fontDisplay, fontSize: 17, fontWeight: 700, marginTop: 4 }}>{a.title}</div>
                </div>
                {a.main && <div style={{ padding: '4px 10px', borderRadius: 12, background: 'rgba(255,255,255,0.12)', fontSize: 10, fontWeight: 800, letterSpacing: 0.5 }}>ОСНОВНОЙ</div>}
              </div>
              <div style={{ marginTop: 22, display: 'flex', alignItems: 'baseline', gap: 6 }}>
                <div style={{ fontFamily: TOK.fontDisplay, fontSize: 32, fontWeight: 800, letterSpacing: -1, lineHeight: 1, fontVariantNumeric: 'tabular-nums' }}>{fmtR(a.balance)}</div>
                <div style={{ fontSize: 16, opacity: 0.7, fontWeight: 700 }}>₽</div>
              </div>
              <div style={{ fontSize: 12, opacity: 0.6, marginTop: 4 }}>≈ {fmtUSD(a.balance / DATA.rates.usd)}</div>
            </div>
          </Card>
        ))}

        <button onClick={() => setEditing('__new')} style={{
          padding: 18, borderRadius: 20, border: `2px dashed ${t.lineStrong}`,
          background: 'transparent', color: t.accent, cursor: 'pointer',
          fontFamily: 'inherit', fontSize: 14, fontWeight: 700,
          display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
        }}>
          <Icon d={ICONS.plus} size={18} stroke={t.accent} sw={2.4} />
          Добавить счёт
        </button>
      </div>

      <SectionLabel dark={dark}>Правила распределения</SectionLabel>
      <div style={{ padding: '0 20px' }}>
        <Card dark={dark} pad={0} radius={20}>
          {DATA.incomeSources.filter(s => s.distributionRules && s.distributionRules.length).map((src, i, arr) => (
            <div key={src.id} style={{ padding: '14px 16px', borderBottom: i === arr.length - 1 ? 'none' : `1px solid ${t.line}` }}>
              <div style={{ fontSize: 12, color: t.muted, fontWeight: 600, marginBottom: 6 }}>{src.title} {fmtR(src.amount)} ₽ →</div>
              <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                {src.distributionRules.map((r, j) => {
                  const acct = DATA.accounts.find(a => a.id === r.accountId);
                  if (!acct) return null;
                  return <Distribution key={j} dark={dark} percent={r.percent} title={acct.bank} sub={`${fmtR(Math.round(src.amount * r.percent/100))} ₽`} color={acct.color} />;
                })}
              </div>
            </div>
          ))}
          {DATA.incomeSources.filter(s => s.distributionRules && s.distributionRules.length).length === 0 && (
            <div style={{ padding: '20px 16px', textAlign: 'center', color: t.muted, fontSize: 13 }}>
              Правил пока нет. Настроить — в источниках дохода.
            </div>
          )}
          <ListRow dark={dark} icon="plus" color={t.accent} title="Добавить правило" sub="Авто-распределение при поступлении" last
            right={<Icon d={ICONS.chevronR} size={14} stroke={t.faint} />}
            onClick={() => onNav('me')} />
        </Card>
      </div>

      <EditAccountSheet dark={dark} editingId={editing}
        initial={editing && editing !== '__new' ? DATA.accounts.find(a => a.id === editing) : null}
        onClose={() => setEditing(null)} onSave={onSave} />
    </Screen>
  );
}

function EditAccountSheet({ dark, editingId, initial, onClose, onSave }) {
  const t = T(dark);
  const isNew = editingId === '__new';
  const [form, setForm] = React.useState(initial || { id: '__new', bank: '', title: '', balance: 0, color: '#5B8BAA', last4: '0000', main: false });

  React.useEffect(() => {
    if (initial) setForm(initial);
    else if (isNew) setForm({ id: '__new', bank: '', title: '', balance: 0, color: '#5B8BAA', last4: '0000', main: false });
  }, [editingId]);

  if (!editingId) return null;
  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));
  const PALETTE = ['#FFDD2D','#1AB55D','#EF3124','#5B8BAA','#7A6BB8','#D4A23A','#3DDBDB','#FF6DB1','#9B6DFF','#1F8F87'];

  const Field = ({ label, value, onChange, suffix, isNum, placeholder }) => (
    <div style={{ marginBottom: 10 }}>
      <div style={{ fontSize: 11, color: t.muted, fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.4, marginBottom: 4 }}>{label}</div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 6, background: t.surface, borderRadius: 12, padding: '10px 14px', border: `1px solid ${t.line}` }}>
        <input value={value} placeholder={placeholder}
          onChange={e => onChange(isNum ? (parseFloat(e.target.value) || 0) : e.target.value)}
          inputMode={isNum ? 'decimal' : 'text'}
          style={{ flex: 1, border: 'none', outline: 'none', background: 'transparent', fontFamily: 'inherit',
            fontSize: 15, fontWeight: 600, color: t.ink, fontVariantNumeric: 'tabular-nums', width: '100%' }} />
        {suffix && <div style={{ fontSize: 12, color: t.muted, fontWeight: 600 }}>{suffix}</div>}
      </div>
    </div>
  );

  return (
    <Sheet open={!!editingId} onClose={onClose} dark={dark} title={isNew ? 'Новый счёт' : 'Изменить счёт'} height="86%">
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
        <Field label="Банк" value={form.bank} onChange={v => set('bank', v)} placeholder="Тинькофф" />
        <Field label="Название" value={form.title} onChange={v => set('title', v)} placeholder="Основная" />
        <Field label="Баланс" value={form.balance} onChange={v => set('balance', v)} suffix="₽" isNum />
        <Field label="Последние 4" value={form.last4} onChange={v => set('last4', String(v).slice(0, 4))} placeholder="4520" />
      </div>
      <div style={{ fontSize: 11, color: t.muted, fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.4, margin: '4px 0 6px' }}>Цвет</div>
      <div style={{ display: 'flex', gap: 8, marginBottom: 14, flexWrap: 'wrap' }}>
        {PALETTE.map(p => (
          <button key={p} onClick={() => set('color', p)} style={{
            width: 34, height: 34, borderRadius: 10, border: 'none', background: p, cursor: 'pointer',
            boxShadow: form.color === p ? `0 0 0 3px ${t.bg}, 0 0 0 5px ${p}` : 'none',
          }}>
            {form.color === p && <Icon d={ICONS.check} size={16} stroke="#fff" sw={3} />}
          </button>
        ))}
      </div>
      <label style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '12px 14px', borderRadius: 12, background: t.surface, cursor: 'pointer', marginBottom: 12 }}>
        <input type="checkbox" checked={!!form.main} onChange={e => set('main', e.target.checked)} />
        <div>
          <div style={{ fontSize: 14, fontWeight: 700, color: t.ink }}>Главный счёт</div>
          <div style={{ fontSize: 11, color: t.muted, marginTop: 2 }}>Показывать на главной как «доступно к тратам».</div>
        </div>
      </label>

      <div style={{ display: 'flex', gap: 10, marginTop: 4 }}>
        {!isNew && DATA.accounts.length > 1 && (
          <button onClick={() => onSave({ ...form, __delete: true })} style={{
            padding: '14px 16px', borderRadius: 14, border: 'none', cursor: 'pointer',
            background: t.dangerSoft, color: t.danger, fontFamily: 'inherit', fontSize: 14, fontWeight: 700,
            display: 'flex', alignItems: 'center', gap: 6,
          }}>
            <Icon d={ICONS.trash} size={16} stroke={t.danger} sw={2} />
            Удалить
          </button>
        )}
        <Btn dark={dark} primary full onClick={() => onSave(form)} icon="check" style={{ flex: 1 }}>
          {isNew ? 'Создать' : 'Сохранить'}
        </Btn>
      </div>
    </Sheet>
  );
}

function Distribution({ dark, percent, title, sub, color }) {
  const t = T(dark);
  return (
    <div style={{ flex: percent, padding: 10, borderRadius: 12, background: color + '20' }}>
      <div style={{ fontSize: 11, fontWeight: 700, color: t.ink }}>{percent}%</div>
      <div style={{ fontSize: 11, color: t.muted, marginTop: 2 }}>{title}</div>
      {sub && <div style={{ fontSize: 9, color: t.faint, marginTop: 2, fontVariantNumeric: 'tabular-nums' }}>{sub}</div>}
    </div>
  );
}

function BackBtn({ dark, onClick }) {
  const t = T(dark);
  return (
    <button onClick={onClick} style={{
      width: 36, height: 36, borderRadius: 18, border: `1px solid ${t.line}`,
      background: t.surface, display: 'flex', alignItems: 'center', justifyContent: 'center',
      cursor: 'pointer',
    }}>
      <Icon d={ICONS.back} size={18} stroke={t.ink} sw={2} />
    </button>
  );
}

// ═══ GOAL ════════════════════════════════════════
function GoalScreen({ dark, onNav }) {
  const t = T(dark);
  const [editing, setEditing] = React.useState(null);
  const [topUp, setTopUp] = React.useState(null);
  const [activeId, setActiveId] = React.useState(DATA.goals[0] ? DATA.goals[0].id : null);
  const [, force] = React.useReducer(x => x + 1, 0);

  if (DATA.goals.length === 0) {
    return (
      <Screen dark={dark}>
        <PageHeader dark={dark} title="Цели" large />
        <div style={{ padding: '0 20px' }}>
          <Card dark={dark} pad={28} radius={22} style={{ textAlign: 'center' }}>
            <div style={{ fontSize: 48, marginBottom: 12 }}>🎯</div>
            <div style={{ fontSize: 16, fontWeight: 700, color: t.ink }}>Поставьте первую цель</div>
            <div style={{ fontSize: 13, color: t.muted, marginTop: 8, lineHeight: 1.5 }}>
              Накопления на поездку, новый ноутбук или подушку безопасности.
            </div>
            <Btn dark={dark} primary onClick={() => setEditing('__new')} icon="plus" style={{ marginTop: 18 }}>Создать цель</Btn>
          </Card>
        </div>
        <EditGoalSheet dark={dark} editingId={editing} initial={null}
          onClose={() => setEditing(null)}
          onSave={(g) => { if (g.id === '__new') { const x = addGoal({ ...g, id: undefined }); setActiveId(x.id); } setEditing(null); force(); }} />
      </Screen>
    );
  }

  const g = DATA.goals.find(x => x.id === activeId) || DATA.goals[0];
  const pct = Math.round((g.saved / g.target) * 100);
  const remaining = g.target - g.saved;
  const monthsLeft = Math.ceil(remaining / (g.monthly || 1));
  const linkedAcct = DATA.accounts.find(a => a.id === g.accountId) || DATA.accounts[0];

  const onSaveGoal = (next) => {
    if (next.__delete) { deleteGoal(next.id); setActiveId(DATA.goals[0] ? DATA.goals[0].id : null); }
    else if (next.id === '__new') { const x = addGoal({ ...next, id: undefined }); setActiveId(x.id); }
    else { updateGoal(next.id, next); }
    setEditing(null); force();
  };

  const onTopUp = (amount) => {
    const next = Math.min(g.target, (g.saved || 0) + amount);
    updateGoal(g.id, { saved: next });
    // mark milestones reached
    const ms = (g.milestones || []).map(m => ({ ...m, done: m.done || next >= m.at }));
    updateGoal(g.id, { milestones: ms });
    setTopUp(null); force();
  };

  return (
    <Screen dark={dark}>
      <PageHeader dark={dark} title={DATA.goals.length > 1 ? 'Мои цели' : 'Моя цель'}
        subtitle={DATA.goals.length > 1 ? `${DATA.goals.length} активных` : 'Большое путешествие'} large
        trailing={
          <button onClick={() => setEditing(g.id)} style={{ width: 36, height: 36, borderRadius: 18, border: `1px solid ${t.line}`, background: t.surface, display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer' }}>
            <Icon d={ICONS.edit} size={16} stroke={t.ink} />
          </button>
        }
      />

      {DATA.goals.length > 1 && (
        <div style={{ padding: '0 20px 12px', display: 'flex', gap: 6, overflowX: 'auto' }}>
          {DATA.goals.map(x => (
            <Pill key={x.id} dark={dark} active={x.id === activeId} onClick={() => setActiveId(x.id)}>
              {x.emoji} {x.title}
            </Pill>
          ))}
          <button onClick={() => setEditing('__new')} style={{
            padding: '7px 12px', borderRadius: 999, background: t.accentSoft, color: t.accent,
            border: 'none', cursor: 'pointer', fontFamily: 'inherit', fontSize: 13, fontWeight: 700,
            display: 'flex', alignItems: 'center', gap: 4,
          }}>
            <Icon d={ICONS.plus} size={12} stroke={t.accent} sw={2.5} /> Цель
          </button>
        </div>
      )}

      <div style={{ padding: '0 20px' }}>
        <Card dark={dark} pad={24} radius={28} style={{
          background: `linear-gradient(135deg, ${t.accent} 0%, ${dark ? '#1F6F50' : '#0B6B47'} 100%)`,
          color: '#fff', position: 'relative', overflow: 'hidden',
        }}>
          <div style={{ position: 'absolute', top: -40, right: -30, fontSize: 180, opacity: 0.15 }}>{g.emoji}</div>
          <div style={{ fontSize: 12, opacity: 0.85, fontWeight: 700, letterSpacing: 0.5 }}>ЦЕЛЬ · {g.deadline ? g.deadline.toUpperCase() : ''}</div>
          <div style={{ fontFamily: TOK.fontDisplay, fontSize: 34, fontWeight: 800, marginTop: 6, letterSpacing: -1, lineHeight: 1.05 }}>{g.title}</div>

          <div style={{ marginTop: 22, display: 'flex', alignItems: 'baseline', gap: 8 }}>
            <div style={{ fontFamily: TOK.fontDisplay, fontSize: 36, fontWeight: 800, fontVariantNumeric: 'tabular-nums', lineHeight: 1 }}>{fmtR(g.saved)} ₽</div>
            <div style={{ fontSize: 14, opacity: 0.75, fontVariantNumeric: 'tabular-nums' }}>/ {fmtR(g.target)}</div>
          </div>
          <div style={{ fontSize: 13, opacity: 0.85, marginTop: 4 }}>Накоплено {pct}% — ещё {fmtR(remaining)} ₽</div>
        </Card>

        <Card dark={dark} pad={20} radius={20} style={{ marginTop: 12 }}>
          <div style={{ fontSize: 13, color: t.muted, fontWeight: 600, marginBottom: 14 }}>Прогресс по этапам</div>
          <div style={{ position: 'relative', height: 14, background: t.surfaceAlt, borderRadius: 7, overflow: 'hidden' }}>
            <div style={{ position: 'absolute', left: 0, top: 0, bottom: 0, width: `${pct}%`, background: `linear-gradient(90deg, ${t.accent}, ${t.accent}cc)`, borderRadius: 7 }} />
          </div>
          <div style={{ position: 'relative', marginTop: 18, height: 70 }}>
            {(g.milestones || []).map((m, i) => {
              const x = (m.at / g.target) * 100;
              return (
                <div key={i} style={{ position: 'absolute', left: `${x}%`, transform: 'translateX(-50%)', textAlign: 'center', width: 78, top: 0 }}>
                  <div style={{
                    width: 18, height: 18, borderRadius: 9, margin: '0 auto',
                    background: m.done ? t.accent : t.surface,
                    border: `2px solid ${m.done ? t.accent : t.lineStrong}`,
                    display: 'flex', alignItems: 'center', justifyContent: 'center',
                  }}>
                    {m.done && <Icon d={ICONS.check} size={11} stroke="#fff" sw={3} />}
                  </div>
                  <div style={{ fontSize: 10, fontWeight: 700, color: m.done ? t.accent : t.muted, marginTop: 6, fontVariantNumeric: 'tabular-nums' }}>{fmtCompact(m.at)}</div>
                  <div style={{ fontSize: 9, color: t.muted, marginTop: 2, lineHeight: 1.2 }}>{m.label}</div>
                </div>
              );
            })}
          </div>
        </Card>

        <Card dark={dark} pad={0} radius={20} style={{ marginTop: 12 }}>
          <ListRow dark={dark} icon="repeat" color={t.accent}
            title="Откладываю ежемесячно" sub={`Авто-перевод`}
            value={`${fmtR(g.monthly)} ₽`} />
          <ListRow dark={dark} icon="card" color={linkedAcct ? linkedAcct.color : t.accent}
            title="Счёт цели" sub={linkedAcct ? `${linkedAcct.bank} · ${linkedAcct.title}` : '—'}
            value={linkedAcct ? `${fmtR(linkedAcct.balance)} ₽` : '—'} />
          <ListRow dark={dark} icon="calendar" color={t.accent2}
            title="Прогноз" sub="При текущем темпе"
            value={`${monthsLeft} мес.`} last />
        </Card>

        <div style={{ marginTop: 16, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
          <Btn dark={dark} primary full icon="plus" onClick={() => setTopUp(g.monthly || 5000)}>Пополнить</Btn>
          <Btn dark={dark} secondary full icon="cog" onClick={() => setEditing(g.id)}>Изменить</Btn>
        </div>
      </div>

      <EditGoalSheet dark={dark} editingId={editing}
        initial={editing && editing !== '__new' ? DATA.goals.find(x => x.id === editing) : null}
        onClose={() => setEditing(null)} onSave={onSaveGoal} />

      <Sheet open={topUp != null} onClose={() => setTopUp(null)} dark={dark} title="Пополнить цель">
        <div style={{ fontSize: 13, color: t.muted, marginBottom: 12 }}>На какую сумму пополнить?</div>
        <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginBottom: 14 }}>
          {[1000, 5000, 10000, 25000, g.monthly || 5000].map((v, i) => (
            <Pill key={i} dark={dark} active={topUp === v} onClick={() => setTopUp(v)}>+{fmtR(v)} ₽</Pill>
          ))}
        </div>
        <Btn dark={dark} primary full icon="check" onClick={() => onTopUp(topUp || 0)}>Пополнить на {fmtR(topUp || 0)} ₽</Btn>
      </Sheet>
    </Screen>
  );
}

function EditGoalSheet({ dark, editingId, initial, onClose, onSave }) {
  const t = T(dark);
  const isNew = editingId === '__new';
  const [form, setForm] = React.useState(initial || { id: '__new', title: '', emoji: '🎯', target: 100000, saved: 0, monthly: 5000, accountId: (DATA.accounts[0] || {}).id, deadline: '', milestones: [] });

  React.useEffect(() => {
    if (initial) setForm(initial);
    else if (isNew) setForm({ id: '__new', title: '', emoji: '🎯', target: 100000, saved: 0, monthly: 5000, accountId: (DATA.accounts[0] || {}).id, deadline: '', milestones: [] });
  }, [editingId]);

  if (!editingId) return null;
  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));

  const Field = ({ label, value, onChange, suffix, isNum, placeholder }) => (
    <div style={{ marginBottom: 10 }}>
      <div style={{ fontSize: 11, color: t.muted, fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.4, marginBottom: 4 }}>{label}</div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 6, background: t.surface, borderRadius: 12, padding: '10px 14px', border: `1px solid ${t.line}` }}>
        <input value={value} placeholder={placeholder}
          onChange={e => onChange(isNum ? (parseFloat(e.target.value) || 0) : e.target.value)}
          inputMode={isNum ? 'decimal' : 'text'}
          style={{ flex: 1, border: 'none', outline: 'none', background: 'transparent', fontFamily: 'inherit',
            fontSize: 15, fontWeight: 600, color: t.ink, fontVariantNumeric: 'tabular-nums', width: '100%' }} />
        {suffix && <div style={{ fontSize: 12, color: t.muted, fontWeight: 600 }}>{suffix}</div>}
      </div>
    </div>
  );

  return (
    <Sheet open={!!editingId} onClose={onClose} dark={dark} title={isNew ? 'Новая цель' : 'Изменить цель'} height="92%">
      <div style={{ display: 'grid', gridTemplateColumns: '64px 1fr', gap: 10 }}>
        <div>
          <div style={{ fontSize: 11, color: t.muted, fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.4, marginBottom: 4 }}>Эмодзи</div>
          <input value={form.emoji} onChange={e => set('emoji', e.target.value.slice(0, 2))} style={{
            width: '100%', textAlign: 'center', padding: '12px 0', borderRadius: 12, border: `1px solid ${t.line}`,
            background: t.surface, fontFamily: 'inherit', fontSize: 24,
          }} />
        </div>
        <Field label="Название" value={form.title} onChange={v => set('title', v)} placeholder="Накопить на ноутбук" />
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
        <Field label="Сумма цели" value={form.target} onChange={v => set('target', v)} suffix="₽" isNum />
        <Field label="Уже накоплено" value={form.saved} onChange={v => set('saved', v)} suffix="₽" isNum />
        <Field label="В месяц" value={form.monthly} onChange={v => set('monthly', v)} suffix="₽" isNum />
        <Field label="Срок" value={form.deadline} onChange={v => set('deadline', v)} placeholder="Авг 2027" />
      </div>

      <div style={{ fontSize: 11, color: t.muted, fontWeight: 700, textTransform: 'uppercase', letterSpacing: 0.4, margin: '8px 0 6px' }}>Счёт</div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 6, marginBottom: 12 }}>
        {DATA.accounts.map(a => (
          <button key={a.id} onClick={() => set('accountId', a.id)} style={{
            padding: '12px 14px', borderRadius: 12, border: `1.5px solid ${form.accountId === a.id ? t.accent : t.line}`,
            background: form.accountId === a.id ? t.accentSoft : t.surface, cursor: 'pointer',
            display: 'flex', alignItems: 'center', gap: 10, fontFamily: 'inherit',
          }}>
            <div style={{ width: 24, height: 24, borderRadius: 6, background: a.color }} />
            <div style={{ flex: 1, textAlign: 'left' }}>
              <div style={{ fontSize: 14, fontWeight: 700, color: t.ink }}>{a.bank} · {a.title}</div>
              <div style={{ fontSize: 11, color: t.muted, marginTop: 2 }}>•• {a.last4}</div>
            </div>
            {form.accountId === a.id && <Icon d={ICONS.check} size={16} stroke={t.accent} sw={2.4} />}
          </button>
        ))}
      </div>

      <div style={{ display: 'flex', gap: 10, marginTop: 8 }}>
        {!isNew && (
          <button onClick={() => onSave({ ...form, __delete: true })} style={{
            padding: '14px 16px', borderRadius: 14, border: 'none', cursor: 'pointer',
            background: t.dangerSoft, color: t.danger, fontFamily: 'inherit', fontSize: 14, fontWeight: 700,
            display: 'flex', alignItems: 'center', gap: 6,
          }}>
            <Icon d={ICONS.trash} size={16} stroke={t.danger} sw={2} />
            Удалить
          </button>
        )}
        <Btn dark={dark} primary full onClick={() => onSave(form)} icon="check" style={{ flex: 1 }}>
          {isNew ? 'Создать' : 'Сохранить'}
        </Btn>
      </div>
    </Sheet>
  );
}

Object.assign(window, { LoginScreen, HomeScreen, AccountsScreen, GoalScreen, BackBtn, CategorySphere, EditAccountSheet, EditGoalSheet });
