// Per-layout-type editor forms + LayoutEditorRightPanel (right column of Layout editor).

// ── LayoutEditorRightPanel ──────────────────────────────────────────────────
// Module preview — a scrubable view of every layout in the module, so the
// author can see how the layout they're editing fits the rest of the module.
// Clicking "Edit" on any thumbnail row swaps the editor to that layout.
function LayoutEditorRightPanel({ context, layout, layoutType, module, onSelectLayout, allDrafts }) {
  return (
    <aside style={{
      borderLeft: '1px solid var(--border)', background: 'var(--surface)',
      display: 'flex', flexDirection: 'column', minHeight: 0,
    }} aria-label="Module preview">
      <div style={{
        padding: '12px 16px',
        borderBottom: '1px solid var(--border)',
        display: 'flex', alignItems: 'center', gap: 8,
      }}>
        <I.Layers size={13} style={{ color: 'var(--text-muted)' }} />
        <span style={{ fontSize: 12.5, fontWeight: 600, color: 'var(--text)' }}>
          Module preview
        </span>
        <div style={{ flex: 1 }} />
        <span style={{ fontSize: 11, color: 'var(--text-faint)',
          fontFamily: 'var(--font-mono)' }}>
          {module?.id} · {(module?.layouts || []).length} layouts
        </span>
      </div>
      <InlineModulePreview module={module} currentLayoutId={layout?.id}
        currentDraft={layout} allDrafts={allDrafts}
        onSelectLayout={onSelectLayout} />
    </aside>
  );
}

// ── InlineModulePreview ────────────────────────────────────────────────────
// A focused larger preview at top + a list of every layout in the module
// below. Each row carries an Edit button — the currently-edited layout's
// button is the solid accent treatment so the author can tell which they're on.
function InlineModulePreview({ module, currentLayoutId, currentDraft, allDrafts, onSelectLayout }) {
  const layouts = module?.layouts || [];
  // Merge in the live draft for the currently-edited layout so the focused
  // thumbnail mirrors edits as they happen.
  const layoutsResolved = layouts.map(l => {
    if (l.id === currentLayoutId && currentDraft) return { ...l, ...currentDraft };
    if (allDrafts && allDrafts[l.id]) return { ...l, ...allDrafts[l.id] };
    return l;
  });
  const fallbackId = currentLayoutId || layouts[0]?.id;
  const [focusedId, setFocusedId] = React.useState(fallbackId);
  React.useEffect(() => { setFocusedId(fallbackId); }, [fallbackId, module?.id]);
  const focused = layouts.find(l => l.id === focusedId) || layouts[0];
  const idx = layouts.findIndex(l => l.id === focused?.id);

  return (
    <div style={{ flex: 1, overflowY: 'auto', padding: '14px 14px 18px',
      display: 'flex', flexDirection: 'column', gap: 12 }}>

      {/* Focused preview — no scrub arrows; navigation is via the list below */}
      <PreviewFrame width={1280} height={720} maxWidth={400}>
        <ModulePreviewSlide layout={layoutsResolved.find(l => l.id === focused?.id) || focused} />
      </PreviewFrame>

      <div style={{
        display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
        fontSize: 11, color: 'var(--text-faint)', fontFamily: 'var(--font-mono)',
      }}>
        <span>{String(idx + 1).padStart(2, '0')} / {String(layouts.length).padStart(2, '0')}</span>
        {focused?.id === currentLayoutId && (
          <>
            <span>·</span>
            <span style={{
              padding: '1px 6px', borderRadius: 3,
              background: 'var(--accent-bg)', color: 'var(--accent-text)',
              fontWeight: 600, letterSpacing: '.04em',
            }}>EDITING</span>
          </>
        )}
      </div>

      {/* Strip of all layouts in module */}
      <div style={{ display: 'grid', gap: 6, marginTop: 4 }}>
        <div style={{ fontSize: 10.5, color: 'var(--text-faint)', fontWeight: 600,
          letterSpacing: '.06em', textTransform: 'uppercase', padding: '0 2px' }}>
          All layouts in module
        </div>
        {layouts.map((l, i) => {
          const resolved = layoutsResolved[i];
          return (
            <ModuleThumbRow key={l.id} layout={resolved} index={i}
              focused={l.id === focused?.id}
              editing={l.id === currentLayoutId}
              onClick={() => setFocusedId(l.id)}
              onEdit={() => {
                if (l.id === currentLayoutId) return;
                onSelectLayout?.(l.id);
              }} />
          );
        })}
      </div>
    </div>
  );
}

