/* ctor-dp-briefs.jsx — Tab «Брифы ДП» (dp_brief sections: style_cards · fields · mini_survey · room_group) · KSH-350 */
const { useState: dpUseState, useEffect: dpUseEffect, useRef: dpUseRef } = React;
const dpCreatePortal = ReactDOM.createPortal;

const DP_SECTION_KINDS = [
  { v: 'style_cards', l: 'Стили · голосование', icon: '🎨', hint: 'Карточки стилей с галереей референсов и голосованием like / unsure / dislike' },
  { v: 'fields', l: 'Блок полей', icon: '📋', hint: 'Chips, текст, бюджет, ссылки' },
  { v: 'mini_survey', l: 'Мини-опросник', icon: '💬', hint: 'Дополнительные вопросы клиенту' },
  { v: 'room_group', l: 'Группа зон', icon: '🏠', hint: 'Повторяемые зоны объекта (опционально)' },
];

const MS_FIELD_TYPES = [
  { v: 'composite', l: 'Набор значений', icon: '⊞' },
  { v: 'text', l: 'Текст', icon: 'T' },
  { v: 'number', l: 'Число', icon: '#' },
  { v: 'textarea', l: 'Текст (многостр.)', icon: '¶' },
  { v: 'checkbox', l: 'Флаг', icon: '☑' },
  { v: 'select', l: 'Список', icon: '▾' },
  { v: 'chips', l: 'Chips (мульти)', icon: '◫' },
  { v: 'chip_single', l: 'Chips (один)', icon: '◉' },
  { v: 'url_list', l: 'Ссылки', icon: '🔗' },
  { v: 'media', l: 'Фото / видео', icon: '📷' },
];

const MS_SUB_FIELD_TYPES = MS_FIELD_TYPES.filter((t) => t.v !== 'composite');
const MS_COMPOSITE_MAX = 10;

function msSubUid() {
  return 'sf-' + Math.random().toString(36).slice(2, 7);
}

function dpCardImages(card) {
  const M = window.DpBriefTemplateMock;
  if (M && M.cardImages) return M.cardImages(card);
  return Array.isArray(card && card.images) ? card.images.filter(Boolean) : [];
}

function dpCardCover(card) {
  const M = window.DpBriefTemplateMock;
  if (M && M.cardCoverUrl) return M.cardCoverUrl(card);
  return dpCardImages(card)[0] || null;
}

function msDefaultCompositeFields() {
  return [
    { id: msSubUid(), type: 'number', label: 'Ширина', placeholder: '0', unit: 'м', step: 0.01 },
    { id: msSubUid(), type: 'number', label: 'Высота', placeholder: '0', unit: 'м', step: 0.01 },
  ];
}

function msEnsureComposite(field) {
  if (field.type !== 'composite') return field;
  return {
    layout: 'row',
    fields: msDefaultCompositeFields(),
    ...field,
    fields: (field.fields && field.fields.length) ? field.fields : msDefaultCompositeFields(),
  };
}

const msStyles = {
  card: (active) => ({
    padding: '14px 16px',
    cursor: 'pointer',
    borderRadius: 10,
    marginBottom: 4,
    background: active ? 'color-mix(in srgb, var(--foreground, #1a1714) 6%, var(--card, #fff))' : 'transparent',
    border: active ? '1px solid color-mix(in srgb, var(--foreground, #1a1714) 14%, var(--border-subtle, #e4ddd2))' : '1px solid transparent',
    transition: 'background 0.15s ease-out, border-color 0.15s ease-out',
  }),
  preview: { background: '#f0ece5', borderRadius: 14, padding: '16px', border: '.5px solid #ece5da' },
  sectionCard: { padding: '14px 16px', background: '#fafafa', borderRadius: 12, border: '.5px solid #ece5da', marginBottom: 10 },
  sectionHead: { display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap', marginBottom: 12 },
  kindBadge: (kind) => {
    const meta = DP_SECTION_KINDS.find((k) => k.v === kind) || {};
    const colors = { style_cards: '#7c3aed', fields: '#5aad6e', mini_survey: '#8b5cf6', room_group: '#e8793a' };
    const c = colors[kind] || '#8a817a';
    return { display: 'inline-flex', alignItems: 'center', gap: 6, padding: '5px 10px', borderRadius: 980, fontSize: 12, fontWeight: 700, color: c, background: c + '18', border: '.5px solid ' + c + '33', flexShrink: 0 };
  },
};

const dpBriefEditorShell = {
  background: 'var(--card)',
  borderRadius: 'var(--radius-md, 10px)',
  border: '1px solid var(--border-subtle)',
  overflow: 'hidden',
};

function loadDpBriefs() {
  const M = window.DpBriefTemplateMock;
  if (!M) return [];
  return M.listSchemas();
}

function saveDpBriefs(schemas) {
  const M = window.DpBriefTemplateMock;
  if (!M) return;
  const obj = {};
  schemas.forEach((s) => { obj[s.id] = M.normalizeSchema(s); });
  M.saveSchemasObject(obj);
}

function editableSections(schema) {
  return schema.sections || [];
}

function sectionKindMeta(kind) {
  return DP_SECTION_KINDS.find((k) => k.v === kind) || { l: kind, icon: '·' };
}

const DP_PT_LABELS = { residential: 'Жилая', house: 'Дом', commercial: 'Коммерция' };

function dpMakeLinkedRef(presetId, propertyTypesOverride) {
  return {
    id: 'ref-' + Math.random().toString(36).slice(2, 8),
    presetId,
    mode: 'linked',
    propertyTypesOverride: propertyTypesOverride || [],
  };
}

function dpDetachRef(ref) {
  const P = window.BriefFieldPresetsMock;
  if (!P) return { ...ref, mode: 'detached', fields: ref.fields || [] };
  const fields = P.resolvePresetFields(ref, P.loadPresetsObject(), P.loadGroupsObject()) || [];
  return { ...ref, mode: 'detached', fields: JSON.parse(JSON.stringify(fields)) };
}

function dpMaterializeSectionPreview(section, briefPropertyTypes) {
  const P = window.BriefFieldPresetsMock;
  if (!P) return section;
  return {
    ...section,
    fields: P.materializeSection(section, { briefPropertyTypes: briefPropertyTypes || [] }),
  };
}

function DpPresetTypeChips({ types, small }) {
  if (!types || !types.length) {
    return <span style={{ fontSize: small ? 10 : 11, color: '#8a817a' }}>все типы</span>;
  }
  return (
    <span style={{ display: 'inline-flex', flexWrap: 'wrap', gap: 4 }}>
      {types.map((t) => (
        <span key={t} style={{ padding: small ? '1px 7px' : '2px 8px', borderRadius: 980, fontSize: small ? 10 : 11, fontWeight: 600, background: '#f0ece5', color: '#6b6259' }}>
          {DP_PT_LABELS[t] || t}
        </span>
      ))}
    </span>
  );
}

function DpPresetInsertDialog({ open, onClose, briefPropertyTypes, onInsertPreset, onInsertGroup }) {
  const P = window.BriefFieldPresetsMock;
  const [step, setStep] = dpUseState('pick');
  const [pending, setPending] = dpUseState(null);
  const [override, setOverride] = dpUseState([]);
  const [tick, setTick] = dpUseState(0);

  dpUseEffect(() => {
    if (!open) {
      setStep('pick');
      setPending(null);
      setOverride([]);
    }
  }, [open]);

  dpUseEffect(() => {
    if (!open || !P) return;
    const h = () => setTick((t) => t + 1);
    window.addEventListener('remontpro:brief-field-presets-updated', h);
    return () => window.removeEventListener('remontpro:brief-field-presets-updated', h);
  }, [open, P]);

  if (!open || !P) return null;

  const groups = P.listGroups();
  const presets = P.listPresets().filter((p) => P.presetVisibleInBrief(p, P.loadGroupsObject(), briefPropertyTypes));
  const visibleGroups = groups.filter((g) => {
    const gPresets = presets.filter((p) => p.groupId === g.id);
    return gPresets.length > 0;
  });

  const toggleOverride = (pt) => {
    const s = new Set(override);
    if (s.has(pt)) s.delete(pt); else s.add(pt);
    setOverride([...s]);
  };

  const confirmInsert = () => {
    const ov = override.length ? override : [];
    if (pending && pending.kind === 'preset') {
      onInsertPreset(dpMakeLinkedRef(pending.id, ov));
    } else if (pending && pending.kind === 'group') {
      onInsertGroup(pending.id, ov);
    }
    onClose();
  };

  if (step === 'override' && pending) {
    return (
      <div role="dialog" aria-modal="true" onClick={onClose}
        style={{ position: 'fixed', inset: 0, zIndex: 300, background: 'rgba(26,23,20,.45)', display: 'grid', placeItems: 'center', padding: 24 }}>
        <div onClick={(e) => e.stopPropagation()} style={{ background: '#fff', borderRadius: 14, border: '.5px solid #ece5da', padding: '20px 22px', maxWidth: 420, width: '100%' }}>
          <div style={{ fontSize: 15, fontWeight: 700, color: '#1a1714', marginBottom: 8 }}>Ограничить типы объекта?</div>
          <div style={{ fontSize: 13, color: '#8a817a', marginBottom: 14, lineHeight: 1.45 }}>
            Необязательно. Пустой выбор — видимость по правилам брифа.
          </div>
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginBottom: 16 }}>
            {P.PROPERTY_TYPES.map((pt) => {
              const on = override.includes(pt);
              return (
                <button key={pt} type="button" onClick={() => toggleOverride(pt)}
                  style={{ padding: '7px 12px', borderRadius: 980, border: '.5px solid ' + (on ? '#e8793a' : '#ece5da'), background: on ? '#fff0e6' : '#fff', color: on ? '#c2410c' : '#5c5249', font: 'inherit', fontSize: 12.5, fontWeight: 600, cursor: 'pointer' }}>
                  {DP_PT_LABELS[pt] || pt}
                </button>
              );
            })}
          </div>
          <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
            <button type="button" onClick={() => setStep('pick')} style={ctorStyles.btnGhost}>Назад</button>
            <button type="button" onClick={confirmInsert} style={ctorStyles.btnPrimary}>Вставить</button>
          </div>
        </div>
      </div>
    );
  }

  return (
    <div role="dialog" aria-modal="true" onClick={onClose}
      style={{ position: 'fixed', inset: 0, zIndex: 300, background: 'rgba(26,23,20,.45)', display: 'grid', placeItems: 'center', padding: 24 }}>
      <div onClick={(e) => e.stopPropagation()} style={{ background: '#fff', borderRadius: 14, border: '.5px solid #ece5da', padding: '20px 22px', maxWidth: 480, width: '100%', maxHeight: 'min(80vh, 560px)', overflow: 'auto' }}>
        <div style={{ fontSize: 15, fontWeight: 700, color: '#1a1714', marginBottom: 4 }}>Вставить из библиотеки</div>
        <div style={{ fontSize: 12.5, color: '#8a817a', marginBottom: 16 }}>Отфильтровано по типам объекта брифа</div>
        {visibleGroups.map((g) => {
          const gPresets = presets.filter((p) => p.groupId === g.id);
          return (
            <div key={g.id} style={{ marginBottom: 14 }}>
              <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 6 }}>
                <span style={{ fontSize: 12, fontWeight: 700, color: '#6b6259', textTransform: 'uppercase', letterSpacing: '.04em' }}>{g.title}</span>
                <button type="button" onClick={() => { setPending({ kind: 'group', id: g.id, title: g.title }); setStep('override'); }}
                  style={{ ...ctorStyles.btnGhost, padding: '4px 10px', fontSize: 11.5 }}>+ Вся группа</button>
              </div>
              {gPresets.map((p) => (
                <button key={p.id} type="button" onClick={() => { setPending({ kind: 'preset', id: p.id, title: p.title }); setStep('override'); }}
                  style={{ display: 'block', width: '100%', textAlign: 'left', padding: '10px 12px', marginBottom: 4, borderRadius: 10, border: '.5px solid #ece5da', background: '#fafafa', cursor: 'pointer', font: 'inherit' }}>
                  <div style={{ fontSize: 13.5, fontWeight: 600, color: '#1a1714' }}>{p.title}</div>
                  {p.hint && <div style={{ fontSize: 12, color: '#8a817a', marginTop: 3 }}>{p.hint}</div>}
                  <div style={{ marginTop: 6 }}><DpPresetTypeChips types={P.effectivePropertyTypes(p, P.loadGroupsObject())} small /></div>
                </button>
              ))}
            </div>
          );
        })}
        {!presets.length && (
          <div style={{ padding: 24, textAlign: 'center', color: '#a89e92', fontSize: 13 }}>Нет подходящих заготовок для типов этого брифа</div>
        )}
        <div style={{ display: 'flex', justifyContent: 'flex-end', marginTop: 8 }}>
          <button type="button" onClick={onClose} style={ctorStyles.btnGhost}>Закрыть</button>
        </div>
      </div>
    </div>
  );
}

