/* super-v2-app.jsx — кабинет руководителя в виде кабинета клиента (KSH-458): шапка, главная со сводкой, разделы — окнами по #hash.
   Собран из библиотеки виджетов lk-widgets.jsx (window.LKW) на данных super-views.js (window.SuperViews).
   Главная: сводка (проекты и отставание, решения, внимание, дебиторка, продажи), «Сейчас важно», «Требует внимания», деньги,
   чаты, проекты, «Мои задачи» (общий таск-менеджер: решения руководителя, задачи сделок, заявки Конструктора), продажи.
   Окна: #projects[=late|debt|handoff] — портфель; #proj=<id> — проект: путь, ход, отставание, тревоги, оплаты, документы, «глазами»;
   #alerts[=crit] — всё, что требует внимания; #money — дебиторка, кредиторка, документы в долге, оплаты по проектам, порог маржи;
   #sales — воронка (отвал по применимым шагам), сделки без движения, менеджеры, источники; #calendar — календарь работ всех бригад
   (SuperWeekZone); #chat[=<src>:<id>:<tid>] — все чаты проектов и сделок; #dec=<kind>:<pid> — решение из «Мои задачи».
   Действия — функции, что и в прежнем кабинете: ProjectStore.decideReplacement / resubmitMaterial / decideGate / overrideGate /
   overridePayGate / setPayGate / chatSend, ExecPrices.decideOffer, ContentPlan.decide, DealChat.send. Люди и доступ, обратная связь,
   настройки, подробная таблица сделок и Конструктор — прежние страницы, ссылки в «Ещё». */
