/* client-intake-forms.jsx — две анкеты клиента в ЛК. Autosave → SiteIntake → калькулятор. */
(function () {
  /* новый кабинет (client-v2*.html) даёт свой интерфейс на классах client-v2.css — старая дизайн-система там не грузится */
  const { Badge, Button } = window.ClientFormsUI || window.DesignSystem_9e0a09;
  const Ic = (window.ClientFormsUI && window.ClientFormsUI.Ic) || window.Ic;
  const { PageHead, LKSection } = window;
  const { useState, useEffect, useCallback, useRef } = React;
  const S = window.ClientIntakeSync;

  const HOUSING = [['apartment', 'Квартира'], ['house', 'Дом'], ['commercial', 'Коммерция']];
  const OBJ_TYPES = [['new_build', 'Новостройка'], ['secondary', 'Вторичка']];
  const FINISH = [['without', 'Без отделки'], ['partial', 'Черновая'], ['renovation', 'С ремонтом']];
  const REPAIR = [['cosmetic', 'Косметический'], ['capital', 'Капитальный']];
  const TIERS = [['economy', 'Эконом'], ['mid', 'Комфорт'], ['premium', 'Премиум']];
  const ACCESS_TAGS = ['Пропуск на КПП', 'Грузовой лифт', 'Парковка для грузовика', 'Режим тишины', 'Домофон / консьерж', 'Строительные лестницы'];
  const MATERIAL_OPTS = ['Плитка крупный формат', 'Ламинат 33 класс', 'Паркет', 'Микроцемент', 'Керамогранит'];
  const PLUMBING_OPTS = ['Скрытые смесители', 'Инсталляция', 'Душ без поддона', 'Тёплый пол в СУ'];
  const ELECTRICAL_OPTS = ['Умный дом', 'Больше розеток', 'Сценарное освещение', 'Тёплый пол'];
  const LIGHTING_OPTS = ['Тёплый свет', 'Нейтральный', 'Холодный', 'Люстра', 'Треки'];
  const COLOR_OPTS = ['Тёплые нейтрали', 'Светлые тона', 'Тёмные акценты', 'Натуральное дерево', 'Монохром'];
  const BUDGET_OPTS = ['до 800 тыс', '800 тыс – 1.2 млн', '1.2–1.6 млн', '1.6–2.2 млн', 'от 2.2 млн'];
  const FURNITURE = [['built_in', 'Встроенная'], ['own', 'Своя'], ['mixed', 'Смешанная']];

  const STYLE_CARDS = [
    { id: 'scandi', title: 'Скандинавский', sub: 'светлый · дерево', bg: 'linear-gradient(155deg, #ebe3d8 0%, #c9b8a4 55%, #8a7560 100%)' },
    { id: 'minimal', title: 'Минимализм', sub: 'монохром · чистые линии', bg: 'linear-gradient(155deg, #ece8e2 0%, #c8bfb4 55%, #7d7268 100%)' },
    { id: 'loft', title: 'Лофт', sub: 'бетон · кирпич', bg: 'linear-gradient(155deg, #e2e2e2 0%, #b8b8b8 55%, #707070 100%)' },
    { id: 'classic', title: 'Современная классика', sub: 'мoulding · тёплый свет', bg: 'linear-gradient(155deg, #f0ebe4 0%, #d4c4b0 55%, #9a8570 100%)' },
    { id: 'japandi', title: 'Япанди', sub: 'натуральные фактуры', bg: 'linear-gradient(155deg, #e8eef2 0%, #b5c8d4 55%, #6a8494 100%)' },
    { id: 'midcentury', title: 'Mid-century', sub: 'дерево · графика', bg: 'linear-gradient(155deg, #f2ece4 0%, #d9cfc0 55%, #9a8878 100%)' },
    { id: 'industrial', title: 'Индustrial soft', sub: 'металл · матовые поверхности', bg: 'linear-gradient(155deg, #dfe8ec 0%, #a8bcc8 55%, #5a7484 100%)' },
    { id: 'eclectic', title: 'Эклектика', sub: 'микс фактур и цвета', bg: 'linear-gradient(155deg, #e8e4ef 0%, #c4bdd4 55%, #7a708a 100%)' },
  ];

  const MEASURE_STEPS = ['Контакты'];

  function FormInstanceList({ dealId }) {
    const FI = window.FormInstances;
    const [list, setList] = useState(() => (FI && FI.loadInstances(dealId)) || []);
    useEffect(() => {
      const reload = () => { if (FI) setList(FI.loadInstances(dealId)); };
      window.addEventListener('remontpro:forms-updated', reload);
      return () => window.removeEventListener('remontpro:forms-updated', reload);
    }, [dealId]);
    if (!FI || !list.length) return null;
    return (
      <section className="ci-form-block">
        <h2 className="ci-form-title">Формы проекта</h2>
        <p className="ci-form-lede">Ссылки создаются автоматически по типу объекта. Статус синхронизируется с CRM.</p>
        <ul className="ci-form-list">
          {list.map((inst) => (
            <li key={inst.id} className="ci-form-list-row">
              <span className="ci-form-list-title">{inst.title}</span>
              <Badge tone={inst.accessState === 'submitted' ? 'success' : inst.accessState === 'pending' ? 'neutral' : 'info'} dot>
                {FI.stateLabel(inst.accessState)}
              </Badge>
              <span className="ci-progress t-num">{inst.progressPct || 0}%</span>
            </li>
          ))}
        </ul>
      </section>
    );
  }

  function fmtTimer(ms) {
    const h = Math.floor(ms / 3600000);
    const m = Math.floor((ms % 3600000) / 60000);
    return h + ' ч ' + m + ' мин';
  }

  function Chip({ on, label, onClick, disabled }) {
    return (
      <button type="button" className={'ci-chip' + (on ? ' on' : '')} onClick={onClick} disabled={disabled}>{label}</button>
    );
  }

  function MultiChip({ options, value, onChange, disabled, custom, onCustom, customPlaceholder }) {
    const v = value || [];
    const toggle = (opt) => {
      if (disabled) return;
      onChange(v.includes(opt) ? v.filter((x) => x !== opt) : [...v, opt]);
    };
    return (
      <div className="ci-chips">
        {options.map((o) => <Chip key={o} label={o} on={v.includes(o)} onClick={() => toggle(o)} disabled={disabled} />)}
        {onCustom != null && (
          <input className="ci-input ci-input-inline" placeholder={customPlaceholder || 'Своё…'} value={custom || ''} onChange={(e) => onCustom(e.target.value)} disabled={disabled} />
        )}
      </div>
    );
  }

  function Field({ label, children }) {
    return (
      <label className="ci-field">
        <span className="ci-label">{label}</span>
        {children}
      </label>
    );
  }

  function FormTimer({ meta }) {
    const [left, setLeft] = useState(S.remainingMs(meta));
    useEffect(() => {
      if (!meta || !meta.openedAt) return undefined;
      const t = setInterval(() => setLeft(S.remainingMs(meta)), 30000);
      return () => clearInterval(t);
    }, [meta]);
    if (!meta || !meta.openedAt) return null;
    const expired = S.isExpired(meta);
    return (
      <div className={'ci-timer' + (expired ? ' expired' : '')}>
        <Ic n="clock" s={14} />
        {expired ? 'Время заполнения истекло · только просмотр' : <>Осталось <span className="t-num">{fmtTimer(left)}</span></>}
      </div>
    );
  }

  /* readOnly — сотрудник смотрит кабинет глазами клиента: поля отключены, ничего не сохраняется */
  function MeasurePrepForm({ dealId, intake, onSaved, readOnly }) {
    const meta = (intake.clientForms || {}).measurePrep || {};
    const locked = !!S.isExpired(meta) || !!readOnly;
    const [step, setStep] = useState(0);
    const [form, setForm] = useState(() => intake.measurePrepDraft || S.defaultMeasurePrep(intake));
    const saveRef = useRef(S.createAutosave(dealId, S.applyMeasurePrep, () => form));

    const patch = useCallback((p) => {
      setForm((prev) => {
        const next = { ...prev, ...p };
        if (!locked) saveRef.current(next);
        return next;
      });
    }, [locked]);

    useEffect(() => {
      const h = () => onSaved && onSaved();
      window.addEventListener('remontpro:intake-updated', h);
      return () => window.removeEventListener('remontpro:intake-updated', h);
    }, [onSaved]);

    useEffect(() => {
      if (!locked) {
        const base = S.loadIntake(dealId);
        S.applyMeasurePrep(base, form);
        S.saveIntake(dealId, S.applyMeasurePrep(base, form));
      }
    }, []);

    const toggleAccess = (tag) => {
      const tags = form.accessTags || [];
      patch({ accessTags: tags.includes(tag) ? tags.filter((t) => t !== tag) : [...tags, tag] });
    };

    return (
      <section className="ci-form-block">
        <div className="ci-form-head">
          <div>
            <h2 className="ci-form-title">Клиентская анкета · контакты</h2>
            <p className="ci-form-lede">Только контакты для связи. Адрес и тип объекта заполняет менеджер в карточке объекта.</p>
          </div>
          <div className="ci-form-meta">
            <Badge tone={meta.status === 'submitted' ? 'success' : meta.status === 'expired' ? 'warning' : 'info'} dot>
              {meta.status === 'submitted' ? 'Отправлена' : meta.status === 'expired' ? 'Истекла' : 'В процессе'}
            </Badge>
            <span className="ci-progress t-num">{meta.progress || 0}%</span>
          </div>
        </div>
        <FormTimer meta={meta} />

        <div className="ci-steps" role="tablist">
          {MEASURE_STEPS.map((l, i) => (
            <button key={l} type="button" role="tab" aria-selected={step === i} className={'ci-step' + (step === i ? ' on' : '')} onClick={() => setStep(i)}>{l}</button>
          ))}
        </div>

        <div className="ci-step-body">
          {step === 0 && (
            <>
              <Field label="ФИО"><input className="ci-input" value={form.clientName} disabled={locked} onChange={(e) => patch({ clientName: e.target.value })} /></Field>
              <Field label="Телефон"><input className="ci-input" type="tel" value={form.clientPhone} disabled={locked} onChange={(e) => patch({ clientPhone: e.target.value })} /></Field>
              <Field label="Желаемая дата замера"><input className="ci-input" type="date" value={form.preferredMeasureDate} disabled={locked} onChange={(e) => patch({ preferredMeasureDate: e.target.value })} /></Field>
              <Field label="Контакт на объекте (если не вы)"><input className="ci-input" value={form.onSiteContact} disabled={locked} onChange={(e) => patch({ onSiteContact: e.target.value })} placeholder="Имя" /></Field>
              <Field label="Телефон контакта на объекте"><input className="ci-input" type="tel" value={form.onSitePhone} disabled={locked} onChange={(e) => patch({ onSitePhone: e.target.value })} /></Field>
            </>
          )}
        </div>

        <div className="ci-step-nav">
          <span className="ci-muted">Данные сохраняются автоматически</span>
        </div>
      </section>
    );
  }

  function StyleCard({ card, vote, onVote, disabled }) {
    const Mock = window.DpBriefTemplateMock;
    const images = Mock && Mock.cardImages ? Mock.cardImages(card) : (Array.isArray(card.images) ? card.images.filter(Boolean) : []);
    const cover = Mock && Mock.cardCoverUrl ? Mock.cardCoverUrl(card) : images[0];
    const extra = Math.max(0, images.length - 1);
    return (
      <div className="ci-style-card">
        <div className="ci-style-thumb" aria-hidden="true">
          {cover ? <img src={cover} alt="" /> : <span className="ci-style-thumb-ph">📷</span>}
          {extra > 0 && <span className="ci-style-ref-badge">+{extra}</span>}
        </div>
        <div className="ci-style-cap">
          <strong>{card.title}</strong>
          <span>{card.sub}</span>
        </div>
        <div className="ci-style-votes">
          {[
            ['like', 'thumbs-up', 'Нравится'],
            ['unsure', 'help-circle', 'Не уверен'],
            ['dislike', 'thumbs-down', 'Не нравится'],
          ].map(([v, icon, label]) => (
            <button key={v} type="button" className={'ci-vote' + (vote === v ? ' on' : '')} disabled={disabled} aria-label={label} title={label} onClick={() => onVote(v)}>
              <Ic n={icon} s={15} />
            </button>
          ))}
        </div>
      </div>
    );
  }

  function DpPrefsSchemaForm({ dealId, intake, onSaved, locked, meta, schema, inst }) {
    const Mock = window.DpBriefTemplateMock;
    const FI = window.FormInstances;
    const Sections = window.FormSchemaSections;
    const draft = intake.dpPrefsDraft || S.defaultDpPrefs(intake);
    const [responses, setResponses] = useState(() => {
      if (inst && inst.responses && Object.keys(inst.responses).length) return inst.responses;
      return Mock ? Mock.draftToResponses(schema, draft) : {};
    });
    const saveTimer = useRef(null);

    const persist = useCallback((next) => {
      if (locked) return;
      clearTimeout(saveTimer.current);
      saveTimer.current = setTimeout(() => {
        if (FI && inst) FI.saveFieldResponses(dealId, inst.id, next, schema);
        const form = Mock.responsesToIntakeForm(next);
        S.saveIntake(dealId, S.applyDpPrefs(S.loadIntake(dealId), form));
        onSaved && onSaved();
      }, 400);
    }, [dealId, inst, schema, locked, onSaved]);

    const patchTop = useCallback((id, val) => {
      setResponses((prev) => {
        const next = { ...prev, [id]: val };
        persist(next);
        return next;
      });
    }, [persist]);

    const patchSection = useCallback((sectionId, val) => {
      setResponses((prev) => {
        const next = { ...prev, [sectionId]: val };
        persist(next);
        return next;
      });
    }, [persist]);

    const progress = FI ? FI.computeSchemaProgress(schema, responses) : meta.progress || 0;

    return (
      <section className="ci-form-block">
        <div className="ci-form-head">
          <div>
            <h2 className="ci-form-title">{schema.title || 'Бриф на дизайн-проект'}</h2>
            <p className="ci-form-lede">Схема из конструктора «Брифы ДП». Ответы видны менеджеру во вкладке «Дизайн-бриф».</p>
          </div>
          <div className="ci-form-meta">
            <Badge tone={meta.status === 'submitted' ? 'success' : 'info'} dot>{meta.status === 'submitted' ? 'Отправлена' : 'В процессе'}</Badge>
            <span className="ci-progress t-num">{progress}%</span>
          </div>
        </div>
        <FormTimer meta={meta} />
        <div className="ci-schema-form">
          {Sections && (
            <Sections
              schema={schema}
              responses={responses}
              canEdit={!locked}
              onPatchTop={patchTop}
              onPatchSection={patchSection}
            />
          )}
        </div>
      </section>
    );
  }

  function DpPrefsLegacyForm({ dealId, intake, onSaved, locked, meta, measureDone, needsDp }) {
    const [form, setForm] = useState(() => intake.dpPrefsDraft || S.defaultDpPrefs(intake));
    const saveRef = useRef(S.createAutosave(dealId, S.applyDpPrefs, () => form));

    const patch = useCallback((p) => {
      setForm((prev) => {
        const next = { ...prev, ...p };
        if (!locked && measureDone && needsDp) saveRef.current(next);
        return next;
      });
    }, [locked, measureDone, needsDp]);

    useEffect(() => {
      const h = () => onSaved && onSaved();
      window.addEventListener('remontpro:intake-updated', h);
      return () => window.removeEventListener('remontpro:intake-updated', h);
    }, [onSaved]);

    const setVote = (id, v) => patch({ styleVotes: { ...(form.styleVotes || {}), [id]: v } });

    return (
      <section className="ci-form-block">
        <div className="ci-form-head">
          <div>
            <h2 className="ci-form-title">Бриф на дизайн-проект</h2>
            <p className="ci-form-lede">Стиль, материалы и ограничения для дизайнера. Ответы видны менеджеру во вкладке «Дизайн-бриф».</p>
          </div>
          <div className="ci-form-meta">
            <Badge tone={meta.status === 'submitted' ? 'success' : 'info'} dot>{meta.status === 'submitted' ? 'Отправлена' : 'В процессе'}</Badge>
            <span className="ci-progress t-num">{meta.progress || 0}%</span>
          </div>
        </div>
        <FormTimer meta={meta} />

        <div className="ci-group">
          <h3 className="ci-group-title">Стиль интерьера</h3>
          <p className="ci-group-sub">Отметьте, что близко. Это не финальный выбор, а ориентир для дизайнера.</p>
          <div className="ci-style-grid">
            {(window.DpBriefTemplateMock ? window.DpBriefTemplateMock.STYLE_CARDS : STYLE_CARDS).map((c) => (
              <StyleCard key={c.id} card={c} vote={(form.styleVotes || {})[c.id]} onVote={(v) => setVote(c.id, v)} disabled={locked} />
            ))}
          </div>
        </div>

        <div className="ci-group">
          <h3 className="ci-group-title">Цвет и материалы</h3>
          <Field label="Цветовая гамма"><MultiChip options={COLOR_OPTS} value={form.colorPresets} onChange={(v) => patch({ colorPresets: v })} disabled={locked} custom={form.colorCustom} onCustom={(v) => patch({ colorCustom: v })} /></Field>
          <Field label="Материалы"><MultiChip options={MATERIAL_OPTS} value={form.material} onChange={(v) => patch({ material: v })} disabled={locked} custom={form.materialCustom} onCustom={(v) => patch({ materialCustom: v })} /></Field>
        </div>

        <div className="ci-group">
          <h3 className="ci-group-title">Инженерия и свет</h3>
          <Field label="Сантехника"><MultiChip options={PLUMBING_OPTS} value={form.plumbing} onChange={(v) => patch({ plumbing: v })} disabled={locked} custom={form.plumbingCustom} onCustom={(v) => patch({ plumbingCustom: v })} /></Field>
          <Field label="Электрика"><MultiChip options={ELECTRICAL_OPTS} value={form.electrical} onChange={(v) => patch({ electrical: v })} disabled={locked} custom={form.electricalCustom} onCustom={(v) => patch({ electricalCustom: v })} /></Field>
          <Field label="Освещение"><MultiChip options={LIGHTING_OPTS} value={form.lighting} onChange={(v) => patch({ lighting: v })} disabled={locked} custom={form.lightingCustom} onCustom={(v) => patch({ lightingCustom: v })} /></Field>
        </div>

        <div className="ci-group">
          <h3 className="ci-group-title">Бюджет и сроки</h3>
          <Field label="Бюджет на чистовую отделку"><div className="ci-chips">{BUDGET_OPTS.map((b) => <Chip key={b} label={b} on={form.budgetFinish === b} onClick={() => patch({ budgetFinish: b })} disabled={locked} />)}</div></Field>
          <Field label="Мебель"><div className="ci-chips">{FURNITURE.map(([k, l]) => <Chip key={k} label={l} on={form.furnitureKey === k} onClick={() => patch({ furnitureKey: k })} disabled={locked} />)}</div></Field>
          <Field label="Нужно к дате"><textarea className="ci-input ci-textarea" rows={2} value={form.deadlineNote} disabled={locked} onChange={(e) => patch({ deadlineNote: e.target.value })} placeholder="Например: заехать к 1 сентября" /></Field>
          <Field label="Что важно / чего не хотим"><textarea className="ci-input ci-textarea" rows={3} value={form.freeText} disabled={locked} onChange={(e) => patch({ freeText: e.target.value })} /></Field>
        </div>

        <div className="ci-group">
          <h3 className="ci-group-title">Референсы</h3>
          <Field label="Ссылки (Pinterest, Behance, фото)">
            <textarea className="ci-input ci-textarea" rows={2} disabled={locked} value={(form.referenceUrls || []).join('\n')} onChange={(e) => patch({ referenceUrls: e.target.value.split('\n').map((s) => s.trim()).filter(Boolean) })} placeholder="По одной ссылке на строку" />
          </Field>
        </div>
      </section>
    );
  }

  function DpPrefsForm({ dealId, intake, onSaved, readOnly }) {
    /* контакты считаются заполненными, когда анкета замера отправлена или их внёс менеджер в мастере сделки */
    const measureDone = ((intake.clientForms || {}).measurePrep || {}).status === 'submitted' || S.fromWizard(intake);
    const needsDp = !!intake.requiresDesign;
    const meta = (intake.clientForms || {}).dpPrefs || {};
    const locked = !!S.isExpired(meta) || !measureDone || !needsDp || !!readOnly;
    const FI = window.FormInstances;
    const Mock = window.DpBriefTemplateMock;
    const inst = FI ? FI.findInstance(dealId, (i) => i.kind === 'dp_brief') : null;
    const schema = Mock ? Mock.materializeSchema(
      Mock.getSchema((inst && inst.templateId) || 'tpl-dp-brief-std'),
      { dealPropertyType: intake.propertyType || intake.housingType }
    ) : null;

    /* бриф, выданный из мастера сделки (уникальная ссылка): статус и переход, пока не согласован */
    if (needsDp && inst && inst.linkToken && inst.accessState !== 'pending') {
      const ST = { active: ['ждёт ваших ответов', 'info'], in_progress: ['заполняется · ' + (inst.progressPct || 0) + '%', 'info'], submitted: ['отправлен дизайнеру', 'warning'], approved: ['согласован', 'success'], expired: ['ссылка истекла', 'neutral'] };
      const st = ST[inst.accessState] || [inst.accessState, 'neutral'];
      const href = 'brief.html?deal=' + encodeURIComponent(dealId) + '&t=' + encodeURIComponent(inst.linkToken);
      return (
        <section className="ci-form-block">
          <h2 className="ci-form-title">Бриф на дизайн-проект</h2>
          <p className="ci-form-lede">Подробная анкета для дизайнера: семья и быт, стиль, референсы, планировка, кухня, инженерия, мебель, бюджет. Заполняйте частями — сохраняется само.</p>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}>
            <Badge tone={st[1]} dot>{st[0]}</Badge>
            {inst.accessState !== 'approved' && <Button variant={inst.accessState === 'submitted' ? 'ghost' : 'accent'} size="md" onClick={() => { window.location.href = href; }}>{inst.accessState === 'submitted' ? 'Посмотреть ответы' : 'Открыть бриф'}</Button>}
            {inst.accessState === 'approved' && <span style={{ fontSize: 13, color: 'var(--text-secondary)' }}>Дизайнер согласовал бриф — дальше концепция.</span>}
          </div>
        </section>
      );
    }
    if (!needsDp) {
      return (
        <section className="ci-form-block ci-form-locked">
          <h2 className="ci-form-title">Бриф на дизайн-проект</h2>
          <p className="ci-form-lede">Откроется после активации пакета ДП менеджером. Стиль и материалы для дизайнера.</p>
        </section>
      );
    }
    if (!measureDone) {
      return (
        <section className="ci-form-block ci-form-locked">
          <h2 className="ci-form-title">Бриф на дизайн-проект</h2>
          <p className="ci-form-lede">Откроется после активации пакета ДП. Заполните контакты в клиентской анкете.</p>
        </section>
      );
    }

    if (schema && window.FormSchemaSections) {
      return (
        <DpPrefsSchemaForm
          dealId={dealId}
          intake={intake}
          onSaved={onSaved}
          locked={locked}
          meta={meta}
          schema={schema}
          inst={inst}
        />
      );
    }

    return (
      <DpPrefsLegacyForm
        dealId={dealId}
        intake={intake}
        onSaved={onSaved}
        locked={locked}
        meta={meta}
        measureDone={measureDone}
        needsDp={needsDp}
      />
    );
  }

  function WizardStatus({ dealId, intake }) {
    const FI = window.FormInstances;
    const inst = FI ? FI.findInstance(dealId, (i) => i.kind === 'dp_brief') : null;
    const ms = intake.measureSession || {};
    let est = null; try { est = JSON.parse(localStorage.getItem('calc2_estimate_v2_' + dealId) || 'null'); } catch (e) {}
    const EST = { draft: ['менеджер собирает', 'neutral'], internal: ['уточняется командой', 'info'], client_review: ['отправлена вам на согласование', 'warning'], ready: ['согласована', 'success'] };
    const measure = ms.status === 'submitted' || intake.status === 'measurement_done'
      ? ['замер выполнен' + (ms.totals && ms.totals.area ? ' · ' + ms.totals.area + ' м²' : ''), 'success']
      : intake.measurementDate ? ['назначен на ' + intake.measurementDate + (intake.measuredBy ? ' · ' + intake.measuredBy : ''), 'info'] : ['дата уточняется', 'neutral'];
    const BR = { pending: ['ждёт заполнения', 'neutral'], active: ['откройте по ссылке', 'info'], in_progress: ['заполняется · ' + ((inst && inst.progressPct) || 0) + '%', 'info'], submitted: ['отправлен дизайнеру', 'warning'], approved: ['согласован дизайнером', 'success'], expired: ['ссылка истекла', 'neutral'] };
    const brief = !intake.requiresDesign ? null : (inst ? (BR[inst.accessState] || [inst.accessState, 'neutral']) : ['ещё не создан', 'neutral']);
    const briefHref = inst && inst.linkToken ? 'brief.html?deal=' + encodeURIComponent(dealId) + '&t=' + encodeURIComponent(inst.linkToken) : null;
    const rows = [
      ['Замер', measure, intake.measurementDate && !(ms.status === 'submitted') ? 'Подготовьте доступ к объекту: ' + [intake.entrance ? 'подъезд ' + intake.entrance : '', intake.doorCode ? 'код ' + intake.doorCode : ''].filter(Boolean).join(', ') : ''],
      brief && !window.DpProcessCard ? ['Бриф на дизайн-проект', brief, brief[0] === 'откройте по ссылке' || brief[0].indexOf('заполняется') === 0 ? 'Подробная анкета для дизайнера: семья и быт, стиль, референсы, планировка, бюджет.' : ''] : null,
      (intake.requiresDesign && !window.DpProcessCard && window.DpPipeline && window.DpPipeline.summary(dealId)) ? (() => { const sm = window.DpPipeline.summary(dealId); return ['Дизайн-проект', [sm.finished + '/' + sm.total + ' · ' + sm.label, sm.review.length ? 'warning' : sm.finished === sm.total ? 'success' : 'info'], sm.review.length ? 'Этапы ждут вашего согласования — раздел «Согласования».' : '']; })() : null,
      ['Смета', est ? (EST[est.status] || [est.status, 'neutral']) : ['после замера' + (intake.requiresDesign ? ' и брифа' : ''), 'neutral'], ''],
    ].filter(Boolean);
    return (
      <section className="ci-form-block">
        <h2 className="ci-form-title">Ход сделки</h2>
        <p className="ci-form-lede">Контакты и объект уже заполнил менеджер — вам ничего повторять не нужно. Здесь статусы этапов.</p>
        {window.TeamStrip && <window.TeamStrip dealId={dealId} title="Ваша команда" />}
        {window.DpProcessCard && intake.requiresDesign && <window.DpProcessCard dealId={dealId} intake={intake} compact who="client" hrefBrief={briefHref} />}
        <div className="ci-status-list">
          {rows.map(([t, st, hint]) => (
            <div key={t} className="ci-status-row">
              <div className="ci-status-main"><b>{t}</b>{hint && <div className="ci-status-hint">{hint}</div>}</div>
              <Badge tone={st[1]} dot>{st[0]}</Badge>
              {t === 'Бриф на дизайн-проект' && briefHref && inst.accessState !== 'approved' && <Button variant={inst.accessState === 'submitted' ? 'ghost' : 'accent'} size="sm" onClick={() => { window.location.href = briefHref; }}>{inst.accessState === 'submitted' ? 'Посмотреть' : 'Открыть бриф'}</Button>}
            </div>
          ))}
        </div>
      </section>
    );
  }

  function ClientIntakePage() {
    const dealId = S.dealIdFromQuery();
    const deal = S.resolveDeal ? S.resolveDeal() : { dealId, isDemo: false };
    const [intake, setIntake] = useState(() => S.loadIntake(dealId));
    const reload = useCallback(() => setIntake(S.loadIntake(dealId)), [dealId]);

    useEffect(() => {
      const h = (e) => { if (!e.detail || e.detail.dealId === dealId) reload(); };
      window.addEventListener('remontpro:intake-updated', h);
      window.addEventListener('storage', reload);
      return () => { window.removeEventListener('remontpro:intake-updated', h); window.removeEventListener('storage', reload); };
    }, [dealId, reload]);

    const address = [intake.address, intake.apt ? 'кв. ' + intake.apt : ''].filter(Boolean).join(', ') || 'объект не указан';
    const who = intake.clientName ? intake.clientName + ' · ' : '';
    const wizard = S.fromWizard(intake);

    return (
      <div className="lk-narrow ci-page">
        <PageHead
          eyebrow={'Сделка ' + dealId + (deal.isDemo ? ' · демо' : '')}
          h1={wizard ? 'Ход сделки и анкеты' : 'Анкеты для замера и дизайна'}
          lede={who + address + (wizard ? ' — контакты и объект заполнил менеджер, здесь статусы замера, брифа и сметы.' : '. Контакты, доступ к объекту и пожелания по интерьеру. Менеджер видит изменения сразу.')}
        />
        {deal.isDemo && (
          <div className="lk-banner" style={{ marginBottom: 14 }}>
            <span className="lk-banner-ic"><Ic n="info" s={18} /></span>
            <div style={{ flex: 1, minWidth: 0 }}><div className="lk-banner-t">Кабинет открыт без ссылки-приглашения</div><div className="lk-banner-s">Показана демо-сделка. Личный кабинет клиента открывается по ссылке с токеном (менеджер выдаёт её при запуске проекта), а бриф — по своей ссылке из этапа «Дизайн-проект».</div></div>
          </div>
        )}
        {!wizard && (
          <div className="ci-sync-note">
            <Ic n="refresh-cw" s={14} />
            <span>Сохранение автоматическое · вкладки «Замер» и «Пожелания» в калькуляторе</span>
          </div>
        )}
        <div className="ci-stack">
          {wizard && <WizardStatus dealId={dealId} intake={intake} />}
          {!wizard && <FormInstanceList dealId={dealId} />}
          {!wizard && <MeasurePrepForm dealId={dealId} intake={intake} onSaved={reload} />}
          {!wizard && <DpPrefsForm dealId={dealId} intake={intake} onSaved={reload} />}
        </div>
      </div>
    );
  }

  window.ClientIntakePage = ClientIntakePage;
  window.ClientIntakeForms = { MeasurePrepForm, DpPrefsForm };
  try {
    window.dispatchEvent(new CustomEvent('remontpro:client-intake-ready'));
  } catch (e) { /* ignore */ }
})();
