/* client-v2-design.jsx — страница «Дизайн-проект» нового кабинета клиента (спека волны 2, раздел 4).
   Отдельная страница (client-v2-design.html, демо — client-v2-design-demo.html) с вкладками по hash:
   #brief «Бриф», #presentation «Презентация», #stages «Этапы», #spec «Спецификация», #schedule «График подбора и закупки», #talk «Обсуждение» (чат с дизайнером и комментарии к этапам).
   Данные — только views ProjectStore: clientDesign (этапы), clientDesignBrief (бриф), clientDesignSlides (слайды и PDF дизайнера),
   clientDesignSpec и clientDesignSchedule (позиции, кто покупает, сроки); отметки клиента по своим позициям — DpPipeline.specTrack; решение по этапу —
   те же DpPipeline.approve / requestChanges, что были в окне #design. Шапка, иконки и просмотрщик — из client-v2-app.jsx (window.CV).
   До запуска проекта страница работает от «субъекта» сделки (ClientDealViews.subject): этапы, бриф, спецификация, график и комментарии
   к этапам — те же, шапка с меню до запуска; чат проекта ещё не создан — «Обсуждение» ведёт в чат кабинета (#chat). */
(function () {
  'use strict';
  const { P, ACCESS, Ic, Header, Lightbox, ChatConv, useProjectData, useAccess, Denied, rub, cabHref } = window.CV;
  const TABS = [['brief', 'Бриф'], ['presentation', 'Презентация'], ['stages', 'Этапы'], ['spec', 'Спецификация'], ['schedule', 'График подбора и закупки'], ['talk', 'Обсуждение']];
  const RU_DATE = (s) => { const d = new Date(s); return isNaN(d) ? '' : d.toLocaleDateString('ru-RU', { day: 'numeric', month: 'long' }); };

  // вкладка в hash; без hash — по умолчанию то, что сейчас важнее (решение по этапу, потом слайды, потом бриф)
  function useTab(def) {
    const read = () => { const h = (window.location.hash || '').replace('#', ''); return TABS.some(([id]) => id === h) ? h : ''; };
    const [t, setT] = React.useState(read());
    React.useEffect(() => { const on = () => setT(read()); window.addEventListener('hashchange', on); return () => window.removeEventListener('hashchange', on); }, []);
    const go = (id) => { history.replaceState(null, '', window.location.pathname + window.location.search + '#' + id); setT(id); window.scrollTo(0, 0); };
    return [t || def, go];
  }
  // этапы ДП и анкеты живут вне ProjectStore — перерисовка по их событиям
  function useDpTick() {
    const [, tick] = React.useState(0);
    React.useEffect(() => {
      const ev = ['obitel:dp-pipeline', 'storage', 'obitel:sync', 'remontpro:forms-updated', 'remontpro:intake-updated'];
      const up = () => tick((x) => x + 1);
      ev.forEach((e) => window.addEventListener(e, up)); return () => ev.forEach((e) => window.removeEventListener(e, up));
    }, []);
    return () => tick((x) => x + 1);
  }

  /* ── «Бриф» ── */
  function Chips({ items }) { return <span className="dz-chips">{items.map((x, i) => <span key={i}>{x}</span>)}</span>; }
  function Facts({ rows }) {
    return <div className="dz-facts">{rows.map((r) => <div key={r.label} className="dz-fact"><span className="lab">{r.label}</span><span>{Array.isArray(r.value) ? <Chips items={r.value} /> : r.value}</span></div>)}</div>;
  }
  function Pics({ urls, label, onView }) {
    return <div className="cv-gal dz-gal">{urls.map((u, i) => <button type="button" key={u + i} onClick={() => onView(urls.map((x) => ({ url: x, work: label })), i)} aria-label="Открыть"><img src={u} alt="" loading="lazy" /></button>)}</div>;
  }
  function Block({ b, title, onView }) {
    if (b.t === 'facts') return <Facts rows={b.rows} />;
    if (b.t === 'text') return <div className="dz-text"><div className="lab">{b.label}</div><p>{b.text}</p></div>;
    if (b.t === 'photos') return <div className="dz-text"><div className="lab">{b.label}</div><Pics urls={b.urls} label={b.label} onView={onView} /></div>;
    if (b.t === 'styles') {
      const all = b.like.flatMap((c) => c.images.map((u) => ({ url: u, work: c.title })));
      let k = -1;
      return (
        <React.Fragment>
          {b.like.length > 0 && <div className="dz-styles">{b.like.map((c) => (
            <figure key={c.id} className="dz-style">
              <div className="imgs">{c.images.map((u) => { k += 1; const at = k; return <button type="button" key={u} onClick={() => onView(all, at)} aria-label={'Открыть: ' + c.title}><img src={u} alt="" loading="lazy" /></button>; })}</div>
              <figcaption><b>{c.title}</b>{c.sub && <span className="lab">{c.sub}</span>}</figcaption>
            </figure>))}</div>}
          {(b.unsure.length > 0 || b.dislike.length > 0) && <Facts rows={[b.unsure.length ? { label: 'Сомневаетесь', value: b.unsure } : null, b.dislike.length ? { label: 'Не нравится', value: b.dislike } : null].filter(Boolean)} />}
        </React.Fragment>);
    }
    if (b.t === 'rooms') {
      return <div className="dz-rooms">{b.rows.map((r) => (
        <div key={r.name} className="dz-room">
          <div className="hd"><b>{r.name}</b>{r.area && <span className="lab num">{r.area}</span>}{r.priorityL && <span className="pill-d">Приоритет: {r.priorityL}</span>}</div>
          {r.mood.length > 0 && <Chips items={r.mood} />}
          {r.note && <p>{r.note}</p>}
        </div>))}</div>;
    }
    if (b.t === 'gallery') {
      return (
        <React.Fragment>
          {b.images.length > 0 && <div className="cv-gal dz-gal">{b.images.map((x, i) => <button type="button" key={x.url + i} onClick={() => onView(b.images.map((y) => ({ url: y.url, work: y.title || title })), i)} aria-label="Открыть"><img src={x.url} alt="" loading="lazy" />{x.title && <span className="cap">{x.title}</span>}</button>)}</div>}
          {b.links.length > 0 && <ul className="dz-links">{b.links.map((x) => <li key={x.url}><a href={x.url} target="_blank" rel="noopener"><Ic n="ne" s={14} />{x.title}</a></li>)}</ul>}
        </React.Fragment>);
    }
    return null;
  }
  function BriefTab({ b, onView }) {
    if (!b) return <div className="empty">Бриф не найден. Если вы его заполняли, напишите менеджеру в чат.</div>;
    if (!b.ready) {
      return (
        <div className="card dz-wait">
          <div className="lab">Бриф на дизайн-проект</div>
          <div className="h2" style={{ marginTop: 8 }}>{b.statusL}</div>
          {b.status === 'in_progress' && <React.Fragment><div className="bar" style={{ maxWidth: 360 }}><i style={{ width: b.progress + '%' }} /></div><div className="lab" style={{ marginTop: 8 }}>Заполнено {b.progress}%</div></React.Fragment>}
          <p className="dz-p">Бриф — анкета о вашей семье, быте, стиле и бюджете. Дизайнер начинает концепцию, когда бриф согласован. Ответы появятся здесь после отправки.</p>
          {b.link && <a className="btn-k" href={b.link}>Открыть анкету</a>}
        </div>);
    }
    const jump = (id) => { const el = document.getElementById('dz-' + id); if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' }); };
    return (
      <div className="dz-brief">
        <nav className="dz-toc" aria-label="Разделы брифа">{b.groups.map((g) => <button type="button" key={g.id} onClick={() => jump(g.id)}>{g.title}</button>)}</nav>
        <div className="dz-doc">
          <section className="card dz-bhero">
            <div className="lab">{b.status === 'approved' ? 'Бриф согласован' : 'Бриф отправлен, дизайнер его проверяет'}{b.at ? ' · ' + RU_DATE(b.at) : ''}</div>
            <h2 className="dz-h">{b.client || 'Бриф на дизайн-проект'}</h2>
            {b.lede && <p className="cv-lede" style={{ marginTop: 10 }}>{b.lede}</p>}
            {b.key.length > 0 && <div className="dz-key">{b.key.map((k) => <div key={k.label}><span className="lab">{k.label}</span><b>{k.value}</b></div>)}</div>}
            {b.facts.length > 0 && <Facts rows={b.facts} />}
          </section>
          {b.groups.map((g) => (
            <section key={g.id} id={'dz-' + g.id} className="dz-sec">
              <h3 className="h2">{g.title}</h3>
              {g.narrative && <p className="dz-narr">{g.narrative}</p>}
              {g.blocks.map((x, i) => <Block key={g.id + i} b={x} title={g.title} onView={onView} />)}
            </section>))}
        </div>
      </div>);
  }

  /* ── «Презентация» ── */
  function Slide({ it, dark }) {
    if (it.kind === 'image') return <img src={it.url} alt={it.name || ''} />;
    return (
      <div className={'dz-file' + (dark ? ' dark' : '')}>
        <Ic n="doc" s={40} c={dark ? '#fff' : '#252528'} />
        <b>{it.name || (it.kind === 'pdf' ? 'PDF-файл' : 'Ссылка')}</b>
        <span className="lab">{it.kind === 'pdf' ? 'PDF-файл' : 'Ссылка'} · {it.title}</span>
        <a className={dark ? 'btn-l' : 'btn-k'} href={it.url} target="_blank" rel="noopener" onClick={(e) => e.stopPropagation()}>Открыть</a>
      </div>);
  }
  function SlideShow({ slides, i, go, onClose }) {
    const it = slides[i]; const n = slides.length;
    React.useEffect(() => {
      const k = (e) => { if (e.key === 'Escape') { e.stopPropagation(); onClose(); } else if (e.key === 'ArrowRight') go(1); else if (e.key === 'ArrowLeft') go(-1); };
      window.addEventListener('keydown', k, true); return () => window.removeEventListener('keydown', k, true);
    }, [i, n]);
    return (
      <div className="lk-big dz-show" role="dialog" aria-modal="true" aria-label="Презентация" onClick={onClose}>
        <figure onClick={(e) => e.stopPropagation()}>
          <Slide it={it} dark />
          <figcaption><span>{it.title} · {it.name}</span><span className="num">{i + 1} из {n}</span></figcaption>
        </figure>
        {n > 1 && <button type="button" className="nav prev" aria-label="Предыдущий слайд" onClick={(e) => { e.stopPropagation(); go(-1); }}><Ic n="left" c="#fff" s={22} /></button>}
        {n > 1 && <button type="button" className="nav next" aria-label="Следующий слайд" onClick={(e) => { e.stopPropagation(); go(1); }}><Ic n="right" c="#fff" s={22} /></button>}
        <button type="button" className="nav cls" aria-label="Закрыть" onClick={onClose}><Ic n="x" c="#fff" /></button>
      </div>);
  }
  function PresentationTab({ s }) {
    const slides = (s && s.slides) || [];
    const n = slides.length;
    const [i, setI] = React.useState(0);
    const [full, setFull] = React.useState(false);
    const go = (d) => setI((x) => (x + d + n) % n);
    React.useEffect(() => {
      if (full || n < 2) return undefined;
      const k = (e) => { if (/input|textarea/i.test((e.target && e.target.tagName) || '')) return; if (e.key === 'ArrowRight') go(1); else if (e.key === 'ArrowLeft') go(-1); };
      window.addEventListener('keydown', k); return () => window.removeEventListener('keydown', k);
    }, [full, n]);
    const cur = slides[Math.min(i, n - 1)];
    const groups = [];
    slides.forEach((x, k) => { const g = groups[groups.length - 1]; if (g && g.stage === x.stage) g.items.push([x, k]); else groups.push({ stage: x.stage, short: x.short, items: [[x, k]] }); });
    return (
      <div className="dz-pres">
        {s && s.presentation && (
          <div className="card dz-pdf">
            <span className="ic ic-s"><Ic n="doc" /></span>
            <div style={{ minWidth: 0 }}><b>PDF-презентация от дизайнера</b><div className="lab">{s.presentation.name} · {s.presentation.date}</div></div>
            <div className="act"><a className="btn-l" href={s.presentation.url} target="_blank" rel="noopener">Смотреть PDF</a><a className="btn-k" href={s.presentation.url} download={s.presentation.name}>Скачать</a></div>
          </div>)}
        {!n
          ? <div className="empty">Слайды появятся, когда вы утвердите первый этап — концепцию. Дальше сюда по порядку добавятся планировка, визуализации и чертежи.</div>
          : <React.Fragment>
            <div className="dz-stage">
              <button type="button" className="dz-view" onClick={() => setFull(true)} aria-label="Смотреть на весь экран"><Slide it={cur} /></button>
              {n > 1 && <button type="button" className="ic ic-l prev" aria-label="Предыдущий слайд" onClick={() => go(-1)}><Ic n="left" /></button>}
              {n > 1 && <button type="button" className="ic ic-l next" aria-label="Следующий слайд" onClick={() => go(1)}><Ic n="right" /></button>}
            </div>
            <div className="dz-cap">
              <span style={{ minWidth: 0 }}><b>{cur.title}</b><span className="lab"> · {cur.name}{cur.version > 1 ? ' · версия ' + cur.version : ''}</span></span>
              <span style={{ display: 'flex', alignItems: 'center', gap: 12 }}><span className="num lab">{Math.min(i, n - 1) + 1} из {n}</span><button type="button" className="btn-l" onClick={() => setFull(true)}>На весь экран</button></span>
            </div>
            <div className="dz-thumbs">{groups.map((g) => (
              <div key={g.stage} className="grp">
                <div className="lab">{g.short}</div>
                <div className="row">{g.items.map(([x, k]) => <button type="button" key={x.id || x.url} className={k === i ? 'on' : ''} onClick={() => setI(k)} aria-label={'Слайд ' + (k + 1)}>{x.kind === 'image' ? <img src={x.url} alt="" loading="lazy" /> : <span className="f"><Ic n="doc" s={18} /><span>{x.kind === 'pdf' ? 'PDF' : 'Ссылка'}</span></span>}</button>)}</div>
              </div>))}</div>
          </React.Fragment>}
        {full && n > 0 && <SlideShow slides={slides} i={Math.min(i, n - 1)} go={go} onClose={() => setFull(false)} />}
      </div>);
  }

  /* ── «Этапы»: этап на согласовании — сверху и раскрыт (файлы, заметка, спецификация, решение), остальные — по порядку ── */
  function DesignStage({ project, s, open: open0, onView, onChange }) {
    const [open, setOpen] = React.useState(open0);
    const [fix, setFix] = React.useState(false);
    const [txt, setTxt] = React.useState('');
    const [err, setErr] = React.useState('');
    const who = (project.client && project.client.name) || 'Клиент';
    const D = window.DpPipeline;
    const run = (ok) => {
      if (!ok && !txt.trim()) { setErr('Напишите, что исправить'); return; }
      try { ok ? D.approve(project.dealId, s.id, who) : D.requestChanges(project.dealId, s.id, txt.trim(), who); setErr(''); setFix(false); setTxt(''); onChange(); } catch (e) { setErr(e.message); }
    };
    const pics = s.items.filter((it) => it.kind === 'image');
    const files = s.items.filter((it) => it.kind !== 'image');
    const tone = s.status === 'approved' || s.status === 'done' ? 'pill-g' : s.decide ? 'pill-k' : 'pill-d';
    return (
      <div className="card cv-dstage">
        <button type="button" className="hd" onClick={() => setOpen(!open)} aria-expanded={open}>
          <span style={{ minWidth: 0 }}><span style={{ fontSize: 16 }}>{s.title}</span><span className="lab" style={{ display: 'block', marginTop: 4 }}>{s.result}</span></span>
          <span className={tone}>{s.decide ? 'ждёт вашего решения' : s.statusL}</span>
        </button>
        {open && <div style={{ padding: '0 22px 22px' }}>
          {s.note && <div style={{ fontSize: 14, color: 'var(--text-2)', whiteSpace: 'pre-wrap' }}>{s.note}</div>}
          {pics.length > 0 && <div className="cv-gal">{pics.map((it, i) => <button type="button" key={it.url} onClick={() => onView(pics.map((x) => ({ ...x, work: s.short })), i)} aria-label="Открыть"><img src={it.url} alt="" loading="lazy" />{it.version > 1 && <span className="tag pill-k">Версия {it.version}</span>}</button>)}</div>}
          {files.length > 0 && <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginTop: 14 }}>{files.map((it) => <a key={it.url} className="btn-l" href={it.url} target="_blank" rel="noopener"><Ic n="clip" s={14} />{it.name || (it.kind === 'pdf' ? 'PDF' : 'Ссылка')}</a>)}</div>}
          {s.rows.length > 0 && <div style={{ marginTop: 14 }}>{s.rows.map((r, i) => <div key={i} className="cv-tot"><span>{r.name}<span className="lab"> · {[r.room, r.article, r.qty + ' ' + r.unit].filter(Boolean).join(' · ')}</span></span><span className="num">{rub(r.sum)}</span></div>)}</div>}
          {s.comments.length > 0 && <div style={{ marginTop: 14 }}>{s.comments.map((c, i) => <div key={i} className="lab" style={{ marginTop: 4 }}>{c.mine ? 'вы' : 'дизайнер'} · {c.date}{c.changes ? ' · правки' : ''}: {c.text}</div>)}</div>}
          {s.decide && <div style={{ marginTop: 16 }}>
            {fix && <textarea className="cv-ta" rows={3} placeholder="Что поправить в этом этапе?" value={txt} onChange={(e) => setTxt(e.target.value)} autoFocus />}
            {err && <div className="att" style={{ marginTop: 8 }}><Ic n="alert" s={16} /><span>{err}</span></div>}
            <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end', marginTop: 10, flexWrap: 'wrap' }}>
              {fix
                ? <React.Fragment><button type="button" className="btn-l" onClick={() => { setFix(false); setErr(''); }}>Отмена</button><button type="button" className="btn-k" style={{ height: 40, fontSize: 13 }} onClick={() => run(false)}>Отправить правки</button></React.Fragment>
                : <React.Fragment><button type="button" className="btn-l" onClick={() => setFix(true)}>Нужны правки</button><button type="button" className="btn-g" style={{ height: 40, fontSize: 13 }} onClick={() => run(true)}>Утвердить этап</button></React.Fragment>}
            </div>
          </div>}
        </div>}
      </div>);
  }
  function StagesTab({ project, d, onView, onChange }) {
    if (!d || !d.started) return <div className="empty">Дизайнер начнёт работу после согласования брифа. Этапы и файлы появятся здесь.</div>;
    const stages = d.stages.filter((s) => s.decide).concat(d.stages.filter((s) => !s.decide));
    return <div className="dz-stages">{stages.map((s) => <DesignStage key={s.id + s.status} project={project} s={s} open={s.decide || (s.id === 'spec' && s.rows.length > 0 && !d.waiting)} onView={onView} onChange={onChange} />)}</div>;
  }

  /* ── «Спецификация»: по помещениям, внутри по категориям; кто покупает; итоги отдельно ── */
  function SpecTab({ sp, onView, go }) {
    if (!sp || !sp.n) return <div className="empty">Спецификацию материалов и мебели дизайнер составит после чертежей. Позиции с фото, артикулами и ценами появятся здесь.</div>;
    return (
      <div className="dz-spec">
        <div className="dz-tots">
          <div className="card"><div className="lab">Покупаем мы</div><div className="stat num">{rub(sp.totals.us)}</div><div className="lab">Входит в смету, закупку и доставки</div></div>
          <div className="card"><div className="lab">Покупаете вы</div><div className="stat num">{rub(sp.totals.client)}</div><div className="lab">В смету не входит, сроки — во вкладке <button type="button" className="dz-lnk" onClick={() => go('schedule')}>«График»</button></div></div>
          <div className="card"><div className="lab">Спецификация</div><div className="h2" style={{ marginTop: 10 }}>{sp.approved ? 'Утверждена' : sp.status === 'review' ? 'Ждёт вашего решения' : 'В работе у дизайнера'}</div>{!sp.approved && sp.status === 'review' && <button type="button" className="btn-l" style={{ marginTop: 12 }} onClick={() => go('stages')}>Решить в «Этапах»</button>}</div>
        </div>
        {sp.rooms.map((rm) => (
          <section key={rm.room} className="dz-room-s">
            <div className="dz-room-h"><h3 className="h2">{rm.room}</h3><span className="num lab">{rub(rm.sum)}</span></div>
            {rm.cats.map((c) => (
              <div key={c.catL} className="dz-cat">
                <div className="lab">{c.catL}</div>
                {c.rows.map((r) => (
                  <div key={r.id} className="dz-srow">
                    {r.photo ? <button type="button" className="dz-ph" onClick={() => onView([{ url: r.photo, work: r.name }], 0)} aria-label={'Фото: ' + r.name}><img src={r.photo} alt="" loading="lazy" /></button> : <span className="dz-ph none"><Ic n="box" s={18} c="#6E6E73" /></span>}
                    <div className="nm"><div>{r.name}</div><div className="lab">{[r.article, r.qty + ' ' + r.unit + ' × ' + rub(r.price)].filter(Boolean).join(' · ')}{r.link && <React.Fragment> · <a href={r.link} target="_blank" rel="noopener" style={{ textDecoration: 'underline' }}>ссылка</a></React.Fragment>}</div></div>
                    <span className={r.buyer === 'client' ? 'pill-k' : 'pill-g'}>{r.buyerL}</span>
                    <span className="num sm">{rub(r.sum)}</span>
                  </div>))}
              </div>))}
          </section>))}
      </div>);
  }

  /* ── «График подбора и закупки»: решить и заказать до / доставка к; свои позиции клиент отмечает сам ── */
  const NEXT_MARK = { approved: ['ordered', 'Заказал'], ordered: ['delivered', 'Привезли'], delivered: ['installed', 'Смонтировано'] };
  function ScheduleTab({ g, project, onChange }) {
    const [who, setWho] = React.useState('all');
    const [err, setErr] = React.useState('');
    if (!g || !g.rows.length) return <div className="empty">График появится вместе со спецификацией: для каждой позиции — когда решить и заказать и к какой дате её ждём на объекте.</div>;
    const rows = g.rows.filter((r) => who === 'all' || r.buyer === who);
    const mark = (r, st) => { try { window.DpPipeline.specTrack(project.dealId, r.id, st, (project.client && project.client.name) || 'Клиент'); setErr(''); onChange(); } catch (e) { setErr(e.message); } };
    const n = (b) => g.rows.filter((r) => b === 'all' || r.buyer === b).length;
    return (
      <div className="dz-sch">
        <p className="dz-p" style={{ marginTop: 0 }}>«Доставка к» — старт этапа ремонта, к которому позиция нужна на объекте. «Решить и заказать до» — эта дата минус срок поставки. Позиции, которые покупаете вы, отмечайте сами: так прораб знает, что их ждать.</p>
        <div className="cv-ctabs" style={{ marginTop: 0 }}>{[['all', 'Все'], ['client', 'Покупаете вы'], ['us', 'Покупаем мы']].map(([id, l]) => <button type="button" key={id} className={who === id ? 'on' : ''} onClick={() => setWho(id)}>{l}<span>{n(id)}</span></button>)}</div>
        {err && <div className="att" style={{ marginTop: 12 }}><Ic n="alert" s={16} /><span>{err}</span></div>}
        <div className="dz-tbl" role="table">
          <div className="dz-tr hd" role="row"><span>Позиция</span><span>Кто покупает</span><span>К этапу</span><span>Решить и заказать до</span><span>Доставка к</span><span>Статус</span></div>
          {rows.map((r) => {
            const nx = r.canMark ? NEXT_MARK[r.status] : null;
            return (
              <div key={r.id} className={'dz-tr' + (r.overdue ? ' is-late' : '')} role="row">
                <span className="nm"><span>{r.name}</span><span className="lab">{r.room} · {r.catL}</span></span>
                <span><span className={r.buyer === 'client' ? 'pill-k' : 'pill-g'}>{r.buyerL}</span></span>
                <span className="lab-m" data-l="К этапу">{r.stageName || '—'}</span>
                <span className={'num' + (r.overdue === 'decide' ? ' late' : '')} data-l="Решить и заказать до">{r.decideByR || '—'}{r.overdue === 'decide' ? ' · просрочено' : ''}</span>
                <span className={'num' + (r.overdue === 'deliver' ? ' late' : '')} data-l="Доставка к">{r.deliverByR || '—'}{r.overdue === 'deliver' ? ' · просрочено' : ''}{r.lead ? <span className="lab"> · поставка {r.lead} дн</span> : null}</span>
                <span className="st">{r.statusL.charAt(0).toUpperCase() + r.statusL.slice(1)}{nx && <button type="button" className="btn-l" onClick={() => mark(r, nx[0])}>{nx[1]}</button>}</span>
              </div>);
          })}
        </div>
      </div>);
  }

  /* ── «Обсуждение»: чат дизайн-проекта (клиент, дизайнер, менеджер) и комментарии к этапам — те же, что видит дизайнер ── */
  function TalkTab({ project, d, onChange }) {
    const thread = project.prestart ? null : P.views.clientChat(project).find((t) => t.ref === 'design') || null;
    const started = d && d.started ? d.stages.filter((s) => s.status !== 'pending') : [];
    const [sid, setSid] = React.useState(() => { const x = started.find((s) => s.status === 'review') || started[started.length - 1]; return x ? x.id : ''; });
    const [txt, setTxt] = React.useState('');
    const [err, setErr] = React.useState('');
    const cur = started.find((s) => s.id === sid) || null;
    const add = () => {
      const t = txt.trim(); if (!t || !cur) return;
      try { window.DpPipeline.addComment(project.dealId, cur.id, { by: (project.client && project.client.name) || 'Клиент', role: 'client', text: t }); setTxt(''); setErr(''); onChange(); } catch (e) { setErr(e.message); }
    };
    return (
      <div className="dz-talk">
        <section className="card dz-chat">
          <div className="dz-talk-h"><h3 className="h2">Чат с дизайнером</h3><span className="lab">В чате дизайнер и менеджер проекта</span></div>
          {project.prestart
            ? <div className="empty" style={{ marginTop: 12 }}>Пока проект не запущен, переписка с командой — в общем чате кабинета. <a className="btn-k" style={{ marginTop: 14 }} href={cabHref('chat')}>Открыть чат</a></div>
            : <ChatConv project={project} active={thread} target={() => (thread ? thread.id : P.chatEnsureDesignThread(project.id))} onSent={onChange} empty="Напишите дизайнеру — чат появится с первым сообщением" />}
        </section>
        <section className="card dz-notes">
          <div className="dz-talk-h"><h3 className="h2">Комментарии к этапам</h3><span className="lab">Дизайнер видит их в карточке этапа</span></div>
          {!started.length ? <div className="empty" style={{ marginTop: 12 }}>Комментировать можно, когда дизайнер начнёт первый этап.</div> : (
            <React.Fragment>
              <div className="cv-ctabs" style={{ marginTop: 12 }}>{started.map((s) => <button type="button" key={s.id} className={sid === s.id ? 'on' : ''} onClick={() => setSid(s.id)}>{s.short}{s.comments.length ? <span>{s.comments.length}</span> : null}</button>)}</div>
              <div className="dz-cmts">
                {cur && !cur.comments.length && <div className="lab" style={{ padding: '12px 0' }}>К этапу «{cur.title}» комментариев пока нет.</div>}
                {cur && cur.comments.map((c, i) => <div key={i} className={'dz-cmt' + (c.mine ? ' mine' : '')}><div className="lab">{c.who} · {c.date}{c.changes ? ' · запрос правок' : ''}</div><div>{c.text}</div></div>)}
              </div>
              {err && <div className="att" style={{ marginTop: 8 }}><Ic n="alert" s={16} /><span>{err}</span></div>}
              <div className="cv-compose">
                <textarea className="cv-ta" rows={2} placeholder={cur ? 'Комментарий к этапу «' + cur.short + '»' : ''} value={txt} onChange={(e) => setTxt(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); add(); } }} />
                <button type="button" className="ic ic-k" aria-label="Добавить комментарий" onClick={add}><Ic n="arrow" c="#fff" /></button>
              </div>
            </React.Fragment>)}
        </section>
      </div>);
  }

  function Page() {
    const launched = useProjectData();
    const refresh = useDpTick();
    const [view, setView] = React.useState(null);
    // проекта ещё нет — страница работает от «субъекта» сделки (команда, этапы, бриф, спецификация — по сделке)
    const dv = window.ClientDealViews;
    const project = launched || (ACCESS.dealId && dv && dv.subject ? dv.subject(ACCESS.dealId) : null);
    const d = project && project.requiresDesign ? P.views.clientDesign(project) : null;
    const b = project && project.requiresDesign ? P.views.clientDesignBrief(project) : null;
    const s = project && project.requiresDesign ? P.views.clientDesignSlides(project) : null;
    const sp = project && project.requiresDesign ? P.views.clientDesignSpec(project) : null;
    const g = project && project.requiresDesign ? P.views.clientDesignSchedule(project) : null;
    const [tab, setTab] = useTab(d && d.waiting ? 'stages' : s && s.slides.length ? 'presentation' : 'brief');
    // доступ по ссылке — как у кабинета (client-v2-app.jsx)
    const why = useAccess('design', project);
    if (window.__CLIENT_CABINET_LEAVING) return null; // client-cabinet.js уже уводит (включён старый, проекта ещё нет)
    if (why) return <Denied why={why} />;
    if (!P) return <div className="cv-page"><div className="empty">Кабинет не загрузился. Обновите страницу.</div></div>;
    if (!project || !project.requiresDesign) {
      return (
        <div className="cv-page">
          <header className="cv-head"><a className="cv-logo" href={cabHref()}>ОБИТЕЛЬ</a></header>
          <div className="empty" style={{ marginTop: 48 }}>{project ? 'В вашем проекте нет дизайн-проекта.' : 'Проект ещё не запущен.'} <a href={cabHref()} style={{ textDecoration: 'underline' }}>Вернуться в кабинет</a></div>
        </div>);
    }
    const h = project.prestart ? dv.home(project.dealId) : P.views.clientHome(project);
    const onView = (items, at) => setView({ items, at });
    return (
      <div className="cv-page">
        <Header name={h.clientName} open={(id) => { window.location.href = cabHref(id); }} design active="design" project={project} menu={project.prestart ? dv.menu(project.dealId) : undefined} />
        <main id="main" tabIndex={-1} className="cv-main">
        <section className="dz-head">
          <div><div className="lab">{d && d.started ? 'Утверждено ' + d.finished + ' из ' + d.total + ' · ' + d.label : 'Этапы начнутся после согласования брифа'}</div><h1 className="cv-hello">Дизайн-проект</h1></div>
          <div className="cv-ctabs dz-tabs" role="tablist" aria-label="Разделы дизайн-проекта">
            {TABS.map(([id, l]) => <button type="button" role="tab" key={id} aria-selected={tab === id} className={tab === id ? 'on' : ''} onClick={() => setTab(id)}>{l}{id === 'stages' && d && d.waiting ? <span>Ждёт вас · {d.waiting}</span> : null}{id === 'schedule' && g && g.overdue ? <span>Просрочено · {g.overdue}</span> : null}</button>)}
          </div>
        </section>
        <div className="dz-body">
          {tab === 'brief' && <BriefTab b={b} onView={onView} />}
          {tab === 'presentation' && <PresentationTab s={s} />}
          {tab === 'stages' && <StagesTab project={project} d={d} onView={onView} onChange={refresh} />}
          {tab === 'spec' && <SpecTab sp={sp} onView={onView} go={setTab} />}
          {tab === 'schedule' && <ScheduleTab g={g} project={project} onChange={refresh} />}
          {tab === 'talk' && <TalkTab project={project} d={d} onChange={refresh} />}
        </div>
        </main>
        {view && <Lightbox items={view.items} at={view.at} onClose={() => setView(null)} />}
      </div>);
  }

  ReactDOM.createRoot(document.getElementById('root')).render(<Page />);
})();