function ModuleThumbRow({ layout, index, focused, editing, onClick, onEdit }) {
  const meta = (window.LAYOUT_TYPES || []).find(t => t.id === layout.type) || {};
  const Icon = I[meta.icon] || I.Hash;
  const content = (window.LAYOUT_CONTENT_SAMPLES || {})[layout.type] || {};
  const data = { ...content, ...layout };
  return (
    <div onClick={onClick} role="button" tabIndex={0}
      onKeyDown={e => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onClick?.(); } }}
      style={{
        display: 'grid', gridTemplateColumns: '64px 1fr auto', gap: 10,
        alignItems: 'center', padding: 6, textAlign: 'left',
        background: focused ? 'var(--accent-bg)' : 'var(--surface)',
        border: '1px solid', borderColor: focused ? 'var(--accent-border)' : 'var(--border)',
        borderRadius: 'var(--radius-md)', cursor: 'default', fontFamily: 'inherit',
        color: 'var(--text)',
      }}
      onMouseOver={e => { if (!focused) e.currentTarget.style.borderColor = 'var(--border-strong)'; }}
      onMouseOut={e => { if (!focused) e.currentTarget.style.borderColor = 'var(--border)'; }}>
      {/* Tiny rendered preview */}
      <div style={{
        width: 64, height: 36, background: '#000',
        borderRadius: 'var(--radius-sm)', overflow: 'hidden', position: 'relative',
        boxShadow: 'var(--shadow-sm)',
      }}>
        <div style={{
          width: 1280, height: 720,
          transform: 'scale(0.05)', transformOrigin: 'top left',
          position: 'absolute', top: 0, left: 0,
        }}>
          <ModulePreviewSlide layout={data} />
        </div>
      </div>
      <div style={{ minWidth: 0 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 5, fontSize: 11.5,
          color: focused ? 'var(--accent-text)' : 'var(--text)' }}>
          <span style={{ fontFamily: 'var(--font-mono)', color: 'var(--text-faint)' }}>
            L{layout.n || index + 1}
          </span>
          <Icon size={11} style={{ color: 'var(--text-muted)' }} />
          <span className="truncate" style={{ fontFamily: 'var(--font-mono)' }}>
            {layout.type}
          </span>
        </div>
        <div className="truncate" style={{ fontSize: 11.5, color: 'var(--text-muted)',
          marginTop: 1 }}>
          {locText(layout.summary)}
        </div>
      </div>
      <EditLayoutButton editing={editing} visible={focused || editing}
        onClick={(e) => { e.stopPropagation(); onEdit?.(); }} />
    </div>
  );
}

// EditLayoutButton — three visual states:
//   editing=true             → solid accent "Editing" badge (always visible)
//   focused (selected) row   → outlined "Edit" button — invites the swap
//   neither                  → empty slot, keeps rows visually quiet
function EditLayoutButton({ editing, visible, onClick }) {
  if (editing) {
    return (
      <span aria-current="true"
        title="You are editing this layout"
        style={{
          display: 'inline-flex', alignItems: 'center', gap: 4,
          fontSize: 10, padding: '3px 7px', borderRadius: 4,
          background: 'var(--accent)', color: '#fff',
          fontWeight: 700, letterSpacing: '.04em', textTransform: 'uppercase',
          border: '1px solid var(--accent)', fontFamily: 'inherit',
        }}>
        <I.PenLine size={10} />Editing
      </span>
    );
  }
  if (!visible) {
    // Reserve the column width so the row layout doesn't reflow.
    return <span style={{ width: 1, height: 1 }} aria-hidden="true" />;
  }
  return (
    <button onClick={onClick}
      title="Switch the editor to this layout"
      style={{
        display: 'inline-flex', alignItems: 'center', gap: 4,
        fontSize: 10.5, padding: '3px 7px', borderRadius: 4,
        background: 'var(--surface)', color: 'var(--accent-text)',
        border: '1px solid var(--accent-border)',
        fontWeight: 600, letterSpacing: '.04em', textTransform: 'uppercase',
        cursor: 'default', fontFamily: 'inherit',
      }}
      onMouseOver={e => { e.currentTarget.style.background = 'var(--accent-bg)'; }}
      onMouseOut={e => { e.currentTarget.style.background = 'var(--surface)'; }}>
      <I.PenLine size={10} />Edit
    </button>
  );
}

