// ============================================================================
// ЗАЯВКИ v2 · МОБІЛЬНА — самодостатня апка LsmApp на РЕАЛЬНИХ даних.
// Дизайн портовано з прототипу leads_split/mobile-*.jsx, логіка обробників —
// ТОЧНО як у final.jsx (LsLeadsModule). Глобалі — через window (окремий babel-скрипт).
// ============================================================================
const { useState: lsmUseState, useEffect: lsmUseEffect } = React;

// ── Картки списків (порт mobile-lists.jsx, поля/логіка — реальні як у tables.jsx) ──
function LsmCardShell({ children, onClick, accent }) {
  return (
    <div onClick={onClick} style={{
      background: "var(--bg-panel)", border: "1px solid var(--border-subtle)",
      borderRadius: 12, padding: "12px 14px", cursor: onClick ? "pointer" : "default",
      borderLeft: accent ? "3px solid " + accent : "1px solid var(--border-subtle)",
    }}>
      {children}
    </div>
  );
}

function LsmTopRow({ l, done }) {
  return (
    <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
      <LsSource source={l.source}/>
      <span style={{ fontFamily: "var(--font-mono)", fontSize: 11, color: "var(--fg-muted)" }}>#{l.id}</span>
      <span style={{ flex: 1 }}></span>
      <LsAge min={l.ageMin} done={done}/>
    </div>
  );
}