function DpPresetRefCard({ refItem, briefPropertyTypes, onChange, onDelete }) {
  const P = window.BriefFieldPresetsMock;
  const [expanded, setExpanded] = dpUseState(false);
  const preset = P ? P.getPreset(refItem.presetId) : null;
  const isLinked = refItem.mode === 'linked';
  const missing = isLinked && !preset;
  const title = isLinked ? (preset ? preset.title : 'Заготовка удалена') : (refItem.fields && refItem.fields[0] ? refItem.fields[0].label : 'Локальный блок');
  const displayTitle = isLinked && preset ? preset.title : title;
  const effTypes = refItem.propertyTypesOverride && refItem.propertyTypesOverride.length
    ? refItem.propertyTypesOverride
    : (preset && P ? P.effectivePropertyTypes(preset, P.loadGroupsObject()) : []);

  const setRefFields = (fn) => onChange({ ...refItem, fields: fn(refItem.fields || []) });
  const patchRefField = (id, p) => setRefFields((fields) => fields.map((f) => (f.id === id ? { ...f, ...p } : f)));
  const delRefField = (id) => setRefFields((fields) => fields.filter((f) => f.id !== id));
  const moveRefField = (id, dir) => setRefFields((fields) => {
    const idx = fields.findIndex((f) => f.id === id);
    if (idx < 0) return fields;
    const next = [...fields];
    const swap = idx + dir;
    if (swap < 0 || swap >= next.length) return fields;
    [next[idx], next[swap]] = [next[swap], next[idx]];
    return next;
  });

  const linkedFields = (isLinked && preset) ? (preset.fields || []) : (refItem.fields || []);

  return (
    <div style={{ padding: '12px 14px', background: missing ? '#fff5f5' : '#fff', borderRadius: 11, border: '.5px solid ' + (missing ? '#fecaca' : '#e8793a44'), marginBottom: 8 }}>
      <div style={{ display: 'flex', alignItems: 'flex-start', gap: 10, flexWrap: 'wrap' }}>
        <div style={{ flex: 1, minWidth: 140 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap', marginBottom: 4 }}>
            <span style={{ fontSize: 13.5, fontWeight: 700, color: missing ? '#dc2626' : '#1a1714' }}>{displayTitle}</span>
            <span style={{ padding: '2px 8px', borderRadius: 980, fontSize: 10.5, fontWeight: 700, background: isLinked ? '#dbeafe' : '#f0fdf4', color: isLinked ? '#1d4ed8' : '#166534' }}>
              {isLinked ? 'Связана' : 'Локальная'}
            </span>
          </div>
          {isLinked && preset && preset.hint && <div style={{ fontSize: 12, color: '#8a817a', marginBottom: 6 }}>{preset.hint}</div>}
          {missing && <div style={{ fontSize: 12, color: '#dc2626', marginBottom: 6 }}>Preset удалён из библиотеки. Отвяжите или удалите ref.</div>}
          <DpPresetTypeChips types={effTypes} small />
          {isLinked && !missing && (
            <div style={{ fontSize: 11.5, color: '#8a817a', marginTop: 8, fontStyle: 'italic' }}>Изменения в библиотеке применятся автоматически</div>
          )}
        </div>
        <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
          {isLinked && (
            <button type="button" onClick={() => onChange(dpDetachRef(refItem))} style={{ ...ctorStyles.btnGhost, padding: '5px 10px', fontSize: 12 }}>Отвязать</button>
          )}
          {!isLinked && (
            <button type="button" onClick={() => setExpanded((e) => !e)} style={{ ...ctorStyles.btnGhost, padding: '5px 10px', fontSize: 12 }}>{expanded ? 'Свернуть' : 'Редактировать'}</button>
          )}
          <button type="button" onClick={onDelete} style={{ ...ctorStyles.iconBtnSm, color: '#f04e62' }}>{Icon.x}</button>
        </div>
      </div>
      {isLinked && !missing && (
        <div style={{ marginTop: 10, paddingTop: 10, borderTop: '.5px solid #f0ece5', display: 'flex', flexDirection: 'column', gap: 6, opacity: 0.85 }}>
          {linkedFields.map((f) => (
            <div key={f.id} style={{ fontSize: 12.5, color: '#6b6259', padding: '4px 0' }}>· {f.label || f.id} <span style={{ color: '#a89e92' }}>({f.type})</span></div>
          ))}
        </div>
      )}
      {!isLinked && expanded && (
        <div style={{ marginTop: 10, paddingTop: 10, borderTop: '.5px solid #f0ece5', display: 'flex', flexDirection: 'column', gap: 8 }}>
          {(refItem.fields || []).map((f, idx) => (
            <MsFieldEditor key={f.id} field={f} idx={idx} total={(refItem.fields || []).length}
              onPatch={patchRefField} onDelete={delRefField} onMove={moveRefField} />
          ))}
        </div>
      )}
    </div>
  );
}

function MsPreviewField({ field, inline }) {
  const inputOnly = inline || field.type === 'checkbox';
  const MediaPreview = window.MsMediaFieldPreview;
  return (
    <div style={{ marginBottom: inline ? 0 : 12 }}>
      {!inputOnly && (
        <div style={{ fontSize: 13, fontWeight: 500, color: '#1a1714', marginBottom: 5 }}>
          {field.label}
          {field.required && <span style={{ fontSize: 10, fontWeight: 700, color: '#f04e62', marginLeft: 6 }}>*</span>}
        </div>
      )}
      {field.type === 'text' && (
        <input disabled placeholder={field.placeholder || 'Введите значение…'} style={{ width: '100%', padding: '8px 11px', borderRadius: 9, border: '.5px solid #e4ddd2', background: '#fff', fontFamily: 'inherit', fontSize: 13 }} />
      )}
      {field.type === 'textarea' && (
        <textarea disabled rows={2} placeholder={field.placeholder || ''} style={{ width: '100%', padding: '8px 11px', borderRadius: 9, border: '.5px solid #e4ddd2', background: '#fff', fontFamily: 'inherit', fontSize: 13, resize: 'none' }} />
      )}
      {field.type === 'number' && (
        <input type="number" disabled placeholder="0" style={{ width: '50%', padding: '8px 11px', borderRadius: 9, border: '.5px solid #e4ddd2', background: '#fff', fontFamily: 'inherit', fontSize: 13 }} />
      )}
      {field.type === 'checkbox' && (
        <label style={{ display: 'flex', alignItems: 'center', gap: 8, cursor: 'default' }}>
          <input type="checkbox" disabled style={{ width: 16, height: 16 }} />
          <span style={{ fontSize: 13, color: '#6b6259' }}>{field.label}</span>
        </label>
      )}
      {field.type === 'select' && (
        <select disabled style={{ width: '100%', padding: '8px 11px', borderRadius: 9, border: '.5px solid #e4ddd2', background: '#fff', fontFamily: 'inherit', fontSize: 13 }}>
          <option>— выберите —</option>
          {(field.options || []).map((o, i) => <option key={i}>{o}</option>)}
        </select>
      )}
      {field.type === 'chips' && (
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
          {(field.options || ['Вариант']).map((o, i) => (
            <span key={i} style={{ padding: '5px 10px', borderRadius: 980, border: '.5px solid #ece5da', background: '#fff', fontSize: 12 }}>{typeof o === 'object' ? o.l : o}</span>
          ))}
        </div>
      )}
      {field.type === 'chip_single' && (
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
          {(field.options || ['Вариант']).map((o, i) => (
            <span key={i} style={{ padding: '5px 10px', borderRadius: 980, border: '.5px solid #ece5da', background: '#fff', fontSize: 12 }}>{typeof o === 'object' ? o.l : o}</span>
          ))}
        </div>
      )}
      {field.type === 'url_list' && (
        <textarea disabled rows={2} placeholder={field.placeholder || 'https://…'} style={{ width: '100%', padding: '8px 11px', borderRadius: 9, border: '.5px solid #e4ddd2', background: '#fff', fontFamily: 'inherit', fontSize: 13, resize: 'none' }} />
      )}
      {field.type === 'media' && MediaPreview && (
        <MediaPreview field={field} />
      )}
      {field.type === 'composite' && (
        <div style={{
          display: 'grid',
          gridTemplateColumns: (field.layout || 'row') === 'row' ? 'repeat(auto-fit, minmax(96px, 1fr))' : '1fr',
          gap: 8,
          padding: '10px 12px',
          borderRadius: 10,
          background: '#fff',
          border: '.5px solid #ece5da',
        }}>
          {(field.fields || []).map((sub) => (
            <div key={sub.id}>
              <div style={{ fontSize: 11, fontWeight: 600, color: '#8a817a', marginBottom: 4 }}>
                {sub.label || 'Подполе'}{sub.unit ? ' · ' + sub.unit : ''}
              </div>
              <MsPreviewField field={sub} inline />
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

function MsCompositeSubEditor({ sub, idx, total, onPatch, onDelete, onMove }) {
  const MediaConfig = window.MsMediaFieldConfigPanel;
  return (
    <div style={{
      display: 'grid',
      gridTemplateColumns: 'minmax(108px, 0.9fr) minmax(120px, 1.2fr) minmax(72px, 0.6fr) auto',
      gap: 8,
      alignItems: 'start',
      padding: '10px 12px',
      background: '#fff',
      borderRadius: 10,
      border: '.5px solid #ece5da',
    }}>
      <select value={sub.type} onChange={(e) => onPatch(sub.id, { type: e.target.value })}
        style={{ ...insStyles.field, padding: '7px 9px', fontSize: 12.5 }}>
        {MS_SUB_FIELD_TYPES.map((t) => <option key={t.v} value={t.v}>{t.icon} {t.l}</option>)}
      </select>
      <input value={sub.label || ''} onChange={(e) => onPatch(sub.id, { label: e.target.value })} placeholder="Подпись"
        style={{ ...insStyles.field, fontSize: 12.5 }} />
      <input value={sub.unit || ''} onChange={(e) => onPatch(sub.id, { unit: e.target.value })} placeholder="ед."
        style={{ ...insStyles.field, fontSize: 12.5 }} title="Единица: м, м², шт." />
      <div style={{ display: 'flex', gap: 3, paddingTop: 4 }}>
        <button type="button" onClick={() => onMove(sub.id, -1)} disabled={idx === 0} style={{ ...ctorStyles.iconBtnSm, opacity: idx === 0 ? .3 : 1 }} aria-label="Выше">
          <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><polyline points="18 15 12 9 6 15"></polyline></svg>
        </button>
        <button type="button" onClick={() => onMove(sub.id, 1)} disabled={idx === total - 1} style={{ ...ctorStyles.iconBtnSm, opacity: idx === total - 1 ? .3 : 1 }} aria-label="Ниже">
          <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><polyline points="6 9 12 15 18 9"></polyline></svg>
        </button>
        <button type="button" onClick={() => onDelete(sub.id)} style={{ ...ctorStyles.iconBtnSm, color: '#f04e62' }} aria-label="Удалить подполе">{Icon.x}</button>
      </div>
      {(sub.type === 'select' || sub.type === 'chips') && (
        <OptionsListEditor label="Варианты" options={sub.options} onChange={(opts) => onPatch(sub.id, { options: opts })}
          placeholder="Вариант" compact style={{ gridColumn: '1 / -1' }} />
      )}
      {(sub.type === 'text' || sub.type === 'number') && (
        <input value={sub.placeholder || ''} onChange={(e) => onPatch(sub.id, { placeholder: e.target.value })} placeholder="Placeholder подполя"
          style={{ ...insStyles.field, gridColumn: '1 / -1', fontSize: 12 }} />
      )}
      {sub.type === 'media' && MediaConfig && (
        <div style={{ gridColumn: '1 / -1' }}>
          <MediaConfig field={sub} onPatch={onPatch} />
        </div>
      )}
    </div>
  );
}

function MsCompositeEditor({ field, onPatch }) {
  const comp = msEnsureComposite(field);
  const subs = comp.fields || [];
  const setSubs = (fn) => onPatch(comp.id, { fields: fn(subs) });
  const patchSub = (id, p) => setSubs((list) => list.map((s) => (s.id === id ? { ...s, ...p } : s)));
  const delSub = (id) => setSubs((list) => list.filter((s) => s.id !== id));
  const moveSub = (id, dir) => setSubs((list) => {
    const idx = list.findIndex((s) => s.id === id);
    if (idx < 0) return list;
    const next = [...list];
    const swap = idx + dir;
    if (swap < 0 || swap >= next.length) return list;
    [next[idx], next[swap]] = [next[swap], next[idx]];
    return next;
  });
  const addSub = () => {
    if (subs.length >= MS_COMPOSITE_MAX) return;
    setSubs((list) => [...list, { id: msSubUid(), type: 'number', label: '', unit: '', placeholder: '' }]);
  };

  return (
    <div style={{ padding: '12px 14px', background: '#f5f2ec', borderRadius: 11, border: '.5px solid #e4ddd2' }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10, flexWrap: 'wrap', marginBottom: 10 }}>
        <div>
          <div style={{ fontSize: 12.5, fontWeight: 700, color: '#1a1714' }}>Подполя в одном пункте</div>
          <div style={{ fontSize: 12, color: '#8a817a', marginTop: 2 }}>До {MS_COMPOSITE_MAX} значений · ширина + высота, размеры, пары чисел</div>
        </div>
        <div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
          <span style={{ fontSize: 12, color: '#8a817a', fontWeight: 600 }}>Раскладка</span>
          <div style={{ display: 'flex', gap: 2, padding: 3, background: '#ece5da', borderRadius: 9 }}>
            {[{ v: 'row', l: 'В строку' }, { v: 'stack', l: 'Столбцом' }].map((o) => (
              <button key={o.v} type="button" onClick={() => onPatch(comp.id, { layout: o.v })}
                style={{
                  padding: '5px 10px', borderRadius: 7, border: 'none', cursor: 'pointer', fontFamily: 'inherit',
                  fontSize: 12, fontWeight: 600,
                  background: (comp.layout || 'row') === o.v ? '#fff' : 'transparent',
                  color: (comp.layout || 'row') === o.v ? '#1a1714' : '#8a817a',
                  boxShadow: (comp.layout || 'row') === o.v ? '0 1px 2px rgba(26,23,20,.08)' : 'none',
                }}>
                {o.l}
              </button>
            ))}
          </div>
        </div>
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
        {subs.map((sub, idx) => (
          <MsCompositeSubEditor key={sub.id} sub={sub} idx={idx} total={subs.length}
            onPatch={patchSub} onDelete={delSub} onMove={moveSub} />
        ))}
      </div>
      <button type="button" onClick={addSub} disabled={subs.length >= MS_COMPOSITE_MAX}
        style={{ ...ctorStyles.btnDashed, alignSelf: 'flex-start', marginTop: 10, fontSize: 12.5, opacity: subs.length >= MS_COMPOSITE_MAX ? .45 : 1 }}>
        {Icon.plus} Подполе ({subs.length}/{MS_COMPOSITE_MAX})
      </button>
    </div>
  );
}

function dpPreviewShortStepLabel(title) {
  const t = String(title || 'Шаг').trim();
  const cuts = {
    'Стиль интерьера': 'Стиль',
    'Цвет и материалы': 'Цвет',
    'Антипримеры': 'Анти',
    'Инженерия и свет': 'Инженерия',
    'Мебель и техника': 'Мебель',
    'Состав семьи и быт': 'Семья',
    'Бюджет и сроки': 'Бюджет',
    'Референсы': 'Рефы',
    'Помещения': 'Комнаты',
    'Кухня': 'Кухня',
  };
  if (cuts[t]) return cuts[t];
  return t.length > 13 ? t.slice(0, 11) + '…' : t;
}

function dpPreviewYesNoSubs(field) {
  if (!field || field.type !== 'composite') return null;
  const L = window.DpBriefLayout;
  if (L && L.isYesNoNoteComposite(field)) {
    const subs = field.fields || [];
    return { select: subs.find((s) => s.type === 'select'), note: subs.find((s) => s.type === 'text' || s.type === 'textarea') };
  }
  return null;
}

function DpBriefPreviewStepTrack({ steps, activeIdx, onGo }) {
  if (!steps || steps.length < 2) return null;
  const current = steps[activeIdx];
  return (
    <div className="ms-steps" role="tablist" aria-label="Шаги брифа">
      <div className="ms-steps-meta">
        <span className="ms-steps-counter">Шаг {activeIdx + 1} из {steps.length}</span>
        <span className="ms-steps-current">{current ? current.title : ''}</span>
      </div>
      <div className="ms-steps-track">
        {steps.map((st, i) => {
          const isActive = i === activeIdx;
          const isDone = i < activeIdx;
          return (
            <React.Fragment key={st.id}>
              {i > 0 && <span className={'ms-steps-line' + (i <= activeIdx ? ' on' : '')} aria-hidden="true" />}
              <button
                type="button"
                role="tab"
                aria-selected={isActive}
                aria-current={isActive ? 'step' : undefined}
                title={st.title}
                className={'ms-step' + (isActive ? ' active' : '') + (isDone ? ' done' : '')}
                onClick={() => onGo(i)}
              >
                <span className="ms-step-index" aria-hidden="true">
                  {isDone ? (
                    <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round">
                      <polyline points="20 6 9 17 4 12" />
                    </svg>
                  ) : (i + 1)}
                </span>
                <span className="ms-step-label">{dpPreviewShortStepLabel(st.title)}</span>
              </button>
            </React.Fragment>
          );
        })}
      </div>
      <div className="ms-steps-progress" aria-hidden="true">
        <div className="ms-steps-progress-fill" style={{ width: ((activeIdx + 1) / steps.length * 100) + '%' }} />
      </div>
    </div>
  );
}

function dpPreviewIsCompositeTile(field) {
  const L = window.DpBriefLayout;
  if (!field || field.type !== 'composite') return false;
  if (L && L.isYesNoNoteComposite(field)) return true;
  const subs = field.fields || [];
  return subs.length <= 3 && (field.layout || 'row') !== 'stack';
}

function dpPreviewChipLabel(o) {
  return typeof o === 'object' ? (o.l || o.v || '') : String(o || '');
}

function MsPreviewFieldA4({ field, inline, compact, value, onChange, compositeBag, onCompositeChange }) {
  const inputOnly = inline || field.type === 'checkbox';
  const MediaPreview = window.MsMediaFieldPreview;
  const yesNo = dpPreviewYesNoSubs(field);
  const isTile = field.type === 'composite' && dpPreviewIsCompositeTile(field);

  if (yesNo) {
    const selVal = (compositeBag || {})[yesNo.select.id] || '';
    const toggle = (opt) => {
      const next = selVal === opt ? '' : opt;
      onCompositeChange(yesNo.select.id, next);
    };
    return (
      <div className="dp-brief-preview-tile dp-brief-preview-tile--yn">
        <div className="dp-brief-preview-tile-label">{field.label}{field.required ? ' *' : ''}</div>
        <div className="dp-brief-preview-chips dp-brief-preview-chips--yn" role="group" aria-label={field.label}>
          {['Да', 'Нет'].map((opt) => (
            <button
              key={opt}
              type="button"
              className={'dp-brief-preview-chip' + (selVal === opt ? ' is-on' : '')}
              onClick={() => toggle(opt)}
              aria-pressed={selVal === opt}
            >
              {opt}
            </button>
          ))}
        </div>
      </div>
    );
  }

  if (isTile) {
    const bag = compositeBag || {};
    const patchSub = (subId, val) => onCompositeChange(subId, val);
    return (
      <div className="dp-brief-preview-tile">
        <div className="dp-brief-preview-tile-label">{field.label}{field.required ? ' *' : ''}</div>
        <div className={'dp-brief-preview-tile-body' + ((field.layout || 'row') === 'row' ? ' dp-brief-preview-tile-body--row' : '')}>
          {(field.fields || []).map((sub) => (
            <MsPreviewFieldA4
              key={sub.id}
              field={sub}
              inline
              compact
              value={bag[sub.id]}
              onChange={(v) => patchSub(sub.id, v)}
            />
          ))}
        </div>
      </div>
    );
  }

  const wrapCls = 'dp-brief-preview-field'
    + (inline ? ' dp-brief-preview-field-inline' : '')
    + (compact ? ' dp-brief-preview-field-compact' : '');

  return (
    <div className={wrapCls}>
      {!inputOnly && <div className="dp-brief-preview-field-label">{field.label}{field.required ? ' *' : ''}</div>}
      {field.type === 'text' && (
        <input
          className="dp-brief-preview-input"
          placeholder={field.placeholder || 'Введите значение…'}
          value={value || ''}
          onChange={(e) => onChange(e.target.value)}
        />
      )}
      {field.type === 'textarea' && (
        <textarea
          rows={compact ? 1 : 2}
          className="dp-brief-preview-input"
          placeholder={field.placeholder || ''}
          value={value || ''}
          onChange={(e) => onChange(e.target.value)}
        />
      )}
      {field.type === 'number' && (
        <input
          type="number"
          className="dp-brief-preview-input dp-brief-preview-input--narrow"
          placeholder="0"
          value={value == null ? '' : value}
          onChange={(e) => onChange(e.target.value === '' ? '' : Number(e.target.value))}
        />
      )}
      {field.type === 'checkbox' && (
        <label className="dp-brief-preview-check">
          <input type="checkbox" checked={!!value} onChange={(e) => onChange(e.target.checked)} />
          <span>{field.label}</span>
        </label>
      )}
      {field.type === 'select' && (
        compact
          ? (
            <div className="dp-brief-preview-chips dp-brief-preview-chips--compact" role="group" aria-label={field.label}>
              {(field.options || ['Да', 'Нет']).slice(0, 4).map((o, i) => {
                const lab = dpPreviewChipLabel(o);
                return (
                  <button
                    key={i}
                    type="button"
                    className={'dp-brief-preview-chip' + (value === lab ? ' is-on' : '')}
                    onClick={() => onChange(value === lab ? '' : lab)}
                    aria-pressed={value === lab}
                  >
                    {lab}
                  </button>
                );
              })}
            </div>
          )
          : (
            <select className="dp-brief-preview-input" value={value || ''} onChange={(e) => onChange(e.target.value)}>
              <option value="">— выберите —</option>
              {(field.options || []).map((o, i) => {
                const lab = dpPreviewChipLabel(o);
                return <option key={i} value={lab}>{lab}</option>;
              })}
            </select>
          )
      )}
      {(field.type === 'chips' || field.type === 'chip_single') && (
        <div className={'dp-brief-preview-chips' + (compact ? ' dp-brief-preview-chips--compact' : '')} role="group" aria-label={field.label}>
          {(field.options || ['Вариант']).map((o, i) => {
            const v = typeof o === 'object' ? (o.v || o.l) : o;
            const l = dpPreviewChipLabel(o);
            const arr = field.type === 'chips' ? (value || []) : null;
            const on = field.type === 'chips' ? arr.includes(l) : value === v;
            const click = () => {
              if (field.type === 'chips') {
                onChange(on ? arr.filter((x) => x !== l) : [...arr, l]);
              } else {
                onChange(on ? '' : v);
              }
            };
            return (
              <button key={i} type="button" className={'dp-brief-preview-chip' + (on ? ' is-on' : '')} onClick={click} aria-pressed={on}>
                {l}
              </button>
            );
          })}
        </div>
      )}
      {field.type === 'url_list' && (
        <textarea
          rows={2}
          className="dp-brief-preview-input"
          placeholder={field.placeholder || 'https://…'}
          value={Array.isArray(value) ? value.join('\n') : (value || '')}
          onChange={(e) => onChange(e.target.value.split('\n').map((s) => s.trim()).filter(Boolean))}
        />
      )}
      {field.type === 'media' && MediaPreview && (
        <MediaPreview field={field} />
      )}
      {field.type === 'composite' && (
        <div className="dp-brief-preview-composite-stack">
          {(field.fields || []).map((sub) => (
            <div key={sub.id} className="dp-brief-preview-composite-row">
              <div className="dp-brief-preview-field-label">{sub.label || 'Подполе'}{sub.unit ? ' · ' + sub.unit : ''}</div>
              <MsPreviewFieldA4
                field={sub}
                inline
                value={(compositeBag || {})[sub.id]}
                onChange={(v) => onCompositeChange(sub.id, v)}
              />
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

function DpBriefPreviewFieldGrid({ fields, answers, setAnswer }) {
  const L = window.DpBriefLayout;
  const rows = L && L.planFieldRows ? L.planFieldRows(fields) : [{ items: (fields || []).map((f) => ({ field: f, span: 4 })) }];
  return (
    <div className="dp-brief-preview-field-grid">
      {rows.map((row, ri) => (
        <div key={ri} className="dp-brief-preview-field-row">
          {row.items.map(({ field, span }) => {
            const bag = answers[field.id];
            const patchComposite = (subId, val) => {
              setAnswer(field.id, { ...(bag && typeof bag === 'object' ? bag : {}), [subId]: val });
            };
            return (
              <div key={field.id} className={'dp-brief-preview-field-cell dp-brief-preview-field-cell--span-' + span}>
                <MsPreviewFieldA4
                  field={field}
                  value={field.type === 'composite' ? undefined : answers[field.id]}
                  onChange={(v) => setAnswer(field.id, v)}
                  compositeBag={field.type === 'composite' ? (bag || {}) : undefined}
                  onCompositeChange={field.type === 'composite' ? patchComposite : undefined}
                />
              </div>
            );
          })}
        </div>
      ))}
    </div>
  );
}

function MsPreviewSectionA4({ section, wizardStyle, hideTitle, answers, setAnswer }) {
  if (section.kind === 'style_cards') {
    const votes = answers[section.id] || {};
    const toggleStyle = (cardId) => {
      const picked = Object.keys(votes).filter((k) => votes[k] === 'pick');
      const on = votes[cardId] === 'pick';
      if (on) {
        const next = { ...votes };
        delete next[cardId];
        setAnswer(section.id, next);
        return;
      }
      if (picked.length >= 3) return;
      setAnswer(section.id, { ...votes, [cardId]: 'pick' });
    };
    const pickedCount = Object.keys(votes).filter((k) => votes[k] === 'pick').length;
    return (
      <section className="dp-brief-preview-section dp-brief-preview-section--style">
        {!hideTitle && <h3 className="dp-brief-preview-section-title">{section.title || 'Стиль'}</h3>}
        {section.subtitle && <p className="dp-brief-preview-section-sub">{section.subtitle}</p>}
        <div className={'dp-brief-preview-style-grid' + (wizardStyle ? ' dp-brief-preview-style-grid--wizard' : '')}>
          {(section.cards || []).map((c) => {
            const on = votes[c.id] === 'pick';
            return (
              <button
                key={c.id}
                type="button"
                className={'dp-brief-preview-style-card' + (on ? ' is-picked' : '')}
                onClick={() => toggleStyle(c.id)}
                aria-pressed={on}
              >
                {dpCardCover(c)
                  ? <img src={dpCardCover(c)} alt="" />
                  : <div className="dp-brief-preview-style-placeholder" aria-hidden="true">📷</div>}
                <span>{c.title}</span>
                {on && <span className="dp-brief-preview-style-check" aria-hidden="true">✓</span>}
              </button>
            );
          })}
        </div>
        {wizardStyle && (
          <p className="dp-brief-preview-style-hint">
            Выбрано {pickedCount} из 3. Нажмите на карточку, чтобы отметить стиль.
          </p>
        )}
      </section>
    );
  }
  if (section.kind === 'room_group') {
    const label = (section.labelTemplate || 'Помещение {n}').replace('{n}', '1');
    return (
      <section className="dp-brief-preview-section">
        {!hideTitle && <h3 className="dp-brief-preview-section-title">{section.title || 'Помещения'}</h3>}
        <div className="dp-brief-preview-room">
          <div className="dp-brief-preview-room-title">{label}</div>
          <DpBriefPreviewFieldGrid fields={section.fields || []} answers={answers} setAnswer={setAnswer} />
        </div>
        <button type="button" className="dp-brief-preview-room-add-btn" tabIndex={-1} disabled>
          {section.addLabel || '+ Добавить помещение'}
        </button>
      </section>
    );
  }
  const showSubTitle = !hideTitle && section.title;
  return (
    <section className="dp-brief-preview-section">
      {showSubTitle && (
        <h3 className="dp-brief-preview-section-title">
          {section.title || sectionKindMeta(section.kind).l}
        </h3>
      )}
      <DpBriefPreviewFieldGrid fields={section.fields || []} answers={answers} setAnswer={setAnswer} />
    </section>
  );
}

function DpBriefPreviewWizardStep({ step, showSectionTitles, answers, setAnswer }) {
  return (
    <div className="dp-brief-preview-step">
      {(step.sections || []).map((sec, i) => (
        <MsPreviewSectionA4
          key={(sec.id || sec.kind) + '-' + i}
          section={sec}
          wizardStyle={step.mode === 'style'}
          hideTitle={!showSectionTitles && step.sections.length === 1}
          answers={answers}
          setAnswer={setAnswer}
        />
      ))}
    </div>
  );
}

function DpBriefClientPreviewDialog({ schema, open, onClose }) {
  const dlgRef = dpUseRef(null);
  const titleId = 'dp-brief-preview-title-' + (schema.id || 'draft');
  const M = window.DpBriefTemplateMock;
  const L = window.DpBriefLayout;
  const [presetTick, setPresetTick] = dpUseState(0);
  const [stepIdx, setStepIdx] = dpUseState(0);
  const [answers, setAnswers] = dpUseState({});

  const setAnswer = (id, val) => setAnswers((prev) => ({ ...prev, [id]: val }));

  dpUseEffect(() => {
    const h = () => setPresetTick((t) => t + 1);
    window.addEventListener('remontpro:brief-field-presets-updated', h);
    return () => window.removeEventListener('remontpro:brief-field-presets-updated', h);
  }, []);

  const previewSchema = (M && M.materializeSchema)
    ? M.materializeSchema(schema, { _presetTick: presetTick })
    : schema;

  const steps = (L && L.buildWizardSteps)
    ? L.buildWizardSteps(previewSchema.sections || [])
    : (previewSchema.sections || []).map((sec, i) => ({
      id: 'step-' + i,
      title: sec.title || 'Раздел',
      sections: [sec],
      mode: sec.kind === 'style_cards' ? 'style' : 'fields',
    }));

  const total = steps.length;
  const safeIdx = total ? Math.min(stepIdx, total - 1) : 0;
  const current = steps[safeIdx];
  const isFirst = safeIdx === 0;
  const isLast = safeIdx >= total - 1;

  dpUseEffect(() => {
    if (open) {
      setStepIdx(0);
      setAnswers({});
    }
  }, [open, schema.id]);

  dpUseEffect(() => {
    const dlg = dlgRef.current;
    if (!dlg) return;
    if (open) {
      if (!dlg.open) dlg.showModal();
    } else if (dlg.open) {
      dlg.close();
    }
  }, [open]);

  if (typeof document === 'undefined') return null;

  return dpCreatePortal(
    <dialog
      ref={dlgRef}
      className="dp-brief-preview-dialog"
      aria-labelledby={titleId}
      onCancel={(e) => { e.preventDefault(); onClose(); }}
      onClose={onClose}
      onClick={(e) => { if (e.target === dlgRef.current) onClose(); }}
    >
      <div className="dp-brief-preview-scroll" onClick={(e) => e.stopPropagation()}>
        <div className="dp-brief-preview-toolbar">
          <button type="button" className="dp-brief-preview-close" onClick={onClose} aria-label="Закрыть">
            {Icon.x}
          </button>
        </div>
        <article className="dp-brief-preview-sheet dp-brief-preview-sheet--wizard">
          <header className="dp-brief-preview-header dp-brief-preview-header--compact">
            <h2 id={titleId}>{previewSchema.title || schema.title || 'Бриф'}</h2>
            <p className="dp-brief-preview-meta">Бриф на дизайн-проект · вид клиента</p>
          </header>
          <div className="dp-brief-preview-body dp-brief-preview-body--wizard">
            <DpBriefPreviewStepTrack steps={steps} activeIdx={safeIdx} onGo={setStepIdx} />
            {current
              ? (
                <DpBriefPreviewWizardStep
                  key={current.id}
                  step={current}
                  showSectionTitles={(current.sections || []).length > 1}
                  answers={answers}
                  setAnswer={setAnswer}
                />
              )
              : <p className="dp-brief-preview-empty">В брифе пока нет секций</p>}
          </div>
          <footer className="dp-brief-preview-footer dp-brief-preview-footer--wizard">
            <div className="dp-brief-preview-nav">
              <button
                type="button"
                className="dp-brief-preview-nav-btn dp-brief-preview-nav-btn--back"
                disabled={isFirst}
                onClick={() => setStepIdx((i) => Math.max(0, i - 1))}
              >
                Назад
              </button>
              {!isLast ? (
                <button
                  type="button"
                  className="dp-brief-preview-nav-btn dp-brief-preview-nav-btn--next"
                  onClick={() => setStepIdx((i) => Math.min(total - 1, i + 1))}
                >
                  Далее
                </button>
              ) : (
                <button type="button" className="dp-brief-preview-submit" disabled tabIndex={-1}>
                  Отправить бриф
                </button>
              )}
            </div>
          </footer>
        </article>
      </div>
    </dialog>,
    document.body
  );
}

const IconEye = (
  <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
    <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" />
    <circle cx="12" cy="12" r="3" />
  </svg>
);

function MsPreviewSection({ section }) {
  if (section.kind === 'style_cards') {
    return (
      <div style={{ marginBottom: 16 }}>
        <div style={{ fontSize: 14, fontWeight: 700, color: '#1a1714', marginBottom: 4 }}>{section.title || 'Стиль'}</div>
        {section.subtitle && <div style={{ fontSize: 12, color: '#8a817a', marginBottom: 10, lineHeight: 1.4 }}>{section.subtitle}</div>}
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(110px, 1fr))', gap: 8 }}>
          {(section.cards || []).slice(0, 4).map((c) => (
            <div key={c.id} style={{ borderRadius: 10, overflow: 'hidden', border: '.5px solid #ece5da', background: '#fff' }}>
              <div style={{ height: 52, background: '#ece5da', overflow: 'hidden' }}>
                {dpCardCover(c)
                  ? <img src={dpCardCover(c)} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
                  : <div style={{ height: '100%', display: 'grid', placeItems: 'center', color: '#a89e92', fontSize: 16 }}>📷</div>}
              </div>
              <div style={{ padding: '6px 8px', fontSize: 11, fontWeight: 600 }}>{c.title}</div>
            </div>
          ))}
        </div>
      </div>
    );
  }
  if (section.kind === 'readonly_header') {
    return (
      <div style={{ marginBottom: 16, padding: '12px 14px', background: '#fff', borderRadius: 10, border: '.5px solid #ece5da' }}>
        <div style={{ fontSize: 12, fontWeight: 700, color: '#a89e92', textTransform: 'uppercase', letterSpacing: '.05em', marginBottom: 8 }}>{section.title || 'Объект'}</div>
        {(section.fields || []).map((f) => (
          <div key={f.id} style={{ display: 'flex', justifyContent: 'space-between', gap: 8, fontSize: 12.5, marginBottom: 4 }}>
            <span style={{ color: '#8a817a' }}>{f.label}</span>
            <span style={{ color: '#1a1714', fontWeight: 600 }}>из профиля</span>
          </div>
        ))}
      </div>
    );
  }
  if (section.kind === 'room_group') {
    const label = (section.labelTemplate || 'Помещение {n}').replace('{n}', '1');
    return (
      <div style={{ marginBottom: 16 }}>
        <div style={{ fontSize: 14, fontWeight: 700, color: '#1a1714', marginBottom: 8 }}>{section.title || 'Помещения'}</div>
        <div style={{ padding: '12px 14px', background: '#fff', borderRadius: 10, border: '.5px solid #ece5da', marginBottom: 8 }}>
          <div style={{ fontSize: 13, fontWeight: 600, color: '#c2410c', marginBottom: 10 }}>{label}</div>
          {(section.fields || []).map((f) => <MsPreviewField key={f.id} field={f} />)}
        </div>
        <div style={{ fontSize: 12, color: '#8a817a', fontStyle: 'italic' }}>{section.addLabel || '+ Добавить помещение'}</div>
      </div>
    );
  }
  return (
    <div style={{ marginBottom: 16 }}>
      <div style={{ fontSize: 14, fontWeight: 700, color: '#1a1714', marginBottom: 8 }}>{section.title || sectionKindMeta(section.kind).l}</div>
      {(section.fields || []).map((f) => <MsPreviewField key={f.id} field={f} />)}
    </div>
  );
}

function MsFieldEditor({ field, idx, total, onPatch, onDelete, onMove }) {
  const msDefaultMediaField = window.msDefaultMediaField;
  const MsMediaFieldConfigPanel = window.MsMediaFieldConfigPanel;
  const setType = (nextType) => {
    if (nextType === 'composite') {
      onPatch(field.id, msEnsureComposite({ ...field, type: 'composite' }));
      return;
    }
    if (nextType === 'media' && msDefaultMediaField) {
      onPatch(field.id, msDefaultMediaField({ ...field, type: 'media', fields: undefined, layout: undefined }));
      return;
    }
    onPatch(field.id, { type: nextType, fields: undefined, layout: undefined });
  };
  const hasOptions = field.type === 'select' || field.type === 'chips' || field.type === 'chip_single';

  return (
    <div className="ms-field-editor">
      <div className="ms-field-editor-row">
        <select className="ms-field-editor-type" value={field.type} onChange={(e) => setType(e.target.value)} aria-label="Тип поля">
          {MS_FIELD_TYPES.map((t) => <option key={t.v} value={t.v}>{t.icon} {t.l}</option>)}
        </select>
        <input className="ms-field-editor-label" value={field.label} onChange={(e) => onPatch(field.id, { label: e.target.value })} placeholder="Название поля" />
        {field.type === 'chips' && (
          <label className="ms-field-editor-flag">
            <input type="checkbox" checked={!!field.allowCustom} onChange={(e) => onPatch(field.id, { allowCustom: e.target.checked, customId: field.customId || (field.id + 'Custom') })} />
            <span>Своё</span>
          </label>
        )}
        <label className="ms-field-editor-flag is-req" title="Обязательное поле">
          <input type="checkbox" checked={!!field.required} onChange={(e) => onPatch(field.id, { required: e.target.checked })} style={{ accentColor: '#f04e62' }} />
          <span>*</span>
        </label>
        <div className="ms-field-editor-actions">
          <button type="button" onClick={() => onMove(field.id, -1)} disabled={idx === 0} style={{ ...ctorStyles.iconBtnSm, opacity: idx === 0 ? .3 : 1 }} aria-label="Выше">
            <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><polyline points="18 15 12 9 6 15"></polyline></svg>
          </button>
          <button type="button" onClick={() => onMove(field.id, 1)} disabled={idx === total - 1} style={{ ...ctorStyles.iconBtnSm, opacity: idx === total - 1 ? .3 : 1 }} aria-label="Ниже">
            <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><polyline points="6 9 12 15 18 9"></polyline></svg>
          </button>
          <button type="button" onClick={() => onDelete(field.id)} style={{ ...ctorStyles.iconBtnSm, color: '#f04e62' }} aria-label="Удалить">{Icon.x}</button>
        </div>
      </div>
      {hasOptions && (
        <OptionsListEditor label="Варианты" options={field.options} onChange={(opts) => onPatch(field.id, { options: opts })}
          placeholder="Подпись" labeled compact className="ms-field-editor-extra" />
      )}
      {(field.type === 'text' || field.type === 'number') && (
        <input className="ms-field-editor-extra" value={field.placeholder || ''} onChange={(e) => onPatch(field.id, { placeholder: e.target.value })} placeholder="Placeholder (необязательно)" />
      )}
      {field.type === 'media' && MsMediaFieldConfigPanel && (
        <MsMediaFieldConfigPanel field={field} onPatch={onPatch} />
      )}
      {field.type === 'composite' && (
        <MsCompositeEditor field={field} onPatch={onPatch} />
      )}
    </div>
  );
}

function MsSectionEditor({ section, secIdx, totalSecs, onChange, onDelete, onMove, briefPropertyTypes }) {
  const uid = () => 'f-' + Math.random().toString(36).slice(2, 7);
  const meta = sectionKindMeta(section.kind);
  const [insertOpen, setInsertOpen] = dpUseState(false);
  const [insertMode, setInsertMode] = dpUseState('preset');
  const [libTick, setLibTick] = dpUseState(0);

  dpUseEffect(() => {
    const h = () => setLibTick((t) => t + 1);
    window.addEventListener('remontpro:brief-field-presets-updated', h);
    return () => window.removeEventListener('remontpro:brief-field-presets-updated', h);
  }, []);

  const setFields = (fn) => onChange({ ...section, fields: fn(section.fields || []) });
  const setPresetRefs = (fn) => onChange({ ...section, presetRefs: fn(section.presetRefs || []) });
  const patchField = (id, p) => setFields((fields) => fields.map((f) => (f.id === id ? { ...f, ...p } : f)));
  const delField = (id) => setFields((fields) => fields.filter((f) => f.id !== id));
  const moveField = (id, dir) => setFields((fields) => {
    const idx = fields.findIndex((f) => f.id === id);
    if (idx < 0) return fields;
    const next = [...fields];
    const swap = idx + dir;
    if (swap < 0 || swap >= next.length) return fields;
    [next[idx], next[swap]] = [next[swap], next[idx]];
    return next;
  });
  const addField = () => setFields((fields) => [...fields, { id: uid(), type: 'text', label: '', required: false }]);

  const insertPresetRef = (ref) => setPresetRefs((refs) => [...refs, ref]);

  const insertGroupRefs = (groupId, propertyTypesOverride) => {
    const P = window.BriefFieldPresetsMock;
    if (!P) return;
    const groups = P.loadGroupsObject();
    const presets = P.listPresets(groupId).filter((p) => P.presetVisibleInBrief(p, groups, briefPropertyTypes || []));
    if (!presets.length) {
      alert('Нет подходящих заготовок в этой группе');
      return;
    }
    if (presets.length > 10 && !confirm('Вставить ' + presets.length + ' заготовок?')) return;
    setPresetRefs((refs) => [
      ...refs,
      ...presets.map((p) => dpMakeLinkedRef(p.id, propertyTypesOverride || [])),
    ]);
  };

  const patchPresetRef = (refId, patch) => setPresetRefs((refs) => refs.map((r) => (r.id === refId ? { ...r, ...patch } : r)));
  const delPresetRef = (refId) => setPresetRefs((refs) => refs.filter((r) => r.id !== refId));

  const openInsert = (mode) => { setInsertMode(mode); setInsertOpen(true); };
  const cardUid = () => 'sc-' + Math.random().toString(36).slice(2, 7);
  const setCards = (fn) => onChange({ ...section, cards: fn(section.cards || []) });
  const patchCard = (id, p) => setCards((cards) => cards.map((c) => (c.id === id ? { ...c, ...p } : c)));
  const delCard = (id) => setCards((cards) => cards.filter((c) => c.id !== id));
  const addCard = () => setCards((cards) => [...cards, { id: cardUid(), title: 'Новый стиль', sub: '', images: [] }]);
  const setCardImages = (id, images) => patchCard(id, { images });
  const addCardImage = (id) => {
    const card = (section.cards || []).find((c) => c.id === id);
    setCardImages(id, [...dpCardImages(card), '']);
  };
  const patchCardImage = (id, idx, url) => {
    const card = (section.cards || []).find((c) => c.id === id);
    const next = [...dpCardImages(card)];
    next[idx] = url;
    setCardImages(id, next);
  };
  const delCardImage = (id, idx) => {
    const card = (section.cards || []).find((c) => c.id === id);
    setCardImages(id, dpCardImages(card).filter((_, i) => i !== idx));
  };

  if (section.kind === 'style_cards') {
    return (
      <div style={msStyles.sectionCard}>
        <div style={msStyles.sectionHead}>
          <span style={msStyles.kindBadge(section.kind)}>{meta.icon} {meta.l}</span>
          <input value={section.title || ''} onChange={(e) => onChange({ ...section, title: e.target.value })} placeholder="Заголовок"
            style={{ ...insStyles.field, flex: 1, minWidth: 140, fontWeight: 600 }} />
          <div style={{ display: 'flex', gap: 4, marginLeft: 'auto' }}>
            <button onClick={() => onMove(secIdx, -1)} disabled={secIdx === 0} style={{ ...ctorStyles.iconBtnSm, opacity: secIdx === 0 ? .3 : 1 }}><svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><polyline points="18 15 12 9 6 15"></polyline></svg></button>
            <button onClick={() => onMove(secIdx, 1)} disabled={secIdx === totalSecs - 1} style={{ ...ctorStyles.iconBtnSm, opacity: secIdx === totalSecs - 1 ? .3 : 1 }}><svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><polyline points="6 9 12 15 18 9"></polyline></svg></button>
            <button onClick={onDelete} style={{ ...ctorStyles.iconBtnSm, color: '#f04e62' }}>{Icon.x}</button>
          </div>
        </div>
        <input value={section.subtitle || ''} onChange={(e) => onChange({ ...section, subtitle: e.target.value })} placeholder="Подзаголовок секции"
          style={{ ...insStyles.field, marginBottom: 10, fontSize: 12.5 }} />
        <div style={{ fontSize: 12, color: '#a89e92', marginBottom: 8 }}>{meta.hint} · id ответа: <code>{section.id || 'styleVotes'}</code></div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
          {(section.cards || []).map((c) => (
            <div key={c.id} style={{ display: 'grid', gridTemplateColumns: '72px 1fr 1fr auto', gap: 8, alignItems: 'start', padding: '10px 12px', background: '#fff', borderRadius: 10, border: '.5px solid #ece5da' }}>
              <div style={{ height: 48, borderRadius: 8, background: '#ece5da', overflow: 'hidden' }} title="Превью референса">
                {dpCardCover(c)
                  ? <img src={dpCardCover(c)} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
                  : <div style={{ height: '100%', display: 'grid', placeItems: 'center', color: '#a89e92', fontSize: 18 }}>📷</div>}
              </div>
              <input value={c.title || ''} onChange={(e) => patchCard(c.id, { title: e.target.value })} placeholder="Название стиля" style={{ ...insStyles.field, fontSize: 12.5 }} />
              <input value={c.sub || ''} onChange={(e) => patchCard(c.id, { sub: e.target.value })} placeholder="Краткое описание" style={{ ...insStyles.field, fontSize: 12.5 }} />
              <button onClick={() => delCard(c.id)} style={{ ...ctorStyles.iconBtnSm, color: '#f04e62' }}>{Icon.x}</button>
              <div style={{ gridColumn: '1 / -1', display: 'flex', flexDirection: 'column', gap: 6 }}>
                <div style={{ fontSize: 11.5, fontWeight: 700, color: '#6b6259' }}>Галерея референсов · примеры интерьера</div>
                {dpCardImages(c).map((url, idx) => (
                  <div key={idx} style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
                    <input value={url} onChange={(e) => patchCardImage(c.id, idx, e.target.value)} placeholder="URL фото интерьера (Pinterest, Behance, CDN…)"
                      style={{ ...insStyles.field, flex: 1, fontSize: 11.5 }} />
                    <button type="button" onClick={() => delCardImage(c.id, idx)} style={{ ...ctorStyles.iconBtnSm, color: '#f04e62' }}>{Icon.x}</button>
                  </div>
                ))}
                <button type="button" onClick={() => addCardImage(c.id)} style={{ ...ctorStyles.btnDashed, alignSelf: 'flex-start', fontSize: 12 }}>{Icon.plus} Фото референса</button>
              </div>
            </div>
          ))}
        </div>
        <button onClick={addCard} style={{ ...ctorStyles.btnDashed, alignSelf: 'flex-start', marginTop: 10, fontSize: 12.5 }}>{Icon.plus} Карточка стиля</button>
      </div>
    );
  }

  return (
    <div style={msStyles.sectionCard}>
      <div style={msStyles.sectionHead}>
        <span style={msStyles.kindBadge(section.kind)}>{meta.icon} {meta.l}</span>
        <input value={section.title || ''} onChange={(e) => onChange({ ...section, title: e.target.value })} placeholder="Заголовок секции"
          style={{ ...insStyles.field, flex: 1, minWidth: 140, fontWeight: 600 }} />
        <div style={{ display: 'flex', gap: 4, marginLeft: 'auto' }}>
          <button onClick={() => onMove(secIdx, -1)} disabled={secIdx === 0} style={{ ...ctorStyles.iconBtnSm, opacity: secIdx === 0 ? .3 : 1 }}>
            <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><polyline points="18 15 12 9 6 15"></polyline></svg>
          </button>
          <button onClick={() => onMove(secIdx, 1)} disabled={secIdx === totalSecs - 1} style={{ ...ctorStyles.iconBtnSm, opacity: secIdx === totalSecs - 1 ? .3 : 1 }}>
            <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><polyline points="6 9 12 15 18 9"></polyline></svg>
          </button>
          <button onClick={onDelete} style={{ ...ctorStyles.iconBtnSm, color: '#f04e62' }} title="Удалить секцию">{Icon.x}</button>
        </div>
      </div>
      {section.kind === 'room_group' && (
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(160px, 1fr))', gap: 10, marginBottom: 12, padding: '10px 12px', background: '#fff0e6', borderRadius: 9, border: '.5px solid #ffd9bf' }}>
          <label style={{ fontSize: 12.5, color: '#6b6259' }}>
            Подпись элемента
            <input value={section.labelTemplate || ''} onChange={(e) => onChange({ ...section, labelTemplate: e.target.value })}
              placeholder="Помещение {n}"
              style={{ ...insStyles.field, marginTop: 4, fontSize: 12.5 }} />
          </label>
          <label style={{ fontSize: 12.5, color: '#6b6259' }}>
            Кнопка добавления
            <input value={section.addLabel || ''} onChange={(e) => onChange({ ...section, addLabel: e.target.value })}
              placeholder="+ Добавить помещение"
              style={{ ...insStyles.field, marginTop: 4, fontSize: 12.5 }} />
          </label>
          <label style={{ fontSize: 12.5, color: '#6b6259' }}>
            Мин. помещений
            <input type="number" min={1} max={20} value={section.minItems == null ? 1 : section.minItems}
              onChange={(e) => onChange({ ...section, minItems: +e.target.value })}
              style={{ ...insStyles.field, marginTop: 4, width: 72, fontSize: 12.5 }} />
          </label>
        </div>
      )}
      <div style={{ fontSize: 12, color: '#a89e92', marginBottom: 8 }}>{meta.hint}</div>
      {(section.kind === 'fields' || section.kind === 'mini_survey') && (section.presetRefs || []).length > 0 && (
        <div style={{ marginBottom: 12 }}>
          <div style={{ fontSize: 12, fontWeight: 700, color: '#6b6259', marginBottom: 8 }}>Заготовки из библиотеки</div>
          {(section.presetRefs || []).map((ref) => (
            <DpPresetRefCard
              key={ref.id + '-' + libTick}
              refItem={ref}
              briefPropertyTypes={briefPropertyTypes}
              onChange={(next) => patchPresetRef(ref.id, next)}
              onDelete={() => delPresetRef(ref.id)}
            />
          ))}
        </div>
      )}
      {(section.kind === 'fields' || section.kind === 'mini_survey') && (section.fields || []).length > 0 && (section.presetRefs || []).length > 0 && (
        <div style={{ fontSize: 12, fontWeight: 700, color: '#6b6259', marginBottom: 8 }}>Собственные поля</div>
      )}
      <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
        {(section.fields || []).map((f, idx) => (
          <MsFieldEditor key={f.id} field={f} idx={idx} total={(section.fields || []).length}
            onPatch={patchField} onDelete={delField} onMove={moveField} />
        ))}
      </div>
      {(section.kind === 'fields' || section.kind === 'mini_survey') ? (
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, marginTop: 10 }}>
          <button type="button" onClick={() => openInsert('preset')} style={{ ...ctorStyles.btnDashed, fontSize: 12.5 }}>{Icon.plus} Заготовка</button>
          <button type="button" onClick={() => openInsert('group')} style={{ ...ctorStyles.btnDashed, fontSize: 12.5 }}>{Icon.plus} Группа</button>
          <button onClick={addField} style={{ ...ctorStyles.btnDashed, fontSize: 12.5 }}>{Icon.plus} Поле в секции</button>
        </div>
      ) : (
        <button onClick={addField} style={{ ...ctorStyles.btnDashed, alignSelf: 'flex-start', marginTop: 10, fontSize: 12.5 }}>{Icon.plus} Поле в секции</button>
      )}
      {(section.kind === 'fields' || section.kind === 'mini_survey') && insertOpen && (
        <DpPresetInsertDialog
          open={insertOpen}
          onClose={() => setInsertOpen(false)}
          briefPropertyTypes={briefPropertyTypes}
          onInsertPreset={insertPresetRef}
          onInsertGroup={insertGroupRefs}
        />
      )}
    </div>
  );
}

function DpBriefPropertyTypesField({ schema, onChange }) {
  const Q = window.QuestionnaireSetMock;
  const types = Q ? Q.getPropertyTypes() : [];
  const selected = new Set(schema.propertyTypes || []);
  const defaults = new Set(schema.defaultForTypes || []);

  const apply = (propertyTypes, defaultForTypes) => {
    onChange({ ...schema, propertyTypes, defaultForTypes });
  };

  const cycleType = (pt) => {
    const inPool = selected.has(pt);
    const isDef = defaults.has(pt);
    if (!inPool) {
      apply([...selected, pt], [...defaults]);
      return;
    }
    if (!isDef) {
      apply([...selected], [...defaults, pt]);
      return;
    }
    apply([...selected].filter((x) => x !== pt), [...defaults].filter((x) => x !== pt));
  };

  return (
    <div style={{
      padding: '11px 14px',
      marginTop: 12,
      background: '#faf8f5',
      borderRadius: 10,
      border: '.5px solid #ece5da',
    }}>
      <div style={{ fontSize: 12, fontWeight: 600, color: '#6b6259', marginBottom: 8 }}>Типы объекта</div>
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
        {types.map((t) => {
          const inPool = selected.has(t.id);
          const isDef = defaults.has(t.id);
          return (
            <button
              key={t.id}
              type="button"
              onClick={() => cycleType(t.id)}
              title={!inPool ? 'Добавить в воронку' : (isDef ? 'Убрать из типа' : 'Сделать дефолтом для materialize')}
              style={{
                padding: '6px 11px',
                borderRadius: 980,
                border: '.5px solid ' + (inPool ? (isDef ? '#c2410c' : '#e8793a') : '#ece5da'),
                background: inPool ? (isDef ? '#ffe4cc' : '#fff0e6') : '#fff',
                color: inPool ? '#c2410c' : '#5c5249',
                font: 'inherit',
                fontSize: 12.5,
                fontWeight: 600,
                cursor: 'pointer',
                display: 'inline-flex',
                alignItems: 'center',
                gap: 5,
              }}
            >
              {t.label}
              {isDef && <span style={{ fontSize: 10, fontWeight: 800, opacity: 0.85 }}>авто</span>}
            </button>
          );
        })}
        {!types.length && <span style={{ fontSize: 12, color: '#a89e92' }}>Нет типов объекта</span>}
      </div>
      <div style={{ fontSize: 11.5, color: '#8a817a', marginTop: 8, lineHeight: 1.45 }}>
        Клик: добавить в воронку · ещё раз: дефолт для ЛК · ещё раз: убрать
      </div>
    </div>
  );
}

function DpBriefSchemaEditor({ schema, onChange }) {
  const sections = editableSections(schema);
  const [previewOpen, setPreviewOpen] = dpUseState(false);
  const setSections = (next) => onChange({ ...schema, sections: next });
  const patchSection = (idx, patch) => setSections(sections.map((s, i) => (i === idx ? { ...s, ...patch } : s)));
  const deleteSection = (idx) => setSections(sections.filter((_, i) => i !== idx));
  const moveSection = (idx, dir) => {
    const next = [...sections];
    const swap = idx + dir;
    if (swap < 0 || swap >= next.length) return;
    [next[idx], next[swap]] = [next[swap], next[idx]];
    setSections(next);
  };
  const addSection = (kind) => {
    const M2 = window.DpBriefTemplateMock;
    let block;
    if (kind === 'style_cards') block = M2 ? M2.blankStyleCards() : { kind: 'style_cards', id: 'styleVotes', title: 'Стиль', cards: [] };
    else if (kind === 'room_group') block = M2 ? M2.blankRoomGroup() : { kind: 'room_group', id: 'zones', title: 'Зоны', fields: [] };
    else if (kind === 'mini_survey') block = M2 ? M2.blankMiniSurvey() : { kind: 'mini_survey', title: 'Мини-опросник', fields: [] };
    else block = M2 ? M2.blankFieldsSection('Блок полей') : { kind: 'fields', title: 'Блок полей', fields: [] };
    setSections([...sections, block]);
  };
  return (
    <>
      <div style={dpBriefEditorShell}>
        <div style={{ padding: '18px 22px 14px', borderBottom: '.5px solid #f0ece5', display: 'flex', alignItems: 'flex-start', gap: 16, flexWrap: 'wrap' }}>
          <div style={{ flex: 1, minWidth: 200 }}>
            <input value={schema.title || ''} onChange={(e) => onChange({ ...schema, title: e.target.value })} placeholder="Название брифа"
              style={{ ...insStyles.field, fontSize: 17, fontWeight: 600, background: 'transparent', border: 'none', padding: '0 0 4px', color: '#1a1714', width: '100%' }} />
            <div style={{ fontSize: 12.5, color: '#8a817a', marginTop: 6, lineHeight: 1.45 }}>
              Секции брифа. Ниже выберите типы объекта, для которых шаблон попадёт в воронку анкет.
            </div>
            <DpBriefPropertyTypesField schema={schema} onChange={onChange} />
          </div>
          <button type="button" className="dp-brief-preview-open" onClick={() => setPreviewOpen(true)} aria-haspopup="dialog">
            {IconEye} Открыть вид клиента
          </button>
        </div>
        <div style={{ padding: '16px 22px 20px' }}>
          {sections.map((sec, idx) => (
            <MsSectionEditor key={(sec.id || sec.kind) + '-' + idx} section={sec} secIdx={idx} totalSecs={sections.length}
              briefPropertyTypes={schema.propertyTypes || []}
              onChange={(patch) => patchSection(idx, patch)} onDelete={() => deleteSection(idx)} onMove={moveSection} />
          ))}
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, marginTop: 4 }}>
            {DP_SECTION_KINDS.map((k) => (
              <button key={k.v} onClick={() => addSection(k.v)} style={{ ...ctorStyles.btnDashed, fontSize: 12.5 }}>{Icon.plus} {k.icon} {k.l}</button>
            ))}
          </div>
        </div>
      </div>
      <DpBriefClientPreviewDialog schema={schema} open={previewOpen} onClose={() => setPreviewOpen(false)} />
    </>
  );
}

function DpBriefsPanel({ briefs, setBriefs }) {
  const M = window.DpBriefTemplateMock;
  const [activeId, setActiveId] = dpUseState(briefs[0]?.id || null);
  const active = briefs.find((m) => m.id === activeId) || null;
  const refresh = () => { const next = loadDpBriefs(); setBriefs(next); return next; };
  const update = (updated) => { if (M) M.upsertSchema(updated); refresh(); };
  const add = () => { const nm = M ? M.createSchema() : null; refresh(); if (nm) setActiveId(nm.id); };
  const del = (id) => { if (!confirm('Удалить шаблон брифа?')) return; if (M) M.deleteSchema(id); const next = refresh(); if (activeId === id) setActiveId(next[0]?.id || null); };
  const dup = (id) => { if (!M) return; const copy = M.duplicateSchema(id); refresh(); setActiveId(copy.id); };
  const sectionCount = (schema) => editableSections(schema).length;
  const fieldCount = (schema) => (M ? M.sectionFieldCount(schema) : 0);
  const PresetsBlock = window.BriefPresetsSidebarBlock;

  return (
    <div style={{ display: 'flex', gap: 22, alignItems: 'flex-start' }}>
      <div className="dp-briefs-sidebar">
        <section className="dp-sidebar-block is-briefs">
          <div className="dp-sidebar-block-head">
            <span className="dp-sidebar-block-title">Брифы ДП</span>
            <button type="button" onClick={add} className="dp-sidebar-icon-btn is-create" title="Создать бриф" aria-label="Создать бриф">{Icon.plus}</button>
          </div>
          <div className="dp-sidebar-block-scroll" role="list" aria-label="Шаблоны брифов">
            {briefs.map((m) => {
              const isActive = m.id === activeId;
              return (
              <div
                key={m.id}
                onClick={() => setActiveId(m.id)}
                className={'dp-sidebar-list-item' + (isActive ? ' is-active' : '')}
                role="listitem"
              >
                <div className="dp-sidebar-list-item-main">
                  <div className="dp-sidebar-list-item-title">{m.title || m.id}</div>
                  <div className="dp-sidebar-list-item-meta">
                    {sectionCount(m)} сек. · {fieldCount(m)} эл.
                    {(m.propertyTypes || []).length > 0 && (
                      <span> · {(m.propertyTypes || []).length} тип.</span>
                    )}
                  </div>
                </div>
                <div className="dp-sidebar-list-item-actions">
                  <button type="button" onClick={(e) => { e.stopPropagation(); dup(m.id); }} title="Дублировать" className="dp-sidebar-list-icon-btn">{Icon.copy}</button>
                  <button type="button" onClick={(e) => { e.stopPropagation(); del(m.id); }} title="Удалить" className="dp-sidebar-list-icon-btn is-muted">{Icon.x}</button>
                </div>
              </div>
            );})}
            {!briefs.length && (
              <button type="button" onClick={add} className="dp-sidebar-create-row" aria-label="Создать бриф">
                {Icon.plus}
              </button>
            )}
          </div>
        </section>
        {PresetsBlock ? <PresetsBlock /> : null}
      </div>
      <div style={{ flex: 1, minWidth: 0 }}>
        {active ? <DpBriefSchemaEditor schema={active} onChange={update} /> : (
          <div style={{ padding: '64px 32px', textAlign: 'center', color: '#a89e92' }}>
            <div style={{ fontSize: 40, marginBottom: 12 }}>🎨</div>
            <div style={{ fontSize: 16, fontWeight: 600, marginBottom: 6 }}>Выберите бриф</div>
            <div style={{ fontSize: 13.5, maxWidth: 360, margin: '0 auto', lineHeight: 1.5 }}>Соберите секции и отметьте типы объекта прямо в шаблоне.</div>
          </div>
        )}
      </div>
    </div>
  );
}

Object.assign(window, { DpBriefsPanel, loadDpBriefs, saveDpBriefs, MsFieldEditor, MS_FIELD_TYPES });