// Render a module layout for module-preview mode. Uses the sample content for
// the layout's type so every thumb has *something* to show, layered with any
// inline fields the layout already carries.
function ModulePreviewSlide({ layout }) {
  if (!layout) return <div style={{ height: '100%', background: '#000' }} />;
  const t = layout.type;
  const content = (window.LAYOUT_CONTENT_SAMPLES || {})[t] || {};
  const data = { ...content, ...layout, type: t };
  return <PlayerPreview layout={data} />;
}

function PreviewFrame({ children, width = 1280, height = 720, maxWidth = 400 }) {
  const scale = maxWidth / width;
  return (
    <div style={{
      width: maxWidth, height: height * scale, position: 'relative',
      background: '#000', borderRadius: 'var(--radius-md)',
      overflow: 'hidden', boxShadow: 'var(--shadow-md)', margin: '0 auto',
    }}>
      <div style={{
        width, height,
        transform: `scale(${scale})`, transformOrigin: 'top left',
        position: 'absolute', top: 0, left: 0,
      }}>{children}</div>
    </div>
  );
}

function SectionContextPanelBody({ context }) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', minHeight: 0, flex: 1 }}
         aria-label="Section context">
      <div style={{
        padding: '14px 18px',
        borderBottom: '1px solid var(--border)',
        background: 'var(--surface)',
      }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
          <I.FileText size={13} style={{ color: 'var(--text-muted)' }} />
          <span style={{ fontSize: 11, fontWeight: 600, color: 'var(--text-muted)',
            letterSpacing: '.06em', textTransform: 'uppercase' }}>Section context</span>
          <div style={{ flex: 1 }} />
          <span className="chip" style={{ fontFamily: 'var(--font-mono)' }}>
            {context.sectionId}
          </span>
        </div>
        <p style={{ margin: 0, fontSize: 12.5, color: 'var(--text-muted)', lineHeight: 1.5 }}>
          {context.summary}
        </p>
      </div>

      <div style={{ flex: 1, overflowY: 'auto', padding: '14px 18px',
        display: 'flex', flexDirection: 'column', gap: 18 }}>

        {/* Coverage */}
        <CtxBlock label="Coverage">
          <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
            <div style={{
              width: 44, height: 44, borderRadius: '50%',
              background: `conic-gradient(var(--success) ${context.coverage.pct}%, var(--surface-inset) 0)`,
              display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
            }}>
              <div style={{ width: 32, height: 32, borderRadius: '50%', background: 'var(--surface)',
                display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                fontSize: 10.5, fontWeight: 700, color: 'var(--text)' }}>
                {context.coverage.pct}%
              </div>
            </div>
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 12.5, fontWeight: 500, color: 'var(--text)' }}>
                {context.coverage.covered} of {context.coverage.total} source claims preserved
              </div>
              <div style={{ fontSize: 11.5, color: 'var(--text-muted)' }}>
                1 unmapped claim — <a href="#" style={{ color: 'var(--accent-text)' }}>review</a>
              </div>
            </div>
          </div>
        </CtxBlock>

        {/* Source paragraphs */}
        <CtxBlock label="Source paragraphs" count={context.paragraphs.length}
          action={<a href="#" style={{ fontSize: 11.5, color: 'var(--accent-text)' }}>Open source</a>}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 6,
            fontSize: 11.5, color: 'var(--text-muted)', marginBottom: 8 }}>
            <I.FileText size={11} />
            <span className="truncate" style={{ flex: 1 }}>{context.sourceFile}</span>
          </div>
          <div style={{ display: 'grid', gap: 6 }}>
            {context.paragraphs.slice(0, 3).map(p => (
              <div key={p.id} style={{
                padding: '6px 8px',
                borderLeft: `2px solid ${p.kept ? 'var(--success)' : 'var(--border)'}`,
                background: 'var(--surface-inset)',
                fontSize: 12, color: 'var(--text-muted)', lineHeight: 1.5,
                borderRadius: '0 4px 4px 0',
              }}>
                "{p.text}"
              </div>
            ))}
            <button className="btn sm ghost" style={{ alignSelf: 'flex-start',
              fontSize: 11.5, color: 'var(--text-muted)' }}>
              <I.ChevronDown size={11} />Show {context.paragraphs.length - 3} more
            </button>
          </div>
        </CtxBlock>

        {/* Anecdotes & verification */}
        <CtxBlock label="Anecdotes" count={context.anecdotes.length}>
          <div style={{ display: 'grid', gap: 6 }}>
            {context.anecdotes.map(a => (
              <div key={a.id} style={{
                display: 'grid', gridTemplateColumns: '1fr auto', gap: 8,
                padding: '8px 10px',
                background: 'var(--surface-inset)', borderRadius: 'var(--radius)',
                alignItems: 'start',
              }}>
                <div>
                  <p style={{ margin: '0 0 4px', fontSize: 12, color: 'var(--text)', lineHeight: 1.45 }}>
                    {a.text}
                  </p>
                  <CitationChip source={a.cite} status={a.status} />
                </div>
                <button className="btn sm ghost" style={{ width: 24, height: 24, padding: 0 }}>
                  <I.MoreHorizontal size={12} />
                </button>
              </div>
            ))}
          </div>
        </CtxBlock>

        {/* Other suggestions */}
        <CtxBlock label="Other layout suggestions" count={context.otherSuggestions.length}>
          <div style={{ display: 'grid', gap: 6 }}>
            {context.otherSuggestions.map(s => {
              const meta = (window.LAYOUT_TYPES || []).find(t => t.id === s.type) || {};
              const Icon = I[meta.icon] || I.Hash;
              const pct = Math.round(s.score * 100);
              return (
                <div key={s.type} style={{
                  display: 'grid', gridTemplateColumns: 'auto 1fr auto', gap: 8,
                  padding: '8px 10px', background: 'var(--surface-inset)',
                  borderRadius: 'var(--radius)', alignItems: 'center',
                }}>
                  <Icon size={14} style={{ color: 'var(--text-muted)' }} />
                  <div>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                      <span style={{ fontFamily: 'var(--font-mono)', fontSize: 11.5, fontWeight: 500,
                        color: 'var(--text)' }}>{s.type}</span>
                      <span style={{ fontSize: 11, color: 'var(--text-faint)' }}>· {pct}</span>
                    </div>
                    <span style={{ fontSize: 11, color: 'var(--text-muted)', lineHeight: 1.4 }}>
                      {s.rationale}
                    </span>
                  </div>
                  <button className="btn sm ghost"><I.RefreshCw size={11} /></button>
                </div>
              );
            })}
          </div>
        </CtxBlock>

        {/* Actions */}
        <div style={{ display: 'grid', gap: 6, marginTop: 4 }}>
          <button className="btn"><I.RefreshCw size={13} />Re-roll this layout</button>
          <button className="btn"><I.Layers size={13} />Replace type…</button>
          <button className="btn ghost"><I.PenLine size={13} />Hand-author content</button>
        </div>
      </div>
    </div>
  );
}