function LsmClientRow({ l }) {
  return (
    <div style={{ display: "flex", alignItems: "center", gap: 8, marginTop: 8 }}>
      <LsAvatar name={l.client} size={22}/>
      <span style={{ fontSize: 12.5, color: "var(--fg-secondary)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{l.client}</span>
      <span style={{ fontFamily: "var(--font-mono)", fontSize: 11, color: "var(--fg-muted)", fontVariantNumeric: "tabular-nums" }}>{lsFmtPhone(l.phone)}</span>
      <span style={{ flex: 1 }}></span>
      <LsCommBadge comm={l.comm}/>
      <LsCallBadge phone={l.phone} since={l.createdAt} compact/>
    </div>
  );
}

// Скільки днів товар у нас (від createdAt)
function lsmDaysHere(l) {
  return l.createdAt ? Math.max(0, Math.floor((Date.now() - l.createdAt) / 86400000)) : 0;
}

// ── Запит товару ──────────────────────────────────────────────────────────
function LsmProductCard({ l, onOpen, onStatus }) {
  const done = l.status === "converted" || l.status === "lost";
  return (
    <LsmCardShell onClick={() => onOpen(l)} accent={l.status === "new" ? "var(--accent)" : null}>
      <LsmTopRow l={l} done={done}/>
      <div style={{ fontSize: 14.5, fontWeight: 600, color: "var(--fg-primary)", marginTop: 7, lineHeight: 1.35 }}>{l.product}</div>
      {l.note && <div style={{ fontSize: 12, color: "var(--fg-muted)", marginTop: 3, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{l.note}</div>}
      <div style={{ display: "flex", alignItems: "center", gap: 8, marginTop: 9, flexWrap: "wrap" }}>
        <LsStatusSelect map={LS_PRODUCT_STATUSES} value={l.status} onChange={s => onStatus && onStatus(l.id, s)}/>
        <LsBotPill reply={l.bot}/>
        <span style={{ flex: 1 }}></span>
        <span style={{ fontFamily: "var(--font-mono)", fontSize: 13, fontWeight: 600, color: "var(--fg-primary)", fontVariantNumeric: "tabular-nums" }}>{lsFmtUah(l.price)}</span>
      </div>
      <LsmClientRow l={l}/>
    </LsmCardShell>
  );
}

// ── Гарантія (реальна логіка: «N днів у нас», в СЦ — відлік 14 днів від inScDate) ──
function LsmWarrantyCard({ l, onOpen, onStatus }) {
  const inSc = l.inScDate && (l.status === "in_sc" || l.status === "wait_return");
  let bar = null;
  if (inSc) {
    const dayN = Math.max(1, Math.floor((Date.now() - l.inScDate) / 86400000) + 1);
    const pct = Math.max(0, Math.min(1, dayN / 14));
    const tone = dayN <= 7 ? "#10B981" : dayN <= 11 ? "#F59E0B" : "#EF4444";
    bar = (
      <div style={{ marginTop: 9 }}>
        <div style={{ display: "flex", justifyContent: "space-between", marginBottom: 4 }}>
          <span style={{ fontSize: 11, color: "var(--fg-muted)" }}>в СЦ</span>
          <span style={{ fontSize: 11, fontWeight: 600, color: tone }}>день {dayN} із 14</span>
        </div>
        <div style={{ height: 4, borderRadius: 2, background: "var(--bg-raised)", overflow: "hidden" }}>
          <div style={{ width: (pct * 100) + "%", height: "100%", background: tone }}></div>
        </div>
      </div>
    );
  } else {
    bar = <div style={{ fontSize: 12, color: "var(--fg-secondary)", fontWeight: 600, marginTop: 9 }}>{lsmDaysHere(l)} дн у нас</div>;
  }
  return (
    <LsmCardShell onClick={() => onOpen(l)}>
      <LsmTopRow l={l} done={l.status === "closed"}/>
      <div style={{ fontSize: 14.5, fontWeight: 600, color: "var(--fg-primary)", marginTop: 7 }}>{l.product}</div>
      <div style={{ fontSize: 11, color: "var(--fg-muted)", fontFamily: "var(--font-mono)", marginTop: 2 }}>SN {l.serial || "—"} · {l.docId || "—"}{l.ttn ? " · ТТН " + l.ttn : ""}</div>
      {l.problem && <div style={{ fontSize: 12.5, color: "var(--fg-secondary)", lineHeight: 1.45, marginTop: 6 }}>{l.problem}</div>}
      {bar}
      <div style={{ display: "flex", alignItems: "center", gap: 8, marginTop: 9 }}>
        <LsStatusSelect map={LS_WARRANTY_STATUSES} value={l.status} onChange={s => onStatus && onStatus(l.id, s)}/>
        {l.verdict && <span style={{ fontSize: 11, color: "#6EE7B7", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{l.verdict}</span>}
      </div>
      <LsmClientRow l={l}/>
    </LsmCardShell>
  );
}

// ── Повернення (день N із 14, refund) ─────────────────────────────────────
function LsmReturnCard({ l, onOpen, onStatus }) {
  const day = l.day;
  const tone = day == null ? "var(--fg-muted)" : day <= 7 ? "#10B981" : day <= 11 ? "#F59E0B" : "#EF4444";
  return (
    <LsmCardShell onClick={() => onOpen(l)} accent={day >= 12 ? "#EF4444" : null}>
      <LsmTopRow l={l} done={l.status === "refunded"}/>
      <div style={{ fontSize: 14.5, fontWeight: 600, color: "var(--fg-primary)", marginTop: 7 }}>{l.product}</div>
      {l.reason && <div style={{ fontSize: 12.5, color: "var(--fg-secondary)", lineHeight: 1.45, marginTop: 4 }}>{l.reason}</div>}
      {day != null && (
        <div style={{ marginTop: 9 }}>
          <div style={{ display: "flex", justifyContent: "space-between", marginBottom: 4 }}>
            <span style={{ fontSize: 11.5, fontWeight: 600, color: tone, fontVariantNumeric: "tabular-nums" }}>день {day} із 14</span>
            <span style={{ fontFamily: "var(--font-mono)", fontSize: 12.5, fontWeight: 600, color: "#F43F5E", fontVariantNumeric: "tabular-nums" }}>−{lsFmtUah(l.refund)}</span>
          </div>
          <div style={{ height: 4, borderRadius: 2, background: "var(--bg-raised)", overflow: "hidden" }}>
            <div style={{ width: (day / 14 * 100) + "%", height: "100%", background: tone }}></div>
          </div>
        </div>
      )}
      <div style={{ display: "flex", alignItems: "center", gap: 8, marginTop: 9 }}>
        <LsStatusSelect map={LS_RETURN_STATUSES} value={l.status} onChange={s => onStatus && onStatus(l.id, s)}/>
        <span style={{ fontSize: 11, color: l.complete ? "var(--fg-muted)" : "#FBBF24" }}>{l.complete ? "повна комплектація" : "неповна комплектація"}</span>
      </div>
      <LsmClientRow l={l}/>
    </LsmCardShell>
  );
}

// ── Питання клієнта (інлайн-відповідь у картці + олівець для повного редагування) ──
function LsmQuestionCard({ l, open, onToggle, onAnswer, onStatus, onEdit }) {
  const answered = l.status !== "open";
  const [draft, setDraft] = lsmUseState("");
  return (
    <LsmCardShell onClick={onToggle} accent={open ? "#10B981" : null}>
      <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
        <LsSource source={l.source}/>
        <span style={{ fontFamily: "var(--font-mono)", fontSize: 11, color: "var(--fg-muted)" }}>#{l.id}</span>
        <span style={{ flex: 1 }}></span>
        <LsAge min={l.ageMin} done={answered}/>
        <button onClick={(e) => { e.stopPropagation(); onEdit && onEdit(l); }} title="Редагувати" style={{ width: 24, height: 24, border: "1px solid var(--border-default)", background: "var(--bg-raised)", color: "var(--fg-muted)", borderRadius: 6, cursor: "pointer", display: "inline-flex", alignItems: "center", justifyContent: "center" }}>
          <LsIcon name="pencil" size={12}/>
        </button>
      </div>
      <div style={{ fontSize: 14, fontWeight: 500, color: "var(--fg-primary)", lineHeight: 1.45, marginTop: 7 }}>«{l.question}»</div>
      {answered && l.reply && (
        <div style={{ display: "flex", gap: 6, marginTop: 7, fontSize: 12, color: "#6EE7B7", alignItems: "flex-start" }}>
          <LsIcon name="corner-down-right" size={12} style={{ marginTop: 2 }}/>
          <span>{l.reply} <span style={{ color: "var(--fg-muted)" }}>— {l.manager}</span></span>
        </div>
      )}
      <div style={{ display: "flex", alignItems: "center", gap: 8, marginTop: 9 }}>
        <LsStatusSelect map={LS_QUESTION_STATUSES} value={l.status} onChange={s => onStatus && onStatus(l.id, s)}/>
      </div>
      <LsmClientRow l={l}/>
      {open && !answered && (
        <div onClick={e => e.stopPropagation()} style={{ marginTop: 10, display: "flex", gap: 8 }}>
          <input value={draft} onChange={e => setDraft(e.target.value)} placeholder="Відповісти клієнту…" style={{
            flex: 1, height: 38, padding: "0 12px", borderRadius: 9, border: "1px solid var(--border-default)",
            background: "var(--bg-raised)", color: "var(--fg-primary)", fontFamily: "inherit", fontSize: 12.5, outline: "none",
          }}/>
          <button onClick={() => draft.trim() && onAnswer(l.id, draft.trim())} style={{
            width: 38, height: 38, borderRadius: 9, border: 0, background: "var(--accent)", color: "#fff",
            display: "inline-flex", alignItems: "center", justifyContent: "center", cursor: "pointer", opacity: draft.trim() ? 1 : 0.5,
          }}><LsIcon name="send" size={15}/></button>
        </div>
      )}
    </LsmCardShell>
  );
}

// ── Документ на відправку (реальні поля, кнопка Надіслати, ТТН) ────────────
function LsmDocCard({ l, onSend, onOpen }) {
  const fileName = l.fileName || (l.file && l.file.name) || null;
  const fileSize = l.fileSize || (l.file && l.file.size) || null;
  return (
    <LsmCardShell onClick={() => onOpen(l)} accent={l.status === "pending" ? "#22D3EE" : null}>
      <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
        <span style={{
          width: 20, height: 20, borderRadius: 5, display: "inline-flex", alignItems: "center", justifyContent: "center",
          background: "rgba(34,211,238,.13)", color: "#22D3EE", flexShrink: 0,
        }}><LsIcon name="file-text" size={11}/></span>
        <span style={{ fontFamily: "var(--font-mono)", fontSize: 11, color: "var(--fg-muted)" }}>#{l.id}</span>
        <span style={{ flex: 1 }}></span>
        <LsAge min={l.ageMin} done={l.status === "sent"}/>
      </div>
      <div style={{ fontSize: 14.5, fontWeight: 600, color: "var(--fg-primary)", marginTop: 7 }}>{l.docType || "Документ"} {l.docNo || ""}</div>
      {fileName && (
        <div style={{ display: "flex", alignItems: "center", gap: 6, fontSize: 11, color: "var(--fg-muted)", marginTop: 3 }}>
          <LsIcon name="paperclip" size={10}/>
          <span style={{ fontFamily: "var(--font-mono)" }}>{fileName}{fileSize ? " · " + fileSize : ""}</span>
          {l.orderId && <span style={{ color: "var(--accent)", fontFamily: "var(--font-mono)", fontWeight: 600 }}>· №{l.orderId}</span>}
        </div>
      )}
      {l.ttn && <div style={{ display: "flex", alignItems: "center", gap: 6, fontSize: 11, color: "#FB7185", marginTop: 3 }}><LsIcon name="truck" size={10}/><span style={{ fontFamily: "var(--font-mono)" }}>ТТН {l.ttn}</span></div>}
      {l.note && <div style={{ fontSize: 12, color: "var(--fg-secondary)", marginTop: 5 }}>{l.note}</div>}
      <div style={{ display: "flex", alignItems: "center", gap: 8, marginTop: 9, minWidth: 0 }}>
        {l.client && <span style={{ fontSize: 12.5, color: "var(--fg-secondary)" }}>{l.client}</span>}
        {l.channel && LS_SEND_CHANNELS[l.channel] && (
          <span style={{ display: "inline-flex", alignItems: "center", gap: 5, fontSize: 11, color: LS_SEND_CHANNELS[l.channel].color }}>
            <LsIcon name={LS_SEND_CHANNELS[l.channel].icon} size={11}/>{l.channelValue || LS_SEND_CHANNELS[l.channel].label}
          </span>
        )}
      </div>
      <div style={{ display: "flex", alignItems: "center", gap: 8, marginTop: 10 }}>
        <LsChip map={LS_DOC_STATUSES} value={l.status}/>
        <span style={{ flex: 1 }}></span>
        {l.manager && <span style={{ fontSize: 11, color: "var(--fg-muted)" }}>від {l.manager}</span>}
      </div>
      {l.status === "pending" && (
        <button onClick={(e) => { e.stopPropagation(); onSend(l.id); }} style={{
          width: "100%", height: 40, marginTop: 10, border: 0, borderRadius: 9,
          background: "var(--accent)", color: "#fff", fontFamily: "inherit", fontSize: 13, fontWeight: 500,
          display: "inline-flex", alignItems: "center", justifyContent: "center", gap: 7, cursor: "pointer",
        }}><LsIcon name="send" size={14}/> Надіслати клієнту</button>
      )}
    </LsmCardShell>
  );
}

// ── Питання менеджера керівнику (інлайн-відповідь + олівець) ───────────────
function LsmMgrCard({ l, open, onToggle, onAnswer, onEdit }) {
  const answered = l.status === "answered";
  const [draft, setDraft] = lsmUseState("");
  return (
    <LsmCardShell onClick={onToggle} accent={open ? "#EC4899" : null}>
      <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
        <LsAvatar name={l.manager} size={24}/>
        <span style={{ fontSize: 12.5, fontWeight: 600, color: "var(--fg-secondary)" }}>{l.manager}</span>
        {l.orderId && (
          <span style={{ display: "inline-flex", alignItems: "center", gap: 4, height: 18, padding: "0 7px", borderRadius: 999, background: "var(--accent-soft)", color: "var(--accent)", fontSize: 10.5, fontWeight: 600, fontFamily: "var(--font-mono)" }}>
            №{l.orderId}
          </span>
        )}
        <span style={{ flex: 1 }}></span>
        <LsAge min={l.ageMin} done={answered}/>
        <button onClick={(e) => { e.stopPropagation(); onEdit && onEdit(l); }} title="Редагувати" style={{ width: 24, height: 24, border: "1px solid var(--border-default)", background: "var(--bg-raised)", color: "var(--fg-muted)", borderRadius: 6, cursor: "pointer", display: "inline-flex", alignItems: "center", justifyContent: "center" }}>
          <LsIcon name="pencil" size={12}/>
        </button>
      </div>
      <div style={{ fontSize: 14, fontWeight: 500, color: "var(--fg-primary)", lineHeight: 1.45, marginTop: 8 }}>{l.question}</div>
      {answered && l.reply && (
        <div style={{ display: "flex", gap: 6, marginTop: 7, fontSize: 12, color: "#6EE7B7", alignItems: "flex-start" }}>
          <LsIcon name="corner-down-right" size={12} style={{ marginTop: 2 }}/>
          <span>{l.reply} <span style={{ color: "var(--fg-muted)" }}>— {l.replyBy}</span></span>
        </div>
      )}
      <div style={{ marginTop: 9 }}><LsChip map={LS_MGRQ_STATUSES} value={l.status}/></div>
      {open && !answered && (
        <div onClick={e => e.stopPropagation()} style={{ marginTop: 10, display: "flex", flexDirection: "column", gap: 8 }}>
          <div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
            {["Погоджую", "Ні, стандартні умови", "Зателефоную"].map((c, i) => (
              <button key={i} onClick={() => setDraft(c)} style={{
                height: 28, padding: "0 11px", borderRadius: 999, border: "1px solid var(--border-default)",
                background: "var(--bg-raised)", color: "var(--fg-secondary)", fontFamily: "inherit", fontSize: 11.5, cursor: "pointer",
              }}>{c}</button>
            ))}
          </div>
          <div style={{ display: "flex", gap: 8 }}>
            <input value={draft} onChange={e => setDraft(e.target.value)} placeholder="Відповісти…" style={{
              flex: 1, height: 38, padding: "0 12px", borderRadius: 9, border: "1px solid var(--border-default)",
              background: "var(--bg-raised)", color: "var(--fg-primary)", fontFamily: "inherit", fontSize: 12.5, outline: "none",
            }}/>
            <button onClick={() => draft.trim() && onAnswer(l.id, draft.trim())} style={{
              width: 38, height: 38, borderRadius: 9, border: 0, background: "var(--accent)", color: "#fff",
              display: "inline-flex", alignItems: "center", justifyContent: "center", cursor: "pointer", opacity: draft.trim() ? 1 : 0.5,
            }}><LsIcon name="send" size={15}/></button>
          </div>
        </div>
      )}
    </LsmCardShell>
  );
}

// ── Горизонтальний скрол карток-вкладок ───────────────────────────────────
function LsmTypeCards({ tab, onTab, counts }) {
  const all = [
    ...Object.values(LS_TYPES).map(t => ({ ...t, ...counts[t.key] })),
    { divider: true },
    { ...LS_INTERNAL_TYPES.docs, ...counts.docs },
    { ...LS_INTERNAL_TYPES.mgrq, label: "Менеджери", ...counts.mgrq },
  ];
  return (
    <div className="lsm-scroll" style={{
      display: "flex", gap: 8, padding: "4px 16px 10px", overflowX: "auto", flexShrink: 0, alignItems: "stretch",
    }}>
      {all.map((t, i) => t.divider ? (
        <div key="div" style={{ width: 1, background: "var(--border-default)", flexShrink: 0, margin: "4px 2px" }}></div>
      ) : (
        <button key={t.key} onClick={() => onTab(t.key)} style={{
          flexShrink: 0, width: 128, display: "flex", flexDirection: "column", gap: 6, padding: "10px 12px", textAlign: "left",
          borderRadius: 11, cursor: "pointer", fontFamily: "inherit",
          border: "1px solid " + (tab === t.key ? "color-mix(in oklab, " + t.color + " 45%, transparent)" : "var(--border-subtle)"),
          background: tab === t.key ? "color-mix(in oklab, " + t.color + " 9%, var(--bg-panel))" : "var(--bg-panel)",
        }}>
          <div style={{ display: "flex", alignItems: "center", gap: 7, width: "100%" }}>
            <span style={{
              width: 22, height: 22, borderRadius: 6, display: "inline-flex", alignItems: "center", justifyContent: "center", flexShrink: 0,
              background: t.soft, color: t.color,
            }}><LsIcon name={t.icon} size={12}/></span>
            <span style={{ fontSize: 18, fontWeight: 600, color: tab === t.key ? t.color : "var(--fg-primary)", fontVariantNumeric: "tabular-nums", lineHeight: 1 }}>{t.count}</span>
            {t.alert > 0 && <span style={{ width: 6, height: 6, borderRadius: "50%", background: "#EF4444", marginLeft: "auto" }}></span>}
          </div>
          <span style={{ fontSize: 11, fontWeight: 500, color: tab === t.key ? "var(--fg-primary)" : "var(--fg-secondary)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", width: "100%" }}>{t.label}</span>
        </button>
      ))}
    </div>
  );
}

// ── Шторка ────────────────────────────────────────────────────────────────
function LsmSheet({ onClose, children, title }) {
  return (
    <div onClick={onClose} style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,0.6)", zIndex: 100, display: "flex", flexDirection: "column", justifyContent: "flex-end" }}>
      <div onClick={e => e.stopPropagation()} style={{
        background: "var(--bg-panel)", borderRadius: "16px 16px 0 0", border: "1px solid var(--border-default)", borderBottom: 0,
        maxHeight: "88dvh", display: "flex", flexDirection: "column",
      }}>
        <div style={{ padding: "10px 16px 0", flexShrink: 0 }}>
          <div style={{ width: 36, height: 4, borderRadius: 2, background: "var(--border-strong)", margin: "0 auto 10px" }}></div>
          <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
            {title}
            <span style={{ flex: 1 }}></span>
            <button onClick={onClose} style={{ width: 28, height: 28, border: 0, background: "var(--bg-raised)", color: "var(--fg-muted)", borderRadius: 7, cursor: "pointer", display: "inline-flex", alignItems: "center", justifyContent: "center" }}>
              <LsIcon name="x" size={15}/>
            </button>
          </div>
        </div>
        {children}
      </div>
    </div>
  );
}

// ── Шторка запиту товару ──────────────────────────────────────────────────
function LsmProductSheet({ lead, onClose, onStatus, onEdit, userName }) {
  const done = lead.status === "converted" || lead.status === "lost";
  const tone = lsAgeTone(lead.ageMin, done);
  const phoneDigits = (lead.phone || "").replace(/\D/g, "");
  const [avail, setAvail] = lsmUseState("");      // "", loading, done, error
  const [availLabel, setAvailLabel] = lsmUseState("");
  const check = () => {
    if (!lead.product) { setAvailLabel("Немає товару"); setAvail("done"); return; }
    setAvail("loading");
    lsApi.checkAvail(lead.product, userName, lead.id)
      .then(r => { setAvailLabel((r && (r.label || r.message)) || "Запит надіслано"); setAvail("done"); })
      .catch(e => { setAvailLabel(e.message || "Помилка"); setAvail("error"); });
  };
  return (
    <LsmSheet onClose={onClose} title={<React.Fragment>
      <span style={{ fontFamily: "var(--font-mono)", fontSize: 13, color: "var(--fg-muted)" }}>#{lead.id}</span>
      <LsStatusSelect map={LS_PRODUCT_STATUSES} value={lead.status} onChange={s => onStatus && onStatus(lead.id, s)}/>
    </React.Fragment>}>
      <div style={{ overflowY: "auto", padding: "14px 16px" }}>
        <div style={{
          padding: "8px 12px", borderRadius: 8, marginBottom: 14,
          background: "color-mix(in oklab, " + tone + " 12%, transparent)",
          border: "1px solid color-mix(in oklab, " + tone + " 28%, transparent)",
          display: "flex", alignItems: "center", gap: 8,
        }}>
          <LsIcon name="clock" size={13} style={{ color: tone }}/>
          <span style={{ fontSize: 12, fontWeight: 500, color: tone }}>{lsFmtAge(lead.ageMin)} тому</span>
          {lead.ts && <span style={{ fontSize: 11, color: "var(--fg-muted)" }}>· {lead.ts}</span>}
        </div>

        <div style={{ padding: "12px 13px", background: "var(--bg-raised)", border: "1px solid var(--border-subtle)", borderRadius: 10, marginBottom: 8 }}>
          <div style={{ fontSize: 15.5, fontWeight: 600, color: "var(--fg-primary)", lineHeight: 1.35 }}>{lead.product}</div>
          <div style={{ display: "flex", alignItems: "center", gap: 8, marginTop: 9 }}>
            <LsBotPill reply={lead.bot}/>
            <span style={{ flex: 1 }}></span>
            <span style={{ fontFamily: "var(--font-mono)", fontSize: 14, fontWeight: 600, color: "var(--fg-primary)" }}>{lsFmtUah(lead.price)}</span>
          </div>
        </div>
        {lead.note && <div style={{ fontSize: 12.5, color: "var(--fg-secondary)", lineHeight: 1.5, marginBottom: 14 }}><span style={{ color: "var(--fg-muted)" }}>Коментар: </span>{lead.note}</div>}

        <div style={{ display: "flex", alignItems: "center", gap: 10, padding: "10px 12px", background: "var(--bg-raised)", borderRadius: 10, border: "1px solid var(--border-subtle)", marginBottom: 8 }}>
          <LsAvatar name={lead.client} size={34}/>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontSize: 13.5, fontWeight: 500, color: "var(--fg-primary)" }}>{lead.client}</div>
            <div style={{ fontFamily: "var(--font-mono)", fontSize: 11.5, color: "var(--fg-muted)" }}>{lsFmtPhone(lead.phone)}</div>
          </div>
          {(lead.phone || "").replace(/\D/g, "") && window.MessengerBtn && (
            <React.Fragment>
              <MessengerBtn kind="viber" phone={lead.phone}/>
              <MessengerBtn kind="telegram" phone={lead.phone}/>
            </React.Fragment>
          )}
          <LsSource source={lead.source} withLabel/>
        </div>

        {/* Шаблони відповідей клієнту — копіюються одним тапом */}
        {window.LsClientContact && (
          <div style={{ marginBottom: 10 }}>
            <div style={{ fontSize: 10, fontWeight: 600, letterSpacing: ".04em", textTransform: "uppercase", color: "var(--fg-muted)", marginBottom: 6 }}>Шаблони відповіді</div>
            <LsClientContact phone={lead.phone} client={lead.client} product={lead.product} hideCall={true}/>
          </div>
        )}

        {avail && (
          <div style={{ fontSize: 11.5, marginBottom: 10, color: avail === "error" ? "#FCA5A5" : avail === "loading" ? "var(--fg-muted)" : "#6EE7B7" }}>
            {avail === "loading" ? "Перевіряю в постачальника…" : availLabel}
          </div>
        )}

        {/* Повна історія дзвінків з клієнтом */}
        {window.LsCallTimeline && phoneDigits.length >= 9 && (
          <div style={{ marginTop: 4, marginBottom: 6 }}>
            <window.LsCallTimeline phone={lead.phone} since={lead.createdAt}/>
          </div>
        )}
      </div>
      <div style={{ padding: "12px 16px max(20px, env(safe-area-inset-bottom, 20px))", borderTop: "1px solid var(--border-subtle)", display: "flex", flexDirection: "column", gap: 8, flexShrink: 0 }}>
        <div style={{ display: "grid", gridTemplateColumns: phoneDigits ? "1fr 1fr" : "1fr", gap: 8 }}>
          {!done && (
            <button onClick={check} disabled={avail === "loading"} style={{
              height: 44, borderRadius: 11, border: "1px solid var(--accent-ring)", background: "var(--accent-soft)",
              color: "#C7D2FE", fontFamily: "inherit", fontSize: 13, fontWeight: 500, cursor: avail === "loading" ? "default" : "pointer",
              display: "inline-flex", alignItems: "center", justifyContent: "center", gap: 7,
            }}><LsIcon name="package-search" size={15}/> {avail === "loading" ? "Перевіряю…" : "Уточнити наявність"}</button>
          )}
          {phoneDigits && (
            <a href={"tel:+" + phoneDigits} style={{
              height: 44, borderRadius: 11, border: "1px solid var(--border-default)", background: "var(--bg-raised)",
              color: "#10B981", fontFamily: "inherit", fontSize: 13, fontWeight: 500, textDecoration: "none",
              display: "inline-flex", alignItems: "center", justifyContent: "center", gap: 7,
            }}><LsIcon name="phone-call" size={15}/> Подзвонити</a>
          )}
        </div>
        <button onClick={() => onEdit && onEdit(lead)} style={{
          height: 44, borderRadius: 11, border: "1px solid var(--border-default)", background: "var(--bg-raised)",
          color: "var(--fg-secondary)", fontFamily: "inherit", fontSize: 13, fontWeight: 500, cursor: "pointer",
          display: "inline-flex", alignItems: "center", justifyContent: "center", gap: 7,
        }}><LsIcon name="pencil" size={15}/> Редагувати</button>
      </div>
    </LsmSheet>
  );
}

// ── Апка ────────────────────────────────────────────────────────────────────
function LsmApp({ userName, userRole, openLeadId, onConsumeOpenLead }) {
  const [tab, setTab] = lsmUseState("product");
  const [records, setRecords] = lsmUseState([]);
  const [managers, setManagers] = lsmUseState([]);
  const [q, setQ] = lsmUseState("");
  const [selProduct, setSelProduct] = lsmUseState(null);
  const [editing, setEditing] = lsmUseState(null);
  const [newModal, setNewModal] = lsmUseState(false);
  const [openQ, setOpenQ] = lsmUseState(null);
  const [openM, setOpenM] = lsmUseState(null);
  const [prodArchive, setProdArchive] = lsmUseState(false); // product: Активні / Архів
  const [warrArchive, setWarrArchive] = lsmUseState(false); // warranty: Активні / Архів
  const [docsArchive, setDocsArchive] = lsmUseState(false); // docs: Активні / Архів
  const [prodCallFilter, setProdCallFilter] = lsmUseState("all");
  const [prodStatusFilter, setProdStatusFilter] = lsmUseState("all");
  const [mgrFilter, setMgrFilter] = lsmUseState("all"); // фільтр по менеджеру (лише для owner)

  const [, setCallsTick] = lsmUseState(0); // ре-рендер коли підтягнулись дзвінки (бейджі активності)
  const [refreshing, setRefreshing] = lsmUseState(false);
  const reload = () => lsApi.list().then(setRecords).catch(e => console.warn("заявки:", e.message));
  // Ручне оновлення: нові заявки/статуси + менеджери + дзвінки, без перезапуску PWA.
  const refreshAll = () => {
    setRefreshing(true);
    const pCalls = (window.API && window.API.get)
      ? window.API.get("/api/calls/recent?days=60").then(d => { if (d && d.calls) { window._crmCalls = { data: d.calls, ts: Date.now() }; setCallsTick(t => t + 1); } }).catch(() => {})
      : Promise.resolve();
    Promise.all([
      lsApi.list().then(setRecords).catch(() => {}),
      lsApi.managers().then(setManagers).catch(() => {}),
      pCalls,
    ]).finally(() => setRefreshing(false));
  };
  // Глибоке відкриття заявки з дошки «Що зробити» (App → openLeadId).
  lsmUseEffect(() => {
    if (openLeadId == null || !records.length) return;
    const isTerm = s => (window.LS_TERMINAL ? window.LS_TERMINAL.has(s) : ["converted", "lost", "closed", "done"].includes(s));
    const rec = records.find(r => Number(r.id) === Number(openLeadId));
    if (rec) {
      setTab(rec.type || "product");
      if ((rec.type || "product") === "product") { setProdArchive(isTerm(rec.status)); setSelProduct(rec); }
      else setEditing(rec);
    }
    if (onConsumeOpenLead) onConsumeOpenLead();
  }, [openLeadId, records]);

  lsmUseEffect(() => {
    reload(); lsApi.managers().then(setManagers);
    const setCalls = arr => { window._crmCalls = { data: arr || [], ts: Date.now() }; setCallsTick(t => t + 1); };
    if (window.API && window.API.get) {
      window.API.get("/api/calls/recent?days=60")
        .then(d => { if (d && d.calls && d.calls.length) setCalls(d.calls); else if (window.API.getCalls) return window.API.getCalls().then(x => setCalls(x.calls)); })
        .catch(() => { if (window.API.getCalls) window.API.getCalls().then(x => setCalls(x.calls)).catch(() => {}); });
    }
  }, []);

  // ── обробники (ТОЧНО як у final.jsx) ──
  const STATUS_MAPS = { product: LS_PRODUCT_STATUSES, warranty: LS_WARRANTY_STATUSES, return: LS_RETURN_STATUSES, question: LS_QUESTION_STATUSES, docs: LS_DOC_STATUSES, mgrq: LS_MGRQ_STATUSES };
  const statusLabel = (type, key) => { const m = STATUS_MAPS[type]; return m && m[key] ? m[key].label : key; };
  const ev = (a) => ({ a, u: userName || "" });

  const sendDoc   = (id)        => lsApi.update(id, { status: "sent", __event: ev("Статус → Надіслано") }).then(reload);
  const answerMgr = (id, reply) => lsApi.update(id, { status: "answered", reply, replyBy: userName || "Ви", __event: ev("Відповідь менеджеру") }).then(reload);
  const changeStatus = (id, status) => {
    const cur = records.find(r => r.id === id);
    const patch = { status, __event: ev("Статус → " + statusLabel(cur ? cur.type : "", status)) };
    if (status === "in_sc" && !(cur && cur.inScDate)) patch.inScDate = Date.now();
    return lsApi.update(id, patch).then(reload);
  };
  const removeLead = (id) => lsApi.remove(id).then(() => { setEditing(null); reload(); });

  const uploadAllFiles = async (id, files) => {
    let firstId = null;
    for (const file of files || []) {
      try { const up = await lsApi.uploadFile(id, file); if (up && up.fileId && firstId == null) firstId = up.fileId; }
      catch (e) { console.warn("файл «" + file.name + "»:", e.message); }
    }
    return firstId;
  };
  const fileMeta = (rest, files) => {
    if (!files.length) return;
    rest.fileName = files[0].name + (files.length > 1 ? " +" + (files.length - 1) : "");
    rest.fileSize = files[0].sizeLabel;
  };
  const editLead = (id, patch) => {
    const { _file, _files, ageMin, ts, updatedStr, history, ...rest } = patch;
    const files = (_files && _files.length) ? _files : (_file ? [_file] : []);
    fileMeta(rest, files);
    rest.__event = ev("Відредаговано");
    return lsApi.update(id, rest)
      .then(() => uploadAllFiles(id, files))
      .then(fid => (fid ? lsApi.update(id, { fileId: fid }) : null))
      .then(() => { setEditing(null); reload(); })
      .catch(e => console.warn("редагування:", e.message));
  };
  const addDoc = (d) => {
    const { _file, _files, ...rest } = d;
    const files = (_files && _files.length) ? _files : (_file ? [_file] : []);
    const body = { type: "docs", status: rest.status || "pending", source: "crm", manager: rest.manager || userName || "", _user: userName, _action: "Створено документ", ...rest };
    fileMeta(body, files);
    return lsApi.create(body)
      .then(res => (files.length && res && res.id)
        ? uploadAllFiles(res.id, files).then(fid => (fid ? lsApi.update(res.id, { fileId: fid }) : null))
        : null)
      .then(() => { setTab("docs"); reload(); })
      .catch(e => console.warn("документ:", e.message));
  };
  const createLead = (type, f) => {
    if (type === "docs") return addDoc(f);
    const files = (f._files && f._files.length) ? f._files : (f._file ? [f._file] : []);
    const body = { type, source: f.source || "call", client: f.client || "Без імені", phone: f.phone || "", manager: f.manager || userName || "", _user: userName, _action: "Створено заявку" };
    if (type === "product")  Object.assign(body, { status: "new",     product: f.product || "", bot: "awaiting", note: f.note || "", price: f.sitePrice != null ? f.sitePrice : null, sitePresence: f.sitePresence || "" });
    if (type === "warranty") Object.assign(body, { status: "new",     product: f.product || "", serial: f.serial || "", problem: f.problem || "", docId: f.docId || "", ttn: f.ttn || "", orderId: parseInt(f.orderId, 10) || null });
    if (type === "return")   Object.assign(body, { status: "review",  product: f.product || "", reason: f.reason || "", purchased: f.purchased || "", day: 1, refund: parseInt(f.refund, 10) || 0, complete: true });
    if (type === "question") Object.assign(body, { status: "open",    question: f.question || "" });
    if (type === "mgrq")     Object.assign(body, { status: "open",    question: f.question || "", orderId: parseInt(f.orderId, 10) || null });
    fileMeta(body, files);
    // Гарантія з фото: перше фото = основне повідомлення в топіку (як на десктопі)
    const defer = type === "warranty" && files.length > 0;
    if (defer) body._deferTopic = true;
    lsApi.create(body)
      .then(res => {
        if (!(files.length && res && res.id)) return null;
        return uploadAllFiles(res.id, files).then(() => (defer ? lsApi.update(res.id, {}) : null));
      })
      .then(() => { setTab(type); reload(); })
      .catch(e => console.warn("створення заявки:", e.message));
  };

  // ── counts (як у final.jsx) ──
  // Розмежування доступу: менеджер бачить ЛИШЕ свої заявки; керівник (owner) — усі,
  // з опційним фільтром по конкретному менеджеру.
  const isOwner = userRole === "owner";
  const sameMgr = (l) => String(l.manager || "").trim().toLowerCase() === String(userName || "").trim().toLowerCase();
  const scopedRecords = !isOwner
    ? records.filter(sameMgr)
    : (mgrFilter === "all" ? records : records.filter(l => String(l.manager || "") === mgrFilter));
  const byType = (t) => scopedRecords.filter(r => r.type === t);
  const products = byType("product"), warranty = byType("warranty"), returns = byType("return");
  const questions = byType("question"), docs = byType("docs"), mgrq = byType("mgrq");
  const warrActive = warranty.filter(l => ["new", "wait_client", "in_sc", "wait_return"].includes(l.status)).length;
  const counts = {
    product:  { count: products.length,  alert: products.filter(l => l.status === "new").length },
    warranty: { count: warranty.length,  alert: warrActive },
    return:   { count: returns.length,   alert: returns.filter(l => l.day >= 12).length },
    question: { count: questions.length, alert: questions.filter(l => l.status === "open").length },
    docs:     { count: docs.length,      alert: docs.filter(d => d.status === "pending").length },
    mgrq:     { count: mgrq.length,      alert: mgrq.filter(qq => qq.status === "open").length },
  };

  // ── пошук (простий includes по client/phone/product/question) ──
  const needle = q.trim().toLowerCase();
  const matchQ = (l) => !needle || [l.client, l.phone, l.product, l.question, l.note, l.reason, l.docNo, l.docType]
    .some(v => v && String(v).toLowerCase().includes(needle));

  const isTerm = s => (window.LS_TERMINAL ? window.LS_TERMINAL.has(s) : ["converted", "lost", "closed", "done"].includes(s));
  const callState = l => { const a = window.lsCallActivity ? window.lsCallActivity(l.phone, l.createdAt) : null; return a ? a.state : "none"; };
  let productsView = products.filter(l => prodArchive ? isTerm(l.status) : !isTerm(l.status));
  if (prodArchive && prodStatusFilter !== "all") productsView = productsView.filter(l => l.status === prodStatusFilter);
  if (prodCallFilter !== "all") productsView = productsView.filter(l => callState(l) === prodCallFilter);
  // Гарантії/документи: Активні або Архів (виконані/надіслані).
  const WARR_DONE = ["done", "closed"], DOC_DONE = ["sent"];
  const warrantyView = warranty.filter(l => warrArchive ? WARR_DONE.includes(l.status) : !WARR_DONE.includes(l.status));
  const docsView     = docs.filter(l => docsArchive ? DOC_DONE.includes(l.status) : !DOC_DONE.includes(l.status));
  const byTypeFiltered = { product: productsView, warranty: warrantyView, return: returns, question: questions, docs: docsView, mgrq };
  const list = (byTypeFiltered[tab] || []).filter(matchQ);
  const todayCount = records.filter(r => r.ageMin < 1440).length;

  const cards = {
    product:  list.map(l => <LsmProductCard  key={l.id} l={l} onOpen={setSelProduct} onStatus={changeStatus}/>),
    warranty: list.map(l => <LsmWarrantyCard key={l.id} l={l} onOpen={setEditing} onStatus={changeStatus}/>),
    return:   list.map(l => <LsmReturnCard   key={l.id} l={l} onOpen={setEditing} onStatus={changeStatus}/>),
    question: list.map(l => <LsmQuestionCard key={l.id} l={l} open={openQ === l.id} onToggle={() => setOpenQ(openQ === l.id ? null : l.id)} onAnswer={(id, reply) => editLead(id, { reply, status: "answered" })} onStatus={changeStatus} onEdit={setEditing}/>),
    docs:     list.map(l => <LsmDocCard      key={l.id} l={l} onSend={sendDoc} onOpen={setEditing}/>),
    mgrq:     list.map(l => <LsmMgrCard      key={l.id} l={l} open={openM === l.id} onToggle={() => setOpenM(openM === l.id ? null : l.id)} onAnswer={answerMgr} onEdit={setEditing}/>),
  };

  return (
    <div style={{ flex: 1, minHeight: 0, width: "100%", background: "var(--bg-base)", display: "flex", flexDirection: "column", overflow: "hidden", position: "relative" }}>
      {/* Компактний верх: лічильник + Нова + пошук */}
      <div style={{ display: "flex", alignItems: "center", gap: 10, padding: "10px 16px 6px", flexShrink: 0 }}>
        <span style={{ fontSize: 12, color: "var(--fg-muted)" }}><span style={{ color: "var(--fg-secondary)", fontWeight: 600 }}>{todayCount}</span> за сьогодні</span>
        <span style={{ flex: 1 }}></span>
        <button onClick={refreshAll} disabled={refreshing} title="Оновити заявки" style={{
          width: 34, height: 34, borderRadius: 9, border: "1px solid var(--border-default)",
          background: "var(--bg-panel)", color: "var(--fg-secondary)", cursor: refreshing ? "default" : "pointer",
          display: "inline-flex", alignItems: "center", justifyContent: "center", flexShrink: 0,
        }}><LsIcon name="refresh-cw" size={15} style={refreshing ? { animation: "spin 0.8s linear infinite" } : null}/></button>
        <button onClick={() => setNewModal(true)} style={{
          height: 34, padding: "0 13px", borderRadius: 9, border: 0,
          background: "var(--accent)", color: "#fff", cursor: "pointer", fontFamily: "inherit",
          fontSize: 12.5, fontWeight: 500, display: "inline-flex", alignItems: "center", gap: 6,
        }}><LsIcon name="plus" size={14}/> Нова</button>
      </div>
      <div style={{ padding: "0 16px 8px", flexShrink: 0 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 8, height: 36, padding: "0 12px", background: "var(--bg-raised)", border: "1px solid var(--border-default)", borderRadius: 9 }}>
          <LsIcon name="search" size={14} style={{ color: "var(--fg-muted)" }}/>
          <input value={q} onChange={e => setQ(e.target.value)} placeholder="Пошук: товар, клієнт, телефон…" style={{
            flex: 1, border: 0, background: "transparent", outline: "none", color: "var(--fg-primary)", fontFamily: "inherit", fontSize: 13,
          }}/>
          {q && <button onClick={() => setQ("")} style={{ border: 0, background: "transparent", color: "var(--fg-muted)", cursor: "pointer", display: "inline-flex" }}><LsIcon name="x" size={14}/></button>}
        </div>
      </div>

      {/* Фільтр по менеджеру — лише для керівника (owner). Менеджер бачить тільки свої. */}
      {isOwner && (
        <div style={{ padding: "0 16px 8px", flexShrink: 0 }}>
          <select value={mgrFilter} onChange={e => setMgrFilter(e.target.value)} style={{
            width: "100%", height: 34, boxSizing: "border-box", padding: "0 10px", borderRadius: 9,
            background: "var(--bg-raised)", color: mgrFilter !== "all" ? "var(--accent)" : "var(--fg-secondary)",
            border: "1px solid " + (mgrFilter !== "all" ? "var(--accent)" : "var(--border-default)"), fontSize: 13, fontFamily: "inherit", outline: "none",
          }}>
            <option value="all">Усі менеджери</option>
            {[...new Set(records.map(r => (r.manager || "").trim()).filter(Boolean))].sort().map(m => <option key={m} value={m}>{m}</option>)}
          </select>
        </div>
      )}

      <LsmTypeCards tab={tab} onTab={setTab} counts={counts}/>

      <div style={{ flex: 1, overflowY: "auto", padding: "2px 0 96px", display: "flex", flexDirection: "column", gap: 8 }}>
        {/* Воронка тепер у зоні скролу + компактна → зʼїжджає при гортанні і не зʼїдає
            екран зі списком заявок (раніше була фіксована зверху, влізало 1-2 заявки). */}
        {tab === "product" && window.LsLeadFunnel && (
          <window.LsLeadFunnel leads={products} archive={prodArchive} onToggleArchive={setProdArchive}
            callFilter={prodCallFilter} onCallFilter={setProdCallFilter} statusFilter={prodStatusFilter} onStatusFilter={setProdStatusFilter} compact/>
        )}
        {(tab === "warranty" || tab === "docs") && (() => {
          const arch = tab === "warranty" ? warrArchive : docsArchive;
          const setArch = tab === "warranty" ? setWarrArchive : setDocsArchive;
          const doneN = tab === "warranty"
            ? warranty.filter(l => WARR_DONE.includes(l.status)).length
            : docs.filter(l => DOC_DONE.includes(l.status)).length;
          const totalN = tab === "warranty" ? warranty.length : docs.length;
          const mkBtn = (on, label, n) => (
            <button onClick={() => setArch(on)} style={{
              flex: 1, height: 34, borderRadius: 9, fontFamily: "inherit", fontSize: 12.5, fontWeight: 500, cursor: "pointer",
              display: "inline-flex", alignItems: "center", justifyContent: "center", gap: 6,
              background: arch === on ? "var(--accent-soft)" : "var(--bg-raised)",
              color: arch === on ? "#C7D2FE" : "var(--fg-secondary)",
              border: "1px solid " + (arch === on ? "var(--accent-ring)" : "var(--border-default)"),
            }}>{label}<span style={{ fontFamily: "var(--font-mono)", fontSize: 11, opacity: 0.8 }}>{n}</span></button>
          );
          return <div style={{ display: "flex", gap: 8, padding: "2px 16px 4px" }}>{mkBtn(false, "Активні", totalN - doneN)}{mkBtn(true, "Архів", doneN)}</div>;
        })()}
        <div style={{ padding: "0 16px", display: "flex", flexDirection: "column", gap: 8 }}>
        {(tab === "docs" || tab === "mgrq") && (
          <div style={{ display: "flex", alignItems: "center", gap: 7, fontSize: 11, color: "var(--fg-muted)", padding: "2px 2px 0" }}>
            <LsIcon name="lock" size={11}/>
            Внутрішня вкладка — бачать лише менеджери та керівник
          </div>
        )}
        {cards[tab]}
        {list.length === 0 && (
          <div style={{ textAlign: "center", padding: "40px 16px", color: "var(--fg-muted)", fontSize: 13 }}>
            {needle ? "Нічого не знайдено" : "Немає заявок у цій вкладці"}
          </div>
        )}
        </div>
      </div>

      {selProduct && <LsmProductSheet lead={selProduct} userName={userName}
        onClose={() => setSelProduct(null)}
        onStatus={(id, s) => { changeStatus(id, s); setSelProduct(p => p && p.id === id ? { ...p, status: s } : p); }}
        onEdit={(l) => { setSelProduct(null); setEditing(l); }}/>}
      {newModal && <LsNewLeadModal managers={managers} userName={userName} onClose={() => setNewModal(false)} onCreate={createLead}/>}
      {editing && <LsNewLeadModal initial={editing} managers={managers} userName={userName} onClose={() => setEditing(null)} onSave={editLead} onDelete={removeLead}/>}
    </div>
  );
}

Object.assign(window, {
  LsmApp,
  LsmProductCard, LsmWarrantyCard, LsmReturnCard, LsmQuestionCard, LsmDocCard, LsmMgrCard, LsmCardShell,
  LsmTypeCards, LsmSheet, LsmProductSheet,
});
