// Finance — Gelir & Gider (Gelişmiş)
const FNIcons = window.Icons;

const INCOME_CATS  = ['Firma Ödemesi', 'Avans', 'Kira Geliri', 'Ek Gelir', 'İade', 'Diğer'];
const EXPENSE_CATS = ['Yazılım & Araç', 'Reklam', 'Barındırma', 'Seyahat', 'Malzeme', 'Personel', 'Abonelik', 'Vergi', 'Diğer'];

// Sabit + kullanıcı tanımlı kategoriler
const fnGetCats = (state, type) => {
  const base = type === 'income' ? INCOME_CATS : EXPENSE_CATS;
  const custom = state?.customCategories?.[type] || [];
  return [...base, ...custom.filter(c => !base.includes(c))];
};

// KDV tutarı — tutar KDV dahil kabul edilir
const fnVatAmount = (tx) => {
  if (!tx.vat || tx.vat <= 0) return 0;
  return tx.amount - tx.amount / (1 + tx.vat / 100);
};

// Aynı gün bir sonraki ay (ay sonuna kıstırılır)
const fnNextMonth = (dateStr) => {
  const d = new Date(dateStr + 'T12:00:00');
  const day = d.getDate();
  d.setDate(1); d.setMonth(d.getMonth() + 1);
  const lastDay = new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate();
  d.setDate(Math.min(day, lastDay));
  const pad = n => String(n).padStart(2, '0');
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
};

const INC_COLOR = '#22c55e';
const EXP_COLOR = '#ef4444';

// ── İşlem Durumu ──────────────────────────────────────────────
const TX_STATUSES = {
  done:      { label: 'Alındı / Ödendi', short: 'Alındı',   color: '#22c55e', bg: 'rgba(34,197,94,.12)'  },
  pending:   { label: 'Bekliyor',        short: 'Bekliyor', color: '#f59e0b', bg: 'rgba(245,158,11,.12)' },
  deferred:  { label: 'Vadeli',          short: 'Vadeli',   color: '#6366f1', bg: 'rgba(99,102,241,.12)' },
  cancelled: { label: 'İptal',           short: 'İptal',    color: '#94a3b8', bg: 'rgba(148,163,184,.12)'},
};

const fnTodayISO = () => new Date().toISOString().slice(0, 10);

// Vade geçmiş + henüz tamamlanmamış → gecikmiş
const fnIsOverdue = (tx) => {
  const s = tx.status || 'done';
  if (s === 'done' || s === 'cancelled') return false;
  const due = tx.dueDate || tx.date;
  return !!due && due < fnTodayISO();
};

// Bakiyeye sayılır mı? (done veya status yok → gerçekleşmiş)
const fnIsRealized = (tx) => !tx.status || tx.status === 'done';

// Durum rozeti bileşeni
function StatusBadge({ tx }) {
  const overdue = fnIsOverdue(tx);
  if (overdue) {
    return <span className="fn-status-badge overdue">Gecikmiş</span>;
  }
  const st = TX_STATUSES[tx.status] || TX_STATUSES.done;
  if (tx.status === 'done' || !tx.status) return null; // done = varsayılan, rozet yok
  return (
    <span className="fn-status-badge" style={{ background: st.bg, color: st.color }}>
      {st.short}
    </span>
  );
}

// Önceki döneme göre değişim rozeti (tutar bazlı)
function FnDelta({ cur, prev, invert }) {
  if (prev === null || prev === undefined) return null;
  const diff = cur - prev;
  if (Math.abs(diff) < 1) return null;
  const up = diff > 0;
  const good = invert ? !up : up;
  return (
    <div className={`rp-delta ${good ? 'good' : 'bad'}`}>
      {up ? '▲' : '▼'} {fmtM(Math.abs(diff))} önceki döneme göre
    </div>
  );
}

const fmtM = (n, compact = false) => {
  if (compact && Math.abs(n) >= 1000) {
    return new Intl.NumberFormat('tr-TR', { maximumFractionDigits: 1 }).format(n / 1000) + 'B';
  }
  return new Intl.NumberFormat('tr-TR', { maximumFractionDigits: 0 }).format(n) + ' ₺';
};
const fmtDate = (d) => new Date(d + 'T00:00:00').toLocaleDateString('tr-TR', { day: 'numeric', month: 'short', year: 'numeric' });

// ── Nakit Akışı Grafik (SVG Grouped Bar + Net Line) ───────────
function CashFlowChart({ data }) {
  const [hov, setHov] = React.useState(null);
  const W = 700, H = 200, pL = 52, pR = 12, pT = 14, pB = 34;
  const cW = W - pL - pR, cH = H - pT - pB;
  const maxVal = Math.max(...data.map(m => Math.max(m.inc, m.exp)), 1);
  const n = data.length;
  const groupW = cW / n;
  const barW = Math.min(18, groupW * 0.35);
  const gap  = 3;

  const barX = (i, which) => pL + i * groupW + groupW / 2 + (which === 0 ? -(barW + gap / 2) : gap / 2);
  const barH = (v) => Math.max(2, (v / maxVal) * cH);
  const barY = (v) => pT + cH - barH(v);

  const netPoints = data.map((m, i) => {
    const net = m.inc - m.exp;
    const cx  = pL + i * groupW + groupW / 2;
    const cy  = pT + cH - ((net / maxVal) * cH);
    return [cx, Math.max(pT + 2, Math.min(pT + cH, cy))];
  });
  // Köşeli kırık çizgi yerine yumuşak kübik eğri — premium his
  const netPath = netPoints.reduce((p, [x1, y1], i) => {
    if (i === 0) return `M${x1},${y1}`;
    const [x0, y0] = netPoints[i - 1];
    const mx = (x0 + x1) / 2;
    return p + ` C${mx},${y0} ${mx},${y1} ${x1},${y1}`;
  }, '');

  const yTicks = [0, 0.5, 1].map(r => ({ y: pT + cH * (1 - r), val: maxVal * r }));

  const TIP_W = 124, TIP_H = 66;

  return (
    <div style={{ width: '100%', overflowX: 'auto' }}>
      <svg viewBox={`0 0 ${W} ${H}`} style={{ width: '100%', maxHeight: H, display: 'block' }}>
        <defs>
          <linearGradient id="fnIncG" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%"   stopColor={INC_COLOR} stopOpacity="0.95" />
            <stop offset="100%" stopColor={INC_COLOR} stopOpacity="0.4" />
          </linearGradient>
          <linearGradient id="fnExpG" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%"   stopColor={EXP_COLOR} stopOpacity="0.95" />
            <stop offset="100%" stopColor={EXP_COLOR} stopOpacity="0.4" />
          </linearGradient>
        </defs>
        {/* Grid */}
        {yTicks.map((t, i) => (
          <g key={i}>
            <line x1={pL} y1={t.y} x2={W - pR} y2={t.y} stroke="var(--border)" strokeWidth={1} />
            <text x={pL - 5} y={t.y + 4} textAnchor="end" fontSize={9} fill="var(--text-faint)">{fmtM(t.val, true)}</text>
          </g>
        ))}

        {/* Bars */}
        {data.map((m, i) => {
          const isHov = hov === i;
          const cx = pL + i * groupW + groupW / 2;
          const tipX = Math.max(pL, Math.min(cx - TIP_W / 2, W - pR - TIP_W));
          const net = m.inc - m.exp;
          return (
            <g key={m.key}
              onMouseEnter={() => setHov(i)}
              onMouseLeave={() => setHov(null)}
              style={{ cursor: 'default' }}
            >
              {/* Income bar */}
              {m.inc > 0 && (
                <rect x={barX(i, 0)} y={barY(m.inc)} width={barW} height={barH(m.inc)}
                  rx={3} fill="url(#fnIncG)" opacity={isHov ? 1 : 0.85}
                  style={{ transformBox: 'fill-box', transformOrigin: 'bottom', animation: 'fn-bar-rise 0.5s ease both' }}
                />
              )}
              {/* Expense bar */}
              {m.exp > 0 && (
                <rect x={barX(i, 1)} y={barY(m.exp)} width={barW} height={barH(m.exp)}
                  rx={3} fill="url(#fnExpG)" opacity={isHov ? 1 : 0.85}
                  style={{ transformBox: 'fill-box', transformOrigin: 'bottom', animation: 'fn-bar-rise 0.5s ease both' }}
                />
              )}
              {/* Hover crosshair + tooltip */}
              {isHov && (
                <g>
                  <line x1={cx} y1={pT} x2={cx} y2={H - pB} stroke="var(--border)" strokeWidth={1} strokeDasharray="3 2" />
                  <rect x={tipX} y={pT} width={TIP_W} height={TIP_H} rx={6}
                    fill="var(--panel)" stroke="var(--border-strong)" strokeWidth={1} />
                  <text x={tipX + TIP_W / 2} y={pT + 13} textAnchor="middle" fontSize={9} fontWeight={700} fill="var(--text-muted)">{m.lbl}</text>
                  <text x={tipX + TIP_W / 2} y={pT + 28} textAnchor="middle" fontSize={9} fill={INC_COLOR}>▲ Gelir: {fmtM(m.inc)}</text>
                  <text x={tipX + TIP_W / 2} y={pT + 42} textAnchor="middle" fontSize={9} fill={EXP_COLOR}>▼ Gider: {fmtM(m.exp)}</text>
                  <line x1={tipX + 12} y1={pT + 50} x2={tipX + TIP_W - 12} y2={pT + 50} stroke="var(--border)" strokeWidth={0.5} />
                  <text x={tipX + TIP_W / 2} y={pT + 62} textAnchor="middle" fontSize={9} fontWeight={700} fill={net >= 0 ? INC_COLOR : EXP_COLOR}>
                    {'= Net: ' + (net >= 0 ? '+' : '') + fmtM(net)}
                  </text>
                </g>
              )}
              {/* Month label */}
              <text x={cx} y={H - pB + 14} textAnchor="middle"
                fontSize={9} fontWeight={600} fill={isHov ? 'var(--text)' : 'var(--text-faint)'}>
                {m.lbl}
              </text>
            </g>
          );
        })}

        {/* Net balance line */}
        {data.some(m => m.inc > 0 || m.exp > 0) && (
          <>
            <path d={netPath} fill="none" stroke="var(--accent)" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" />
            {netPoints.map(([cx, cy], i) => (
              <circle key={i} cx={cx} cy={cy} r={hov === i ? 5 : 3} fill="var(--accent)"
                style={{ transition: 'r 0.15s ease' }} />
            ))}
          </>
        )}

        {/* Axes */}
        <line x1={pL} y1={pT} x2={pL} y2={H - pB} stroke="var(--border-strong)" strokeWidth={1.5} />
        <line x1={pL} y1={H - pB} x2={W - pR} y2={H - pB} stroke="var(--border-strong)" strokeWidth={1.5} />
      </svg>
    </div>
  );
}