function CtxBlock({ label, count, action, children }) {
  return (
    <section>
      <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 8 }}>
        <span style={{ fontSize: 11, fontWeight: 600, color: 'var(--text-faint)',
          letterSpacing: '.06em', textTransform: 'uppercase' }}>{label}</span>
        {count != null && <span style={{ fontSize: 11, color: 'var(--text-faint)',
          fontFamily: 'var(--font-mono)' }}>· {count}</span>}
        <div style={{ flex: 1 }} />
        {action}
      </div>
      {children}
    </section>
  );
}

// ── FullscreenVideoEditor ──────────────────────────────────────────────────
function FullscreenVideoEditor() {
  return (
    <>
      <EditorBlock label="Video"
        action={<button className="btn sm ghost"><I.Sparkle size={12} />Generate</button>}>
        <MediaSlot kind="video"
          bound={{ alt: 'sarah-jordan_full-scenario_v2.mp4 · 04:32',
            duration: '04:32', generatedBy: 'HeyGen',
            previewBg: 'linear-gradient(135deg, #1e293b, #020617)' }} />
      </EditorBlock>
      <EditorBlock label="Subtitles"
        action={<button className="btn sm ghost"><I.Upload size={12} />Replace</button>}>
        <div style={{ padding: 12, background: 'var(--surface)', border: '1px solid var(--border)',
          borderRadius: 'var(--radius-md)', display: 'flex', alignItems: 'center', gap: 10 }}>
          <I.FileText size={14} style={{ color: 'var(--text-muted)' }} />
          <span style={{ fontSize: 12.5, flex: 1 }}>sarah-jordan_subtitles_EN.txt</span>
          <span className="pill accepted">Auto-generated</span>
          <button className="btn sm">Edit inline</button>
        </div>
      </EditorBlock>
      <EditorBlock label="Behaviour">
        <div style={{ display: 'grid', gap: 8 }}>
          <FieldRow label="Blocking section" hint="Learner must watch through to enable next button">
            <Switch on={true} />
          </FieldRow>
          <FieldRow label="Title text color"><ColorSwatchRow value="#ffffff" /></FieldRow>
          <FieldRow label="Title background"><ColorSwatchRow value="rgba(0,0,0,0.4)" /></FieldRow>
        </div>
      </EditorBlock>
      <EditorBlock label="In-video interactions" count={1}
        action={<button className="btn sm"><I.Plus size={12} />Add interaction</button>}>
        <VideoTimelineEditor />
      </EditorBlock>
    </>
  );
}

