/* manager-site-intake.jsx — CRM вкладка «Объект»: замер и анкеты (SiteIntake).
   Канон: docs/tz/crm-site-intake/TZ.md. Прогрессивное наполнение карточки объекта:
   Обзор (готовность %) · Пожелания (форма A) · Замер (форма B, mobile-preview) ·
   Медиа · Дизайн-бриф (при requiresDesign) · Пакет для сметы (handoff).
   Прототип v1: состояние в localStorage remontpro_site_intake_{dealId}, без backend.
   Экспорт → window.SiteIntakePanel({ deal }) */
(function () {
  const { Badge, Button } = window.DesignSystem_9e0a09;
  const { Ic } = window;
  const { useState, useEffect, useRef } = React;

  /* ============ SEED (одна демо-сделка с полным замером) ============ */
  const DEMO_ID = 'D-404';
  const seedRoom = (id, name, l, w, h, openings, eng, photos) => ({
    id, name, lengthM: l, widthM: w, heightM: h,
    openings: openings || [], geometryIssues: [], constructElements: [],
    engineering: eng || { sockets: 0, switches: 0, water: 0, sewer: 0, vent: false, radiators: 0, panelPhoto: false },
    substrate: 'none', keep: '', demolish: '', relocate: '', roomPrefs: '',
    photos: photos || { plan: false, walls: false, floor: false, ceiling: false, nodes: false },
  });
  function demoIntake() {
    return {
      created: true, status: 'measurement_done', requiresDesign: true, repairType: 'capital',
      address: '', clientName: '', clientPhone: '',
      measurementDate: '2026-06-21', measuredBy: '',
      objectType: 'new_build', finishState: 'without', housingType: 'apartment', propertyType: 'residential',
      prefs: { sent: true, finishTier: 'mid', material: ['Плитка крупный формат', 'Ламинат 33 класс'], plumbing: ['Скрытые смесители', 'Инсталляция'], electrical: ['Больше розеток', 'Сценарное освещение'], lighting: ['Тёплый свет', 'Треки'], deadlineNote: '', logisticsNote: '', freeText: '' },
      references: [],
      rooms: [
        seedRoom('r1', 'Кухня-гостиная', 5.2, 4.1, 2.75,
          [{ id:'o1', kind:'window', w:1.8, h:1.5, off:0.4, dir:'in' }, { id:'o2', kind:'door', w:0.9, h:2.1, off:0.2, dir:'L' }],
          { sockets: 12, switches: 3, water: 2, sewer: 1, vent: true, radiators: 2, panelPhoto: true },
          { plan:true, walls:true, floor:true, ceiling:true, nodes:true }),
        seedRoom('r2', 'Санузел', 2.4, 1.9, 2.75,
          [{ id:'o3', kind:'door', w:0.7, h:2.0, off:0.15, dir:'R' }],
          { sockets: 2, switches: 1, water: 3, sewer: 2, vent: true, radiators: 1, panelPhoto: false },
          { plan:true, walls:true, floor:true, ceiling:false, nodes:true }),
        seedRoom('r3', 'Спальня', 3.6, 3.2, 2.75,
          [{ id:'o4', kind:'window', w:1.5, h:1.5, off:0.5, dir:'in' }, { id:'o5', kind:'door', w:0.8, h:2.1, off:0.2, dir:'L' }],
          { sockets: 6, switches: 2, water: 0, sewer: 0, vent: false, radiators: 1, panelPhoto: false },
          { plan:true, walls:true, floor:false, ceiling:false, nodes:false }),
      ],
      media: [
        { id: 'm1', room: 'r1', label: 'Общий план кухни', kind: 'photo', thumb: 0 },
        { id: 'm2', room: 'r1', label: 'Стена с окном', kind: 'photo', thumb: 1 },
        { id: 'm3', room: 'r1', label: 'Обзор кухни-гостиной', kind: 'video', thumb: 2, duration: '0:42' },
        { id: 'm4', room: 'r1', label: 'Узел под окном', kind: 'photo', thumb: 3 },
        { id: 'm5', room: 'r2', label: 'Санузел · стояки', kind: 'photo', thumb: 4 },
        { id: 'm6', room: 'r2', label: 'Слив и трапы', kind: 'photo', thumb: 5 },
        { id: 'm7', room: 'r2', label: 'Обход санузла', kind: 'video', thumb: 6, duration: '1:15' },
        { id: 'm8', room: 'engineering', label: 'Электрощиток', kind: 'photo', thumb: 7 },
        { id: 'm9', room: 'engineering', label: 'Стояки · видео', kind: 'video', thumb: 8, duration: '0:28' },
        { id: 'm10', room: 'r3', label: 'Спальня · общий', kind: 'photo', thumb: 9 },
        { id: 'm11', room: 'r3', label: 'Ниша под кондиционер', kind: 'photo', thumb: 10 },
      ],
      plan: { file: 'plan-bti-severny-42.pdf', sketch: false },
      brief: { style: ['Минимализм', 'Тёплый сканди'], colors: '', furniture: '', lighting: '', extra: '', budgetFinish: '1.2–1.6 млн', deadline: '2026-09-01' },
    };
  }
  // сделка с заведённым объектом, но без замера (для empty «Назначьте дату»)
  function blankIntake(deal) {
    return {
      created: true, status: 'draft', requiresDesign: false, repairType: 'cosmetic',
      address: deal.prop || '', clientName: deal.client || '', clientPhone: '',
      measurementDate: '', measuredBy: '', objectType: 'secondary', finishState: 'partial', housingType: 'apartment', propertyType: 'residential',
      prefs: { sent: false }, rooms: [], media: [], plan: { file: null, sketch: false }, brief: null,
    };
  }

  const LS = (id) => 'remontpro_site_intake_' + id;
  function loadIntake(deal) {
    try {
      const r = localStorage.getItem(LS(deal.id));
      if (r) {
        const it = JSON.parse(r);
        if (!it.repairType) it.repairType = it.requiresDesign ? 'capital' : 'cosmetic';
        return patchDemoReferences(deal.id, patchDemoMedia(deal.id, it));
      }
    } catch (e) {}
    if (deal.id === DEMO_ID) return demoIntake();
    return null; // нет объекта
  }
  function patchDemoReferences(dealId, it) {
    if (dealId !== DEMO_ID || !it) return it;
    const demo = demoIntake().references;
    const refs = it.references || [];
    if (refs.length >= 4) return it;
    return { ...it, references: demo };
  }
  function patchDemoMedia(dealId, it) {
    if (dealId !== DEMO_ID || !it) return it;
    const demo = demoIntake().media;
    const media = it.media || [];
    const stale = !media.length || media.length < 8 || media.some((m) => !m.kind);
    if (stale) return { ...it, media: demo };
    return it;
  }
  function saveIntake(deal, data) { try { localStorage.setItem(LS(deal.id), JSON.stringify(data)); } catch (e) {} }

  /* ============ readiness (§2.3) ============ */
  function readiness(it) {
    if (!it) return { score: 0, rows: [] };
    const has = (v) => v != null && v !== '';
    const room1 = (it.rooms || [])[0];
    const anyArea = (it.rooms || []).some(r => r.lengthM && r.widthM);
    const photoCount = (it.media || []).length;
    const rows = [
      ['Квалификация', 15, has(it.address) && has(it.objectType) && has(it.clientPhone)],
      ['Анкета заказчика', 15, it.prefs && (it.prefs.sent || it.prefs.finishTier)],
      ['Замер: шапка', 10, has(it.measurementDate) && has(it.clientName) && has(it.address)],
      ['Замер: помещения', 25, room1 && room1.lengthM && room1.widthM && room1.heightM],
      ['Замер: площади', 15, anyArea],
      ['Медиа', 10, photoCount >= 3 || it.mediaSkipped],
      ['План', 10, (it.plan && (it.plan.file || it.plan.sketch))],
    ];
    if (it.requiresDesign) rows.push(['ДП-анкета', 10, !!it.brief]);
    const applicable = rows.reduce((a, r) => a + r[1], 0);
    const earned = rows.reduce((a, r) => a + (r[2] ? r[1] : 0), 0);
    return { score: Math.round(earned / applicable * 100), rows };
  }

  /* ============ Readiness ring ============ */
  function ReadinessRing({ score, size = 58 }) {
    const r = (size - 8) / 2, c = 2 * Math.PI * r;
    const tone = score >= 60 ? 'var(--success-strong)' : score >= 30 ? 'var(--warning-strong)' : 'var(--destructive)';
    return (
      <div style={{ position: 'relative', width: size, height: size, flexShrink: 0 }}>
        <svg width={size} height={size} style={{ transform: 'rotate(-90deg)' }}>
          <circle cx={size/2} cy={size/2} r={r} fill="none" stroke="var(--border-subtle)" strokeWidth="5" />
          <circle cx={size/2} cy={size/2} r={r} fill="none" stroke={tone} strokeWidth="5" strokeLinecap="round"
            strokeDasharray={c} strokeDashoffset={c * (1 - score/100)} style={{ transition: 'stroke-dashoffset .5s var(--ease-out, ease)' }} />
        </svg>
        <div style={{ position: 'absolute', inset: 0, display: 'grid', placeItems: 'center' }}>
          <span className="t-num" style={{ fontSize: size > 50 ? 16 : 13, fontWeight: 800, color: 'var(--foreground)' }}>{score}<span style={{ fontSize: 9, fontWeight: 700 }}>%</span></span>
        </div>
      </div>
    );
  }

  /* ============ ChipPicker (форма A) ============ */
  function ChipPicker({ label, options, value, onChange }) {
    const v = value || [];
    const toggle = (o) => onChange(v.includes(o) ? v.filter(x => x !== o) : [...v, o]);
    return (
      <div style={{ marginBottom: 14 }}>
        <div className="si-flabel">{label}</div>
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 7 }}>
          {options.map(o => <button key={o} className={'si-chip' + (v.includes(o) ? ' on' : '')} onClick={() => toggle(o)}>{o}</button>)}
        </div>
      </div>
    );
  }

  const REPAIR_SCOPE = [['cosmetic', 'Косметический'], ['capital', 'Капитальный']];

  /* ============ Sub-tab: Заказ на замер ============ */
  function MeasureOrderTab({ deal, it, patch }) {
    const FI = window.FormInstances;
    const [order, setOrder] = useState(() => (FI && FI.findInstance(deal.id, (i) => i.kind === 'measure_order')) || null);
    const [form, setForm] = useState(() => (order && order.responses) || {
      measureDate: it.measurementDate || '',
      measureTime: '',
      addressDetail: '',
      routeNote: '',
      doorCode: it.accessNote || '',
      parkingNote: '',
      onSiteContact: it.onSiteContact || '',
      onSitePhone: it.onSitePhone || '',
    });
    const [copied, setCopied] = useState(false);

    useEffect(() => {
      const reload = () => {
        if (!FI) return;
        const o = FI.findInstance(deal.id, (i) => i.kind === 'measure_order');
        setOrder(o);
        if (o && o.responses) setForm(function (prev) { return { ...prev, ...o.responses }; });
      };
      window.addEventListener('remontpro:forms-updated', reload);
      return () => window.removeEventListener('remontpro:forms-updated', reload);
    }, [deal.id]);

    const save = () => {
      if (!FI) return;
      FI.saveMeasureOrder(deal.id, form);
      patch({ measurementDate: form.measureDate, onSiteContact: form.onSiteContact, accessNote: form.doorCode });
      setCopied(false);
    };

    const copyFieldLink = () => {
      if (!FI) return;
      const field = FI.findInstance(deal.id, (i) => i.kind === 'measure_field');
      if (!field) return;
      FI.copyFormLink(field, deal.id).then(() => { setCopied(true); setTimeout(() => setCopied(false), 2000); });
    };

    return (
      <div>
        <div className="si-banner">
          <Ic n="calendar" s={16} c="var(--text-tertiary)" />
          <span style={{ flex: 1 }}>Заказ на замер после брифинга с клиентом. Адрес объекта берётся из профиля: <strong>{it.address || '—'}</strong></span>
        </div>
        <div className="si-objhead">
          <div className="si-grid2">
            <label className="si-num-field"><span>Дата замера</span><input className="si-input" type="date" value={form.measureDate} onChange={(e) => setForm({ ...form, measureDate: e.target.value })} /></label>
            <label className="si-num-field"><span>Время / слот</span><input className="si-input" type="time" value={form.measureTime} onChange={(e) => setForm({ ...form, measureTime: e.target.value })} /></label>
          </div>
          <div className="si-flabel" style={{ marginTop: 10 }}>Уточнение адреса (подъезд, этаж)</div>
          <input className="si-input" placeholder="Подъезд 2, этаж 12" value={form.addressDetail} onChange={(e) => setForm({ ...form, addressDetail: e.target.value })} />
          <div className="si-flabel" style={{ marginTop: 10 }}>Маршрут и комментарий для замерщика</div>
          <textarea className="si-input" rows={2} value={form.routeNote} onChange={(e) => setForm({ ...form, routeNote: e.target.value })} placeholder="Как проехать, где парковаться" />
          <div className="si-grid2" style={{ marginTop: 10 }}>
            <label className="si-num-field"><span>Код домофона / пропуск</span><input className="si-input" value={form.doorCode} onChange={(e) => setForm({ ...form, doorCode: e.target.value })} /></label>
            <label className="si-num-field"><span>Парковка</span><input className="si-input" value={form.parkingNote} onChange={(e) => setForm({ ...form, parkingNote: e.target.value })} /></label>
          </div>
          <div className="si-grid2" style={{ marginTop: 10 }}>
            <label className="si-num-field"><span>Контакт на объекте</span><input className="si-input" value={form.onSiteContact} onChange={(e) => setForm({ ...form, onSiteContact: e.target.value })} /></label>
            <label className="si-num-field"><span>Телефон контакта</span><input className="si-input" type="tel" value={form.onSitePhone} onChange={(e) => setForm({ ...form, onSitePhone: e.target.value })} /></label>
          </div>
        </div>
        <div className="si-cta-row" style={{ marginTop: 14 }}>
          <Button variant="primary" size="sm" iconLeft={<Ic n="save" s={14} />} onClick={save}>Сохранить заказ · создать задачу</Button>
          <Button variant="ghost" size="sm" iconLeft={<Ic n="link" s={14} />} onClick={copyFieldLink}>{copied ? 'Ссылка скопирована' : 'Скопировать ссылку замерщику'}</Button>
        </div>
        {order && <div className="si-hint-row" style={{ marginTop: 10 }}><Ic n="info" s={13} /><span>Статус заказа: {window.FormInstances.stateLabel(order.accessState)} · {order.progressPct || 0}%</span></div>}
      </div>
    );
  }

  /* ============ Sub-tab: Формы (DealFormBundle) ============ */
  function FormsBundleTab({ deal, it, patch }) {
    const FI = window.FormInstances;
    const [instances, setInstances] = useState(() => (FI && FI.loadInstances(deal.id)) || []);
    const [bundle, setBundle] = useState(() => (FI && FI.loadBundle(deal.id)) || null);
    const [flash, setFlash] = useState('');
    const [propertyTypes, setPropertyTypes] = useState(() => (
      window.QuestionnaireSetMock
        ? window.QuestionnaireSetMock.getPropertyTypes().map((x) => [x.id, x.label])
        : [['residential', 'Жилая'], ['house', 'Дом'], ['commercial', 'Коммерция']]
    ));

    const reload = () => {
      if (!FI) return;
      setInstances(FI.loadInstances(deal.id));
      setBundle(FI.loadBundle(deal.id));
    };

    useEffect(() => {
      const onSets = () => {
        if (window.QuestionnaireSetMock) {
          setPropertyTypes(window.QuestionnaireSetMock.getPropertyTypes().map((x) => [x.id, x.label]));
        }
      };
      window.addEventListener('remontpro:forms-updated', reload);
      window.addEventListener('remontpro:questionnaire-sets-updated', onSets);
      return () => {
        window.removeEventListener('remontpro:forms-updated', reload);
        window.removeEventListener('remontpro:questionnaire-sets-updated', onSets);
      };
    }, [deal.id]);

    const setPropertyType = (pt) => {
      patch({ propertyType: pt, housingType: pt === 'house' ? 'house' : pt === 'commercial' ? 'commercial' : 'apartment' });
      if (FI) FI.materializeForPropertyType(deal.id, pt, { clientActive: true });
      reload();
    };

    const onCopy = (inst) => {
      FI.copyFormLink(inst, deal.id).then(() => { setFlash('Ссылка скопирована · ' + inst.title); setTimeout(() => setFlash(''), 2500); });
    };

    const onSendClient = (inst) => {
      onCopy(inst);
      setFlash('Отправьте ссылку клиенту вручную или через мессенджер · ' + inst.title);
    };

    const onActivateDp = () => {
      FI.activateDpBrief(deal.id);
      reload();
    };

    return (
      <div>
        <div className="si-objhead">
          <div className="si-flabel" style={{ marginTop: 0 }}>Тип недвижимости · QuestionnaireSet</div>
          <div style={{ display: 'flex', gap: 7, flexWrap: 'wrap' }}>
            {propertyTypes.map(([k, l]) => (
              <button key={k} type="button" className={'si-chip' + ((it.propertyType || 'residential') === k ? ' on' : '')} onClick={() => setPropertyType(k)}>{l}</button>
            ))}
          </div>
          {bundle && <div className="si-hint-row" style={{ marginTop: 8 }}><Ic n="layers" s={13} /><span>Набор: {bundle.questionnaireSetId} · форм: {instances.length}</span></div>}
        </div>
        {flash && <div className="si-banner" style={{ marginTop: 12 }}><Ic n="check" s={14} /><span>{flash}</span></div>}
        <div className="si-break" style={{ marginTop: 14 }}>
          {instances.map((inst) => (
            <div key={inst.id} className="si-break-row" style={{ alignItems: 'center', flexWrap: 'wrap', gap: 8 }}>
              <span className="si-break-l" style={{ flex: 1, minWidth: 140 }}>{inst.title}</span>
              <Badge tone={inst.accessState === 'submitted' ? 'success' : inst.accessState === 'active' ? 'info' : 'neutral'} dot>
                {FI.stateLabel(inst.accessState)} · {inst.progressPct || 0}%
              </Badge>
              {(inst.kind === 'client' || inst.kind === 'dp_brief') && inst.accessState !== 'pending' && (
                <>
                  <Button variant="ghost" size="sm" onClick={() => onCopy(inst)} iconLeft={<Ic n="copy" s={13} />}>Скопировать ссылку</Button>
                  <Button variant="ghost" size="sm" onClick={() => onSendClient(inst)} iconLeft={<Ic n="send" s={13} />}>Отправить клиенту</Button>
                </>
              )}
              {inst.kind === 'measure_field' && (
                <>
                  <Button variant="ghost" size="sm" onClick={() => onCopy(inst)} iconLeft={<Ic n="link" s={13} />}>Ссылка замерщику</Button>
                  <Button variant="ghost" size="sm" onClick={() => window.open(FI.fieldUrl(deal.id, inst.linkToken), '_blank')} iconLeft={<Ic n="external-link" s={13} />}>Открыть замер</Button>
                  {inst.accessState === 'expired' && (
                    <Button variant="ghost" size="sm" onClick={() => { FI.reopenMeasureField(deal.id, inst.id); reload(); }} iconLeft={<Ic n="rotate-ccw" s={13} />}>Переоткрыть</Button>
                  )}
                </>
              )}
            </div>
          ))}
        </div>
        {it.requiresDesign && (
          <div className="si-cta-row" style={{ marginTop: 14 }}>
            <Button variant="primary" size="sm" iconLeft={<Ic n="palette" s={14} />} onClick={onActivateDp}>Активировать пакет ДП · бриф</Button>
          </div>
        )}
      </div>
    );
  }

  /* ============ Sub-tab: Обзор ============ */
  function OverviewTab({ it, deal, setSub, patch }) {
    const rd = readiness(it);
    const areaLine = window.CalcIntakeReadiness
      ? (() => {
        const d = window.CalcIntakeReadiness.deriveArea(it);
        if (d.source === 'measured') return d.value + ' м² · по замеру';
        if (d.source === 'approx') return '≈ ' + d.value + ' м² · ориентир';
        return 'Площадь не указана';
      })()
      : '—';
    return (
      <div>
        <div className="si-overcard">
          <ReadinessRing score={rd.score} size={64} />
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 9, flexWrap: 'wrap' }}>
              <span style={{ fontSize: 15, fontWeight: 800, letterSpacing: '-.01em' }}>Готовность к смете</span>
              {rd.score < 60
                ? <Badge tone="warning" dot>Пакет неполный</Badge>
                : <Badge tone="success" dot>Можно считать</Badge>}
            </div>
            <div style={{ fontSize: 12.5, color: 'var(--text-tertiary)', marginTop: 3 }}>{it.address} · {it.clientName}</div>
            <div style={{ fontSize: 12, color: 'var(--text-secondary)', marginTop: 4 }}>{areaLine}</div>
          </div>
        </div>

        <div className="si-objhead" style={{ marginTop: 14 }}>
          <div className="si-flabel" style={{ marginTop: 0 }}>Основное · тип ремонта</div>
          <div style={{ display: 'flex', gap: 7, flexWrap: 'wrap' }}>
            {REPAIR_SCOPE.map(([k, l]) => (
              <button key={k} type="button" className={'si-chip' + ((it.repairType || 'capital') === k ? ' on' : '')} onClick={() => patch({ repairType: k })}>{l}</button>
            ))}
          </div>
          <label className="si-eng-check" style={{ marginTop: 12 }}>
            <input type="checkbox" checked={!!it.requiresDesign} onChange={(e) => patch({ requiresDesign: e.target.checked })} />
            <span>Нужен дизайн-проект (откроет вкладку «Дизайн-бриф» и ветку ДП в калькуляторе)</span>
          </label>
        </div>

        {/* breakdown */}
        <div className="si-break">
          {rd.rows.map((r, i) => (
            <div key={i} className="si-break-row">
              <span className={'si-break-ic' + (r[2] ? ' on' : '')}>{r[2] ? <Ic n="check" s={12} /> : <span className="si-break-dot" />}</span>
              <span className="si-break-l">{r[0]}</span>
              <span className="si-break-w t-num">{r[1]}%</span>
            </div>
          ))}
        </div>

        <div className="si-cta-row">
          <Button variant="ghost" size="sm" iconLeft={<Ic n="send" s={14} />} onClick={() => setSub('forms')}>Формы и ссылки</Button>
          <Button variant="ghost" size="sm" iconLeft={<Ic n="calendar" s={14} />} onClick={() => setSub('order')}>Заказ на замер</Button>
          <Button variant="ghost" size="sm" iconLeft={<Ic n="ruler" s={14} />} onClick={() => setSub('measure')}>{it.measurementDate ? 'Открыть замер' : 'Назначить замер'}</Button>
          <Button variant="primary" size="sm" iconLeft={<Ic n="calculator" s={14} />} onClick={() => setSub('handoff')} title={rd.score < 60 ? 'Расчёт доступен даже при неполном пакете' : ''}>Новый расчёт</Button>
        </div>
        {rd.score < 60 && <div className="si-hint-row"><Ic n="info" s={13} c="var(--text-tertiary)" /><span>«Новый расчёт» не блокируется — можно считать и дополнять пакет параллельно.</span></div>}
      </div>
    );
  }

  /* ============ Sub-tab: Пожелания (форма A) ============ */
  const TIERS = [['economy', 'Эконом'], ['mid', 'Средний'], ['premium', 'Премиум']];
  const OBJ_TYPES = [['new_build', 'Новостройка'], ['secondary', 'Вторичка']];
  const HOUSING = [['apartment', 'Квартира'], ['house', 'Дом / таунхаус'], ['commercial', 'Коммерция']];
  const FINISH_STATE = [['without', 'Без отделки'], ['partial', 'Черновая'], ['renovation', 'С ремонтом (переделка)']];
  function PrefsTab({ it, patch, deal }) {
    const p = it.prefs || {};
    const setP = (k, v) => patch({ prefs: { ...p, [k]: v } });
    return (
      <div>
        <div className="si-banner">
          <Ic n={p.sent ? 'check-circle' : 'mail'} s={16} c={p.sent ? 'var(--success-strong)' : 'var(--text-tertiary)'} />
          <span style={{ flex: 1 }}>{p.sent ? 'Анкета отправлена заказчику · можно править на звонке' : 'Заполните на звонке или отправьте ссылку заказчику'}</span>
          {!p.sent && <Button variant="ghost" size="sm" onClick={() => {
            const id = (deal && deal.id) || 'D-404';
            window.open('client-intake.html?deal=' + encodeURIComponent(id), '_blank');
            setP('sent', true);
          }} iconLeft={<Ic n="link" s={13} />}>Отправить ссылку</Button>}
        </div>

        {/* Шапка «Объект» (TZ §4.1–4.2) */}
        <div className="si-objhead">
          <div className="si-flabel" style={{ marginTop: 0 }}>Объект</div>
          <input className="si-input" placeholder="Адрес объекта" value={it.address || ''} onChange={(e) => patch({ address: e.target.value })} />
          <div className="si-grid2" style={{ marginTop: 10 }}>
            <label className="si-num-field"><span>Площадь-ориентир</span><div className="si-num-wrap"><input type="number" step="1" value={it.areaApprox || ''} onChange={(e) => patch({ areaApprox: +e.target.value })} placeholder="ор." /><i>м²</i></div></label>
            <div><div className="si-flabel">Тип жилья</div>
              <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>{HOUSING.map(([k, l]) => <button key={k} className={'si-chip sm' + (it.housingType === k ? ' on' : '')} onClick={() => patch({ housingType: k })}>{l}</button>)}</div>
            </div>
          </div>
          <div className="si-grid2" style={{ marginTop: 10 }}>
            <div><div className="si-flabel">Тип объекта</div>
              <div style={{ display: 'flex', gap: 6 }}>{OBJ_TYPES.map(([k, l]) => <button key={k} className={'si-chip sm' + (it.objectType === k ? ' on' : '')} onClick={() => patch({ objectType: k })}>{l}</button>)}</div>
            </div>
            <div><div className="si-flabel">Состояние отделки</div>
              <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>{FINISH_STATE.map(([k, l]) => <button key={k} className={'si-chip sm' + (it.finishState === k ? ' on' : '')} onClick={() => patch({ finishState: k })}>{l}</button>)}</div>
            </div>
          </div>
          <label className="si-eng-check" style={{ marginTop: 12 }}><input type="checkbox" checked={!!it.requiresDesign} onChange={(e) => patch({ requiresDesign: e.target.checked })} /><span>Нужен дизайн-проект (откроет вкладку «Дизайн-бриф»)</span></label>
        </div>

        <div className="si-flabel" style={{ marginTop: 4 }}>Уровень отделки</div>
        <div style={{ display: 'flex', gap: 7, marginBottom: 14 }}>
          {TIERS.map(([k, l]) => <button key={k} className={'si-chip' + (p.finishTier === k ? ' on' : '')} onClick={() => setP('finishTier', k)}>{l}</button>)}
        </div>

        <ChipPicker label="Материалы" options={['Плитка крупный формат', 'Ламинат 33 класс', 'Паркет', 'Микроцемент', 'Керамогранит']} value={p.material} onChange={(v) => setP('material', v)} />
        <ChipPicker label="Сантехника" options={['Скрытые смесители', 'Инсталляция', 'Душ без поддона', 'Тёплый пол в СУ']} value={p.plumbing} onChange={(v) => setP('plumbing', v)} />
        <ChipPicker label="Электрика" options={['Умный дом', 'Больше розеток', 'Сценарное освещение', 'Тёплый пол']} value={p.electrical} onChange={(v) => setP('electrical', v)} />
        <ChipPicker label="Освещение" options={['Тёплый свет', 'Нейтральный', 'Холодный', 'Люстра', 'Треки']} value={p.lighting} onChange={(v) => setP('lighting', v)} />

        <div className="si-flabel">Логистика и сроки</div>
        <input className="si-input" placeholder="Подъём, вывоз мусора, лифт, парковка…" value={p.logisticsNote || ''} onChange={(e) => setP('logisticsNote', e.target.value)} />
        <input className="si-input" placeholder="Нужно к дате…" value={p.deadlineNote || ''} onChange={(e) => setP('deadlineNote', e.target.value)} style={{ marginTop: 8 }} />

        <div className="si-flabel" style={{ marginTop: 14 }}>Что важно / чего не хотим</div>
        <textarea className="si-input" rows={3} placeholder="Свободный комментарий заказчика" value={p.freeText || ''} onChange={(e) => setP('freeText', e.target.value)} style={{ resize: 'vertical' }} />
      </div>
    );
  }

  /* ============ Замер: helpers ============ */
  function roomAreas(r) {
    const L = +r.lengthM || 0, W = +r.widthM || 0, H = +r.heightM || 0;
    const floor = L * W;
    const ceiling = L * W;
    const grossWall = 2 * (L + W) * H;
    const ded = (r.openings || []).reduce((a, o) => a + (+o.w || 0) * (+o.h || 0), 0);
    return { floor, ceiling, grossWall, ded, netWall: Math.max(0, grossWall - ded) };
  }
  const f1 = (n) => (Math.round(n * 10) / 10).toFixed(1);

  function OpeningRow({ op, onPatch, onDel }) {
    return (
      <div className="si-oprow">
        <select className="si-mini" value={op.kind} onChange={(e) => onPatch({ kind: e.target.value })}>
          <option value="window">Окно</option><option value="door">Дверь</option>
        </select>
        <input className="si-mini num" type="number" step="0.05" value={op.w} onChange={(e) => onPatch({ w: +e.target.value })} placeholder="Ш" />
        <input className="si-mini num" type="number" step="0.05" value={op.h} onChange={(e) => onPatch({ h: +e.target.value })} placeholder="В" />
        <select className="si-mini" value={op.dir} onChange={(e) => onPatch({ dir: e.target.value })}>
          <option value="L">←</option><option value="R">→</option><option value="in">внутрь</option><option value="out">наружу</option>
        </select>
        <button className="si-x" onClick={onDel}><Ic n="x" s={13} /></button>
      </div>
    );
  }

  const ENG_FIELDS = [['sockets', 'Розетки'], ['switches', 'Выключатели'], ['water', 'Выводы воды'], ['sewer', 'Канализация'], ['radiators', 'Радиаторы']];
  const PHOTO_REQ = [['plan', 'Общий план'], ['walls', 'Стены'], ['floor', 'Пол'], ['ceiling', 'Потолок'], ['nodes', 'Узлы']];

  function RoomAccordion({ room, open, onToggle, onPatch, onDel }) {
    const a = roomAreas(room);
    const photoDone = PHOTO_REQ.filter(([k]) => room.photos && room.photos[k]).length;
    const setEng = (k, v) => onPatch({ engineering: { ...room.engineering, [k]: v } });
    const setOpening = (id, p) => onPatch({ openings: room.openings.map(o => o.id === id ? { ...o, ...p } : o) });
    const addOpening = () => onPatch({ openings: [...(room.openings || []), { id: 'o' + Math.random().toString(36).slice(2, 6), kind: 'window', w: 1.2, h: 1.4, off: 0.3, dir: 'in' }] });
    const setPhoto = (k) => onPatch({ photos: { ...room.photos, [k]: !room.photos[k] } });
    return (
      <div className={'si-room' + (open ? ' open' : '')}>
        <button className="si-room-h" onClick={onToggle}>
          <Ic n={open ? 'chevron-down' : 'chevron-right'} s={16} c="var(--text-tertiary)" />
          <input className="si-room-name" value={room.name} onClick={(e) => e.stopPropagation()} onChange={(e) => onPatch({ name: e.target.value })} />
          <span className="si-room-meta t-num">{room.lengthM && room.widthM ? f1(a.floor) + ' м²' : '—'}</span>
          <span className={'si-photo-pill' + (photoDone >= 4 ? ' ok' : '')}><Ic n="camera" s={11} />{photoDone}/5</span>
        </button>
        {open && (
          <div className="si-room-body">
            {/* геометрия */}
            <div className="si-geo">
              {[['lengthM', 'Длина'], ['widthM', 'Ширина'], ['heightM', 'Высота']].map(([k, l]) => (
                <label key={k} className="si-num-field">
                  <span>{l}</span>
                  <div className="si-num-wrap"><input type="number" step="0.05" value={room[k] || ''} onChange={(e) => onPatch({ [k]: +e.target.value })} /><i>м</i></div>
                </label>
              ))}
            </div>
            <div className="si-areas">
              <span>Пол <b className="t-num">{f1(a.floor)}</b> м²</span>
              <span>Потолок <b className="t-num">{f1(a.ceiling)}</b> м²</span>
              <span title="Периметр × H − проёмы">Стены <b className="t-num">{f1(a.netWall)}</b> м² <em>(−{f1(a.ded)})</em></span>
            </div>

            {/* проёмы */}
            <div className="si-subh"><span>Проёмы</span><button className="si-add" onClick={addOpening}><Ic n="plus" s={12} />окно/дверь</button></div>
            {(room.openings || []).length === 0 && <div className="si-empty-sm">Нет проёмов</div>}
            {(room.openings || []).map(o => <OpeningRow key={o.id} op={o} onPatch={(p) => setOpening(o.id, p)} onDel={() => onPatch({ openings: room.openings.filter(x => x.id !== o.id) })} />)}

            {/* инженерия */}
            <div className="si-subh"><span>Инженерия</span></div>
            <div className="si-eng">
              {ENG_FIELDS.map(([k, l]) => (
                <div key={k} className="si-eng-row">
                  <span>{l}</span>
                  <div className="si-stepper">
                    <button onClick={() => setEng(k, Math.max(0, (room.engineering[k] || 0) - 1))}>−</button>
                    <span className="t-num">{room.engineering[k] || 0}</span>
                    <button onClick={() => setEng(k, (room.engineering[k] || 0) + 1)}>+</button>
                  </div>
                </div>
              ))}
              <label className="si-eng-check"><input type="checkbox" checked={!!room.engineering.vent} onChange={(e) => setEng('vent', e.target.checked)} /><span>Вентиляция</span></label>
              <label className="si-eng-check"><input type="checkbox" checked={!!room.engineering.panelPhoto} onChange={(e) => setEng('panelPhoto', e.target.checked)} /><span>Фото щитка</span></label>
            </div>

            {/* фото-чеклист */}
            <div className="si-subh"><span>Фото помещения</span></div>
            <div className="si-photos">
              {PHOTO_REQ.map(([k, l]) => (
                <button key={k} className={'si-photo' + (room.photos && room.photos[k] ? ' done' : '')} onClick={() => setPhoto(k)}>
                  <Ic n={room.photos && room.photos[k] ? 'check' : 'camera'} s={14} />{l}
                </button>
              ))}
            </div>

            <button className="si-room-del" onClick={onDel}><Ic n="trash-2" s={13} />Удалить помещение</button>
          </div>
        )}
      </div>
    );
  }

  /* ============ Sub-tab: Замер (mobile-preview) ============ */
  function MeasureTab({ it, patch }) {
    const [openRoom, setOpenRoom] = useState((it.rooms[0] || {}).id || null);
    if (!it.measurementDate && it.rooms.length === 0) {
      return (
        <div className="si-empty">
          <div className="si-empty-ic"><Ic n="ruler" s={26} c="var(--text-tertiary)" /></div>
          <div className="si-empty-t">Замер не начат</div>
          <div className="si-empty-s">Назначьте дату или отправьте форму замерщику</div>
          <div style={{ display: 'flex', gap: 8, marginTop: 14, justifyContent: 'center' }}>
            <Button variant="primary" size="sm" iconLeft={<Ic n="calendar" s={14} />} onClick={() => patch({ measurementDate: new Date().toISOString().slice(0,10) })}>Назначить дату</Button>
            <Button variant="ghost" size="sm" iconLeft={<Ic n="send" s={14} />}>Форму замерщику</Button>
          </div>
        </div>
      );
    }
    const setRoom = (id, p) => patch({ rooms: it.rooms.map(r => r.id === id ? { ...r, ...p } : r) });
    const addRoom = () => { const id = 'r' + Math.random().toString(36).slice(2, 6); patch({ rooms: [...it.rooms, { id, name: 'Комната ' + (it.rooms.length + 1), openings: [], constructElements: [], geometryIssues: [], engineering: { sockets:0, switches:0, water:0, sewer:0, vent:false, radiators:0, panelPhoto:false }, photos: {} }] }); setOpenRoom(id); };
    const delRoom = (id) => patch({ rooms: it.rooms.filter(r => r.id !== id) });
    const filled = it.rooms.filter(r => r.lengthM && r.widthM && r.heightM).length;
    return (
      <div>
        <div className="si-mobile-note"><Ic n="smartphone" s={13} />Так форму замера видит замерщик на объекте (390px)</div>
        <div className="si-phone">
          <div className="si-phone-notch" />
          <div className="si-phone-screen">
            <div className="si-offline"><Ic n="cloud-off" s={12} />Офлайн — изменения отправятся позже</div>
            <div className="si-measure-head">
              <div className="si-mh-title">Замер · {it.address}</div>
              <div className="si-mh-sub">{it.clientName} · {it.measurementDate || '—'} · {it.measuredBy || 'замерщик'}</div>
              <div className="si-progress"><div className="si-progress-bar"><span style={{ width: (it.rooms.length ? filled / it.rooms.length * 100 : 0) + '%' }} /></div><span className="t-num">{filled}/{it.rooms.length} помещений</span></div>
            </div>
            <div className="si-rooms">
              {it.rooms.map(r => <RoomAccordion key={r.id} room={r} open={openRoom === r.id} onToggle={() => setOpenRoom(openRoom === r.id ? null : r.id)} onPatch={(p) => setRoom(r.id, p)} onDel={() => delRoom(r.id)} />)}
            </div>
            <button className="si-add-room" onClick={addRoom}><Ic n="plus" s={15} />Добавить помещение</button>
          </div>
        </div>
      </div>
    );
  }

  /* ============ Sub-tab: Медиа ============ */
  function MediaTab({ it, patch }) {
    const roomName = (rid) => rid === 'engineering' ? 'Инженерия объекта' : rid === 'plan' ? 'План' : (it.rooms.find(r => r.id === rid) || {}).name || 'Объект';
    const groups = {};
    (it.media || []).forEach(m => { (groups[m.room] = groups[m.room] || []).push(m); });
    return (
      <div>
        {(it.media || []).length === 0 && (
          <div className="si-empty">
            <div className="si-empty-ic"><Ic n="image" s={26} c="var(--text-tertiary)" /></div>
            <div className="si-empty-t">Нет фото</div>
            <div className="si-empty-s">Фото необязательны, но ускорят смету</div>
          </div>
        )}
        {Object.keys(groups).map(rid => (
          <div key={rid} style={{ marginBottom: 16 }}>
            <div className="si-flabel">{roomName(rid)} · {groups[rid].length}</div>
            <div className="si-media-grid">
              {groups[rid].map(m => (
                <div key={m.id} className="si-media-cell"><Ic n="image" s={20} c="var(--text-tertiary)" /><span>{m.label}</span></div>
              ))}
              <button className="si-media-add"><Ic n="plus" s={18} /></button>
            </div>
          </div>
        ))}
        {(it.media || []).length > 0 && (
          <label className="si-eng-check" style={{ marginTop: 4 }}><input type="checkbox" checked={!!it.mediaSkipped} onChange={(e) => patch({ mediaSkipped: e.target.checked })} /><span>Пометить «медиа достаточно / пропущено»</span></label>
        )}
      </div>
    );
  }

  /* ============ Sub-tab: Дизайн-бриф ============ */
  function BriefTab({ it, patch }) {
    const b = it.brief || {};
    const setB = (k, v) => patch({ brief: { ...b, [k]: v } });
    return (
      <div>
        <div className="si-banner"><Ic n="palette" s={16} c="var(--chart-4, #7c3aed)" /><span style={{ flex: 1 }}>Дизайн-бриф · сделка с дизайн-проектом (requiresDesign)</span></div>
        <ChipPicker label="Стиль" options={['Минимализм', 'Тёплый сканди', 'Современная классика', 'Лофт', 'Япанди']} value={b.style} onChange={(v) => setB('style', v)} />
        <div className="si-flabel">Цвет / материалы</div>
        <textarea className="si-input" rows={2} placeholder="Предпочтения и антипримеры" value={b.colors || ''} onChange={(e) => setB('colors', e.target.value)} style={{ resize: 'vertical' }} />
        <div className="si-flabel" style={{ marginTop: 12 }}>Мебель / техника</div>
        <input className="si-input" placeholder="Встроенная / своя" value={b.furniture || ''} onChange={(e) => setB('furniture', e.target.value)} />
        <div className="si-flabel" style={{ marginTop: 12 }}>Освещение · сценарии</div>
        <input className="si-input" placeholder="Сценарии освещения" value={b.lighting || ''} onChange={(e) => setB('lighting', e.target.value)} />
        <div className="si-grid2" style={{ marginTop: 12 }}>
          <div><div className="si-flabel">Бюджет на чистовые</div><input className="si-input" value={b.budgetFinish || ''} onChange={(e) => setB('budgetFinish', e.target.value)} /></div>
          <div><div className="si-flabel">Срок концепции</div><input className="si-input" type="date" value={b.deadline || ''} onChange={(e) => setB('deadline', e.target.value)} /></div>
        </div>
        <div className="si-flabel" style={{ marginTop: 12 }}>Дети / животные / аллергии</div>
        <input className="si-input" placeholder="Опционально" value={b.extra || ''} onChange={(e) => setB('extra', e.target.value)} />
      </div>
    );
  }

  /* ============ Sub-tab: Пакет для сметы ============ */
  function HandoffTab({ it }) {
    const totals = (it.rooms || []).reduce((acc, r) => {
      const a = roomAreas(r);
      acc.floor += a.floor; acc.ceiling += a.ceiling; acc.wall += a.netWall;
      if (/(санузел|ванн|кухн|су\b)/i.test(r.name)) acc.wet += a.floor;
      return acc;
    }, { floor: 0, ceiling: 0, wall: 0, wet: 0 });
    const living = (it.rooms || []).filter(r => /(комнат|спал|гостин|детск)/i.test(r.name)).length;
    const tierL = { economy: 'Эконом', mid: 'Средний', premium: 'Премиум' }[(it.prefs || {}).finishTier] || '—';
    const rows = [
      ['Σ площадь пола', f1(totals.floor) + ' м²', 'area'],
      ['Σ стены (чистые)', f1(totals.wall) + ' м²', 'workVolume'],
      ['Σ потолок', f1(totals.ceiling) + ' м²', '—'],
      ['Мокрые зоны', f1(totals.wet) + ' м²', 'wetArea'],
      ['Жилых комнат', living, 'roomCount'],
    ];
    return (
      <div>
        <div className="si-banner"><Ic n="package-check" s={16} c="var(--success-strong)" /><span style={{ flex: 1 }}>Сводка для сметчика · автоген из карточки объекта</span></div>

        <div className="si-flabel">Сводные площади → переменные сметы</div>
        <div className="si-handoff-tbl">
          {rows.map((r, i) => (
            <div key={i} className="si-handoff-row">
              <span className="si-hr-l">{r[0]}</span>
              <span className="si-hr-v t-num">{r[1]}</span>
              <code className="si-hr-var">{r[2]}</code>
            </div>
          ))}
        </div>

        <div className="si-flabel" style={{ marginTop: 14 }}>Состав пакета</div>
        <div className="si-pkg">
          {[
            ['map', 'План с размерами', it.plan && it.plan.file ? it.plan.file : 'нет'],
            ['list-checks', 'Перечень работ по помещениям', (it.rooms || []).length + ' помещ.'],
            ['layers', 'Уровень отделки', tierL],
            ['truck', 'Логистика', (it.prefs || {}).logisticsNote ? 'указана' : '—'],
            ['images', 'Фото-галерея', (it.media || []).length + ' фото'],
            ['palette', 'ДП-бриф', it.brief ? 'есть' : 'N/A'],
          ].map(([ic, l, v], i) => (
            <div key={i} className="si-pkg-row"><Ic n={ic} s={15} c="var(--text-tertiary)" /><span className="si-pkg-l">{l}</span><span className="si-pkg-v">{v}</span></div>
          ))}
        </div>

        <div className="si-cta-row" style={{ margintop: 16 }}>
          <Button variant="primary" size="sm" iconLeft={<Ic n="calculator" s={14} />} onClick={() => { try { window.location.href = 'calculator.html?role=manager'; } catch(e){} }}>Открыть в калькуляторе</Button>
          <Button variant="ghost" size="sm" iconLeft={<Ic n="hard-hat" s={14} />}>Отправить прорабу</Button>
          <Button variant="ghost" size="sm" iconLeft={<Ic n="file-down" s={14} />}>PDF</Button>
        </div>
      </div>
    );
  }

  /* ============ Главная панель ============ */
  function SiteIntakePanel({ deal }) {
    const [intake, setIntake] = useState(() => loadIntake(deal));
    const [sub, setSub] = useState('overview');
    useEffect(() => { if (intake) saveIntake(deal, intake); }, [intake]);
    const patch = (p) => setIntake(it => ({ ...it, ...p }));

    // объект не заведён
    if (!intake || !intake.created) {
      return (
        <div className="si-empty" style={{ paddingTop: 40 }}>
          <div className="si-empty-ic"><Ic n="home" s={28} c="var(--text-tertiary)" /></div>
          <div className="si-empty-t">Объект не заведён</div>
          <div className="si-empty-s">Заведите объект, чтобы назначить замер и собрать данные для сметы</div>
          <div style={{ marginTop: 16 }}>
            <Button variant="primary" size="md" iconLeft={<Ic n="plus" s={15} />} onClick={() => {
              const blank = blankIntake(deal);
              setIntake(blank);
              if (window.FormInstances) window.FormInstances.materializeForPropertyType(deal.id, blank.propertyType || 'residential', { clientActive: true });
            }}>Завести объект</Button>
          </div>
        </div>
      );
    }

    const SUBS = [['overview', 'Обзор'], ['forms', 'Формы'], ['order', 'Заказ на замер'], ['prefs', 'Пожелания'], ['measure', 'Замер'], ['media', 'Медиа']];
    if (intake.requiresDesign) SUBS.push(['brief', 'Дизайн-бриф']);
    SUBS.push(['handoff', 'Пакет для сметы']);

    return (
      <div className="si-wrap">
        <div className="si-subtabs">
          {SUBS.map(([k, l]) => <button key={k} className={'si-subtab' + (sub === k ? ' on' : '')} onClick={() => setSub(k)}>{l}</button>)}
        </div>
        <div className="si-content">
          {sub === 'overview' && <OverviewTab it={intake} deal={deal} setSub={setSub} patch={patch} />}
          {sub === 'forms' && <FormsBundleTab deal={deal} it={intake} patch={patch} />}
          {sub === 'order' && <MeasureOrderTab deal={deal} it={intake} patch={patch} />}
          {sub === 'prefs' && <PrefsTab it={intake} patch={patch} deal={deal} />}
          {sub === 'measure' && <MeasureTab it={intake} patch={patch} />}
          {sub === 'media' && <MediaTab it={intake} patch={patch} />}
          {sub === 'brief' && intake.requiresDesign && <BriefTab it={intake} patch={patch} />}
          {sub === 'handoff' && <HandoffTab it={intake} />}
        </div>
      </div>
    );
  }

  Object.assign(window, {
    SiteIntakePanel,
    loadSiteIntake: (dealId) => loadIntake({ id: dealId }),
    saveSiteIntake: (dealId, data) => saveIntake({ id: dealId }, data),
    siteIntakeReadiness: (deal) => readiness(loadIntake(deal)).score,
    siteIntakeExists: (deal) => !!loadIntake(deal),
  });
})();