// ── Kategori Yatay Bar ────────────────────────────────────────
function CategoryBars({ data, color, max }) {
  if (!data.length) return <div className="report-empty">Bu dönemde veri yok</div>;
  const maxVal = max || Math.max(...data.map(d => d.value), 1);
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
      {data.map((d, i) => (
        <div key={i} className="fn-catbar-row" style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          <span style={{ fontSize: 11.5, color: 'var(--text-2)', width: 110, flexShrink: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{d.label}</span>
          <div style={{ flex: 1, height: 8, background: 'var(--bg-sunken)', borderRadius: 4, overflow: 'hidden' }}>
            <div className="fn-catbar-fill" style={{
              height: '100%',
              width: `${(d.value / maxVal) * 100}%`,
              background: `linear-gradient(90deg, color-mix(in srgb, ${color} 55%, transparent), ${color})`,
              borderRadius: 4,
              transition: 'width .35s ease',
            }} />
          </div>
          <span style={{ fontSize: 11.5, fontWeight: 700, color: 'var(--text-muted)', minWidth: 70, textAlign: 'right', fontVariantNumeric: 'tabular-nums', flexShrink: 0 }}>{fmtM(d.value)}</span>
        </div>
      ))}
    </div>
  );
}

// ── Yaklaşan Ödemeler & Tahsilatlar (14 gün) ─────────────────
function YaklasanOdemeler({ transactions, projById }) {
  const today = fnTodayISO();
  const in14  = new Date(); in14.setDate(in14.getDate() + 14);
  const in14S = in14.toISOString().slice(0, 10);
  const upcoming = transactions
    .filter(t => {
      if (t.status === 'done' || t.status === 'cancelled') return false;
      const due = t.dueDate || t.date;
      return due >= today && due <= in14S;
    })
    .sort((a, b) => (a.dueDate||a.date).localeCompare(b.dueDate||b.date))
    .slice(0, 6);
  if (!upcoming.length) return null;
  const fmtShort = d => new Date(d+'T00:00:00').toLocaleDateString('tr-TR',{day:'numeric',month:'short'});
  const diffDays = d => Math.round((new Date(d+'T00:00:00') - new Date().setHours(0,0,0,0)) / 86400000);
  return (
    <div className="card fn-upcoming-card">
      <div className="fn-chart-hdr">
        <span className="fn-section-title">Yaklaşan Ödemeler & Tahsilatlar</span>
        <span style={{ fontSize: 11, color: 'var(--text-faint)' }}>{upcoming.length} işlem · 14 gün</span>
      </div>
      <div className="fn-upcoming-list">
        {upcoming.map(t => {
          const proj = projById[t.projectId];
          const due  = t.dueDate || t.date;
          const d    = diffDays(due);
          const lbl  = d === 0 ? 'Bugün' : d === 1 ? 'Yarın' : `${d}g`;
          const isUrgent = d <= 1;
          return (
            <div key={t.id} className={`fn-upcoming-row ${t.type}${isUrgent ? ' urgent' : ''}`}>
              <div className={`fn-upcoming-date-box${isUrgent ? ' urgent' : ''}`}>
                <span className="fn-upc-days">{lbl}</span>
                <span className="fn-upc-dt">{fmtShort(due)}</span>
              </div>
              <div className="fn-upc-info">
                <span className="fn-upc-desc">{t.description}</span>
                {proj && <span className="fn-upc-proj" style={{ color: proj.color }}>{proj.name}</span>}
              </div>
              <span className={`fn-upc-amt ${t.type}`}>
                {t.type === 'income' ? '+' : '-'}{fmtM(t.amount)}
              </span>
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ── Nakit Akış Tahmini (önümüzdeki 30 gün) ───────────────────
function NakitTahmini({ transactions, projById }) {
  const today = fnTodayISO();
  const in30  = new Date(); in30.setDate(in30.getDate() + 30);
  const in30S = in30.toISOString().slice(0, 10);
  const pending = transactions.filter(t => {
    if (t.status === 'done' || t.status === 'cancelled') return false;
    const due = t.dueDate || t.date;
    return due <= in30S;
  });
  const overdueIncluded = pending.filter(t => (t.dueDate || t.date) < today).reduce((s,t) => s+t.amount, 0);
  const expInc = pending.filter(t => t.type === 'income') .reduce((s,t) => s+t.amount, 0);
  const expExp = pending.filter(t => t.type === 'expense').reduce((s,t) => s+t.amount, 0);
  const netP   = expInc - expExp;
  const total  = expInc + expExp || 1;
  const incPct = Math.round(expInc / total * 100);
  const expPct = Math.round(expExp / total * 100);

  const sorted = [...pending]
    .sort((a, b) => (a.dueDate||a.date).localeCompare(b.dueDate||b.date))
    .slice(0, 5);
  const fmtShort = d => new Date(d+'T00:00:00').toLocaleDateString('tr-TR',{day:'numeric',month:'short'});
  const diffDays = d => Math.round((new Date(d+'T00:00:00') - new Date().setHours(0,0,0,0)) / 86400000);

  return (
    <div className="card fn-proj-card">
      <div className="fn-chart-hdr">
        <span className="fn-section-title">30 Günlük Projeksiyon</span>
        <span style={{ fontSize: 11, color: 'var(--text-faint)' }}>{pending.length} bekleyen işlem</span>
      </div>
      {pending.length === 0 ? (
        <div className="report-empty" style={{ padding: '20px 0' }}>Bekleyen işlem yok</div>
      ) : (
        <>
          <div className="fn-proj-stats">
            <div className="fn-proj-stat">
              <span className="fn-proj-val inc">{fmtM(expInc)}</span>
              <span className="fn-proj-lbl">Beklenen Gelir</span>
            </div>
            <div className="fn-proj-stat">
              <span className="fn-proj-val exp">{fmtM(expExp)}</span>
              <span className="fn-proj-lbl">Beklenen Gider</span>
            </div>
            <div className="fn-proj-stat">
              <span className="fn-proj-val" style={{ color: netP >= 0 ? '#16a34a' : '#dc2626' }}>
                {netP >= 0 ? '+' : ''}{fmtM(netP)}
              </span>
              <span className="fn-proj-lbl">Net Projeksiyon</span>
            </div>
          </div>
          <div className="fn-proj-bar-track">
            <div className="fn-proj-bar-inc" style={{ width: incPct + '%' }} />
            <div className="fn-proj-bar-exp" style={{ width: expPct + '%' }} />
          </div>
          <div className="fn-proj-bar-labels">
            <span style={{ color: '#16a34a', fontSize: 10, fontWeight: 600 }}>Gelir {incPct}%</span>
            <span style={{ color: '#dc2626', fontSize: 10, fontWeight: 600 }}>Gider {expPct}%</span>
          </div>
          {sorted.length > 0 && (
            <div className="fn-proj-items">
              {sorted.map(t => {
                const due = t.dueDate || t.date;
                const d   = diffDays(due);
                const lbl = d < 0 ? Math.abs(d) + 'g gecikmiş' : d === 0 ? 'Bugün' : d === 1 ? 'Yarın' : d + 'g';
                const overdue = d < 0;
                const proj = projById[t.projectId];
                return (
                  <div key={t.id} className="fn-proj-item">
                    <span className={'fn-proj-item-dot ' + t.type} />
                    <div className="fn-proj-item-info">
                      <span className="fn-proj-item-desc">{t.description}</span>
                      {proj && <span className="fn-proj-item-proj" style={{ color: proj.color }}>{proj.name}</span>}
                    </div>
                    <div className="fn-proj-item-right">
                      <span className={'fn-proj-item-amt ' + t.type}>
                        {t.type === 'income' ? '+' : '-'}{fmtM(t.amount)}
                      </span>
                      <span className="fn-proj-item-due" style={{ color: overdue ? '#dc2626' : 'var(--text-faint)' }}>
                        {lbl}
                      </span>
                    </div>
                  </div>
                );
              })}
            </div>
          )}
          {overdueIncluded > 0 && (
            <div style={{ fontSize: 10, color: 'var(--text-faint)', marginTop: 8, paddingTop: 6, borderTop: '1px solid var(--border)' }}>
              ⚠ {fmtM(overdueIncluded)} vadesi geçmiş işlem dahil
            </div>
          )}
        </>
      )}
    </div>
  );
}

// ── Kâr Marjı Trendi ─────────────────────────────────────────
function MarjTrend({ monthlyData }) {
  const [hovIdx, setHovIdx] = React.useState(null);
  const pts = monthlyData.filter(m => m.inc > 0 || m.exp > 0);
  if (pts.length < 2) return null;
  const margins = pts.map(m => m.inc > 0 ? Math.round((m.inc-m.exp)/m.inc*100) : 0);
  const W = 400, H = 60;
  const minM = Math.min(...margins, -10), maxM = Math.max(...margins, 30);
  const range = maxM - minM || 1;
  const step  = W / (pts.length - 1);
  const path  = margins.map((v, i) => `${i===0?'M':'L'}${(i*step).toFixed(1)},${(H - ((v-minM)/range)*H).toFixed(1)}`).join(' ');
  const lastM = margins[margins.length-1];
  const color = lastM >= 0 ? '#16a34a' : '#dc2626';
  const zeroY = H - ((0-minM)/range)*H;
  const TIP_W = 90, TIP_H = 30;
  return (
    <div className="fn-marj-trend">
      <div className="fn-marj-hdr">
        <span>12 Aylık Kâr Marjı Trendi</span>
        <span style={{ color, fontWeight: 700 }}>{lastM}%</span>
      </div>
      <svg viewBox={`0 0 ${W} ${H + 14}`} style={{ width: '100%', height: H + 14, display: 'block', overflow: 'visible' }}>
        {zeroY >= 0 && zeroY <= H && (
          <line x1={0} y1={zeroY} x2={W} y2={zeroY} stroke="var(--border)" strokeWidth={1} strokeDasharray="3 2" />
        )}
        <path d={path} fill="none" stroke={color} strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" />
        {margins.map((v, i) => {
          const cx = i * step;
          const cy = H - ((v - minM) / range) * H;
          return (
            <circle key={i} cx={cx} cy={cy}
              r={hovIdx === i ? 4.5 : 2.5}
              fill={v >= 0 ? '#16a34a' : '#dc2626'}
              style={{ transition: 'r 0.12s ease' }} />
          );
        })}
        {/* Invisible hit zones */}
        {pts.map((m, i) => {
          const zoneW = pts.length > 1 ? step : W;
          const zoneX = Math.max(0, i * step - zoneW / 2);
          return (
            <rect key={'hz' + i} x={zoneX} y={0} width={zoneW} height={H}
              fill="transparent" style={{ cursor: 'crosshair' }}
              onMouseEnter={() => setHovIdx(i)}
              onMouseLeave={() => setHovIdx(null)} />
          );
        })}
        {pts.map((m, i) => (
          <text key={i} x={i*step} y={H+11} textAnchor="middle" fontSize={8} fill={hovIdx === i ? 'var(--text)' : 'var(--text-faint)'}>{m.lbl}</text>
        ))}
        {/* Hover tooltip */}
        {hovIdx !== null && (() => {
          const v = margins[hovIdx];
          const cx = hovIdx * step;
          const cy = H - ((v - minM) / range) * H;
          const tipX = Math.max(0, Math.min(cx - TIP_W / 2, W - TIP_W));
          const tipY = Math.max(2, cy - TIP_H - 8);
          const c = v >= 0 ? '#16a34a' : '#dc2626';
          return (
            <g>
              <line x1={cx} y1={0} x2={cx} y2={H} stroke="var(--border)" strokeWidth={1} strokeDasharray="2 2" />
              <rect x={tipX} y={tipY} width={TIP_W} height={TIP_H}
                rx={5} fill="var(--panel)" stroke="var(--border-strong)" strokeWidth={0.5} />
              <text x={tipX + TIP_W / 2} y={tipY + 12} textAnchor="middle" fontSize={8.5} fill="var(--text-muted)">{pts[hovIdx].lbl}</text>
              <text x={tipX + TIP_W / 2} y={tipY + 24} textAnchor="middle" fontSize={9} fontWeight={700} fill={c}>{v}% kâr marjı</text>
            </g>
          );
        })()}
      </svg>
    </div>
  );
}

// ── Firma K&Z Tablosu ─────────────────────────────────────────
function ProjectPLTable({ rows, monthlyData, onStatement }) {
  if (!rows.length) return null;
  return (
    <div className="fn-pl-table">
      <div className="fn-pl-header">
        <span>Firma</span>
        <span>Gelir</span>
        <span>Gider</span>
        <span>Net</span>
        <span>Kâr Marjı</span>
      </div>
      {rows.map(({ project: p, inc, exp, net, margin }) => {
        const isPos = net >= 0;
        const barPct = Math.min(100, Math.abs(margin ?? 0));
        return (
          <div key={p.id} className="fn-pl-row">
            <div className="fn-pl-proj">
              <span className="project-dot" style={{ background: p.color }} />
              <span>{p.name}</span>
              <button className="fn-tx-action-btn" style={{ marginLeft: 4 }} title="Cari ekstre"
                onClick={() => onStatement?.(p)}>
                <FNIcons.FileText size={12} />
              </button>
            </div>
            <span className="fn-pl-val inc">{inc > 0 ? fmtM(inc) : '—'}</span>
            <span className="fn-pl-val exp">{exp > 0 ? fmtM(exp) : '—'}</span>
            <span className={`fn-pl-val net ${isPos ? 'pos' : 'neg'}`}>{net !== 0 ? (isPos ? '+' : '') + fmtM(net) : '—'}</span>
            <div className="fn-pl-margin">
              {margin !== null ? (
                <>
                  <div className="fn-pl-bar-track">
                    <div className="fn-pl-bar-fill" style={{ width: `${barPct}%`, background: isPos ? INC_COLOR : EXP_COLOR }} />
                  </div>
                  <span className="fn-pl-pct" style={{ color: isPos ? NET_POS : NET_NEG }}>{margin}%</span>
                </>
              ) : <span style={{ color: 'var(--text-faint)', fontSize: 12 }}>—</span>}
            </div>
          </div>
        );
      })}
    </div>
  );
}

const NET_POS = '#16a34a';
const NET_NEG = '#dc2626';

// ── Firma Cari Ekstresi ──────────────────────────────────────
function StatementModal({ project, transactions, onClose }) {
  const rows = React.useMemo(() => {
    const txs = transactions
      .filter(t => t.projectId === project.id && t.status !== 'cancelled')
      .sort((a, b) => a.date.localeCompare(b.date) || (a.createdAt||'').localeCompare(b.createdAt||''));
    let balance = 0;
    return txs.map(t => {
      balance += t.type === 'income' ? t.amount : -t.amount;
      return { tx: t, balance };
    });
  }, [project.id, transactions]);

  const totInc = rows.reduce((s, r) => s + (r.tx.type === 'income'  ? r.tx.amount : 0), 0);
  const totExp = rows.reduce((s, r) => s + (r.tx.type === 'expense' ? r.tx.amount : 0), 0);
  const finalBalance = rows.length ? rows[rows.length - 1].balance : 0;

  React.useEffect(() => {
    const fn = e => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', fn);
    return () => window.removeEventListener('keydown', fn);
  }, [onClose]);

  const printStatement = () => {
    const root = document.documentElement;
    const prevTheme = root.dataset.theme;
    root.dataset.theme = 'light';
    document.body.classList.add('fn-printing-statement');
    setTimeout(() => {
      window.print();
      root.dataset.theme = prevTheme;
      document.body.classList.remove('fn-printing-statement');
    }, 100);
  };

  return (
    <div className="confirm-overlay" onClick={onClose}>
      <div className="finance-modal fn-statement-modal" onClick={e => e.stopPropagation()}>
        <div className="finance-modal-hdr">
          <span className="project-dot" style={{ background: project.color }} />
          <h3>{project.name} — Cari Ekstre</h3>
          <button className="btn btn-ghost btn-sm fn-no-print" onClick={printStatement} title="PDF olarak yazdır">
            <FNIcons.FileText size={13} /> PDF
          </button>
          <button className="icon-btn icon-btn-sm fn-no-print" onClick={onClose}><FNIcons.X size={16} /></button>
        </div>
        <div className="fn-statement-body">
          <div className="fn-statement-meta">
            Düzenleme tarihi: {fmtDate(fnTodayISO())} · {rows.length} işlem
          </div>
          {rows.length === 0 ? (
            <div className="report-empty">Bu firmaya ait işlem yok</div>
          ) : (
            <>
              <div className="fn-stmt-row fn-stmt-head">
                <span>Tarih</span>
                <span>Açıklama</span>
                <span className="num">Gelir</span>
                <span className="num">Gider</span>
                <span className="num">Bakiye</span>
              </div>
              {rows.map(({ tx: t, balance }) => (
                <div key={t.id} className="fn-stmt-row">
                  <span>{fmtDate(t.date)}</span>
                  <span className="fn-stmt-desc">
                    {t.description}
                    {t.invoiceNo && <span className="fn-invoice-chip">#{t.invoiceNo}</span>}
                    {t.status && t.status !== 'done' && <span style={{ color: TX_STATUSES[t.status]?.color, fontSize: 10, marginLeft: 4 }}>({TX_STATUSES[t.status]?.short})</span>}
                  </span>
                  <span className="num" style={{ color: t.type === 'income' ? NET_POS : 'var(--text-faint)' }}>
                    {t.type === 'income' ? fmtM(t.amount) : '—'}
                  </span>
                  <span className="num" style={{ color: t.type === 'expense' ? NET_NEG : 'var(--text-faint)' }}>
                    {t.type === 'expense' ? fmtM(t.amount) : '—'}
                  </span>
                  <span className="num" style={{ fontWeight: 700, color: balance >= 0 ? NET_POS : NET_NEG }}>
                    {balance < 0 ? '-' : ''}{fmtM(Math.abs(balance))}
                  </span>
                </div>
              ))}
              <div className="fn-stmt-row fn-stmt-total">
                <span />
                <span style={{ fontWeight: 700 }}>Toplam</span>
                <span className="num" style={{ color: NET_POS, fontWeight: 700 }}>{fmtM(totInc)}</span>
                <span className="num" style={{ color: NET_NEG, fontWeight: 700 }}>{fmtM(totExp)}</span>
                <span className="num" style={{ fontWeight: 800, color: finalBalance >= 0 ? NET_POS : NET_NEG }}>
                  {finalBalance < 0 ? '-' : ''}{fmtM(Math.abs(finalBalance))}
                </span>
              </div>
            </>
          )}
        </div>
      </div>
    </div>
  );
}

// ── Kategori Bütçe Hedefleri Modalı ──────────────────────────
function BudgetModal({ state, onSetCategoryBudget, onClose }) {
  const cats = fnGetCats(state, 'expense');
  const budgets = state.categoryBudgets || {};
  const [draft, setDraft] = React.useState(() => {
    const d = {};
    cats.forEach(c => { d[c] = budgets[c] ? String(budgets[c]) : ''; });
    return d;
  });

  React.useEffect(() => {
    const fn = e => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', fn);
    return () => window.removeEventListener('keydown', fn);
  }, [onClose]);

  const save = () => {
    cats.forEach(c => {
      const v = parseFloat(String(draft[c]).replace(',', '.'));
      onSetCategoryBudget(c, v > 0 ? v : null);
    });
    onClose();
  };

  return (
    <div className="confirm-overlay" onClick={onClose}>
      <div className="finance-modal" onClick={e => e.stopPropagation()} style={{ maxWidth: 420 }}>
        <div className="finance-modal-hdr">
          <h3>Aylık Bütçe Hedefleri</h3>
          <button className="icon-btn icon-btn-sm" onClick={onClose}><FNIcons.X size={16} /></button>
        </div>
        <div className="finance-modal-body">
          <p style={{ fontSize: 12, color: 'var(--text-muted)', margin: '0 0 4px' }}>
            Kategori bazında aylık gider limiti belirle. Boş bırakılan kategori takip edilmez.
          </p>
          {cats.map(c => (
            <label key={c} className="login-field" style={{ flexDirection: 'row', alignItems: 'center', gap: 10 }}>
              <span style={{ flex: 1, marginBottom: 0 }}>{c}</span>
              <input type="number" className="input" style={{ width: 120 }} min="0" placeholder="—"
                value={draft[c]}
                onChange={e => setDraft(d => ({ ...d, [c]: e.target.value }))} />
            </label>
          ))}
        </div>
        <div className="finance-modal-footer">
          <button className="btn btn-ghost btn-sm" onClick={onClose}>Vazgeç</button>
          <button className="btn btn-primary btn-sm" onClick={save}><FNIcons.Save size={14} /> Kaydet</button>
        </div>
      </div>
    </div>
  );
}

// ── Ana Ekran ─────────────────────────────────────────────────
function FinanceView({ state, onAddTransaction, onUpdateTransaction, onDeleteTransaction, onSetCategoryBudget, onAddCustomCategory }) {
  const [period,       setPeriod]      = React.useState('month');
  const [customFrom,   setCustomFrom]  = React.useState('');
  const [customTo,     setCustomTo]    = React.useState('');
  const [filterType,   setFilterType]  = React.useState('all');
  const [filterProj,   setFilterProj]  = React.useState('all');
  const [filterCat,    setFilterCat]   = React.useState('all');
  const [filterStatus, setFilterStatus]= React.useState('all');
  const [search,       setSearch]      = React.useState('');
  const [catTab,       setCatTab]      = React.useState('expense');
  const [showAdd,      setShowAdd]     = React.useState(false);
  const [addType,      setAddType]     = React.useState('income');
  const [editTx,       setEditTx]      = React.useState(null);
  const [delConfirm,   setDelConfirm]  = React.useState(null);
  const [statementProj,setStatementProj] = React.useState(null);
  const [showBudgets,  setShowBudgets] = React.useState(false);
  const [editGoal,     setEditGoal]    = React.useState(false);
  const [goalDraft,    setGoalDraft]   = React.useState('');

  const transactions = state.transactions || [];
  const projects     = state.projects     || [];
  const projById     = Object.fromEntries(projects.map(p => [p.id, p]));

  // ── Tekrarlayan işlemler — şablonlardan eksik ayları üret ──
  const recurGenRef = React.useRef(false);
  React.useEffect(() => {
    if (recurGenRef.current) return;
    recurGenRef.current = true;
    const today = fnTodayISO();
    transactions.filter(t => t.recurrence?.type === 'monthly').forEach(tpl => {
      const children = transactions.filter(t => t.recurrenceSourceId === tpl.id);
      let lastDate = children.reduce((m, c) => c.date > m ? c.date : m, tpl.date);
      let next = fnNextMonth(lastDate);
      let guard = 0;
      while (next <= today && guard < 12) {
        onAddTransaction({
          type: tpl.type, amount: tpl.amount,
          description: tpl.description,
          category: tpl.category, projectId: tpl.projectId || null,
          invoiceNo: null, notes: tpl.notes || null, vat: tpl.vat || null,
          date: next, status: 'pending', dueDate: next,
          recurrenceSourceId: tpl.id,
        });
        lastDate = next; next = fnNextMonth(lastDate); guard++;
      }
    });
  }, []);

  // Dönem hesaplama
  const todayStr = new Date().toISOString().slice(0, 10);
  const dateRange = React.useMemo(() => {
    const now = new Date();
    const pad = n => String(n).padStart(2, '0');
    const fmt = d => `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())}`;
    if (period === 'month') {
      return { from: fmt(new Date(now.getFullYear(), now.getMonth(), 1)), to: fmt(new Date(now.getFullYear(), now.getMonth() + 1, 0)) };
    }
    if (period === 'last') {
      return { from: fmt(new Date(now.getFullYear(), now.getMonth() - 1, 1)), to: fmt(new Date(now.getFullYear(), now.getMonth(), 0)) };
    }
    if (period === 'year') {
      return { from: `${now.getFullYear()}-01-01`, to: `${now.getFullYear()}-12-31` };
    }
    if (period === 'custom') return { from: customFrom, to: customTo };
    return { from: '', to: '' };
  }, [period, customFrom, customTo]);

  // Döneme göre filtre
  const periodTx = React.useMemo(() => transactions.filter(t => {
    if (dateRange.from && t.date < dateRange.from) return false;
    if (dateRange.to   && t.date > dateRange.to)   return false;
    return true;
  }), [transactions, dateRange]);

  // Liste için ek filtre
  const filtered = React.useMemo(() => [...periodTx].filter(t => {
    if (filterType   !== 'all' && t.type      !== filterType)  return false;
    if (filterProj   !== 'all' && t.projectId !== filterProj)  return false;
    if (filterCat    !== 'all' && t.category  !== filterCat)   return false;
    if (filterStatus !== 'all') {
      if (filterStatus === 'overdue') { if (!fnIsOverdue(t)) return false; }
      else { if ((t.status || 'done') !== filterStatus) return false; }
    }
    if (search) {
      const q = search.toLowerCase();
      const hay = [t.description, t.invoiceNo, t.notes, t.category].filter(Boolean).join(' ').toLowerCase();
      if (!hay.includes(q)) return false;
    }
    return true;
  }).sort((a, b) => b.date.localeCompare(a.date) || (b.createdAt||'').localeCompare(a.createdAt||'')),
  [periodTx, filterType, filterProj, filterCat, filterStatus, search]);

  // KPI'lar — gerçekleşen vs beklenen ayrımı
  const realizedInc  = periodTx.filter(t => t.type === 'income'  && fnIsRealized(t)).reduce((s,t) => s+t.amount, 0);
  const realizedExp  = periodTx.filter(t => t.type === 'expense' && fnIsRealized(t)).reduce((s,t) => s+t.amount, 0);
  const netRealized  = realizedInc - realizedExp;

  const expectedInc  = periodTx.filter(t => t.type === 'income'  && !fnIsRealized(t) && t.status !== 'cancelled').reduce((s,t) => s+t.amount, 0);
  const expectedExp  = periodTx.filter(t => t.type === 'expense' && !fnIsRealized(t) && t.status !== 'cancelled').reduce((s,t) => s+t.amount, 0);

  const overdueList  = periodTx.filter(t => fnIsOverdue(t));
  const overdueAmt   = overdueList.reduce((s,t) => s+t.amount, 0);

  // Gelir hedefi
  const incomeGoal = (state.categoryBudgets || {})['__income_goal__'] || 0;
  const goalPct    = incomeGoal > 0 ? Math.round((realizedInc / incomeGoal) * 100) : 0;

  // Önceki dönem karşılaştırması
  const prevKpi = React.useMemo(() => {
    const now = new Date();
    const pad = n => String(n).padStart(2, '0');
    const fmt = d => `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())}`;
    let from = '', to = '';
    if (period === 'month') {
      from = fmt(new Date(now.getFullYear(), now.getMonth() - 1, 1));
      to   = fmt(new Date(now.getFullYear(), now.getMonth(), 0));
    } else if (period === 'last') {
      from = fmt(new Date(now.getFullYear(), now.getMonth() - 2, 1));
      to   = fmt(new Date(now.getFullYear(), now.getMonth() - 1, 0));
    } else if (period === 'year') {
      from = `${now.getFullYear()-1}-01-01`; to = `${now.getFullYear()-1}-12-31`;
    } else if (period === 'custom' && customFrom && customTo) {
      const f = new Date(customFrom + 'T00:00:00'), t2 = new Date(customTo + 'T00:00:00');
      const span = Math.round((t2 - f) / 86400000) + 1;
      const pf = new Date(f); pf.setDate(pf.getDate() - span);
      const pt = new Date(f); pt.setDate(pt.getDate() - 1);
      from = fmt(pf); to = fmt(pt);
    } else return null;
    const ptx = transactions.filter(t => t.date >= from && t.date <= to);
    return {
      inc: ptx.filter(t => t.type === 'income'  && fnIsRealized(t)).reduce((s,t) => s+t.amount, 0),
      exp: ptx.filter(t => t.type === 'expense' && fnIsRealized(t)).reduce((s,t) => s+t.amount, 0),
    };
  }, [period, customFrom, customTo, transactions]);

  // KDV özeti (seçili dönem, iptal hariç)
  const vatCollected = periodTx.filter(t => t.type === 'income'  && t.status !== 'cancelled').reduce((s,t) => s + fnVatAmount(t), 0);
  const vatPaid      = periodTx.filter(t => t.type === 'expense' && t.status !== 'cancelled').reduce((s,t) => s + fnVatAmount(t), 0);
  const hasVat       = vatCollected > 0 || vatPaid > 0;

  // Eski KPI isimleri (grafik ve diğer hesaplar için)
  const totalIncome  = realizedInc + expectedInc;
  const totalExpense = realizedExp + expectedExp;
  const netBalance   = netRealized;
  const txCount      = periodTx.filter(t => t.status !== 'cancelled').length;

  // Son 12 ay verisi (grafik her zaman 12 ayı gösterir)
  const monthlyData = React.useMemo(() => {
    const now = new Date();
    return Array.from({ length: 12 }, (_, i) => {
      const d   = new Date(now.getFullYear(), now.getMonth() - (11 - i), 1);
      const key = `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`;
      const lbl = d.toLocaleDateString('tr-TR', { month: 'short' });
      const inc = transactions.filter(t => t.type === 'income'  && t.date.startsWith(key)).reduce((s,t) => s+t.amount, 0);
      const exp = transactions.filter(t => t.type === 'expense' && t.date.startsWith(key)).reduce((s,t) => s+t.amount, 0);
      return { key, lbl, inc, exp };
    });
  }, [transactions]);

  // Aylık ortalamalar (veri olan aylardan)
  const activeMonths = Math.max(1, monthlyData.filter(m => m.inc > 0 || m.exp > 0).length);
  const allIncome    = transactions.filter(t => t.type === 'income') .reduce((s,t) => s+t.amount, 0);
  const allExpense   = transactions.filter(t => t.type === 'expense').reduce((s,t) => s+t.amount, 0);
  const avgIncome    = allIncome  / activeMonths;
  const avgExpense   = allExpense / activeMonths;

  // En iyi ay (net)
  const bestMonth = monthlyData.reduce((best, m) => (m.inc - m.exp) > (best ? best.inc - best.exp : -Infinity) ? m : best, null);

  // Kategori dağılımı (seçilen dönem)
  const catData = React.useMemo(() => {
    const cats = fnGetCats(state, catTab);
    return cats.map(cat => ({
      label: cat,
      value: periodTx.filter(t => t.type === catTab && t.category === cat).reduce((s,t) => s+t.amount, 0),
    })).filter(c => c.value > 0).sort((a,b) => b.value - a.value);
  }, [periodTx, catTab, state.customCategories]);

  // Bütçe takibi — her zaman içinde bulunulan aya göre
  const categoryBudgets = state.categoryBudgets || {};
  const budgetRows = React.useMemo(() => {
    const monthKey = todayStr.slice(0, 7);
    return Object.entries(categoryBudgets).filter(([cat]) => cat !== '__income_goal__').map(([cat, limit]) => {
      const spent = transactions
        .filter(t => t.type === 'expense' && t.category === cat && t.status !== 'cancelled' && t.date.startsWith(monthKey))
        .reduce((s, t) => s + t.amount, 0);
      return { cat, limit, spent, pct: Math.round((spent / limit) * 100) };
    }).sort((a, b) => b.pct - a.pct);
  }, [categoryBudgets, transactions, todayStr]);

  // Proje K&Z
  const projectPL = React.useMemo(() => projects.map(p => {
    const txs = periodTx.filter(t => t.projectId === p.id);
    const inc = txs.filter(t => t.type === 'income') .reduce((s,t) => s+t.amount, 0);
    const exp = txs.filter(t => t.type === 'expense').reduce((s,t) => s+t.amount, 0);
    const net = inc - exp;
    const margin = inc > 0 ? Math.round((net / inc) * 100) : null;
    return { project: p, inc, exp, net, margin, count: txs.length };
  }).filter(r => r.count > 0).sort((a,b) => Math.abs(b.net) - Math.abs(a.net)),
  [projects, periodTx]);

  // İşlemleri aya göre grupla
  const txGroups = React.useMemo(() => {
    const groups = {};
    filtered.forEach(t => {
      const key = t.date.slice(0, 7);
      if (!groups[key]) {
        const d = new Date(key + '-01');
        groups[key] = { key, label: d.toLocaleDateString('tr-TR', { month: 'long', year: 'numeric' }), items: [], inc: 0, exp: 0 };
      }
      groups[key].items.push(t);
      if (t.type === 'income')  groups[key].inc += t.amount;
      else                       groups[key].exp += t.amount;
    });
    return Object.values(groups).sort((a,b) => b.key.localeCompare(a.key));
  }, [filtered]);

  // Tüm kategoriler (liste filtresi için)
  const allCats = React.useMemo(() => {
    const set = new Set(periodTx.map(t => t.category));
    return [...set].sort();
  }, [periodTx]);

  // İşlem kopyalama (rest spread kullanılmıyor — Babel global scope çakışması)
  const duplicateTx = (t) => {
    onAddTransaction({
      type:        t.type,
      amount:      t.amount,
      description: t.description,
      category:    t.category,
      projectId:   t.projectId   || null,
      invoiceNo:   t.invoiceNo   || null,
      notes:       t.notes       || null,
      vat:         t.vat         || null,
      date:        todayStr,
      status:      'pending',
      dueDate:     null,
    });
  };

  // Hızlı durum güncelleme
  const cycleStatus = (t) => {
    const ORDER = ['done','pending','deferred','cancelled'];
    const cur  = t.status || 'done';
    const next = ORDER[(ORDER.indexOf(cur) + 1) % ORDER.length];
    onUpdateTransaction?.(t.id, { status: next });
  };

  // CSV dışa aktarma
  const exportCSV = () => {
    const rows = [
      ['Tarih','Tür','Tutar','Açıklama','Kategori','Firma'],
      ...filtered.map(t => [
        t.date,
        t.type === 'income' ? 'Gelir' : 'Gider',
        t.amount.toFixed(2),
        `"${(t.description||'').replace(/"/g,'""')}"`,
        t.category || '',
        projById[t.projectId]?.name || '',
      ].join(','))
    ];
    const blob = new Blob(['﻿' + rows.join('\n')], { type: 'text/csv;charset=utf-8;' });
    const url  = URL.createObjectURL(blob);
    const a    = document.createElement('a');
    a.href = url; a.download = `finans-${todayStr}.csv`;
    a.click(); URL.revokeObjectURL(url);
  };

  const PERIODS = [
    { id: 'month', label: 'Bu Ay' },
    { id: 'last',  label: 'Geçen Ay' },
    { id: 'year',  label: 'Bu Yıl' },
    { id: 'all',   label: 'Tümü' },
    { id: 'custom',label: 'Özel' },
  ];

  return (
    <div className="content finance-content">

      {/* ── Header ──────────────────────────────────────────── */}
      <div className="finance-header">
        <h2 className="finance-title">Gelir & Gider</h2>
        <div className="fn-period-pills">
          {PERIODS.map(p => (
            <button key={p.id} className={`fn-period-pill ${period === p.id ? 'active' : ''}`} onClick={() => setPeriod(p.id)}>
              {p.label}
            </button>
          ))}
        </div>
        {period === 'custom' && (
          <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
            <input type="date" className="select-control" value={customFrom} onChange={e => setCustomFrom(e.target.value)} />
            <span style={{ color: 'var(--text-faint)', fontSize: 12 }}>—</span>
            <input type="date" className="select-control" value={customTo}   onChange={e => setCustomTo(e.target.value)} />
          </div>
        )}
        <div style={{ flex: 1 }} />
        <button className="btn btn-ghost btn-sm" onClick={exportCSV} title="CSV dışa aktar">
          <FNIcons.FileDown size={14} /> CSV
        </button>
        <button className="btn btn-ghost btn-sm" onClick={() => { setAddType('income'); setShowAdd(true); }} style={{ color: NET_POS }}>
          <FNIcons.TrendingUp size={14} /> Gelir Ekle
        </button>
        <button className="btn btn-primary btn-sm" onClick={() => { setAddType('expense'); setShowAdd(true); }}>
          <FNIcons.TrendingDown size={14} /> Gider Ekle
        </button>
      </div>

      {/* Cetvel imzası */}
      <div className="dash-rule" aria-hidden="true" />

      {/* ── KPI Kartları — gerçekleşen / beklenen / gecikmiş ── */}
      <div className="fn-kpi-strip">
        {/* Gerçekleşen Gelir */}
        <div className="fn-kpi inc">
          <div className="fn-kpi-icon"><FNIcons.TrendingUp size={18} /></div>
          <div>
            <div className="fn-kpi-label">Tahsil Edilen</div>
            <div className="fn-kpi-val inc">{fmtM(realizedInc)}</div>
            {expectedInc > 0 && <div className="fn-kpi-sub">+ {fmtM(expectedInc)} bekleniyor</div>}
            {prevKpi && <FnDelta cur={realizedInc} prev={prevKpi.inc} />}
          </div>
        </div>
        {/* Gerçekleşen Gider */}
        <div className="fn-kpi exp">
          <div className="fn-kpi-icon"><FNIcons.TrendingDown size={18} /></div>
          <div>
            <div className="fn-kpi-label">Ödenen</div>
            <div className="fn-kpi-val exp">{fmtM(realizedExp)}</div>
            {expectedExp > 0 && <div className="fn-kpi-sub">+ {fmtM(expectedExp)} bekliyor</div>}
            {prevKpi && <FnDelta cur={realizedExp} prev={prevKpi.exp} invert />}
          </div>
        </div>
        {/* Net Gerçek */}
        <div className={`fn-kpi ${netRealized >= 0 ? 'net-pos' : 'net-neg'}`}>
          <div className="fn-kpi-icon"><FNIcons.Wallet size={18} /></div>
          <div>
            <div className="fn-kpi-label">Net Gerçek</div>
            <div className={`fn-kpi-val ${netRealized >= 0 ? 'inc' : 'exp'}`}>
              {netRealized < 0 ? '-' : ''}{fmtM(Math.abs(netRealized))}
            </div>
            {(expectedInc > 0 || expectedExp > 0) && (
              <div className="fn-kpi-sub">
                Proj: {(netRealized + expectedInc - expectedExp) >= 0 ? '+' : ''}{fmtM(netRealized + expectedInc - expectedExp)}
              </div>
            )}
            {prevKpi && <FnDelta cur={netRealized} prev={prevKpi.inc - prevKpi.exp} />}
          </div>
        </div>
        {/* Gecikmiş */}
        <div className={`fn-kpi ${overdueList.length > 0 ? 'net-neg' : 'neutral'}`}>
          <div className="fn-kpi-icon">
            <FNIcons.AlertCircle size={18} />
          </div>
          <div>
            <div className="fn-kpi-label">Gecikmiş</div>
            <div className={`fn-kpi-val ${overdueList.length > 0 ? 'exp' : ''}`} style={overdueList.length === 0 ? {color:'var(--text-faint)'} : {}}>
              {overdueList.length > 0 ? fmtM(overdueAmt) : 'Yok'}
            </div>
            {overdueList.length > 0
              ? <div className="fn-kpi-sub" style={{color:'var(--p-high)'}}>{overdueList.length} işlem</div>
              : <div className="fn-kpi-sub" style={{color:'#22c55e'}}>Her şey vadesinde ✓</div>}
          </div>
        </div>
        {/* İşlem sayısı / ort. gelir */}
        <div className="fn-kpi neutral">
          <div className="fn-kpi-icon"><FNIcons.Receipt size={18} style={{ opacity:.6 }} /></div>
          <div>
            <div className="fn-kpi-label">Ort. Aylık Gelir <span style={{ fontWeight: 400, opacity: .7 }}>(tüm zamanlar)</span></div>
            <div className="fn-kpi-val" style={{ color:'var(--text)' }}>{transactions.length ? fmtM(avgIncome) : '—'}</div>
            <div className="fn-kpi-sub">{txCount} işlem</div>
          </div>
        </div>
      </div>

      {/* ── Gelir Hedefi Şeridi ─────────────────────────────── */}
      <div className={`fn-goal-strip${incomeGoal > 0 ? '' : ' fn-goal-strip--empty'}`}
        onClick={() => { setGoalDraft(incomeGoal > 0 ? String(incomeGoal) : ''); setEditGoal(true); }}>
        <FNIcons.Target size={14} style={{ flexShrink: 0, color: incomeGoal > 0 ? 'var(--accent)' : 'var(--text-faint)' }} />
        <span className="fn-goal-strip-label">Gelir Hedefi</span>
        {incomeGoal > 0 ? (
          <>
            <div className="fn-goal-strip-track">
              <div className="fn-goal-strip-fill" style={{
                width: Math.min(100, goalPct) + '%',
                background: goalPct >= 100 ? '#22c55e' : goalPct >= 75 ? '#f59e0b' : 'var(--accent)',
              }} />
            </div>
            <span className="fn-goal-strip-pct" style={{ color: goalPct >= 100 ? '#22c55e' : goalPct >= 75 ? '#f59e0b' : 'var(--accent)' }}>
              {goalPct}%
            </span>
            <span className="fn-goal-strip-vals">{fmtM(realizedInc)} / {fmtM(incomeGoal)}</span>
          </>
        ) : (
          <span style={{ fontSize: 12, color: 'var(--text-faint)' }}>Tıkla ve hedef belirle</span>
        )}
        <button className="fn-tx-action-btn" style={{ marginLeft: 'auto', flexShrink: 0 }} title="Hedefi düzenle"
          onClick={e => { e.stopPropagation(); setGoalDraft(incomeGoal > 0 ? String(incomeGoal) : ''); setEditGoal(true); }}>
          <FNIcons.Edit size={12} />
        </button>
      </div>

      {/* ── KDV Özeti ──────────────────────────────────────── */}
      {hasVat && (
        <div className="fn-vat-strip">
          <FNIcons.Receipt size={14} style={{ opacity: .6 }} />
          <span>KDV Özeti (seçili dönem):</span>
          <span className="fn-vat-item">Hesaplanan <strong style={{ color: INC_COLOR }}>{fmtM(vatCollected)}</strong></span>
          <span className="fn-vat-item">İndirilecek <strong style={{ color: EXP_COLOR }}>{fmtM(vatPaid)}</strong></span>
          <span className="fn-vat-item">Fark <strong style={{ color: vatCollected - vatPaid >= 0 ? NET_NEG : NET_POS }}>{fmtM(vatCollected - vatPaid)}</strong></span>
        </div>
      )}

      {/* ── Yaklaşan Ödemeler ──────────────────────────────── */}
      <YaklasanOdemeler transactions={transactions} projById={projById} />

      {/* ── Grafik + Kategori ───────────────────────────────── */}
      <div className="fn-charts-grid">
        {/* Nakit akış grafiği */}
        <div className="card fn-chart-main">
          <div className="fn-chart-hdr">
            <span className="fn-section-title">Son 12 Ay — Nakit Akışı</span>
            <div className="fn-chart-legend">
              <span className="fn-leg inc">Gelir</span>
              <span className="fn-leg exp">Gider</span>
              <span className="fn-leg net">Net</span>
            </div>
          </div>
          {transactions.length > 0
            ? <CashFlowChart data={monthlyData} />
            : <div className="report-empty" style={{ padding: '40px 0' }}>Henüz veri yok</div>
          }
          {bestMonth && (bestMonth.inc > 0 || bestMonth.exp > 0) && (
            <div className="fn-best-month">
              <FNIcons.Star size={12} />
              En iyi ay: <strong>{bestMonth.lbl}</strong> — net {fmtM(bestMonth.inc - bestMonth.exp)}
            </div>
          )}
        </div>

        {/* Nakit tahmini */}
        <NakitTahmini transactions={transactions} projById={projById} />

        {/* Kategori dağılımı */}
        <div className="card fn-chart-side">
          <div className="fn-chart-hdr">
            <span className="fn-section-title">Kategori Dağılımı</span>
            <div className="fn-cat-toggle">
              <button className={catTab === 'income'  ? 'active' : ''} onClick={() => setCatTab('income') }>Gelir</button>
              <button className={catTab === 'expense' ? 'active' : ''} onClick={() => setCatTab('expense')}>Gider</button>
            </div>
            {catTab === 'expense' && (
              <button className="fn-tx-action-btn" title="Aylık bütçe hedefleri" onClick={() => setShowBudgets(true)}>
                <FNIcons.Target size={13} />
              </button>
            )}
          </div>
          <CategoryBars data={catData} color={catTab === 'income' ? INC_COLOR : EXP_COLOR} />
          {catTab === 'expense' && budgetRows.length > 0 && (
            <div className="fn-budget-section">
              <div className="fn-budget-title">Bu Ay — Bütçe Hedefleri</div>
              {budgetRows.map(({ cat, limit, spent, pct }) => {
                const over = spent > limit;
                const warn = !over && pct >= 80;
                return (
                  <div key={cat} className="fn-budget-row">
                    <span className="fn-budget-lbl">{cat}</span>
                    <div className="fn-budget-track">
                      <div className="fn-budget-fill"
                        style={{ width: `${Math.min(pct, 100)}%`, background: over ? NET_NEG : warn ? '#f59e0b' : INC_COLOR }} />
                    </div>
                    <span className="fn-budget-val" style={{ color: over ? NET_NEG : warn ? '#f59e0b' : 'var(--text-muted)' }}>
                      {fmtM(spent, true)} / {fmtM(limit, true)}{over ? ' ⚠' : ''}
                    </span>
                  </div>
                );
              })}
            </div>
          )}
        </div>
      </div>

      {/* ── Firma K&Z ───────────────────────────────────────── */}
      {projectPL.length > 0 && (
        <div className="card fn-pl-wrap">
          <div className="fn-chart-hdr" style={{ marginBottom: 14 }}>
            <span className="fn-section-title">Firma Kâr & Zarar</span>
            <span style={{ fontSize: 12, color: 'var(--text-faint)' }}>{period === 'all' ? 'Tüm zamanlar' : 'Seçili dönem'}</span>
          </div>
          <ProjectPLTable rows={projectPL} onStatement={setStatementProj} />
          <MarjTrend monthlyData={monthlyData} />
        </div>
      )}

      {/* ── İşlem Listesi ───────────────────────────────────── */}
      <div className="fn-list-section">
        {/* Filtre çubuğu */}
        <div className="fn-list-toolbar">
          <div className="fn-search-wrap">
            <FNIcons.Search size={14} style={{ color: 'var(--text-faint)', flexShrink: 0 }} />
            <input
              className="fn-search-input"
              placeholder="Açıklamada ara..."
              value={search}
              onChange={e => setSearch(e.target.value)}
            />
            {search && <button className="fn-search-clear" onClick={() => setSearch('')}><FNIcons.X size={12} /></button>}
          </div>
          <div className="fn-type-chips">
            {[{id:'all',label:'Tümü'},{id:'income',label:'Gelirler'},{id:'expense',label:'Giderler'}].map(t => (
              <button key={t.id} className={`fn-chip-btn ${filterType === t.id ? 'active' : ''}`} onClick={() => setFilterType(t.id)}>{t.label}</button>
            ))}
          </div>
          <select className="select-control" value={filterProj} onChange={e => setFilterProj(e.target.value)}>
            <option value="all">Tüm Firmalar</option>
            {projects.map(p => <option key={p.id} value={p.id}>{p.name}</option>)}
          </select>
          {allCats.length > 0 && (
            <select className="select-control" value={filterCat} onChange={e => setFilterCat(e.target.value)}>
              <option value="all">Tüm Kategoriler</option>
              {allCats.map(c => <option key={c} value={c}>{c}</option>)}
            </select>
          )}
          <select className="select-control" value={filterStatus} onChange={e => setFilterStatus(e.target.value)}>
            <option value="all">Tüm Durumlar</option>
            <option value="done">Alındı / Ödendi</option>
            <option value="pending">Bekliyor</option>
            <option value="deferred">Vadeli</option>
            <option value="overdue">Gecikmiş</option>
            <option value="cancelled">İptal</option>
          </select>
          <span className="fn-count-badge">{filtered.length} kayıt</span>
        </div>

        {/* Gruplu liste */}
        {filtered.length === 0 ? (
          <div className="finance-empty">
            <FNIcons.Receipt size={32} />
            <p>{transactions.length === 0 ? 'Henüz kayıt yok' : 'Filtre kriterlerine uyan kayıt bulunamadı'}</p>
            {transactions.length === 0 && (
              <div style={{ display: 'flex', gap: 8, marginTop: 4 }}>
                <button className="btn btn-ghost btn-sm" onClick={() => { setAddType('income'); setShowAdd(true); }} style={{ color: NET_POS }}>
                  <FNIcons.TrendingUp size={13} /> Gelir Ekle
                </button>
                <button className="btn btn-primary btn-sm" onClick={() => { setAddType('expense'); setShowAdd(true); }}>
                  <FNIcons.TrendingDown size={13} /> Gider Ekle
                </button>
              </div>
            )}
          </div>
        ) : (
          <div className="fn-tx-groups">
            {txGroups.map(g => (
              <div key={g.key} className="fn-tx-group">
                <div className="fn-tx-group-hdr">
                  <span className="fn-tx-month">{g.label}</span>
                  <div className="fn-tx-month-totals">
                    {g.inc > 0 && <span style={{ color: NET_POS, fontWeight: 700, fontSize: 12 }}>+{fmtM(g.inc)}</span>}
                    {g.exp > 0 && <span style={{ color: NET_NEG, fontWeight: 700, fontSize: 12 }}>-{fmtM(g.exp)}</span>}
                    <span className={`fn-tx-net ${(g.inc - g.exp) >= 0 ? 'pos' : 'neg'}`}>
                      Net: {(g.inc - g.exp) < 0 ? '-' : ''}{fmtM(Math.abs(g.inc - g.exp))}
                    </span>
                  </div>
                </div>
                {g.items.map(t => {
                  const proj = projById[t.projectId];
                  return (
                    <div key={t.id} className={`fn-tx-row ${t.type}${fnIsOverdue(t) ? ' overdue' : ''}${t.status === 'cancelled' ? ' cancelled' : ''}`}>
                      <div className="fn-tx-dot" />
                      <div className="fn-tx-info">
                        <div className="fn-tx-desc">
                          {t.description}
                          {t.invoiceNo && (
                            <span className="fn-invoice-chip">#{t.invoiceNo}</span>
                          )}
                          <span
                            className={`fn-status-badge-inline${fnIsOverdue(t) ? ' overdue' : ''}`}
                            style={!fnIsOverdue(t) && t.status && t.status !== 'done'
                              ? { background: TX_STATUSES[t.status]?.bg, color: TX_STATUSES[t.status]?.color, cursor: 'pointer' }
                              : fnIsOverdue(t) ? { cursor: 'pointer' } : {}}
                            onClick={() => cycleStatus(t)}
                            title="Tıkla: durumu değiştir"
                          >
                            {fnIsOverdue(t) ? 'Gecikmiş' : (t.status && t.status !== 'done' ? TX_STATUSES[t.status]?.short : null)}
                          </span>
                        </div>
                        <div className="fn-tx-meta">
                          <span className="fn-cat-chip">{t.category}</span>
                          {t.vat > 0 && <><span className="fn-sep">·</span><span style={{ fontSize: 10.5 }}>KDV %{t.vat}</span></>}
                          {t.recurrence && <><span className="fn-sep">·</span><span className="fn-recur-badge" title="Aylık tekrarlayan şablon"><FNIcons.Repeat size={10} /> Aylık</span></>}
                          {proj && (
                            <><span className="fn-sep">·</span><span className="project-dot" style={{ background: proj.color }} /><span>{proj.name}</span></>
                          )}
                          <span className="fn-sep">·</span>
                          <span>{fmtDate(t.date)}</span>
                          {t.dueDate && t.dueDate !== t.date && (
                            <><span className="fn-sep">·</span>
                            <span className={`fn-due${fnIsOverdue(t) ? ' overdue' : ''}`}>
                              Vade: {fmtDate(t.dueDate)}
                            </span></>
                          )}
                          {t.notes && (
                            <><span className="fn-sep">·</span>
                            <span className="fn-notes-text" title={t.notes}>📝 {t.notes.slice(0, 40)}{t.notes.length > 40 ? '…' : ''}</span></>
                          )}
                        </div>
                      </div>
                      <div className={`fn-tx-amount ${t.type}${t.status === 'cancelled' ? ' cancelled' : ''}`}>
                        {t.type === 'income' ? '+' : '-'}{fmtM(t.amount)}
                      </div>
                      <div className="fn-tx-actions">
                        {t.receipt && (
                          <button className="fn-tx-action-btn" title={`Eki aç: ${t.receipt.name}`}
                            onClick={() => {
                              const win = window.open();
                              if (win) win.document.write(
                                t.receipt.data.startsWith('data:application/pdf')
                                  ? `<iframe src="${t.receipt.data}" style="width:100%;height:100%;border:none"></iframe>`
                                  : `<img src="${t.receipt.data}" style="max-width:100%" alt="${t.receipt.name}">`
                              );
                            }}>
                            <FNIcons.Paperclip size={13} />
                          </button>
                        )}
                        <button className="fn-tx-action-btn" onClick={() => setEditTx(t)} title="Düzenle">
                          <FNIcons.Edit size={13} />
                        </button>
                        <button className="fn-tx-action-btn" onClick={() => duplicateTx(t)} title="Kopyala">
                          <FNIcons.Layers size={13} />
                        </button>
                        <button className="fn-tx-action-btn danger" onClick={() => setDelConfirm(t.id)} title="Sil">
                          <FNIcons.Trash size={13} />
                        </button>
                      </div>
                    </div>
                  );
                })}
              </div>
            ))}
          </div>
        )}
      </div>

      {/* ── Modallar ────────────────────────────────────────── */}
      {(showAdd || editTx) && (
        <AddTransactionModal
          state={state}
          initialType={editTx ? editTx.type : addType}
          editingTx={editTx}
          onAddCustomCategory={onAddCustomCategory}
          onSave={(data) => {
            if (editTx) {
              onUpdateTransaction?.(editTx.id, data);
              setEditTx(null);
            } else {
              if (Array.isArray(data)) data.forEach(d => onAddTransaction(d));
              else onAddTransaction(data);
              setShowAdd(false);
            }
          }}
          onClose={() => { setShowAdd(false); setEditTx(null); }}
        />
      )}

      {statementProj && (
        <StatementModal project={statementProj} transactions={transactions} onClose={() => setStatementProj(null)} />
      )}

      {showBudgets && (
        <BudgetModal state={state} onSetCategoryBudget={onSetCategoryBudget} onClose={() => setShowBudgets(false)} />
      )}

      {/* ── Gelir Hedefi Modalı ──────────────────────────────── */}
      {editGoal && (
        <div className="confirm-overlay" onClick={() => setEditGoal(false)}>
          <div className="confirm-modal" style={{ maxWidth: 320 }} onClick={e => e.stopPropagation()}>
            <div className="confirm-title">Aylık Gelir Hedefi</div>
            <p style={{ fontSize: 12, color: 'var(--text-faint)', margin: '4px 0 14px' }}>
              Seçili dönemde tahsil edilen gelirle karşılaştırılır.
            </p>
            <input
              type="number"
              className="select-control"
              style={{ width: '100%', fontSize: 15, textAlign: 'right' }}
              placeholder="Hedef tutar (₺)"
              value={goalDraft}
              onChange={e => setGoalDraft(e.target.value)}
              autoFocus
              onKeyDown={e => {
                if (e.key === 'Enter') {
                  const v = parseFloat(String(goalDraft).replace(',', '.'));
                  onSetCategoryBudget('__income_goal__', v > 0 ? v : null);
                  setEditGoal(false);
                }
                if (e.key === 'Escape') setEditGoal(false);
              }}
            />
            <div className="confirm-actions" style={{ marginTop: 14 }}>
              {incomeGoal > 0 && (
                <button className="btn btn-ghost btn-sm" style={{ color: '#dc2626' }}
                  onClick={() => { onSetCategoryBudget('__income_goal__', null); setEditGoal(false); }}>
                  Kaldır
                </button>
              )}
              <div style={{ flex: 1 }} />
              <button className="btn btn-ghost btn-sm" onClick={() => setEditGoal(false)}>İptal</button>
              <button className="btn btn-primary btn-sm" onClick={() => {
                const v = parseFloat(String(goalDraft).replace(',', '.'));
                onSetCategoryBudget('__income_goal__', v > 0 ? v : null);
                setEditGoal(false);
              }}>Kaydet</button>
            </div>
          </div>
        </div>
      )}

      {delConfirm && (
        <div className="confirm-overlay" onClick={() => setDelConfirm(null)}>
          <div className="confirm-dialog" onClick={e => e.stopPropagation()}>
            <div className="confirm-icon danger"><FNIcons.AlertCircle size={20} /></div>
            <div className="confirm-copy"><h3>Kayıt silinsin mi?</h3><p>Bu işlem geri alınamaz.</p></div>
            <div className="confirm-actions">
              <button className="btn btn-ghost" onClick={() => setDelConfirm(null)}>Vazgeç</button>
              <button className="btn btn-danger" onClick={() => { onDeleteTransaction(delConfirm); setDelConfirm(null); }}>Sil</button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

// ── Yeni Kayıt / Düzenleme Modalı ────────────────────────────
function AddTransactionModal({ state, initialType, editingTx, onSave, onClose, onAddCustomCategory }) {
  const isEdit = !!editingTx;
  const [type,        setType]        = React.useState(editingTx?.type        || initialType || 'income');
  const [amount,      setAmount]      = React.useState(editingTx ? String(editingTx.amount) : '');
  const [description, setDescription] = React.useState(editingTx?.description || '');
  const [invoiceNo,   setInvoiceNo]   = React.useState(editingTx?.invoiceNo   || '');
  const [notes,       setNotes]       = React.useState(editingTx?.notes       || '');
  const [category,    setCategory]    = React.useState(
    editingTx?.category || fnGetCats(state, initialType === 'expense' ? 'expense' : 'income')[0]
  );
  const [projectId,   setProjectId]   = React.useState(editingTx?.projectId || state.activeProjectId || '');
  const [date,        setDate]        = React.useState(editingTx?.date || new Date().toISOString().slice(0, 10));
  const [status,      setStatus]      = React.useState(editingTx?.status || 'done');
  const [dueDate,     setDueDate]     = React.useState(editingTx?.dueDate || '');
  const [vat,         setVat]         = React.useState(editingTx?.vat ? String(editingTx.vat) : '');
  const [recurring,   setRecurring]   = React.useState(!!editingTx?.recurrence);
  const [installments,setInstallments]= React.useState(1);
  const [receipt,     setReceipt]     = React.useState(editingTx?.receipt || null);
  const [newCatMode,  setNewCatMode]  = React.useState(false);
  const [newCatName,  setNewCatName]  = React.useState('');
  const [fileError,   setFileError]   = React.useState('');

  React.useEffect(() => {
    if (!isEdit) setCategory(fnGetCats(state, type)[0]);
  }, [type]);

  const handleFile = (e) => {
    const f = e.target.files?.[0];
    if (!f) return;
    setFileError('');
    if (f.size > 1.5 * 1024 * 1024) { setFileError('Dosya 1,5 MB\'tan büyük olamaz.'); return; }
    const reader = new FileReader();
    reader.onload = () => setReceipt({ name: f.name, data: reader.result });
    reader.readAsDataURL(f);
  };

  const addNewCat = () => {
    const name = newCatName.trim();
    if (!name) return;
    onAddCustomCategory?.(type, name);
    setCategory(name);
    setNewCatMode(false); setNewCatName('');
  };

  React.useEffect(() => {
    const fn = e => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', fn);
    return () => window.removeEventListener('keydown', fn);
  }, [onClose]);

  const save = () => {
    const amt = parseFloat(String(amount).replace(',', '.'));
    if (!amt || amt <= 0 || !description.trim()) return;
    const vatNum = parseFloat(String(vat).replace(',', '.')) || null;
    const base = {
      type, amount: amt,
      description: description.trim(),
      invoiceNo:  invoiceNo.trim() || null,
      notes:      notes.trim()     || null,
      category,
      projectId: projectId || null,
      date,
      status,
      dueDate: (status !== 'done' && status !== 'cancelled' && dueDate) ? dueDate : null,
      vat: vatNum && vatNum > 0 ? vatNum : null,
      recurrence: !isEdit && recurring ? { type: 'monthly' } : (isEdit && editingTx.recurrence && recurring ? editingTx.recurrence : null),
      receipt,
    };
    // Taksitlendirme — ilk taksit seçilen tarih/durumla, kalanlar aylık vadeli
    if (!isEdit && !recurring && installments > 1) {
      const per = Math.floor((amt / installments) * 100) / 100;
      const first = Math.round((amt - per * (installments - 1)) * 100) / 100;
      const list = [];
      let d = date;
      for (let i = 0; i < installments; i++) {
        list.push({
          ...base,
          amount: i === 0 ? first : per,
          description: `${base.description} (${i + 1}/${installments})`,
          date: d,
          status: i === 0 ? status : 'deferred',
          dueDate: i === 0 ? base.dueDate : d,
          receipt: i === 0 ? receipt : null,
        });
        d = fnNextMonth(d);
      }
      onSave(list);
      return;
    }
    onSave(base);
  };

  const isValid = parseFloat(String(amount).replace(',', '.')) > 0 && description.trim();
  const cats = fnGetCats(state, type);
  const borderColor = type === 'income' ? INC_COLOR : EXP_COLOR;

  return (
    <div className="confirm-overlay" onClick={onClose}>
      <div className="finance-modal" onClick={e => e.stopPropagation()}>
        <div className="finance-modal-hdr" style={{ borderBottomColor: borderColor }}>
          <span className={`finance-modal-type-dot ${type}`} />
          <h3>{isEdit ? 'Kaydı Düzenle' : (type === 'income' ? 'Gelir Ekle' : 'Gider Ekle')}</h3>
          <button className="icon-btn icon-btn-sm" onClick={onClose}><FNIcons.X size={16} /></button>
        </div>
        <div className="finance-modal-body">
          {!isEdit && (
            <div className="finance-toggle">
              <button className={`finance-toggle-btn ${type === 'income'  ? 'active income'  : ''}`} onClick={() => setType('income') }>
                <FNIcons.TrendingUp size={14} /> Gelir
              </button>
              <button className={`finance-toggle-btn ${type === 'expense' ? 'active expense' : ''}`} onClick={() => setType('expense')}>
                <FNIcons.TrendingDown size={14} /> Gider
              </button>
            </div>
          )}

          {/* Tutar + Fatura No yan yana */}
          <div style={{ display: 'grid', gridTemplateColumns: '1fr auto', gap: 12 }}>
            <label className="login-field">
              <span>Tutar (₺)</span>
              <input type="number" className="input" value={amount} onChange={e => setAmount(e.target.value)}
                placeholder="0" min="0" step="1" autoFocus onKeyDown={e => e.key === 'Enter' && save()} />
            </label>
            <label className="login-field">
              <span>Fatura No <span style={{ color: 'var(--text-faint)', fontWeight: 400 }}>(opsiyonel)</span></span>
              <input className="input" value={invoiceNo} onChange={e => setInvoiceNo(e.target.value)}
                placeholder="001" style={{ width: 90 }} />
            </label>
          </div>

          <label className="login-field">
            <span>Açıklama</span>
            <input className="input" value={description} onChange={e => setDescription(e.target.value)}
              placeholder="Kısa açıklama..." onKeyDown={e => e.key === 'Enter' && save()} />
          </label>

          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
            <label className="login-field">
              <span>Kategori</span>
              {newCatMode ? (
                <div style={{ display: 'flex', gap: 6 }}>
                  <input className="input" value={newCatName} onChange={e => setNewCatName(e.target.value)}
                    placeholder="Yeni kategori adı" autoFocus
                    onKeyDown={e => { if (e.key === 'Enter') addNewCat(); if (e.key === 'Escape') setNewCatMode(false); }} />
                  <button type="button" className="btn btn-primary btn-sm" onClick={addNewCat} disabled={!newCatName.trim()}>
                    <FNIcons.Check size={13} />
                  </button>
                </div>
              ) : (
                <select className="input" value={category}
                  onChange={e => { if (e.target.value === '__new') setNewCatMode(true); else setCategory(e.target.value); }}>
                  {cats.map(c => <option key={c} value={c}>{c}</option>)}
                  <option value="__new">+ Yeni kategori…</option>
                </select>
              )}
            </label>
            <label className="login-field">
              <span>Tarih</span>
              <input type="date" className="input" value={date} onChange={e => setDate(e.target.value)} />
            </label>
          </div>

          {/* KDV + Tekrar / Taksit */}
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
            <label className="login-field">
              <span>KDV % <span style={{ color: 'var(--text-faint)', fontWeight: 400 }}>(tutar KDV dahil)</span></span>
              <select className="input" value={vat} onChange={e => setVat(e.target.value)}>
                <option value="">KDV yok</option>
                <option value="1">%1</option>
                <option value="10">%10</option>
                <option value="20">%20</option>
              </select>
            </label>
            {!isEdit && !recurring && (
              <label className="login-field">
                <span>Taksit</span>
                <select className="input" value={installments} onChange={e => setInstallments(parseInt(e.target.value, 10))}>
                  <option value={1}>Tek seferlik</option>
                  <option value={2}>2 taksit (aylık)</option>
                  <option value={3}>3 taksit (aylık)</option>
                  <option value={4}>4 taksit (aylık)</option>
                </select>
              </label>
            )}
          </div>

          {/* Aylık tekrar */}
          {installments === 1 && (
            <label className="fn-recur-check">
              <input type="checkbox" checked={recurring} onChange={e => setRecurring(e.target.checked)} />
              <span>Her ay tekrarla <span style={{ color: 'var(--text-faint)', fontWeight: 400 }}>(abonelik, kira vb. — her ay otomatik bekleyen kayıt oluşturulur)</span></span>
            </label>
          )}

          {/* Durum seçici */}
          <label className="login-field">
            <span>Durum</span>
            <div className="fn-status-picker">
              {Object.entries(TX_STATUSES).map(([id, s]) => (
                <button
                  key={id}
                  type="button"
                  className={`fn-status-opt${status === id ? ' active' : ''}`}
                  style={status === id ? { background: s.bg, borderColor: s.color, color: s.color } : {}}
                  onClick={() => setStatus(id)}
                >
                  {s.short}
                </button>
              ))}
            </div>
          </label>

          {/* Vade tarihi */}
          {status !== 'done' && status !== 'cancelled' && (
            <label className="login-field">
              <span>Vade Tarihi <span style={{ color: 'var(--text-faint)', fontWeight: 400 }}>(ne zaman tahsil/ödeme olacak)</span></span>
              <input type="date" className="input" value={dueDate} onChange={e => setDueDate(e.target.value)} />
            </label>
          )}

          <label className="login-field">
            <span>Firma <span style={{ color: 'var(--text-faint)', fontWeight: 400 }}>(opsiyonel)</span></span>
            <select className="input" value={projectId} onChange={e => setProjectId(e.target.value)}>
              <option value="">— Firma seçin —</option>
              {state.projects.map(p => <option key={p.id} value={p.id}>{p.name}</option>)}
            </select>
          </label>

          <label className="login-field">
            <span>Not <span style={{ color: 'var(--text-faint)', fontWeight: 400 }}>(opsiyonel)</span></span>
            <input className="input" value={notes} onChange={e => setNotes(e.target.value)}
              placeholder="Ek açıklama, ödeme bilgisi..." />
          </label>

          {/* Fiş / fatura eki */}
          <div className="login-field">
            <span>Fiş / Fatura <span style={{ color: 'var(--text-faint)', fontWeight: 400 }}>(görsel veya PDF, en çok 1,5 MB)</span></span>
            {receipt ? (
              <div className="fn-receipt-chip">
                <FNIcons.Paperclip size={13} />
                <span className="fn-receipt-name">{receipt.name}</span>
                <button type="button" className="icon-btn icon-btn-sm" onClick={() => setReceipt(null)} title="Eki kaldır">
                  <FNIcons.X size={12} />
                </button>
              </div>
            ) : (
              <input type="file" className="input" accept="image/*,.pdf" onChange={handleFile} />
            )}
            {fileError && <span style={{ color: NET_NEG, fontSize: 11 }}>{fileError}</span>}
          </div>
        </div>
        <div className="finance-modal-footer">
          <button className="btn btn-ghost btn-sm" onClick={onClose}>Vazgeç</button>
          <button className={`btn btn-sm finance-save-btn ${type}`} onClick={save} disabled={!isValid}>
            {isEdit
              ? <><FNIcons.Save size={14} /> Değişiklikleri Kaydet</>
              : type === 'income'
                ? <><FNIcons.TrendingUp size={14} /> Gelir Kaydet</>
                : <><FNIcons.TrendingDown size={14} /> Gider Kaydet</>}
          </button>
        </div>
      </div>
    </div>
  );
}

window.FinanceView = FinanceView;
console.log('[finance.jsx] loaded, FinanceView=', typeof window.FinanceView);