// ── SmallVideoEditor ───────────────────────────────────────────────────────
function SmallVideoEditor() {
  return (
    <>
      <EditorBlock label="Video"><MediaSlot kind="video"
        bound={{ alt: 'SME interview · 02:14', duration: '02:14',
          previewBg: 'linear-gradient(135deg, #475569, #0f172a)' }} /></EditorBlock>
      <EditorBlock label="Body text">
        <RichTextEditor placeholder="Companion body text shown beside the video…" />
      </EditorBlock>
    </>
  );
}

// ── TextAndImageRowsEditor ─────────────────────────────────────────────────
function TextAndImageRowsEditor() {
  const rows = [
    { id: 'r1', side: 'left', kind: 'image', body: 'When the volume rises in the room.' },
    { id: 'r2', side: 'right', kind: 'image', body: 'When two people stop making eye contact.' },
    { id: 'r3', side: 'left', kind: 'video', body: 'When silence stretches longer than feels natural.' },
  ];
  return (
    <EditorBlock label="Rows" count={rows.length}
      action={<button className="btn sm"><I.Plus size={12} />Add row</button>}>
      <div style={{ display: 'grid', gap: 8 }}>
        {rows.map((r, i) => (
          <div key={r.id} style={{
            display: 'grid', gridTemplateColumns: 'auto 1fr 1fr auto', gap: 10,
            padding: 10, alignItems: 'center',
            background: 'var(--surface)', border: '1px solid var(--border)',
            borderRadius: 'var(--radius-md)',
          }}>
            <I.GripVertical size={14} style={{ color: 'var(--text-faint)' }} />
            {r.side === 'left' ? (
              <>
                <MiniMediaThumb kind={r.kind} />
                <input className="field" defaultValue={r.body} />
              </>
            ) : (
              <>
                <input className="field" defaultValue={r.body} />
                <MiniMediaThumb kind={r.kind} />
              </>
            )}
            <button className="btn sm ghost"><I.MoreHorizontal size={12} /></button>
          </div>
        ))}
      </div>
    </EditorBlock>
  );
}