(function () {
  'use strict';
  const P = window.ProjectStore, SV = window.SuperViews, W = window.LKW, A = window.AccessStore;
  const { Ic, Header, Modal, FactsBand, NowCards, RowsW, ChatsW, PhaseMini, rub, plural, cap, dateR, short } = W;
  const t0 = () => new Date().toISOString().slice(0, 10);
  const dm = (d) => (d ? String(d).slice(8, 10) + '.' + String(d).slice(5, 7) + '.' + String(d).slice(2, 4) : '');

  function useMe() {
    const acc = A && A.currentAccount ? A.currentAccount() : null;
    const ac = W.access(acc, ['super']);
    // главная (dash), «Финансы», «Календарь», «Чаты» — отдельные разделы доступа, как у прежних страниц
    const home = ac.zone('dash'), money = ac.zone('finance'), cal = ac.zone('calendar'), chat = ac.zone('chat');
    return { id: acc ? acc.profileId : null, name: (acc && acc.name) || 'Руководитель',
      deny: ac.deny || (home || money || cal || chat ? '' : 'Кабинет руководителя не входит в выданный доступ.'), home, money, cal, chat };
  }
  const EVS = ['storage', 'obitel:sync', 'obitel:deal-chat', 'obitel:exec-prices', 'obitel:staff', 'remontpro:forms-updated', 'remontpro:intake-updated'];
  function useStore() {
    const [, tick] = React.useState(0);
    React.useEffect(() => {
      const on = () => tick((x) => x + 1);
      const offs = [P && P.subscribe ? P.subscribe(on) : null, window.ContentPlan && window.ContentPlan.subscribe ? window.ContentPlan.subscribe(on) : null];
      EVS.forEach((e) => window.addEventListener(e, on));
      return () => { offs.forEach((f) => typeof f === 'function' && f()); EVS.forEach((e) => window.removeEventListener(e, on)); };
    }, []);
  }
  function useHash() {
    const read = () => decodeURIComponent((window.location.hash || '').replace('#', ''));
    const [m, setM] = React.useState(read());
    React.useEffect(() => { const on = () => setM(read()); window.addEventListener('hashchange', on); return () => window.removeEventListener('hashchange', on); }, []);
    const open = (id) => { window.location.hash = id; };
    const close = () => { history.replaceState(null, '', window.location.pathname + window.location.search); setM(''); };
    return [m, open, close];
  }
  const greeting = () => { const h = new Date().getHours(); return h < 5 ? 'Доброй ночи' : h < 12 ? 'Доброе утро' : h < 18 ? 'Добрый день' : 'Добрый вечер'; };
  const todayLine = () => cap(new Date().toLocaleDateString('ru-RU', { weekday: 'long', day: 'numeric', month: 'long' }));
  const when = (iso) => { if (!iso) return ''; const d = String(iso).slice(0, 10); return d === t0() ? new Date(iso).toLocaleTimeString('ru-RU', { hour: '2-digit', minute: '2-digit' }) : dateR(d, { month: 'short' }); };
  const go = (url) => { window.location.href = url; };
  // прежние страницы руководителя: та же ссылка (?t=), без своего #окна
  const keep = (page) => { const s = window.location.search; return page + (s || ''); };
  // кабинеты ролей глазами руководителя (только чтение), как «глазами» в прежней диспетчерской
  const eyes = (role, p) => {
    if (role === 'client') return A && A.clientViewUrl ? A.clientViewUrl({ projectId: p.id, name: p.client }) : 'client-v2.html?project=' + encodeURIComponent(p.id) + '&superView=1';
    if (role === 'foreman') return 'foreman-v2.html?superView=1&name=' + encodeURIComponent(p.foreman || '') + '#obj=' + encodeURIComponent(p.id);
    return role + '.html?superView=1';
  };
  const ROLE_L = { manager: 'менеджера', foreman: 'прораба', procurement: 'закупок', accountant: 'бухгалтера' };

  const NAV = [['', 'Главная'], ['projects', 'Проекты'], ['alerts', 'Внимание'], ['money', 'Деньги'], ['sales', 'Продажи'], ['chat', 'Чаты']];
  const PHONE = ['', 'projects', 'alerts', 'chat'];
  const MORE = [['calendar', 'Календарь работ'], ['tools.html?role=super', 'Инструменты'], ['super-deals.html', 'Сделки и команда подробно'], ['super-users.html', 'Люди и доступ'], ['super-feedback.html', 'Обратная связь'], ['super-settings.html', 'Настройки'], ['../Конструктор проектов.html', 'Конструктор проектов']];
  const SEV_L = { crit: 'срочно', warn: 'внимание', info: 'к сведению' };

  /* «Мои задачи»: строка решения открывает окно #dec, заявка Конструктора — Конструктор, задача сделки — её ссылку */
  function myInbox(data, open) {
    return data.inbox.map((r) => Object.assign({}, r, { act: () => (r.kind === 'ctor' || (!r.dkind && r.href) ? go(r.href) : r.dkind ? open('dec=' + r.dkind + ':' + (r.pid || '')) : r.href ? go(r.href) : null) }));
  }

  /* ───── главная ───── */
  function Home({ me, data, open }) {
    const f = data.facts;
    const first = (me.name || '').split(' ')[0];
    const T = window.TaskViews;
    const inbox = myInbox(data, open);
    const now = data.alerts.filter((a) => a.sev === 'crit').slice(0, 3).map((a) => ({ key: a.id, kindL: cap(a.kindL), late: true, title: a.title, sub: a.sub, right: a.money ? rub(a.money) : a.days ? a.days + ' дн' : '',
      act: { text: a.pid ? 'Проект' : 'Открыть', onClick: () => open(a.pid ? 'proj=' + a.pid : 'alerts') } }));
    const act = data.projects.filter((p) => p.status !== 'done');
    const arRows = data.money.ar.rows.slice(0, 6).map((x) => ({ key: x.id, ic: 'wallet', late: x.overdue, title: x.client + ' · ' + rub(x.amount), sub: x.title + ' · ' + short(x.obj), right: x.overdue ? 'просрочка ' + x.days + ' дн' : x.due ? 'до ' + dm(x.due) : '', onClick: () => open(x.pid ? 'proj=' + x.pid : 'money') }));
    return (
      <div className="cv-page lw-page">
        <Header nav={NAV} more={MORE} phone={PHONE} cur="" right={<span className="cv-ava" title={me.name}>{me.name.split(' ').map((w) => w[0]).join('').slice(0, 2)}</span>} />
        <main id="main" tabIndex={-1} className="cv-main">
          <section className="cv-hero">
            <div><div className="lab">{todayLine()} · {f.active} {plural(f.active, 'проект', 'проекта', 'проектов')} в работе</div><h1 className="cv-hello">{greeting()}{first && first !== 'Руководитель' ? ', ' + first : ''}</h1></div>
            <div className="cv-hero-act">
              <button type="button" className="btn-l" onClick={() => open('calendar')}>Календарь работ</button>
              {f.crit ? <button type="button" className="btn-k" onClick={() => open('alerts=crit')}>Срочно · {f.crit}</button>
                : <span className="pill-g"><span className="dot" />Срочного нет</span>}
            </div>
          </section>

          <FactsBand label="Сводка компании"
            live={<span className="now">{data.chats.filter((c) => c.isNew).length ? <React.Fragment><i className="cv-pulse" aria-hidden="true" /><span>Новые сообщения: {data.chats.filter((c) => c.isNew).map((c) => short(c.obj)).filter((x, i, a) => a.indexOf(x) === i).slice(0, 3).join(', ')}</span></React.Fragment> : 'Новых сообщений нет'}</span>}
            facts={[
              { v: f.active + ' ' + plural(f.active, 'проект', 'проекта', 'проектов'), lab: f.lagging ? f.lagging + ' ' + plural(f.lagging, 'отстаёт', 'отстают', 'отстают') + ' · до ' + f.lagMax + ' дн' : f.handoff ? f.handoff + ' ждут прораба · без отставания' : 'В работе · без отставания', late: f.lagging > 0, onClick: () => open(f.lagging ? 'projects=late' : 'projects') },
              { v: f.decide + ' ' + plural(f.decide, 'решение', 'решения', 'решений'), lab: f.decideLate ? f.decideLate + ' просрочено · ждут вас' : 'Ждут вашего решения', late: f.decideLate > 0, onClick: () => { const el = document.getElementById('sv-tasks'); if (el) el.scrollIntoView({ behavior: 'smooth' }); } },
              { v: f.alerts ? f.alerts + ' ' + plural(f.alerts, 'сигнал', 'сигнала', 'сигналов') : 'Спокойно', lab: f.crit ? f.crit + ' срочных · сроки, счета, подрядчики' : 'Сроки, счета, подрядчики', late: f.crit > 0, onClick: () => open('alerts') },
              { v: rub(f.ar), lab: 'Дебиторка' + (f.arOverdue ? ' · просрочено ' + rub(f.arOverdue) : ' · без просрочки'), late: f.arOverdue > 0, onClick: () => open('money') },
              { v: f.deals + ' ' + plural(f.deals, 'сделка', 'сделки', 'сделок'), lab: (f.pipeline ? rub(f.pipeline) + ' в воронке' : 'В воронке') + (f.dealsStuck ? ' · ' + f.dealsStuck + ' без движения' : ''), onClick: () => open('sales') },
            ]} />

          <NowCards rows={now} label="Срочно" />

          <section className="cv-two lw-two">
            <RowsW id="sv-alerts-h" title="Требует внимания" n={f.crit} className="is-grow" max={420} onOpen={() => open('alerts')} text={'Всё' + (data.alerts.length > 8 ? ' · ' + data.alerts.length : '')}
              rows={data.alerts.slice(0, 8).map((a) => ({ key: a.id, ic: a.sev === 'crit' ? 'alert' : 'clock', late: a.sev === 'crit', title: a.title, sub: cap(a.kindL) + (a.sub ? ' · ' + a.sub : ''), right: a.money ? rub(a.money) : '', onClick: () => open(a.pid ? 'proj=' + a.pid : 'alerts') }))}
              empty="Сроки, счета и подрядчики — в порядке." />
            <div className="cv-side">
              <RowsW id="sv-ar-h" title="Деньги клиентов" n={data.money.ar.rows.filter((x) => x.overdue).length} rows={arRows} empty="Выставленных неоплаченных счетов нет." onOpen={() => open('money')} text="Все деньги" max={300} />
              <ChatsW chats={data.chats.slice(0, 8).map((c) => Object.assign({}, c, { when: when(c.at) }))} onOpen={() => open('chat')} onPick={(c) => open('chat=' + c.src + ':' + c.pid + ':' + c.id)} />
            </div>
          </section>

          <section className="lw-sec" aria-labelledby="sv-proj-h">
            <div className="lw-sech"><h2 className="h2" id="sv-proj-h">Проекты</h2><button type="button" className="cv-arrow-btn" onClick={() => open('projects')}>Все проекты{act.length > 4 ? ' · ' + act.length : ''}<Ic n="ne" s={14} /></button></div>
            {act.length ? <div className="lw-tiles">{act.slice(0, 4).map((p) => <ProjTile key={p.id} p={p} onOpen={() => open('proj=' + p.id)} />)}</div>
              : <div className="note-s" style={{ marginTop: 12 }}><span>Запущенных проектов пока нет.</span></div>}
          </section>

          {T && T.outbox && <section id="sv-tasks" className="cv-w lw-gw lw-scope" aria-label="Мои задачи">
            <W.TaskList title="Мои задачи" t0={t0()} onOpen={(r) => (r.act ? r.act() : r.href ? go(r.href) : null)}
              tabs={[
                { id: 'in', label: 'Мне сделать', rows: inbox, empty: 'Сейчас решений руководителя не ждёт ничего.', boardRows: inbox.concat(T.inboxDone ? T.inboxDone('super', t0()) : []), columns: T.IN_COLUMNS, colOf: T.inCol },
                { id: 'out', label: 'Отправлено мной', rows: T.outbox({ role: 'super' }, t0()), empty: 'Вы ничего не возвращали на доработку.',
                  buckets: T.OUT_BUCKETS, bucketOf: T.outBucket, boardRows: T.outbox({ role: 'super' }, t0(), { closed: true }), columns: T.OUT_COLUMNS, colOf: T.outCol }]} />
          </section>}

          <section className="cv-two lw-two">
            <RowsW id="sv-stuck-h" title="Сделки без движения" n={data.sales.stuck.length} className="is-grow" onOpen={() => open('sales')} text="Продажи"
              rows={data.sales.stuck.slice(0, 6).map((d) => ({ key: d.dealId, ic: 'clock', late: d.idle > 7, title: (d.client || d.dealId) + (d.amount ? ' · ' + rub(d.amount) : ''), sub: d.stuck.map((s) => s.l).join(' · '), right: d.idle != null ? d.idle + ' дн' : '', onClick: () => open('sales') }))}
              empty="Все сделки двигаются." />
            <div className="cv-side">
              <RowsW id="sv-mgr-h" title="Менеджеры" rows={data.mgrs.slice(0, 5).map((m, i) => ({ key: m.name, ic: 'user', title: (i + 1) + '. ' + m.name, sub: m.deals + ' ' + plural(m.deals, 'сделка', 'сделки', 'сделок') + ' · запущено ' + m.won + ' · в работе ' + rub(m.pipeline), right: m.win + ' %', rightLab: 'до проекта', onClick: () => open('sales') }))}
                empty="Ни одна сделка пока не закреплена за менеджером." />
            </div>
          </section>
        </main>
      </div>);
  }

  // плитка проекта: путь фаз, ход, отставание, прораб, оплата
  function ProjTile({ p, onOpen }) {
    const flags = [p.lag ? 'отставание ' + p.lag + ' дн' : '', p.overdueN ? p.overdueN + ' ' + plural(p.overdueN, 'счёт просрочен', 'счёта просрочены', 'счетов просрочены') : '', p.handoff ? 'ждёт прораба' : ''].filter(Boolean);
    return (
      <button type="button" className="lw-tile" onClick={onOpen} aria-label={'Открыть проект ' + p.title}>
        <span className="lw-tph">{p.cover ? <img src={p.cover} alt="" loading="lazy" /> : <span className="ph0"><Ic n="key" s={28} /></span>}
          {flags.length ? <span className="fl">{flags.map((x) => <span key={x} className={/отстав|просроч/.test(x) ? 'is-late' : ''}>{x}</span>)}</span> : null}
          <span className="cv-wcirc lw-tcirc" aria-hidden="true"><Ic n="ne" s={18} /></span></span>
        <span className="tx">
          <span className="t">{p.title}</span>
          <span className="lab">{[p.client, p.foreman ? 'прораб ' + p.foreman : 'без прораба'].filter(Boolean).join(' · ')}</span>
          {p.path && p.path.length ? <PhaseMini path={p.path} /> : null}
          <span className="row"><span className="st">{p.stage}</span><span className="pc">{p.pct} %</span></span>
          <span className="lab">Оплачено {rub(p.paid)} из {rub(p.contract)}</span>
        </span>
      </button>);
  }

  /* ───── портфель проектов ───── */
  function ProjectsModal({ data, sel, open, close }) {
    const [tab, setTab] = React.useState(sel || 'all');
    const all = data.projects;
    const pick = { all: all.filter((p) => p.status !== 'done'), late: all.filter((p) => p.lag > 0 && p.status !== 'done'), debt: all.filter((p) => p.overdue > 0), handoff: all.filter((p) => p.handoff), done: all.filter((p) => p.status === 'done') };
    const TABS = [['all', 'В работе'], ['late', 'Отстают'], ['debt', 'Долги клиентов'], ['handoff', 'Ждут прораба'], ['done', 'Сданы, деньги открыты']];
    const list = pick[tab] || pick.all;
    return (
      <Modal size="wide" title="Проекты" sub={pick.all.length + ' в работе · ' + pick.late.length + ' отстают · договоры на ' + rub(pick.all.reduce((a, p) => a + p.contract, 0))} onClose={close}>
        <div className="mg-tabs"><W.CalTabs kinds={TABS} tab={tab} setTab={setTab} counts={Object.keys(pick).reduce((a, k) => Object.assign(a, { [k]: pick[k].length }), {})} label="Какие проекты показать" /></div>
        {!list.length ? <div className="lab" style={{ marginTop: 20 }}>{tab === 'late' ? 'Ни один проект не отстаёт.' : 'Проектов нет.'}</div>
          : <div className="sv-table" role="table" aria-label="Портфель проектов">
            <div className="sv-tr sv-th" role="row"><span role="columnheader">Проект</span><span role="columnheader">Прораб</span><span role="columnheader">Этап и ход</span><span role="columnheader">Отставание</span><span role="columnheader">Договор · оплачено</span><span role="columnheader">Долг</span></div>
            {list.map((p) => (
              <button type="button" key={p.id} className="sv-tr" role="row" onClick={() => open('proj=' + p.id)}>
                <span role="cell" className="tx"><span className="t">{p.title}</span><span className="lab">{[p.client, p.id].filter(Boolean).join(' · ')}</span></span>
                <span role="cell" className="lab">{p.foreman || 'не назначен'}</span>
                <span role="cell" className="tx"><span className="t sv-st">{p.stage}</span>{p.path && p.path.length ? <PhaseMini path={p.path} /> : null}<span className="lab">{p.pct} % работ · {p.doneN} из {p.allN}</span></span>
                <span role="cell" className={p.lag ? 'sv-late' : 'lab'}>{p.lag ? p.lag + ' дн' + (p.late ? ' · ' + p.late + ' ' + plural(p.late, 'работа', 'работы', 'работ') : '') : 'нет'}</span>
                <span role="cell" className="tx"><span className="t">{rub(p.contract)}</span><span className="lab">оплачено {rub(p.paid)}</span></span>
                <span role="cell" className={p.overdue ? 'sv-late' : 'lab'}>{p.overdue ? rub(p.overdue) : '—'}</span>
              </button>))}
          </div>}
      </Modal>);
  }

  const PST = { paid: ['Оплачено', 'pill-g'], check: ['Оплачено · ждёт сверки', 'pill-g'], overdue: ['Просрочено', 'pill-n cv-over'], expected: ['Ожидаем оплату', 'pill-n'], planned: ['По графику', 'pill-g'] };
  function ProjectModal({ id, data, open, close }) {
    const p = data.projects.find((x) => x.id === id);
    if (!p) return <Modal title="Проект не найден" onClose={close}><div className="lab" style={{ marginTop: 16 }}>Такого проекта нет.</div></Modal>;
    const al = data.alerts.filter((a) => a.pid === p.id);
    const docs = data.docs.filter((d) => d.pid === p.id);
    return (
      <Modal size="wide" title={p.title} sub={[p.client, p.id, p.area ? p.area + ' м²' : ''].filter(Boolean).join(' · ')} onClose={close}
        head={<React.Fragment>
          {p.foremanId ? <a className="lw-btn-l" href={eyes('foreman', p)} target="_blank" rel="noopener">Кабинет прораба глазами</a> : null}
          <a className="lw-btn-l" href={eyes('client', p)} target="_blank" rel="noopener">Глазами клиента</a>
          <button type="button" className="lw-btn-l" onClick={() => open('chat=project:' + p.id + ':')}>Чат проекта</button></React.Fragment>}>
        <FactsBand label="Проект" path={p.path} facts={[
          { v: p.stage, lab: p.foreman ? 'Прораб ' + p.foreman : 'Прораб не назначен' },
          { v: p.pct + ' %', lab: 'Работ принято · ' + p.doneN + ' из ' + p.allN + (p.dueIso ? ' · сдача ' + dm(p.dueIso) : '') },
          { v: p.lag ? p.lag + ' дн' : 'Без отставания', lab: p.lag ? (p.late ? p.late + ' ' + plural(p.late, 'работа', 'работы', 'работ') + ' после срока' : 'Этап после срока') : 'Сроки соблюдаются', late: p.lag > 0 },
          { v: rub(p.paid), lab: 'Оплачено из ' + rub(p.contract) },
          { v: p.overdue ? rub(p.overdue) : rub(p.expected), lab: p.overdue ? 'Просрочено клиентом' : 'Ожидаем оплату', late: p.overdue > 0 },
        ]} />
        <div className="cv-two lw-two">
          <RowsW id="sv-pal-h" title="Требует внимания" n={al.filter((a) => a.sev === 'crit').length} rows={al.map((a) => ({ key: a.id, ic: a.sev === 'crit' ? 'alert' : 'clock', late: a.sev === 'crit', title: a.title.replace(p.id + ' · ', ''), sub: cap(a.kindL) + (a.sub ? ' · ' + a.sub : '') }))}
            empty="По объекту всё в порядке." className="is-grow" />
          <section className="cv-w cv-apr-w lw-rows" aria-labelledby="sv-pays-h">
            <W.WHead id="sv-pays-h" title="График оплат" n={p.overdueN} />
            <div className="rows">{p.pays.map((x) => (
              <div key={x.id} className={'r lw-r' + (x.state === 'overdue' ? ' is-over' : '')}>
                <span className="tx"><span className="t">{x.title}</span><span className="lab">{rub(x.amount)}{x.due ? ' · срок ' + dm(x.due) : ''}{x.state === 'overdue' ? ' · просрочка ' + x.days + ' дн' : ''}</span></span>
                <span className={PST[x.state][1]}>{PST[x.state][0]}</span>
              </div>))}</div>
            {!p.pays.length && <div className="lab" style={{ marginTop: 10 }}>Графика оплат нет.</div>}
            {/* правило проекта «этап после оплаты» доступно всегда, а не только когда оплата что-то держит */}
            <button type="button" className="cv-arrow-btn" style={{ marginTop: 12 }} onClick={() => open('dec=pay:' + p.id)}>Этап после оплаты: {(P.get(p.id) || {}).payGate ? 'включено' : 'выключено'}<Ic n="ne" s={14} /></button>
          </section>
        </div>
        {(docs.length > 0 || p.lateStages.length > 0) && <div className="cv-two lw-two">
          <RowsW id="sv-pdocs-h" title="Документы в долге" n={docs.length} rows={docs.map((d) => ({ key: d.id, ic: 'doc', late: d.status === 'rejected', title: d.title, sub: (d.status === 'rejected' ? 'клиент отклонил' : 'ждёт подписи клиента') + (d.amount ? ' · ' + rub(d.amount) : ''), right: d.days ? d.days + ' дн' : '' }))} empty="Все документы подписаны." className="is-grow" />
          <RowsW id="sv-pst-h" title="Этапы после срока" n={p.lateStages.length} rows={p.lateStages.map((s) => ({ key: s.id, ic: 'clock', late: s.days > 7, title: s.name, sub: 'срок прошёл ' + s.days + ' дн назад' }))} empty="Этапы идут в срок." />
        </div>}
      </Modal>);
  }

  /* ───── внимание ───── */
  function AlertsModal({ data, sel, open, close }) {
    const [tab, setTab] = React.useState(sel === 'crit' ? 'crit' : 'all');
    const list = tab === 'all' ? data.alerts : data.alerts.filter((a) => a.sev === tab);
    const byPid = {}; data.projects.forEach((p) => { byPid[p.id] = p; });
    const TABS = [['all', 'Все'], ['crit', 'Срочно'], ['warn', 'Внимание'], ['info', 'К сведению']];
    return (
      <Modal size="wide" title="Требует внимания" sub="Просроченные счета и работы, этапы после срока, возвраты, подрядчики. Решения — в «Мои задачи»." onClose={close}>
        <div className="mg-tabs"><W.CalTabs kinds={TABS} tab={tab} setTab={setTab} counts={TABS.reduce((a, [k]) => Object.assign(a, { [k]: k === 'all' ? data.alerts.length : data.alerts.filter((x) => x.sev === k).length }), {})} label="Какие сигналы показать" /></div>
        {!list.length ? <div className="note-s" style={{ marginTop: 18 }}><Ic n="check" c="#2F5D3A" s={16} /><span>Здесь пусто — всё в порядке.</span></div>
          : <div className="rows mg-rows">{list.map((a) => { const p = byPid[a.pid]; return (
            <div key={a.id} className={'r lw-r' + (a.sev === 'crit' ? ' is-over' : '')}>
              <span className="tx"><span className="t">{a.title}</span><span className="lab">{cap(SEV_L[a.sev])} · {a.kindL}{a.sub ? ' · ' + a.sub : ''}</span></span>
              {a.money ? <span className="by"><b>{rub(a.money)}</b></span> : null}
              <span className="lw-racts">
                {p ? <button type="button" className="lw-btn-l" onClick={() => open('proj=' + p.id)}>Проект</button> : null}
                {p && a.ownerRole && ROLE_L[a.ownerRole] && (a.ownerRole !== 'foreman' || p.foremanId) ? <a className="lw-btn-l" href={eyes(a.ownerRole, p)} target="_blank" rel="noopener" title={'Открыть кабинет ' + ROLE_L[a.ownerRole] + ' глазами'}>Глазами {ROLE_L[a.ownerRole]}</a> : null}
              </span>
            </div>); })}</div>}
      </Modal>);
  }

  /* ───── деньги ───── */
  function MoneyModal({ data, open, close }) {
    const { ar, ap } = data.money;
    const act = data.projects.filter((p) => p.contract > 0);
    const sec = (title, n, body) => <section style={{ marginTop: 24 }}><h2 className="h2" style={{ fontSize: 20 }}>{title}{n ? <span className="lab"> · {n}</span> : null}</h2>{body}</section>;
    const line = (key, title, sub, right, late, onClick) => (
      <button type="button" key={key} className={'r lw-r mg-row' + (late ? ' is-over' : '')} onClick={onClick}><span className="tx"><span className="t">{title}</span><span className="lab">{sub}</span></span><span className="by"><b>{right}</b></span></button>);
    return (
      <Modal size="wide" title="Деньги" sub={'Дебиторка ' + rub(ar.total) + (ar.overdue ? ' · просрочено ' + rub(ar.overdue) : '') + ' · подрядчикам к оплате ' + rub(ap.subSum)} onClose={close}>
        <div className="ac-buckets is-wide">{ar.buckets.map((b) => <div key={b.key} className={'ac-bucket' + (b.key !== 'cur' && b.amount ? ' is-over' : '')}><span className="lab">{b.label}</span><b>{rub(b.amount)}</b><span className="lab">{b.count} {plural(b.count, 'счёт', 'счёта', 'счетов')}</span></div>)}</div>
        {sec('Дебиторка', ar.rows.length, ar.rows.length ? <div className="rows mg-rows">{ar.rows.map((x) => line(x.id, x.client + ' · ' + rub(x.amount), x.title + ' · ' + short(x.obj), x.overdue ? 'просрочка ' + x.days + ' дн' : x.due ? 'до ' + dm(x.due) : '', x.overdue, () => x.pid && open('proj=' + x.pid)))}</div> : <div className="lab" style={{ marginTop: 8 }}>Клиенты ничего не должны.</div>)}
        {sec('Подрядчикам к оплате', ap.subs.length, ap.subs.length ? <div className="rows mg-rows">{ap.subs.map((x) => line(x.pid + x.orderId + x.part, x.subName + ' · ' + rub(x.amount), x.partLabel + ' · ' + x.basis + ' · ' + short(x.obj), 'оплачивает бухгалтер', false, () => open('proj=' + x.pid)))}</div> : <div className="lab" style={{ marginTop: 8 }}>Подрядчикам платить сейчас нечего.</div>)}
        {ap.orders.length > 0 && sec('Заказы поставщикам в пути', ap.orders.length, <div className="rows mg-rows">{ap.orders.map((x) => line(x.id, x.supplier + (x.amount ? ' · ' + rub(x.amount) : ''), short(x.obj), x.eta ? 'привезут ' + (/^\d{4}-\d{2}-\d{2}/.test(x.eta) ? dm(x.eta) : x.eta) : '', false, () => open('proj=' + x.pid)))}</div>)}
        {sec('Документы в долге', data.docs.length, data.docs.length ? <div className="rows mg-rows">{data.docs.map((d) => line(d.id, d.title + (d.amount ? ' · ' + rub(d.amount) : ''), short(d.obj) + ' · ' + (d.status === 'rejected' ? 'клиент отклонил' : 'ждёт подписи клиента'), d.days ? d.days + ' дн' : '', d.status === 'rejected', () => open('proj=' + d.pid)))}</div> : <div className="lab" style={{ marginTop: 8 }}>Все акты подписаны.</div>)}
        {sec('Оплаты по проектам', act.length, <div className="sv-table" role="table" aria-label="Оплаты по проектам">
          <div className="sv-tr sv-th is-money" role="row"><span role="columnheader">Проект</span><span role="columnheader">Договор</span><span role="columnheader">Оплачено</span><span role="columnheader">Ожидаем</span><span role="columnheader">Просрочено</span></div>
          {act.map((p) => <button type="button" key={p.id} className="sv-tr is-money" role="row" onClick={() => open('proj=' + p.id)}>
            <span role="cell" className="tx"><span className="t">{p.title}</span><span className="lab">{p.client}</span></span><span role="cell">{rub(p.contract)}</span><span role="cell">{rub(p.paid)}</span><span role="cell" className="lab">{p.expected ? rub(p.expected) : '—'}</span><span role="cell" className={p.overdue ? 'sv-late' : 'lab'}>{p.overdue ? rub(p.overdue) : '—'}</span></button>)}
        </div>)}
        <div className="note-s" style={{ marginTop: 22 }}><Ic n="info" s={16} /><span>Порог маржи сметы — не ниже {data.marginMin} % (Настройки → Контроль сметы). Маржа по проектам появится вместе с колонками «Мастеру» и «Остаток» в Конструкторе.</span></div>
      </Modal>);
  }

  /* ───── продажи ───── */
  function SalesModal({ data, close }) {
    const s = data.sales; const max = Math.max(1, ...(s.funnel.length ? [s.funnel[0].n] : [1]));
    return (
      <Modal size="wide" title="Продажи" sub={s.sum.active + ' в работе · ' + rub(s.sum.pipelineAmount) + ' в воронке · заявка → проект ' + s.sum.convProject + ' %'} onClose={close}
        head={<a className="lw-btn-l" href={keep('super-deals.html')}>Все сделки и команда<Ic n="ne" s={14} /></a>}>
        <section style={{ marginTop: 18 }}><h2 className="h2" style={{ fontSize: 20 }}>Воронка</h2>
          <div className="sv-funnel">{s.funnel.map((r, i) => { const na = i > 0 && r.applicable === 0; return (
            <div key={r.id} className={'sv-fr' + (na ? ' is-na' : '')}>
              <span className="t">{r.label}{!na && r.applicable !== s.funnel[0].n ? <span className="lab"> · применимо к {r.applicable}</span> : null}</span>
              <span className="mg-bar"><i style={{ width: Math.max(r.n ? 3 : 0, Math.round(r.n / max * 100)) + '%' }} /></span>
              <b>{r.n}</b>
              <span className="lab">{i === 0 ? 'все сделки' : na ? 'не применимо' : r.convPrev == null ? 'ещё не дошли' : r.convPrev + ' % от пред.' + (r.drop ? ' · −' + r.drop + ' отвал' : '')}</span>
            </div>); })}</div>
          {!s.funnel.length && <div className="lab">Сделок пока нет.</div>}
        </section>
        <div className="cv-two lw-two" style={{ marginTop: 18 }}>
          <RowsW id="sv-sst-h" title="Без движения" n={s.stuck.length} className="is-grow" rows={s.stuck.map((d) => ({ key: d.dealId, ic: 'clock', late: d.idle > 7, title: (d.client || d.dealId) + (d.amount ? ' · ' + rub(d.amount) : ''), sub: d.stuck.map((x) => x.l).join(' · '), right: d.idle != null ? d.idle + ' дн' : '' }))} empty="Все сделки двигаются." />
          <RowsW id="sv-smg-h" title="Менеджеры" rows={data.mgrs.map((m, i) => ({ key: m.name, ic: 'user', title: (i + 1) + '. ' + m.name, sub: m.deals + ' ' + plural(m.deals, 'сделка', 'сделки', 'сделок') + ' · запущено ' + m.won + ' на ' + rub(m.revenue) + ' · в работе ' + rub(m.pipeline), right: m.win + ' %', rightLab: 'до проекта' }))} empty="Ни одна сделка пока не закреплена за менеджером." />
        </div>
        {s.sources.length > 0 && <section style={{ marginTop: 18 }}><h2 className="h2" style={{ fontSize: 20 }}>Источники</h2>
          <div className="mg-reasons">{s.sources.map((x) => <span key={x.label} className="pill-n">{x.label && x.label !== '—' ? x.label : 'Без источника'} · {x.n}{x.amount ? ' · ' + rub(x.amount) : ''}</span>)}</div></section>}
      </Modal>);
  }

  /* ───── чаты: все проекты и сделки ───── */
  function ChatModal({ data, sel, open, close, me }) {
    const [src0, pid0, tid0] = (sel || '').split(':');
    const cur = data.chats.find((c) => c.src === src0 && c.pid === pid0 && (!tid0 || c.id === tid0)) || (src0 ? null : data.chats[0]) || data.chats[0] || null;
    const [txt, setTxt] = React.useState('');
    const DC = window.DealChat;
    const thread = !cur ? null : cur.src === 'project' ? ((P.get(cur.pid) || {}).chat || { threads: [] }).threads.find((t) => t.id === cur.id) : (DC ? DC.load(cur.pid).threads.find((t) => t.id === cur.id) : null);
    React.useEffect(() => { if (cur && cur.src === 'deal' && DC && DC.markRead) DC.markRead(cur.pid, cur.id, 'super'); }, [cur && cur.pid, cur && cur.id]);
    const send = () => {
      if (!txt.trim() || !thread) return;
      if (cur.src === 'project') P.chatSend(cur.pid, thread.id, { from: 'super', t: txt.trim() }); else DC.send(cur.pid, thread.id, { from: 'super', name: me.name, t: txt.trim() });
      setTxt('');
    };
    return (
      <Modal size="wide" title="Чаты" sub="все проекты и сделки · вы видите каждый тред, написав — становитесь участником" onClose={close}>
        <div className="lw-chat">
          <div className="lw-chl">{data.chats.map((c) => (
            <button type="button" key={c.src + c.pid + c.id} className={'lw-chr' + (cur && c.src === cur.src && c.pid === cur.pid && c.id === cur.id ? ' on' : '') + (c.isNew ? ' is-new' : '')} onClick={() => open('chat=' + c.src + ':' + c.pid + ':' + c.id)}>
              <span className="t">{short(c.obj)}<span className="lab"> · {c.title}</span></span><span className="lab">{c.who ? c.who + ': ' : ''}{c.text}</span><span className="tm lab">{when(c.at)}</span></button>))}</div>
          <div className="lw-chv">{thread ? <React.Fragment>
            <div className="lw-chh"><b>{cur.obj}</b><span className="lab">{thread.members.filter((m) => m !== 'super').map((m) => SV.RU[m] || m).join(', ')}</span>
              {cur.src === 'project' ? <button type="button" className="cv-arrow-btn" onClick={() => open('proj=' + cur.pid)}>Проект<Ic n="ne" s={14} /></button> : <a className="cv-arrow-btn" href={keep('super-deals.html')}>Сделки<Ic n="ne" s={14} /></a>}</div>
            <div className="lw-msgs cv-scroll">{thread.msgs.length ? thread.msgs.map((m) => (
              <div key={m.id} className={'lw-msg' + (m.from === 'super' ? ' mine' : '')}><span className="lab">{SV.RU[m.from] || m.name || m.from} · {when(m.at)}</span>
                {m.t ? <span className="b">{m.t}</span> : null}{m.media && m.media.url ? <img src={m.media.url} alt={m.media.name || ''} /> : null}</div>)) : <div className="lab">Сообщений пока нет.</div>}</div>
            <div className="lw-send"><input value={txt} onChange={(e) => setTxt(e.target.value)} placeholder="Написать" aria-label="Сообщение" onKeyDown={(e) => { if (e.key === 'Enter') send(); }} /><button type="button" className="lw-btn-k" disabled={!txt.trim()} onClick={send}>Отправить</button></div>
          </React.Fragment> : <div className="empty">Переписки пока нет. Чаты появляются вместе со сделками и проектами.</div>}</div>
        </div>
      </Modal>);
  }

  /* ───── решения из «Мои задачи»: те же функции, что блоки прежнего кабинета (lk-common.jsx, content-plan-panel.jsx) ───── */
  const DEC_T = { rpl: 'Замены материалов', xp: 'Цены мастеров выше цены компании', spec: 'Правки клиента по спецификации', gate: 'Приёмка работ и фотоконтроль', pay: 'Оплата открывает этап', sub: 'Подрядчики', post: 'Посты на проверке' };
  function DecModal({ arg, me, close }) {
    const [kind, pid] = arg.split(':');
    const [why, setWhy] = React.useState({});
    const [rej, setRej] = React.useState(null);
    const [err, setErr] = React.useState('');
    const [f, setF] = React.useState({});
    const [big, setBig] = React.useState(null);
    const actor = { role: 'super', name: me.name };
    const p = pid ? P.get(pid) : null;
    const run = (fn) => { try { fn(); setErr(''); setRej(null); } catch (e) { setErr(e.message); } };
    const wait = (turn) => <div className="lab" style={{ marginTop: 8 }}>Сейчас ход за: {String(turn || '').toLowerCase()}. Вы можете решить за них — решение запишется от вашего имени.</div>;
    let cards = [];
    if (kind === 'rpl') cards = (P.views.pendingReplacements ? P.views.pendingReplacements('super') : []).filter((r) => !pid || r.pid === pid).map((r) => (
      <div key={r.id} className="card cv-dec">
        <div className="cv-chead"><div style={{ minWidth: 0 }}><div className="t">{r.prop.name}</div><div className="lab" style={{ marginTop: 3 }}>вместо «{r.orig.name}» · {r.room} · {r.reasonL}</div></div></div>
        <dl className="cv-meta"><div><dt>Было</dt><dd>{r.orig.supplier} · {rub(r.orig.price)} · {r.orig.lead} дн</dd></div><div><dt>Аналог</dt><dd>{r.prop.supplier} · {rub(r.prop.price)} · {r.prop.lead} дн</dd></div>
          <div><dt>Цена</dt><dd className={r.priceDelta > 0 ? 'hot' : ''}>{r.priceDelta === 0 ? 'без изменений' : (r.priceDelta > 0 ? '+' : '') + rub(r.priceDelta) + (r.priceDelta > 0 ? ' · потребует согласия клиента' : '')}</dd></div><div><dt>Срок</dt><dd>{r.leadDelta <= 0 ? 'не хуже' : '+' + r.leadDelta + ' дн'}</dd></div></dl>
        {r.turn && r.turn !== 'Руководитель' ? wait(r.turn) : null}
        <Decide k={r.id} rej={rej} setRej={setRej} why={why} setWhy={setWhy} onYes={() => run(() => P.decideReplacement(r.pid, r.id, 'approve', '', actor))} onNo={() => run(() => P.decideReplacement(r.pid, r.id, 'reject', (why[r.id] || '').trim(), actor))} noL="Отклонить" />
      </div>));
    if (kind === 'xp') { const EP = window.ExecPrices; const ERR = { rights: 'Решать может менеджер или руководитель', reason: 'Напишите причину отказа', state: 'Предложение уже решено или отозвано' };
      const key = (o) => o.pid + '~' + o.executorId + '~' + o.nodeId;
      const decide = (o, d) => { const r = EP.decideOffer(o.executorId, o.pid, o.nodeId, d, { by: me.name, role: 'super', reason: why[key(o)] || '' }); setErr(r.ok ? '' : ERR[r.error] || 'Не получилось'); if (r.ok) setRej(null); };
      cards = (EP && EP.projectOffers ? P.list().filter((x) => !pid || x.id === pid).flatMap((x) => EP.projectOffers(x)).filter((o) => o.level === 'manager') : []).map((o) => (
        <div key={key(o)} className="card cv-dec">
          <div className="cv-chead"><div style={{ minWidth: 0 }}><div className="t">{o.name}</div><div className="lab" style={{ marginTop: 3 }}>{[o.obj, o.executorName].filter(Boolean).join(' · ')}</div></div></div>
          <dl className="cv-meta"><div><dt>Предлагает</dt><dd className="hot">{rub(o.price)}</dd></div><div><dt>Сейчас</dt><dd>{rub(o.current)}</dd></div><div><dt>Цена компании</dt><dd>{o.norm != null ? rub(o.norm) : '—'}</dd></div></dl>
          <Decide k={key(o)} rej={rej} setRej={setRej} why={why} setWhy={setWhy} onYes={() => decide(o, 'approve')} onNo={() => decide(o, 'reject')} noL="Отказать" />
        </div>)); }
    if (kind === 'spec') cards = (P.views.specRevisions ? P.views.specRevisions('super') : []).filter((r) => !pid || r.pid === pid).map((r) => {
      const v = f[r.id] || { name: r.name, price: String(r.price || ''), note: '' };
      const set = (k, x) => setF(Object.assign({}, f, { [r.id]: Object.assign({}, v, { [k]: x }) }));
      return (
        <div key={r.id} className="card cv-dec">
          <div className="cv-chead"><div style={{ minWidth: 0 }}><div className="t">{r.name}</div><div className="lab" style={{ marginTop: 3 }}>{[r.obj, r.stageName, 'готовит ' + (r.owner === 'designer' ? 'дизайнер' : 'менеджер')].filter(Boolean).join(' · ')}</div></div></div>
          {r.comment ? <div className="lab" style={{ marginTop: 8 }}>Клиент: «{r.comment}»</div> : null}
          <div className="mg-form"><input value={v.name} onChange={(e) => set('name', e.target.value)} aria-label="Название" placeholder="Название" /><input value={v.price} onChange={(e) => set('price', e.target.value)} aria-label="Цена" placeholder="Цена, ₽" inputMode="numeric" />
            <textarea className="cv-ta" rows={2} value={v.note} onChange={(e) => set('note', e.target.value)} placeholder="Что изменили — клиент увидит" aria-label="Что изменили" /></div>
          <div className="cv-dbtn" style={{ display: 'flex', marginTop: 10 }}><button type="button" className="btn-k" disabled={!v.note.trim()} onClick={() => run(() => P.resubmitMaterial(r.pid, r.matId, { name: v.name, price: v.price, note: v.note }, actor))}>Отправить клиенту</button></div>
        </div>); });
    if (kind === 'gate') {
      cards = (P.views.pendingGates ? P.views.pendingGates('super', pid || undefined) : []).map((g) => { const k = g.pid + g.taskId + (g.pointId || ''); return (
        <div key={k} className="card cv-dec">
          <div className="cv-chead"><div style={{ minWidth: 0 }}><div className="t">{g.title}{g.pointName ? ' · ' + g.pointName : ''}</div><div className="lab" style={{ marginTop: 3 }}>{g.obj} · сдано {g.when} · очередь: {g.turnName}</div></div></div>
          {g.photos.length > 0 && <div className="sv-ph">{g.photos.slice(0, 12).map((ph, i) => <button key={i} type="button" onClick={() => setBig(ph)} aria-label={'Фото ' + (i + 1)}><img src={ph.url} alt={ph.name || ''} loading="lazy" /></button>)}</div>}
          {g.items.some((it) => it.type !== 'photo') && <ul className="sv-chk">{g.items.filter((it) => it.type !== 'photo').map((it) => { const v = g.values[it.id]; return <li key={it.id}>{it.label}: {v === true ? '✓' : Array.isArray(v) ? v.length + ' фото' : v || '—'}</li>; })}</ul>}
          {g.turn !== 'super' ? wait(g.turnName) : null}
          <Decide k={k} rej={rej} setRej={setRej} why={why} setWhy={setWhy} onYes={() => run(() => P.decideGate(g.pid, g.taskId, g.pointId, 'approve', '', actor))} onNo={() => run(() => P.decideGate(g.pid, g.taskId, g.pointId, 'return', (why[k] || '').trim(), actor))} noL="Вернуть" yesL="Принять" />
        </div>); });
      (P.views.gateLocks ? P.views.gateLocks() : []).filter((l) => !pid || l.pid === pid).forEach((l) => { const k = 'lock' + l.pid + l.taskId; cards.push(
        <div key={k} className="card cv-dec">
          <div className="cv-chead"><div style={{ minWidth: 0 }}><div className="t">Держит работы: {l.name}</div><div className="lab" style={{ marginTop: 3 }}>{l.obj} · держит {l.blocked} {plural(l.blocked, 'работу', 'работы', 'работ')} · {l.turn ? 'ждёт: ' + (P.GATE_ROLE_NAME || {})[l.turn] : 'ещё не сдана'}</div></div></div>
          <Decide k={k} rej={rej} setRej={setRej} why={why} setWhy={setWhy} onNo={() => run(() => P.overrideGate(l.pid, l.taskId, (why[k] || '').trim(), actor))} noL="Снять блокировку" noOnly />
        </div>); });
    }
    if (kind === 'pay') {
      cards = (P.views.payLocks ? P.views.payLocks(pid || undefined) : []).map((r) => { const k = r.pid + r.paymentId; return (
        <div key={k} className="card cv-dec">
          <div className="cv-chead"><div style={{ minWidth: 0 }}><div className="t">{r.title}</div><div className="lab" style={{ marginTop: 3 }}>{r.obj}</div></div></div>
          <dl className="cv-meta"><div><dt>Сумма</dt><dd className="hot">{rub(r.amount)}</dd></div><div><dt>Статус</dt><dd>{r.waitingFor === 'confirm' ? 'клиент оплатил, подтверждает бухгалтер' : 'ждём оплату клиента'}</dd></div>{r.blocked ? <div><dt>Держит</dt><dd>{r.blocked} {plural(r.blocked, 'работу', 'работы', 'работ')}</dd></div> : null}</dl>
          {r.blocked > 0 ? <Decide k={k} rej={rej} setRej={setRej} why={why} setWhy={setWhy} onNo={() => run(() => P.overridePayGate(r.pid, r.paymentId, (why[k] || '').trim(), actor))} noL="Снять блокировку" noOnly /> : null}
        </div>); });
      P.list().filter((x) => !pid || x.id === pid).forEach((x) => cards.push(
        <div key={'rule' + x.id} className="card cv-dec sv-rule">
          <div className="cv-chead"><div style={{ minWidth: 0 }}><div className="t">{pid ? 'Этап начинается после оплаты' : x.title}</div><div className="lab" style={{ marginTop: 3 }}>{x.payGate ? 'Включено: работа не стартует без подтверждённой оплаты' : 'Выключено: оплата старт не держит'}</div></div>
            <button type="button" className={x.payGate ? 'btn-l' : 'btn-k'} onClick={() => run(() => P.setPayGate(x.id, !x.payGate, actor))}>{x.payGate ? 'Выключить' : 'Включить'}</button></div>
        </div>));
    }
    if (kind === 'sub') cards = P.list().filter((x) => !pid || x.id === pid).flatMap((x) => {
      const ws = P.views.subWarnings ? P.views.subWarnings(x) : [];
      return (P.views.subOrders ? P.views.subOrders(x) : []).filter((o) => ws.some((w) => w.orderId === o.id)).map((o) => (
        <div key={x.id + o.id} className="card cv-dec">
          <div className="cv-chead"><div style={{ minWidth: 0 }}><div className="t">{o.subName} · {o.stageName}</div><div className="lab" style={{ marginTop: 3 }}>{x.title} · {o.statusLabel} · {o.worksN} {plural(o.worksN, 'работа', 'работы', 'работ')}{o.firstStart ? ' · монтаж с ' + dm(o.firstStart) : ''}</div></div></div>
          <ul className="sv-chk">{ws.filter((w) => w.orderId === o.id).map((w, i) => <li key={i} className={w.level === 'danger' ? 'hot' : ''}>{w.text}</li>)}</ul>
          <div className="lab" style={{ marginTop: 8 }}>Решает {o.owner === 'foreman' ? 'прораб' : 'менеджер'} — владелец заказа. Напишите в чат проекта или откройте его кабинет глазами.</div>
        </div>)); });
    if (kind === 'post') { const C = window.ContentPlan;
      cards = (C && C.reviewQueue ? C.reviewQueue() : []).map((x) => (
        <div key={x.id} className="card cv-dec">
          <div className="cv-chead"><div style={{ minWidth: 0 }}><div className="t">{x.title || 'Пост'}</div><div className="lab" style={{ marginTop: 3 }}>{[x.date ? dm(x.date) + (x.time ? ' ' + x.time : '') : '', (x.channels || []).join(', ')].filter(Boolean).join(' · ')}</div></div></div>
          {(x.mediaRefs || []).length > 0 && <div className="sv-ph">{x.mediaRefs.filter((m) => m.url).slice(0, 8).map((m) => <button key={m.url} type="button" onClick={() => setBig(m)} aria-label="Медиа поста">{m.kind === 'video' ? <video src={m.url} muted /> : <img src={m.url} alt="" loading="lazy" />}</button>)}</div>}
          {x.text ? <div className="sv-text">{x.text}</div> : null}
          {x.hashtags ? <div className="lab">{x.hashtags}</div> : null}
          <Decide k={x.id} rej={rej} setRej={setRej} why={why} setWhy={setWhy} onYes={() => run(() => C.decide(x.id, 'approve', '', actor))} onNo={() => run(() => C.decide(x.id, 'return', (why[x.id] || '').trim(), actor))} noL="Вернуть" yesL="Одобрить" />
        </div>)); }
    return (
      <Modal size="wide" title={DEC_T[kind] || 'Решение'} sub={p ? p.title : ''} onClose={close}>
        {err && <div className="att" role="alert" style={{ marginTop: 12 }}><Ic n="alert" s={16} /><span>{err}</span></div>}
        {cards.length ? <div className="cv-cards" style={{ marginTop: 18 }}>{cards}</div> : <div className="note-s" style={{ marginTop: 18 }}><Ic n="check" c="#2F5D3A" s={16} /><span>Решено — здесь больше ничего не ждёт вас.</span></div>}
        {big && <div className="sv-big" role="dialog" aria-label="Фото" onClick={() => setBig(null)}>{big.kind === 'video' ? <video src={big.url} controls autoPlay /> : <img src={big.url} alt={big.name || ''} />}</div>}
      </Modal>);
  }
  function Decide({ k, rej, setRej, why, setWhy, onYes, onNo, noL, yesL, noOnly }) {
    return (
      <div style={{ marginTop: 14 }}>
        {rej === k && <textarea className="cv-ta" rows={2} autoFocus placeholder="Почему — увидят в истории" aria-label="Причина" value={why[k] || ''} onChange={(e) => setWhy(Object.assign({}, why, { [k]: e.target.value }))} onKeyDown={(e) => { if (e.key === 'Escape') setRej(null); }} />}
        <div className="cv-dbtn" style={{ display: 'flex', gap: 8, marginTop: rej === k ? 10 : 0 }}>
          {rej === k ? <React.Fragment><button type="button" className="btn-l" onClick={() => setRej(null)}>Отмена</button><button type="button" className="btn-k" disabled={!(why[k] || '').trim()} onClick={onNo}>Отправить</button></React.Fragment>
            : <React.Fragment><button type="button" className={noOnly ? 'btn-k' : 'btn-l'} onClick={() => setRej(k)}>{noL}</button>{noOnly ? null : <button type="button" className="btn-k" onClick={onYes}>{yesL || 'Согласовать'}</button>}</React.Fragment>}
        </div>
      </div>);
  }

  /* ───── данные и маршрутизация окон ───── */
  function collect() {
    const t = t0();
    const sales = SV.sales();
    const d = { projects: SV.portfolio(t), alerts: SV.alerts(t), docs: SV.docsDebt(), inbox: SV.inbox(t), money: SV.money(t), sales, mgrs: SV.managers(sales.deals), chats: SV.chats(), marginMin: SV.marginMin() };
    d.facts = SV.facts(d);
    return d;
  }

  function App() {
    useStore();
    const me = useMe();
    const [modal, open, close] = useHash();
    if (!P || !SV) return <div className="cv-page"><div className="empty">Кабинет не загрузился. Обновите страницу.</div></div>;
    if (me.deny) return <W.Denied why={me.deny} />;
    const data = collect();
    const arg = (k) => (modal.indexOf(k + '=') === 0 ? modal.slice(k.length + 1) : null);
    const chatWin = modal === 'chat' || arg('chat') != null;
    const moneyWin = modal === 'money';
    const calWin = modal === 'calendar';
    return (
      <React.Fragment>
        {me.home ? <Home me={me} data={data} open={open} /> : <W.Denied title="Главная закрыта" why="Главная кабинета не входит в выданный доступ — открытые вам разделы работают по своим ссылкам." />}
        {moneyWin && !me.money && <W.Closed what="Раздел «Финансы»" onClose={close} />}
        {calWin && !me.cal && <W.Closed what="Раздел «Календарь работ»" onClose={close} />}
        {chatWin && !me.chat && <W.Closed what="Раздел «Чаты»" onClose={close} />}
        {(modal === 'projects' || arg('projects')) && me.home && <ProjectsModal key={modal} data={data} sel={arg('projects')} open={open} close={close} />}
        {arg('proj') && me.home && <ProjectModal key={arg('proj')} id={arg('proj')} data={data} open={open} close={close} />}
        {(modal === 'alerts' || arg('alerts')) && me.home && <AlertsModal key={modal} data={data} sel={arg('alerts')} open={open} close={close} />}
        {moneyWin && me.money && <MoneyModal data={data} open={open} close={close} />}
        {modal === 'sales' && me.home && <SalesModal data={data} close={close} />}
        {calWin && me.cal && <Modal size="wide" title="Календарь работ" sub="все бригады · только просмотр" onClose={close}>{window.SuperWeekZone ? <window.SuperWeekZone /> : <div className="lab">Календарь не загрузился.</div>}</Modal>}
        {chatWin && me.chat && <ChatModal data={data} sel={arg('chat')} open={open} close={close} me={me} />}
        {arg('dec') && me.home && <DecModal key={arg('dec')} arg={arg('dec')} me={me} close={close} />}
      </React.Fragment>);
  }

  window.SuperV2 = { App, collect, myInbox };
  if (!window.__SUPER_V2_NO_MOUNT) ReactDOM.createRoot(document.getElementById('root')).render(<App />);
})();
