/* ops2-clients.jsx — зона «Клиенты»: вкладки Клиенты | Проекты | Картотека */
(function () {
  const { Card, Badge, IconTile, Button, Input } = window.DesignSystem_9e0a09;
  const { CardHead, OpsWgt, OpsModal } = window;
  const O = () => window.OPS;

  function StageBadge({ stage }) {
    const s = window.OPS.STAGE[stage];
    return <Badge bg={s.bg} color={s.fg} dot>{s.l}</Badge>;
  }

  /* ============================================================
     ВКЛАДКА «КЛИЕНТЫ» — реестр
     ============================================================ */
  function ClientsRegistry({ openClient }) {
    const [q, setQ] = React.useState('');
    const rows = O().CLIENTS.filter((c) => !q || c.name.toLowerCase().includes(q.toLowerCase()) || c.city.toLowerCase().includes(q.toLowerCase()));
    return (
      <Card variant="glass" padding="lg" style={{ overflow: 'hidden', gridColumn: '1 / -1' }}>
        <CardHead title="Реестр клиентов" sub="Клик по строке — полная карточка клиента"
          action={<span style={{ fontSize: 12.5, color: 'var(--text-tertiary)', fontWeight: 600 }}>{rows.length} клиентов</span>} />
        <div className="cl-toolbar">
          <div style={{ maxWidth: 300, flex: 1 }}>
            <Input placeholder="Поиск по имени, городу" leading={<Ic n="search" s={16} />} value={q} onChange={(e) => setQ(e.target.value)} />
          </div>
        </div>
        <div className="ov-inset">
        <div className="tbl-scroll">
          <table className="cl-tbl">
            <thead><tr>
              <th>Клиент</th><th>Стадия воронки</th><th>Менеджер</th><th>Бюджет / чек</th><th>Маржа</th><th>≈ Прибыль</th><th>Дебиторка</th><th>Контакт</th><th></th>
            </tr></thead>
            <tbody>
              {rows.map((c) =>
              <tr key={c.id} onClick={() => openClient(c.id)}>
                  <td><div className="cell-person"><span className="row-ava" style={{ background: c.avaC }}>{c.ava}</span><div><div className="cell-name">{c.name}</div><div className="cell-sub">{c.city}</div></div></div></td>
                  <td><StageBadge stage={c.stage} /></td>
                  <td>{c.manager !== '—' ? <span style={{ fontSize: 13 }}>{c.manager}</span> : <span style={{ color: 'var(--destructive)', fontWeight: 600, fontSize: 13 }}>в пуле</span>}</td>
                  <td className="t-num" style={{ fontWeight: 600 }}>{fmtCompact(c.budget)} ₽</td>
                  <td>{c.margin != null ? <span className="t-num" style={{ fontWeight: 700, color: c.margin < 12 ? 'var(--destructive)' : c.margin < 18 ? 'var(--warning-strong)' : 'var(--success-strong)' }}>{c.margin}%</span> : <span style={{ color: 'var(--text-tertiary)' }}>—</span>}</td>
                  <td>{c.profit != null ? <span className="t-num" style={{ fontWeight: 700, color: 'var(--text-secondary)' }}>≈ {fmtCompact(c.profit)} ₽</span> : <span style={{ color: 'var(--text-tertiary)' }}>—</span>}</td>
                  <td>{c.debt > 0 ? <span className="t-num" style={{ fontWeight: 600, color: c.sla === 'red' || c.sla === 'amber' ? 'var(--destructive)' : 'var(--text-secondary)' }}>{fmtCompact(c.debt)} ₽</span> : <span style={{ color: 'var(--text-tertiary)' }}>нет</span>}</td>
                  <td><span style={{ fontSize: 12.5, color: c.sla === 'red' ? 'var(--destructive)' : c.sla === 'amber' ? 'var(--warning-strong)' : 'var(--text-secondary)', fontWeight: c.sla !== 'ok' ? 600 : 400 }}>{c.lastContact}</span></td>
                  <td style={{ textAlign: 'right' }}><Ic n="chevron-right" s={16} c="var(--text-tertiary)" /></td>
                </tr>
              )}
            </tbody>
          </table>
        </div>
        </div>
      </Card>);

  }

  /* ============================================================
     ПОЛНОЭКРАННАЯ КАРТОЧКА КЛИЕНТА
     ============================================================ */
  function ClientCard({ id, onBack, openProject }) {
    const c = O().CLIENTS.find((x) => x.id === id);
    if (!c) return null;
    const costTotal = c.costs.reduce((s, x) => s + x.value, 0);
    return (
      <div className="cc-wrap">
        <button className="cc-back" onClick={onBack}><Ic n="arrow-left" s={16} sw={2.2} />К реестру клиентов</button>

        {/* Шапка */}
        <Card variant="glass" padding="lg" style={{ marginBottom: 18 }}>
          <div className="cc-head">
            <span className="cc-ava" style={{ background: c.avaC }}>{c.ava}</span>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>
                <span className="t-h2" style={{ fontSize: 26 }}>{c.name}</span>
                <StageBadge stage={c.stage} />
              </div>
              <div className="cc-meta">
                <span><Ic n="map-pin" s={14} c="var(--text-tertiary)" />{c.city}</span>
                <span><Ic n="home" s={14} c="var(--text-tertiary)" />{c.type} · {c.area} м² · {c.housing}</span>
                <span><Ic n="user-round" s={14} c="var(--text-tertiary)" />{c.manager !== '—' ? `Менеджер ${c.manager}` : 'Без менеджера'}</span>
              </div>
            </div>
            <div className="cc-budget">
              <div className="ov-foot-l">Бюджет</div>
              <div className="t-num" style={{ fontSize: 24, fontWeight: 800, letterSpacing: '-0.02em' }}>{fmtCompact(c.budget)} ₽</div>
              {c.manager === '—' ? <Button variant="accent" size="sm" iconLeft={<Ic n="user-plus" s={14} />} style={{ marginTop: 8 }}>Назначить менеджера</Button> :
              <Button variant="primary" size="sm" iconLeft={<Ic n="user-cog" s={14} />} style={{ marginTop: 8 }}>Войти как клиент</Button>}
            </div>
          </div>
        </Card>

        <div className="cc-grid">
          {/* Стадия воронки drill-in: смета + фидбэк */}
          <Card variant="glass" padding="lg">
            <CardHead title="Стадия воронки · смета и фидбэк" sub="Активная смета и обратная связь клиента" />
            {c.estimate ?
            <div className="cc-estimate">
                <div>
                  <div className="ov-foot-l">Активная смета</div>
                  <div className="t-num" style={{ fontSize: 20, fontWeight: 800 }}>{fmtCompact(c.estimate.sum)} ₽</div>
                  <div style={{ fontSize: 12, color: 'var(--text-tertiary)', marginTop: 2 }}>{c.estimate.items} позиций</div>
                </div>
                <Badge tone={c.estimate.status.includes('Принята') ? 'success' : 'warning'}>{c.estimate.status}</Badge>
              </div> :
            <div className="cc-empty">Смета ещё не выставлена</div>}
            <div className="cc-feedback">
              <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>
                <span className="ov-foot-l">Фидбэк клиента</span>
                {c.feedback.rating && <span style={{ display: 'flex', alignItems: 'center', gap: 3 }}><Ic n="star" s={13} c="var(--chart-5)" /><span className="t-num" style={{ fontWeight: 700, fontSize: 12.5 }}>{c.feedback.rating}</span></span>}
              </div>
              <div style={{ fontSize: 13, color: 'var(--text-secondary)', lineHeight: 1.5, fontStyle: 'italic' }}>«{c.feedback.text}»</div>
            </div>
          </Card>

          {/* Маржинальность + расчётная прибыль */}
          <Card variant="glass" padding="lg">
            <CardHead title="Маржинальность и прибыль" sub="Выручка, затраты и расчётная маржа по клиенту" />
            <div className="cc-margin-row">
              <Figure2 value={fmtCompact(c.budget)} unit="₽" label="Выручка (план)" />
              <Figure2 value={fmtCompact(c.fact || c.costs.reduce((s, x) => s + x.value, 0))} unit="₽" label="Затраты (факт)" />
            </div>
            <div className="cc-profit">
              <div>
                <div className="ov-foot-l">Расчётная прибыль</div>
                <div className="t-num" style={{ fontSize: 22, fontWeight: 800, color: c.margin != null && c.margin < 12 ? 'var(--destructive)' : 'var(--success-strong)' }}>{c.profit != null ? fmtCompact(c.profit) + ' ₽' : '—'}</div>
              </div>
              {c.margin != null &&
              <div style={{ textAlign: 'right' }}>
                  <div className="ov-foot-l">Маржа</div>
                  <div className="t-num" style={{ fontSize: 22, fontWeight: 800, color: c.margin < 12 ? 'var(--destructive)' : c.margin < 18 ? 'var(--warning-strong)' : 'var(--success-strong)' }}>{c.margin}%</div>
                  {c.margin < 18 && <div style={{ fontSize: 11, color: 'var(--destructive)', fontWeight: 600, marginTop: 2 }}>ниже целевой 18%</div>}
                </div>
              }
            </div>
          </Card>

          {/* Структура затрат per client */}
          <Card variant="glass" padding="lg">
            <CardHead title="Структура затрат клиента" sub="Распределение фактических затрат по категориям" />
            {c.costs.length ? <Donut slices={c.costs} size={150} stroke={24} centerTop={`${fmtCompact(costTotal)} ₽`} centerBottom="всего затрат" /> :
            <div className="cc-empty">Затрат ещё нет — проект не начат</div>}
          </Card>

          {/* Все чаты */}
          <Card variant="glass" padding="lg">
            <CardHead
              title="Все чаты клиента"
              sub="Треды с менеджером, прорабом и подрядчиками"
              action={c.chats.length > 0 ? <Badge tone="neutral" dot>{c.chats.length}</Badge> : null} />
            {c.chats.length ? c.chats.map((ch, i) =>
            <button key={i} className="cc-chat">
                <span className="cc-chat-ic"><Ic n="message-circle" s={16} c="var(--text-secondary)" /></span>
                <div style={{ flex: 1, minWidth: 0, textAlign: 'left' }}>
                  <div style={{ display: 'flex', justifyContent: 'space-between', gap: 8 }}><span style={{ fontSize: 13, fontWeight: 600, color: 'var(--foreground)' }}>{ch.with}</span><span style={{ fontSize: 11.5, color: 'var(--text-tertiary)' }}>{ch.when}</span></div>
                  <div style={{ fontSize: 12.5, color: 'var(--text-tertiary)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{ch.last}</div>
                </div>
                {ch.unread > 0 && <span className="cc-unread">{ch.unread}</span>}
              </button>
            ) : <div className="cc-empty">Чатов пока нет</div>}
          </Card>

          {/* Проекты клиента */}
          <Card variant="glass" padding="lg" style={{ gridColumn: '1 / -1' }}>
            <CardHead title="Проекты клиента" sub="Активные объекты и стадии готовности" />
            {c.projects.length ?
            <div className="cc-proj-list">
                {c.projects.map((pid) => {
                const p = O().PROJECTS.find((x) => x.id === pid);
                if (!p) return null;
                const h = O().HEALTH[p.health];
                return (
                  <button key={pid} className="cc-proj-row" onClick={() => openProject(pid)}>
                      <span style={{ fontSize: 12, fontWeight: 700, color: 'var(--text-tertiary)' }}>{p.code}</span>
                      <span style={{ flex: 1, fontSize: 13.5, fontWeight: 600, color: 'var(--foreground)' }}>{p.address}</span>
                      <Badge bg={`var(--cat-${p.cat}-bg)`} color={`var(--cat-${p.cat}-fg)`}>{p.phase}</Badge>
                      <span style={{ width: 110, display: 'flex', alignItems: 'center', gap: 8 }}><Bar pct={p.pct} color={`var(--cat-${p.cat}-fg)`} /><span className="t-num" style={{ fontSize: 12, fontWeight: 700 }}>{p.pct}%</span></span>
                      <Ic n="chevron-right" s={16} c="var(--text-tertiary)" />
                    </button>);

              })}
              </div> :
            <div className="cc-empty">Активных проектов нет — клиент на стадии {O().STAGE[c.stage].l.toLowerCase()}</div>}
          </Card>
        </div>
      </div>);

  }

  function Figure2({ value, unit, label }) {
    return (
      <div>
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 3 }}>
          <span className="t-num" style={{ fontSize: 19, fontWeight: 800, letterSpacing: '-0.02em' }}>{value}</span>
          {unit && <span style={{ fontSize: 13, fontWeight: 700, color: 'var(--text-tertiary)' }}>{unit}</span>}
        </div>
        <div style={{ fontSize: 12, color: 'var(--text-tertiary)', marginTop: 2 }}>{label}</div>
      </div>);

  }

  /* ============================================================
     ВКЛАДКА «ПРОЕКТЫ» — карточки заказов
     ============================================================ */
  function ProjectsTab({ openProject }) {
    const projects = O().PROJECTS;
    const counts = { risk: projects.filter((p) => p.health === 'risk').length, wait: projects.filter((p) => p.health === 'wait').length, ok: projects.filter((p) => p.health === 'ok').length };
    return (
      <Card variant="glass" padding="lg" style={{ gridColumn: '1 / -1' }}>
        <CardHead title="Проекты" sub={`${projects.length} объектов · фильтр по статусу здоровья`} />
        <div className="ov-pipe-seg seg cl-filter-seg" role="tablist" aria-label="Фильтр проектов">
          <button type="button" className="on" role="tab" aria-selected="true">Все · {projects.length}</button>
          <button type="button" role="tab" aria-selected="false"><span className="cl-dot cl-dot--risk" aria-hidden="true" />Риск · {counts.risk}</button>
          <button type="button" role="tab" aria-selected="false"><span className="cl-dot cl-dot--wait" aria-hidden="true" />Ожидание · {counts.wait}</button>
          <button type="button" role="tab" aria-selected="false"><span className="cl-dot cl-dot--ok" aria-hidden="true" />В графике · {counts.ok}</button>
        </div>
        <div className="pj-grid">
          {projects.map((p) => {
            const h = O().HEALTH[p.health];
            return (
              <Card key={p.id} variant="glass" padding="sm" interactive style={{ overflow: 'hidden', cursor: 'pointer', padding: 0 }}>
                <div onClick={() => openProject(p.id)}>
                  <div className="pj-img" style={{ backgroundImage: `url('${p.img}')` }}>
                    <span className="pj-health" style={{ background: h.bg, color: h.c }}><Ic n={h.ic} s={12} sw={2.4} />{h.l}</span>
                    <span className="pj-code">{p.code}</span>
                  </div>
                  <div className="pj-body">
                    <div className="pj-title">{p.client}</div>
                    <div className="pj-addr">{p.address}</div>
                    <div className="pj-phase">
                      <Badge bg={`var(--cat-${p.cat}-bg)`} color={`var(--cat-${p.cat}-fg)`} dot>{p.phase}</Badge>
                      <span className="t-num" style={{ fontSize: 12.5, color: 'var(--text-tertiary)', fontWeight: 600 }}>{p.area}</span>
                    </div>
                    <div style={{ marginTop: 14 }}>
                      <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12, color: 'var(--text-tertiary)', marginBottom: 6 }}><span>Готовность</span><span className="t-num" style={{ fontWeight: 700, color: 'var(--foreground)' }}>{p.pct}%</span></div>
                      <Bar pct={p.pct} color={`var(--cat-${p.cat}-fg)`} />
                      <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 11.5, color: 'var(--text-tertiary)', marginTop: 6 }}><span>{p.day}</span><span>бюджет {p.spendPct}%</span></div>
                    </div>
                    <div className="pj-team">
                      <div>
                        <div className="ov-foot-l" style={{ marginBottom: 7 }}>Прораб</div>
                        {p.foremanAva ? <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}><span className="mini-ava" style={{ background: p.foremanC, width: 26, height: 26, fontSize: 10 }}>{p.foremanAva}</span><span style={{ fontSize: 13, fontWeight: 600 }}>{p.foreman}</span></div> : <span style={{ fontSize: 12.5, color: 'var(--text-tertiary)' }}>не назначен</span>}
                      </div>
                      <div>
                        <div className="ov-foot-l" style={{ marginBottom: 7 }}>Бригада</div>
                        {p.crew.length ? <AvatarStackWrap items={p.crew} /> : <span style={{ fontSize: 12.5, color: 'var(--text-tertiary)' }}>нет</span>}
                      </div>
                    </div>
                    <div className={'pj-signal ' + (p.stuck ? 'stuck' : 'flow')}>
                      <Ic n={p.stuck ? 'alert-circle' : 'activity'} s={14} sw={2.2} /><span>{p.stuck || p.momentum}</span>
                    </div>
                  </div>
                </div>
              </Card>);

          })}
        </div>
      </Card>);

  }

  function AvatarStackWrap({ items }) {
    const { AvatarStack } = window.DesignSystem_9e0a09;
    return <AvatarStack items={items} max={4} size={26} ring="var(--card)" />;
  }

  /* ============================================================
     КАРТОТЕКА — аналитика по типам сделок
     ============================================================ */
  // приглушённая палитра (по фидбэку «цвета менее яркие»)
  const SOFT = ['#c98a4a', '#6a9e78', '#6d83b8', '#9a86c4', '#c2a23e'];

  function aggregate(records, groups) {
    const totalCount = records.length || 1;
    const totalProfit = records.reduce((s, r) => s + (r.profit || 0), 0) || 1;
    return groups.map((g, i) => {
      const rs = records.filter(g.match);
      const profit = rs.reduce((s, r) => s + (r.profit || 0), 0);
      const durs = rs.filter((r) => r.durationDays > 0).map((r) => r.durationDays);
      const margins = rs.filter((r) => r.margin != null).map((r) => r.margin);
      return {
        label: g.label, color: SOFT[i % SOFT.length],
        count: rs.length, countShare: rs.length / totalCount,
        profit, profitShare: profit / totalProfit,
        avgDur: durs.length ? Math.round(durs.reduce((a, b) => a + b, 0) / durs.length) : null,
        avgMargin: margins.length ? Math.round(margins.reduce((a, b) => a + b, 0) / margins.length) : null,
        avgBudget: rs.length ? Math.round(rs.reduce((s, r) => s + r.budgetPlan, 0) / rs.length) : 0,
      };
    });
  }

  function DealBreakdown({ title, sub, segs, primary }) {
    const maxBar = Math.max(...segs.map((s) => primary === 'margin' ? (s.avgMargin || 0) : (s && s.countShare || 0) * 100), 1);
    const best = primary === 'margin' ? segs.reduce((a, b) => (b.avgMargin || 0) > (a.avgMargin || 0) ? b : a, segs[0]) : null;
    return (
      <div className="kart-panel">
        <div className="kart-panel-head">
          <div className="kart-panel-title">{title}</div>
          {sub && <div className="kart-panel-sub">{sub}</div>}
        </div>
        <div className="kart-seg-list">
          {segs.map((s, i) => {
            const barVal = primary === 'margin' ? (s.avgMargin || 0) : (s && s.countShare || 0) * 100;
            const isBest = best && s.label === best.label && primary === 'margin';
            const marginTone = s.avgMargin == null ? 'var(--text-tertiary)' : s.avgMargin >= 20 ? 'var(--success-strong)' : s.avgMargin < 14 ? 'var(--destructive)' : 'var(--foreground)';
            return (
              <div key={i} className="kart-seg">
                <div className="kart-seg-top">
                  <span className="kart-seg-label">{s.label}</span>
                  {isBest && <span className="dl-best">топ-маржа</span>}
                </div>
                <div className="kart-seg-bar"><div className="kart-seg-bar-fill" style={{ width: `${(barVal / maxBar) * 100}%`, background: s.color }} /></div>
                <div className="kart-seg-metrics">
                  <div className="kart-metric">
                    <span className="kart-m-l">Доля</span>
                    <span className="kart-m-v t-num">{s.count} · {Math.round((s && s.countShare || 0) * 100)}%</span>
                  </div>
                  <div className="kart-metric">
                    <span className="kart-m-l">Прибыль</span>
                    <span className="kart-m-v t-num">{Math.round(s.profitShare * 100)}%</span>
                  </div>
                  <div className="kart-metric">
                    <span className="kart-m-l">Срок</span>
                    <span className="kart-m-v t-num">{s.avgDur ? s.avgDur + ' дн' : '—'}</span>
                  </div>
                  <div className="kart-metric">
                    <span className="kart-m-l">Маржа</span>
                    <span className="kart-m-v t-num" style={{ color: marginTone }}>{s.avgMargin != null ? s.avgMargin + '%' : '—'}</span>
                  </div>
                </div>
              </div>
            );
          })}
        </div>
      </div>
    );
  }

  function MiniDonutRing({ slices, size = 68, stroke = 11 }) {
    const total = slices.reduce((s, x) => s + x.value, 0) || 1;
    const r = (size - stroke) / 2;
    const C = 2 * Math.PI * r;
    let acc = 0;
    return (
      <svg width={size} height={size} className="kart-donut-svg" style={{ flexShrink: 0, transform: 'rotate(-90deg)' }}>
        {slices.map((sl, i) => {
          const frac = sl.value / total;
          const dash = frac * C;
          const el = <circle key={i} cx={size / 2} cy={size / 2} r={r} fill="none" stroke={sl.color} strokeWidth={stroke} strokeDasharray={`${dash} ${C - dash}`} strokeDashoffset={-acc * C} />;
          acc += frac;
          return el;
        })}
      </svg>
    );
  }

  function SegDonutWidget({ title, segs, onExpand }) {
    const slices = segs.filter((s) => s.count > 0).map((s) => ({ label: s.label, value: s.count, color: s.color }));
    const lead = segs[0];
    if (!lead) return null;
    return (
      <OpsWgt title={title} expand={onExpand}>
        <div className="kart-donut-wgt">
          <div className="kart-donut-ring-wrap">
            <MiniDonutRing slices={slices} />
            <div className="kart-donut-center">
              <b className="t-num">{Math.round((lead && lead.countShare || 0) * 100)}%</b>
              <span>{lead.label}</span>
            </div>
          </div>
          <ul className="kart-donut-keys">
            {segs.slice(0, 3).map((s, i) => (
              <li key={i}><span style={{ background: s.color }} />{s.label}<b className="t-num">{Math.round(s.profitShare * 100)}%</b></li>
            ))}
          </ul>
        </div>
      </OpsWgt>
    );
  }

  function ManagerRating({ compact, embedded, onExpand }) {
    const M = [...O().MANAGERS].sort((a, b) => b.winRate - a.winRate);
    const rows = compact ? M.slice(0, 3) : M;
    const body = (
      <div className={embedded ? 'mgr-list--compact' : 'mgr-list'}>
        {rows.map((m, i) => (
          <div key={m.name} className={(embedded ? 'mgr-row--compact' : 'mgr-row') + (i === 0 && !embedded ? ' lead' : '')}>
            <span className="mgr-rank">{i + 1}</span>
            <span className="mini-ava" style={{ background: m.avaC, width: embedded ? 30 : 34, height: embedded ? 30 : 34, fontSize: embedded ? 10 : 12 }}>{m.ava}</span>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                <span style={{ fontSize: 13, fontWeight: 700, color: 'var(--foreground)' }}>{m.name}</span>
                {i === 0 && <Ic n="crown" s={13} c="#c2a23e" />}
              </div>
              {!embedded && (
                <div style={{ fontSize: 11.5, color: 'var(--text-tertiary)', marginTop: 1 }}>
                  {m.won} из {m.deals} сделок{!compact && <> · ср. чек {fmtCompact(m.avgCheck)} ₽</>}
                </div>
              )}
            </div>
            <span className="t-num" style={{ fontSize: 13, fontWeight: 800, color: m.winRate >= 60 ? 'var(--success-strong)' : m.winRate >= 45 ? 'var(--warning-strong)' : 'var(--destructive)' }}>{m.winRate}%</span>
            {!embedded && (
              <div className="mgr-profit">
                <div className="t-num" style={{ fontSize: 14, fontWeight: 800, color: 'var(--foreground)' }}>{fmtCompact(m.profit)} ₽</div>
                {!compact && <div style={{ fontSize: 10.5, color: 'var(--text-tertiary)' }}>≈ {fmtCompact(m.profitPerDeal)} / сделку</div>}
              </div>
            )}
          </div>
        ))}
      </div>
    );
    if (embedded) return body;
    return (
      <Card variant="glass" padding={compact ? 'md' : 'lg'}>
        <CardHead title="Рейтинг менеджеров" sub={compact ? 'Топ-3 по win-rate' : 'Win-rate и прибыль на менеджера'}
          action={compact && onExpand ? <Button variant="ghost" size="sm" onClick={onExpand}>Все</Button> : null} />
        <div className="ov-inset">{body}</div>
      </Card>
    );
  }

  function SegWidget({ title, segs, onExpand }) {
    return (
      <OpsWgt title={title} expand={onExpand}>
        <div className="kart-mini-list">
          {segs.slice(0, 3).map((s, i) => (
            <div key={i} className="kart-mini-row">
              <div className="kart-mini-row-top">
                <span className="kart-mini-row-label">{s.label}</span>
                <span className="kart-mini-row-val t-num">{Math.round((s && s.countShare || 0) * 100)}%</span>
              </div>
              <div className="kart-mini-row-bar"><div className="kart-mini-row-fill" style={{ width: `${Math.max(10, Math.round((s && s.countShare || 0) * 100))}%`, background: s.color }} /></div>
              <div className="kart-mini-row-meta">
                <span>прибыль {Math.round(s.profitShare * 100)}%</span>
                <span className="t-num">{s.avgMargin != null ? `${s.avgMargin}% маржа` : 'маржа —'}</span>
              </div>
            </div>
          ))}
        </div>
      </OpsWgt>
    );
  }

  /* ---------- картотека: реестр с сортировкой + фильтрами + пресетами ---------- */
  const KART_PRESETS_KEY = 'ops_kart_presets'; /* legacy key, presets UI removed */
  const SRC_LABELS = { calc: 'Калькулятор', ads: 'Реклама', ref: 'Рекомендация', manual: 'Ручной' };
  const catOf = (type) => type === 'Капитальный' ? 'plumb' : type === 'Дизайнерский' ? 'tile' : 'paint';

  function Kartoteka() {
    const O2 = O();
    const K = O2.KARTOTEKA;
    const blankF = { type: '', housing: '', source: '', result: '', budgetSeg: '', areaSeg: '' };
    const [f, setF] = React.useState(blankF);
    const [sort, setSort] = React.useState({ key: 'budgetPlan', dir: 'desc' });
    const [modal, setModal] = React.useState(null);

    const setFilter = (k, v) => setF((p) => ({ ...p, [k]: v }));
    const activeCount = Object.values(f).filter(Boolean).length;
    const areaSeg = (a) => (O2.AREA_SEGMENTS.find((s) => s.test(a)) || {}).id;
    const budgetSeg = (b) => (O2.BUDGET_SEGMENTS.find((s) => s.test(b)) || {}).id;

    let rows = K.filter((r) =>
      (!f.type || r.type === f.type) &&
      (!f.housing || r.housing === f.housing) &&
      (!f.source || r.source === f.source) &&
      (!f.result || r.result === f.result) &&
      (!f.areaSeg || areaSeg(r.area) === f.areaSeg) &&
      (!f.budgetSeg || budgetSeg(r.budgetPlan) === f.budgetSeg)
    );
    rows = [...rows].sort((a, b) => {
      const k = sort.key; let av = a[k], bv = b[k];
      if (typeof av === 'string') { av = av || ''; bv = bv || ''; return sort.dir === 'asc' ? av.localeCompare(bv) : bv.localeCompare(av); }
      av = av == null ? -1 : av; bv = bv == null ? -1 : bv;
      return sort.dir === 'asc' ? av - bv : bv - av;
    });

    const toggleSort = (key) => setSort((s) => s.key === key ? { key, dir: s.dir === 'asc' ? 'desc' : 'asc' } : { key, dir: 'desc' });
    const SortTh = ({ k, children, num }) => (
      <th onClick={() => toggleSort(k)} className={'sort-th' + (sort.key === k ? ' on' : '')} style={num ? { textAlign: 'right' } : null}>
        <span style={{ display: 'inline-flex', alignItems: 'center', gap: 4, justifyContent: num ? 'flex-end' : 'flex-start' }}>{children}<Ic n={sort.key === k ? (sort.dir === 'asc' ? 'arrow-up' : 'arrow-down') : 'chevrons-up-down'} s={12} /></span>
      </th>
    );

    const byType = aggregate(K, [...new Set(K.map((r) => r.type))].map((t) => ({ label: t, match: (r) => r.type === t })));
    const byArea = aggregate(K, O2.AREA_SEGMENTS.map((s) => ({ label: s.label, match: (r) => s.test(r.area) })));
    const byHousing = aggregate(K, [...new Set(K.map((r) => r.housing))].map((h) => ({ label: h, match: (r) => r.housing === h })));
    const bySource = aggregate(K, Object.entries(SRC_LABELS).map(([id, label]) => ({ label, match: (r) => r.source === id })));
    const byResult = aggregate(K, Object.entries(O2.KART_RESULT).map(([id, meta]) => ({ label: meta.l, match: (r) => r.result === id })));
    const quickPresets = [
      ['type', ...[...new Set(K.map((r) => r.type))].map((t) => [t, t])],
      ['areaSeg', ...O2.AREA_SEGMENTS.map((s) => [s.id, s.label])],
      ['housing', ...[...new Set(K.map((r) => r.housing))].map((h) => [h, h])],
      ['source', ...Object.entries(SRC_LABELS)],
      ['result', ...Object.entries(O2.KART_RESULT).map(([k, v]) => [k, v.l])]
    ];

    const fieldLabel = (field) => (
      field === 'type' ? 'Тип' : field === 'areaSeg' ? 'Площадь' : field === 'housing' ? 'Жильё' : field === 'source' ? 'Источник' : 'Результат'
    );

    return (
      <>
        <div className="ov-wgt-grid" style={{ gridColumn: '1 / -1', marginBottom: 12 }}>
          <SegDonutWidget title="По типу ремонта" segs={byType} onExpand={() => setModal('type')} />
          <SegDonutWidget title="По площади" segs={byArea} onExpand={() => setModal('area')} />
          <SegDonutWidget title="По жилью" segs={byHousing} onExpand={() => setModal('housing')} />
          <SegDonutWidget title="По источнику" segs={bySource} onExpand={() => setModal('source')} />
          <SegDonutWidget title="По результату" segs={byResult} onExpand={() => setModal('result')} />
          <OpsWgt title="Рейтинг менеджеров" expand={() => setModal('managers')}>
            <ManagerRating compact embedded onExpand={() => setModal('managers')} />
          </OpsWgt>
        </div>

        <OpsWgt title={`Картотека · ${rows.length} из ${K.length}`} className="ov-wgt-wide" style={{ gridColumn: '1 / -1' }}
          badge={activeCount || null}
          action={
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8, marginLeft: 'auto' }}>
              {activeCount > 0 ? <button type="button" className="lk-wgt-more" onClick={() => setF(blankF)}>Сбросить фильтры</button> : null}
              <Button variant="outline" size="sm" iconLeft={<Ic n="download" s={14} />}>Экспорт</Button>
            </span>
          }
          expand={() => setModal('table')}>
          <div className="kart-filter-strip" role="group" aria-label="Фильтры картотеки">
            {quickPresets.map(([field, ...items]) => (
              <div key={field} className="kart-filter-group">
                <span className="kart-filter-grp-l">{fieldLabel(field)}</span>
                <div className="kart-filter-chips">
                  {items.map(([value, label]) => (
                    <button key={value} type="button" className={'kart-filter-chip' + (f[field] === value ? ' on' : '')} onClick={() => setFilter(field, f[field] === value ? '' : value)}>
                      {label}
                    </button>
                  ))}
                </div>
              </div>
            ))}
          </div>
          <div className="ov-wgt-scrolllist" style={{ maxHeight: 220, marginTop: 10 }}>
            {rows.slice(0, 8).map((r, i) => {
              const res = O2.KART_RESULT[r.result];
              return (
                <div key={i} className="ov-scrollrow ov-scrollrow--static">
                  <span className="ov-scrollrow-main">
                    <span className="ov-scrollrow-t">{r.name} <span className="ov-wgt-row-code">{r.city}</span></span>
                    <span className="ov-scrollrow-s">{r.type} · {r.area} м² · {SRC_LABELS[r.source]}</span>
                  </span>
                  <span className="ov-scrollrow-r">
                    <span className="t-num ov-scrollrow-v">{fmtCompact(r.budgetPlan)} ₽</span>
                    <Badge bg={res.bg} color={res.fg} dot>{res.l}</Badge>
                  </span>
                </div>
              );
            })}
            {rows.length === 0 && <div className="ov-flow-empty">Нет строк по выбранным фильтрам</div>}
            {rows.length > 8 && <button type="button" className="lk-wgt-more" onClick={() => setModal('table')}>Ещё {rows.length - 8} · открыть полностью</button>}
          </div>
        </OpsWgt>

        {modal === 'table' && (
          <OpsModal title={`Картотека · ${rows.length} из ${K.length}`} subtitle="Полная таблица · клик по заголовку — сортировка" onClose={() => setModal(null)} wide
            action={<Button variant="outline" size="sm" iconLeft={<Ic n="download" s={14} />}>Экспорт</Button>}>
            <div className="tbl-scroll">
              <table className="cl-tbl kart-tbl">
                <thead><tr>
                  <SortTh k="name">Клиент</SortTh>
                  <th>Город / район</th>
                  <SortTh k="type">Тип</SortTh>
                  <SortTh k="area" num>Площадь</SortTh>
                  <th>Жильё</th>
                  <SortTh k="budgetPlan" num>Бюджет план</SortTh>
                  <SortTh k="margin" num>Маржа</SortTh>
                  <SortTh k="profit" num>≈ Прибыль</SortTh>
                  <SortTh k="durationDays" num>Срок</SortTh>
                  <th>Источник</th>
                  <SortTh k="result">Результат</SortTh>
                </tr></thead>
                <tbody>
                  {rows.map((r, i) => {
                    const res = O2.KART_RESULT[r.result];
                    return (
                      <tr key={i}>
                        <td className="cell-name">{r.name}</td>
                        <td style={{ fontSize: 12.5, color: 'var(--text-secondary)' }}>{r.city}</td>
                        <td><Badge bg={`var(--cat-${catOf(r.type)}-bg)`} color={`var(--cat-${catOf(r.type)}-fg)`}>{r.type}</Badge></td>
                        <td className="t-num" style={{ textAlign: 'right' }}>{r.area} м²</td>
                        <td style={{ fontSize: 12.5, color: 'var(--text-secondary)' }}>{r.housing}</td>
                        <td className="t-num" style={{ fontWeight: 600, textAlign: 'right' }}>{fmtCompact(r.budgetPlan)} ₽</td>
                        <td className="t-num" style={{ textAlign: 'right', fontWeight: 700, color: r.margin == null ? 'var(--text-tertiary)' : r.margin < 14 ? 'var(--destructive)' : r.margin >= 20 ? 'var(--success-strong)' : 'var(--foreground)' }}>{r.margin != null ? r.margin + '%' : '—'}</td>
                        <td className="t-num" style={{ textAlign: 'right', color: 'var(--text-secondary)' }}>{r.profit ? '≈ ' + fmtCompact(r.profit) + ' ₽' : '—'}</td>
                        <td className="t-num" style={{ textAlign: 'right', color: 'var(--text-secondary)' }}>{r.durationDays ? r.durationDays + ' дн' : '—'}</td>
                        <td style={{ fontSize: 12.5, color: 'var(--text-secondary)' }}>{SRC_LABELS[r.source]}</td>
                        <td><Badge bg={res.bg} color={res.fg}>{res.l}</Badge></td>
                      </tr>);
                  })}
                </tbody>
              </table>
            </div>
          </OpsModal>
        )}

        {modal && modal !== 'table' && (
          <OpsModal
            title={modal === 'managers' ? 'Рейтинг менеджеров' : 'Аналитика картотеки'}
            subtitle={modal === 'managers' ? 'Полный список по win-rate и прибыли' : 'Полный срез по выбранному измерению'}
            onClose={() => setModal(null)}
          >
            {modal === 'managers' && <ManagerRating />}
            {modal === 'type' && <DealBreakdown title="По типу ремонта" sub="Доля в потоке, вклад в прибыль и средняя длительность" segs={byType} primary="count" />}
            {modal === 'area' && <DealBreakdown title="По площади" sub="Сегменты площадей: доля, вклад в прибыль, срок" segs={byArea} primary="count" />}
            {modal === 'housing' && <DealBreakdown title="По типу жилья" sub="Доля потока, прибыль и длительность по типу жилья" segs={byHousing} primary="count" />}
            {modal === 'source' && <DealBreakdown title="По источнику" sub="Какие каналы дают поток и прибыль" segs={bySource} primary="count" />}
            {modal === 'result' && <DealBreakdown title="По результату" sub="Статус сделок по итоговому исходу" segs={byResult} primary="count" />}
          </OpsModal>
        )}
      </>);

  }

  /* Sel removed — фильтры только чипами */

  /* ============================================================
     ZONE WRAPPER
     ============================================================ */
  function ClientsZone({ subTab, setSubTab, openProject, onCardOpen }) {
    const [client, setClient] = React.useState(null);
    React.useEffect(() => {
      const c = client ? window.OPS.CLIENTS.find((x) => x.id === client) : null;
      if (onCardOpen) onCardOpen(c ? c.name : null);
    }, [client]);

    if (client) return <ClientCard id={client} onBack={() => setClient(null)} openProject={openProject} />;

    return (
      <div className="ov ov-zone">
        <div className="ov-pipe-seg seg cl-zone-seg zone-seg" role="tablist" aria-label="Раздел клиентов" style={{ gridColumn: '1 / -1' }}>
          {[['kartoteka', 'Картотека'], ['clients', 'Клиенты'], ['projects', 'Проекты']].map(([k, l]) =>
          <button key={k} type="button" role="tab" aria-selected={subTab === k} className={subTab === k ? 'on' : ''} onClick={() => setSubTab(k)}>{l}</button>
          )}
        </div>
        {subTab === 'clients' && <ClientsRegistry openClient={setClient} />}
        {subTab === 'projects' && <ProjectsTab openProject={openProject} />}
        {subTab === 'kartoteka' && <Kartoteka />}
      </div>);

  }

  window.ClientsZone = ClientsZone;
})();