function MiniMediaThumb({ kind }) {
  const Icon = kind === 'video' ? I.Film : I.Image;
  return (
    <div style={{
      width: '100%', height: 60, borderRadius: 'var(--radius)',
      background: 'linear-gradient(135deg, #94a3b8, #475569)',
      display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
      color: 'rgba(255,255,255,.8)',
    }}>
      <Icon size={20} />
    </div>
  );
}

// ── IconsDiscoverEditor ────────────────────────────────────────────────────
function IconsDiscoverEditor() {
  const icons = [
    { id: 'i1', label: 'Personalising', body: 'Making the issue about who they are.' },
    { id: 'i2', label: 'Stockpiling',   body: 'Storing up grievances for one big release.' },
    { id: 'i3', label: 'Mind-reading',  body: 'Assuming you know their intent.' },
    { id: 'i4', label: 'Public airing', body: 'Choosing the wrong audience.' },
  ];
  return (
    <EditorBlock label="Icon tiles" count={4}
      action={<button className="btn sm"><I.Plus size={12} />Add icon</button>}>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
        {icons.map(ic => (
          <div key={ic.id} style={{
            display: 'grid', gridTemplateColumns: '60px 1fr', gap: 10, padding: 10,
            background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 'var(--radius-md)',
          }}>
            <div style={{
              width: 60, height: 60, borderRadius: 'var(--radius)',
              background: 'var(--ai-bg)', display: 'inline-flex',
              alignItems: 'center', justifyContent: 'center', color: 'var(--ai-text)',
            }}><I.Sparkle size={20} /></div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
              <input className="field" defaultValue={ic.label} style={{ height: 26, fontSize: 12, fontWeight: 600 }} />
              <textarea className="field" defaultValue={ic.body} style={{ minHeight: 30, fontSize: 11.5 }} />
            </div>
          </div>
        ))}
      </div>
    </EditorBlock>
  );
}

// ── MandatoryQuestionEditor ────────────────────────────────────────────────
function MandatoryQuestionEditor() {
  return (
    <>
      <EditorBlock label="Question prompt">
        <RichTextEditor placeholder="What should Sarah do first?" minHeight={60} />
      </EditorBlock>
      <EditorBlock label="Answers"
        action={<div style={{ display: 'flex', gap: 4 }}>
          <button className="btn sm">4-answer</button>
          <button className="btn sm ghost">Free-form</button>
        </div>}>
        <FourAnswerEditor answers={{
          correct: 'Start with the behaviour: "When you missed the spec review on Tuesday…"',
          'wrong-1': 'Open with the impact first to soften the message.',
          'wrong-2': 'Suggest Jordan take some time off to think it over.',
          playful: 'Hand Jordan a 47-page deck and leave the room.',
        }} />
      </EditorBlock>
      <EditorBlock label="Pass threshold">
        <div style={{ display: 'flex', alignItems: 'center', gap: 12,
          padding: 12, background: 'var(--surface)', border: '1px solid var(--border)',
          borderRadius: 'var(--radius-md)' }}>
          <input type="range" min="0" max="100" defaultValue="100" style={{ flex: 1 }} />
          <span style={{ fontFamily: 'var(--font-mono)', fontSize: 13, fontWeight: 600,
            minWidth: 40, textAlign: 'right' }}>100%</span>
        </div>
      </EditorBlock>
    </>
  );
}

// ── StubEditor — covers the remaining types without bespoke forms ─────────
function StubEditor({ type }) {
  const meta = (window.LAYOUT_TYPES || []).find(t => t.id === type) || {};
  const Icon = I[meta.icon] || I.Hash;
  return (
    <EditorBlock label={`${meta.label} fields`}>
      <div style={{
        padding: '28px 20px', background: 'var(--surface)',
        border: '1px dashed var(--border-strong)', borderRadius: 'var(--radius-md)',
        display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12, textAlign: 'center',
      }}>
        <Icon size={28} style={{ color: 'var(--text-faint)' }} />
        <div>
          <h4 style={{ margin: '0 0 4px', fontSize: 14, color: 'var(--text)' }}>
            {meta.label} editor
          </h4>
          <p style={{ margin: 0, fontSize: 12.5, color: 'var(--text-muted)', maxWidth: 460, lineHeight: 1.55 }}>
            The full form for <code style={{ fontFamily: 'var(--font-mono)' }}>{type}</code> is documented
            in the design spec — fields, validation, media affordances and (where applicable) the
            type-specific authoring widgets (timeline editor, hotspot placer, etc.).
          </p>
        </div>
        <button className="btn sm">View specification</button>
      </div>
    </EditorBlock>
  );
}

// ── Mini video timeline editor ─────────────────────────────────────────────
function VideoTimelineEditor() {
  return (
    <div style={{
      padding: 14, background: 'var(--surface)', border: '1px solid var(--border)',
      borderRadius: 'var(--radius-md)',
    }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 11,
        fontFamily: 'var(--font-mono)', color: 'var(--text-muted)', marginBottom: 8 }}>
        <span>00:00</span><span>01:00</span><span>02:00</span><span>03:00</span><span>04:32</span>
      </div>
      <div style={{ position: 'relative', height: 36,
        background: 'var(--surface-inset)', borderRadius: 'var(--radius-sm)',
        marginBottom: 12, overflow: 'hidden' }}>
        {/* Marker */}
        <div style={{ position: 'absolute', left: '28%', top: 0, bottom: 0, width: 2,
          background: 'var(--accent)' }} />
        <div style={{ position: 'absolute', left: 'calc(28% - 6px)', top: -2,
          width: 14, height: 14, borderRadius: '50%', background: 'var(--accent)',
          border: '2px solid var(--surface)' }} />
        <span style={{ position: 'absolute', left: 'calc(28% + 8px)', top: 8,
          fontSize: 11, color: 'var(--accent-text)', fontWeight: 500,
          padding: '2px 6px', background: 'var(--accent-bg)', borderRadius: 3 }}>
          discover @ 01:18
        </span>
      </div>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
        <span style={{ fontSize: 12, color: 'var(--text-muted)' }}>
          1 interaction · drag the marker to retime
        </span>
        <button className="btn sm"><I.Edit size={12} />Edit interaction</button>
      </div>
    </div>
  );
}

// ── tiny form helpers ──────────────────────────────────────────────────────
function FieldRow({ label, hint, children }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between',
      padding: '10px 12px', background: 'var(--surface)',
      border: '1px solid var(--border)', borderRadius: 'var(--radius-md)' }}>
      <div>
        <div style={{ fontSize: 12.5, fontWeight: 500, color: 'var(--text)' }}>{label}</div>
        {hint && <div style={{ fontSize: 11.5, color: 'var(--text-muted)' }}>{hint}</div>}
      </div>
      {children}
    </div>
  );
}

function Switch({ on }) {
  const [v, setV] = React.useState(on);
  return (
    <button onClick={() => setV(!v)}
      style={{
        width: 32, height: 18, padding: 0, border: 0,
        background: v ? 'var(--accent)' : 'var(--border-strong)',
        borderRadius: 9, cursor: 'default', position: 'relative',
        transition: 'background 150ms',
      }}>
      <span style={{
        position: 'absolute', top: 2, left: v ? 16 : 2,
        width: 14, height: 14, borderRadius: '50%',
        background: '#fff', transition: 'left 150ms',
        boxShadow: 'var(--shadow-sm)',
      }} />
    </button>
  );
}

function ColorSwatchRow({ value }) {
  return (
    <div style={{ display: 'inline-flex', alignItems: 'center', gap: 6,
      padding: '4px 8px', background: 'var(--surface-inset)', borderRadius: 'var(--radius)',
      border: '1px solid var(--border)' }}>
      <span style={{ width: 14, height: 14, background: value, borderRadius: 3,
        border: '1px solid var(--border)' }} />
      <span style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--text-muted)' }}>{value}</span>
    </div>
  );
}

Object.assign(window, {
  SectionContextPanel: SectionContextPanelBody, LayoutEditorRightPanel,
  InlineModulePreview, PreviewFrame, EditLayoutButton,
  CtxBlock,
  FullscreenVideoEditor, SmallVideoEditor, TextAndImageRowsEditor,
  IconsDiscoverEditor, MandatoryQuestionEditor, StubEditor,
  Switch, FieldRow,
});
