﻿import React from 'react'
import { Head, Link, usePage } from '@inertiajs/react'
import StudioLayout from '../../Layouts/StudioLayout'
import NovaCardCanvasPreview from '../../components/nova-cards/NovaCardCanvasPreview'
import NovaCardTemplatePicker from '../../components/nova-cards/NovaCardTemplatePicker'
import NovaCardGradientPicker from '../../components/nova-cards/NovaCardGradientPicker'
import NovaCardFontPicker from '../../components/nova-cards/NovaCardFontPicker'
import NovaCardAutosaveIndicator from '../../components/nova-cards/NovaCardAutosaveIndicator'
import NovaCardPresetPicker from '../../components/nova-cards/NovaCardPresetPicker'
import Checkbox from '../../components/ui/Checkbox'
import NovaSelect from '../../components/ui/NovaSelect'

const defaultMobileSteps = [
  { key: 'format', label: 'Format', description: 'Choose the canvas shape and basic direction.' },
  { key: 'background', label: 'Template & Background', description: 'Pick the visual foundation for the card.' },
  { key: 'content', label: 'Text', description: 'Write the quote, author, and source.' },
  { key: 'style', label: 'Style', description: 'Fine-tune typography and layout.' },
  { key: 'preview', label: 'Preview', description: 'Check the live composition before publish.' },
  { key: 'publish', label: 'Publish', description: 'Review metadata and release settings.' },
]

const overlayOptions = [
  { value: 'none', label: 'None' },
  { value: 'dark-soft', label: 'Dark Soft' },
  { value: 'dark-strong', label: 'Dark Strong' },
  { value: 'light-soft', label: 'Light Soft' },
]

const layoutPresetMap = {
  quote_heavy: { alignment: 'center', position: 'center', padding: 'comfortable', max_width: 'balanced' },
  author_emphasis: { alignment: 'left', position: 'lower-middle', padding: 'comfortable', max_width: 'compact' },
  centered: { alignment: 'center', position: 'center', padding: 'airy', max_width: 'compact' },
  minimal: { alignment: 'left', position: 'upper-middle', padding: 'tight', max_width: 'wide' },
}

function deepMerge(target, source) {
  if (!source || typeof source !== 'object') return target

  const next = Array.isArray(target) ? [...target] : { ...(target || {}) }

  Object.entries(source).forEach(([key, value]) => {
    if (Array.isArray(value)) {
      next[key] = value
      return
    }

    if (value && typeof value === 'object') {
      next[key] = deepMerge(next[key], value)
      return
    }

    next[key] = value
  })

  return next
}

function pillClasses(active) {
  return `rounded-full border px-3 py-1.5 text-xs font-semibold uppercase tracking-[0.16em] transition ${active ? 'border-sky-300/30 bg-sky-400/15 text-sky-100' : 'border-white/10 bg-white/[0.03] text-slate-300 hover:bg-white/[0.05]'}`
}

function defaultTextBlocks() {
  return [
    { key: 'title', type: 'title', text: 'Untitled card', enabled: true, style: { role: 'eyebrow' } },
    { key: 'quote', type: 'quote', text: 'Your next quote starts here.', enabled: true, style: { role: 'headline' } },
    { key: 'author', type: 'author', text: '', enabled: false, style: { role: 'byline' } },
    { key: 'source', type: 'source', text: '', enabled: false, style: { role: 'caption' } },
  ]
}

function truncateText(value, limit = 96) {
  const normalized = String(value || '').trim()
  if (normalized.length <= limit) return normalized
  return `${normalized.slice(0, limit - 1).trimEnd()}...`
}

function projectTextValue(project, type, fallbackKey = null) {
  const blocks = Array.isArray(project?.text_blocks) ? project.text_blocks : []
  const block = blocks.find((item) => item?.type === type && String(item?.text || '').trim() !== '')
  if (block) return String(block.text || '')
  if (!fallbackKey) return ''
  return String(project?.content?.[fallbackKey] || '')
}

function summarizeProjectSnapshot(project) {
  const blocks = Array.isArray(project?.text_blocks) ? project.text_blocks : []
  const enabledBlocks = blocks.filter((block) => block?.enabled !== false && String(block?.text || '').trim() !== '').length

  return {
    title: truncateText(projectTextValue(project, 'title', 'title') || 'Untitled card', 64),
    quote: truncateText(projectTextValue(project, 'quote', 'quote_text'), 88),
    blockCount: blocks.length,
    enabledBlocks,
    layout: String(project?.layout?.layout || 'quote_heavy'),
    font: String(project?.typography?.font_preset || 'modern-sans'),
    background: String(project?.background?.gradient_preset || project?.background?.solid_color || project?.background?.type || 'gradient'),
  }
}

function compareProjectSnapshots(currentProject, versionProject) {
  if (!versionProject || typeof versionProject !== 'object') {
    return ['Snapshot unavailable']
  }

  const changes = []
  const currentSummary = summarizeProjectSnapshot(currentProject)
  const versionSummary = summarizeProjectSnapshot(versionProject)

  if (currentSummary.title !== versionSummary.title) changes.push('Title copy changed')
  if (currentSummary.quote !== versionSummary.quote) changes.push('Quote copy changed')
  if (currentSummary.blockCount !== versionSummary.blockCount) changes.push(`Block count ${versionSummary.blockCount}`)
  if (currentSummary.layout !== versionSummary.layout) changes.push(`Layout ${versionSummary.layout.replace(/_/g, ' ')}`)
  if (currentSummary.font !== versionSummary.font) changes.push(`Font ${versionSummary.font.replace(/-/g, ' ')}`)
  if (currentSummary.background !== versionSummary.background) changes.push('Background treatment changed')

  return changes.length ? changes.slice(0, 4) : ['Matches current draft']
}

function normalizeProject(project, options, card = null) {
  const defaultGradient = options.gradient_presets?.[0] || null
  const defaultFont = options.font_presets?.[0] || null
  const content = project?.content || {}
  const blocks = Array.isArray(project?.text_blocks) && project.text_blocks.length ? project.text_blocks : defaultTextBlocks().map((block) => ({
    ...block,
    text: block.type === 'title'
      ? (card?.title || content.title || block.text)
      : block.type === 'quote'
        ? (card?.quote_text || content.quote_text || block.text)
        : block.type === 'author'
          ? (card?.quote_author || content.quote_author || '')
          : (card?.quote_source || content.quote_source || ''),
    enabled: block.type === 'title' || block.type === 'quote' ? true : Boolean(block.type === 'author' ? (card?.quote_author || content.quote_author) : (card?.quote_source || content.quote_source)),
  }))

  return {
    schema_version: Number(project?.schema_version || 3),
    meta: project?.meta || { editor: 'nova-cards-v3' },
    template: project?.template || { id: card?.template_id || null, slug: card?.template?.slug || null },
    content: {
      title: card?.title || content.title || 'Untitled card',
      quote_text: card?.quote_text || content.quote_text || 'Your next quote starts here.',
      quote_author: card?.quote_author || content.quote_author || '',
      quote_source: card?.quote_source || content.quote_source || '',
    },
    text_blocks: blocks,
    layout: {
      layout: project?.layout?.layout || 'quote_heavy',
      position: project?.layout?.position || 'center',
      alignment: project?.layout?.alignment || 'center',
      padding: project?.layout?.padding || 'comfortable',
      max_width: project?.layout?.max_width || 'balanced',
    },
    typography: {
      font_preset: project?.typography?.font_preset || defaultFont?.key || 'modern-sans',
      text_color: project?.typography?.text_color || '#ffffff',
      accent_color: project?.typography?.accent_color || '#e0f2fe',
      quote_size: Number(project?.typography?.quote_size || 72),
      quote_width: project?.typography?.quote_width != null ? Number(project.typography.quote_width) : null,
      author_size: Number(project?.typography?.author_size || 28),
      letter_spacing: Number(project?.typography?.letter_spacing || 0),
      line_height: Number(project?.typography?.line_height || 1.2),
      shadow_preset: project?.typography?.shadow_preset || 'soft',
    },
    background: {
      type: project?.background?.type || card?.background_type || 'gradient',
      gradient_preset: project?.background?.gradient_preset || defaultGradient?.key || 'midnight-nova',
      gradient_colors: project?.background?.gradient_colors || defaultGradient?.colors || ['#0f172a', '#1d4ed8'],
      solid_color: project?.background?.solid_color || '#111827',
      background_image_id: project?.background?.background_image_id || card?.background_image_id || null,
      overlay_style: project?.background?.overlay_style || 'dark-soft',
      focal_position: project?.background?.focal_position || 'center',
      blur_level: Number(project?.background?.blur_level || 0),
      opacity: Number(project?.background?.opacity || 50),
    },
    canvas: {
      density: project?.canvas?.density || 'standard',
      safe_zone: project?.canvas?.safe_zone !== false,
    },
    frame: {
      preset: project?.frame?.preset || 'none',
      color: project?.frame?.color || null,
      width: Number(project?.frame?.width || 1),
    },
    effects: {
      color_grade: project?.effects?.color_grade || 'none',
      effect_preset: project?.effects?.effect_preset || 'none',
      intensity: Number(project?.effects?.intensity || 50),
    },
    export_preferences: {
      allow_export: project?.export_preferences?.allow_export !== false,
      default_format: project?.export_preferences?.default_format || 'preview',
    },
    source_context: {
      style_family: project?.source_context?.style_family || null,
      palette_family: project?.source_context?.palette_family || null,
      editor_mode: project?.source_context?.editor_mode || card?.editor_mode_last_used || 'full',
    },
    decorations: Array.isArray(project?.decorations) ? project.decorations : [],
    assets: {
      pack_ids: Array.isArray(project?.assets?.pack_ids) ? project.assets.pack_ids : [],
      template_pack_ids: Array.isArray(project?.assets?.template_pack_ids) ? project.assets.template_pack_ids : [],
      items: Array.isArray(project?.assets?.items) ? project.assets.items : [],
    },
  }
}

function syncTextBlocks(blocks, type, text) {
  const list = Array.isArray(blocks) ? [...blocks] : defaultTextBlocks()
  const index = list.findIndex((block) => block.type === type)
  const next = {
    key: type,
    type,
    text,
    enabled: type === 'title' || type === 'quote' ? true : Boolean(String(text || '').trim()),
    style: list[index]?.style || {},
  }

  if (index === -1) {
    list.push(next)
    return list
  }

  list[index] = { ...list[index], ...next }
  return list
}

function normalizeCard(card, options) {
  if (!card) {
    const defaultTemplate = options.templates?.[0] || null
    const defaultCategory = options.categories?.[0] || null
    const project = normalizeProject(null, options)

    return {
      id: null,
      title: 'Untitled card',
      quote_text: 'Your next quote starts here.',
      quote_author: '',
      quote_source: '',
      description: '',
      format: options.formats?.[0]?.key || 'square',
      visibility: 'private',
      status: 'draft',
      moderation_status: 'pending',
      allow_download: true,
      background_type: 'gradient',
      template_id: defaultTemplate?.id || null,
      category_id: defaultCategory?.id || null,
      background_image_id: null,
      tags: [],
      preview_url: null,
      public_url: null,
      schema_version: 2,
      allow_remix: true,
      likes_count: 0,
      favorites_count: 0,
      saves_count: 0,
      remixes_count: 0,
      challenge_entries_count: 0,
      lineage: { original_card: null, root_card: null },
      editor_mode_last_used: 'full',
      project_json: project,
    }
  }

  return {
    ...card,
    tags: Array.isArray(card.tags) ? card.tags : [],
    allow_remix: card.allow_remix !== false,
    project_json: normalizeProject(card.project_json || {}, options, card),
  }
}

function mergeServerCard(currentCard, serverCard) {
  if (!currentCard) return serverCard
  if (!serverCard) return currentCard

  // Keep the latest local editing state, especially project_json drag positions,
  // while still accepting server-authored metadata like preview URLs and status.
  return {
    ...currentCard,
    ...serverCard,
    title: currentCard.title,
    quote_text: currentCard.quote_text,
    quote_author: currentCard.quote_author,
    quote_source: currentCard.quote_source,
    description: currentCard.description,
    format: currentCard.format,
    visibility: currentCard.visibility,
    template_id: currentCard.template_id,
    category_id: currentCard.category_id,
    background_type: currentCard.background_type,
    background_image_id: currentCard.background_image_id,
    allow_download: currentCard.allow_download,
    allow_remix: currentCard.allow_remix,
    allow_background_reuse: currentCard.allow_background_reuse,
    allow_export: currentCard.allow_export,
    style_family: currentCard.style_family,
    palette_family: currentCard.palette_family,
    editor_mode_last_used: currentCard.editor_mode_last_used,
    tags: currentCard.tags,
    project_json: currentCard.project_json,
  }
}

function buildPayload(card, tagInput) {
  return {
    title: card.title,
    quote_text: card.quote_text,
    quote_author: card.quote_author,
    quote_source: card.quote_source,
    description: card.description,
    format: card.format,
    visibility: card.visibility,
    allow_download: Boolean(card.allow_download),
    allow_remix: Boolean(card.allow_remix),
    allow_background_reuse: Boolean(card.allow_background_reuse),
    allow_export: Boolean(card.allow_export !== false),
    style_family: card.style_family || null,
    palette_family: card.palette_family || null,
    editor_mode_last_used: card.editor_mode_last_used || card.project_json?.source_context?.editor_mode || 'full',
    background_type: card.background_type,
    background_image_id: card.background_image_id,
    template_id: card.template_id,
    category_id: card.category_id,
    tags: String(tagInput || '')
      .split(',')
      .map((item) => item.trim())
      .filter(Boolean),
    project_json: card.project_json,
  }
}

function fillPattern(pattern, replacements) {
  let resolved = String(pattern || '')
  Object.entries(replacements).forEach(([key, value]) => {
    resolved = resolved.replace(`__${key}__`, String(value))
  })
  return resolved
}

function apiUrl(pattern, id) {
  return fillPattern(pattern, { CARD: id })
}

export default function StudioCardEditor() {
  const { props } = usePage()
  const editorOptions = props.editorOptions || {}
  const endpoints = props.endpoints || {}
  const previewMode = Boolean(props.previewMode)
  const mobileSteps = Array.isArray(props.mobileSteps) && props.mobileSteps.length ? props.mobileSteps : defaultMobileSteps

  const [card, setCard] = React.useState(() => normalizeCard(props.card, editorOptions))
  const [cardId, setCardId] = React.useState(props.card?.id || null)
  const [tagInput, setTagInput] = React.useState(() => (props.card?.tags || []).map((tag) => tag.name).join(', '))
  const [versions, setVersions] = React.useState(() => Array.isArray(props.versions) ? props.versions : [])
  const [collections, setCollections] = React.useState([])
  const [selectedCollectionId, setSelectedCollectionId] = React.useState('')
  const [autosaveStatus, setAutosaveStatus] = React.useState(props.card ? 'saved' : 'idle')
  const [autosaveMessage, setAutosaveMessage] = React.useState(props.card ? 'Loaded' : 'Preparing draft')
  const [busy, setBusy] = React.useState(false)
  const [uploading, setUploading] = React.useState(false)
  const [currentMobileStep, setCurrentMobileStep] = React.useState(previewMode ? 'preview' : mobileSteps[0]?.key || 'format')
  const [activeTab, setActiveTab] = React.useState('background')
  // v3 state
  const [creatorPresets, setCreatorPresets] = React.useState(() => editorOptions.creator_presets || {})
  const [aiSuggestions, setAiSuggestions] = React.useState(null)
  const [loadingAi, setLoadingAi] = React.useState(false)
  const [exportStatus, setExportStatus] = React.useState(null)
  const [requestingExport, setRequestingExport] = React.useState(false)
  const [activeExportType, setActiveExportType] = React.useState('preview')
  const createStarted = React.useRef(false)
  const lastSerialized = React.useRef(JSON.stringify(buildPayload(normalizeCard(props.card, editorOptions), tagInput)))
  const autosaveAbortRef = React.useRef(null)

  React.useEffect(() => {
    setCurrentMobileStep(previewMode ? 'preview' : mobileSteps[0]?.key || 'format')
  }, [mobileSteps, previewMode])

  function replaceTextBlocks(nextBlocks) {
    setCard((current) => {
      const blocks = Array.isArray(nextBlocks) ? nextBlocks : []
      const next = { ...current }
      next.project_json = deepMerge(current.project_json || {}, { text_blocks: blocks })

      const quoteBlock = blocks.find((block) => block?.type === 'quote')
      const titleBlock = blocks.find((block) => block?.type === 'title')
      const authorBlock = blocks.find((block) => block?.type === 'author')
      const sourceBlock = blocks.find((block) => block?.type === 'source')
      next.title = titleBlock?.text || next.title
      next.quote_text = quoteBlock?.text || next.quote_text
      next.quote_author = authorBlock?.text || ''
      next.quote_source = sourceBlock?.text || ''
      next.project_json.content = {
        ...(next.project_json.content || {}),
        title: next.title,
        quote_text: next.quote_text,
        quote_author: next.quote_author,
        quote_source: next.quote_source,
      }

      return next
    })
  }

  function loadVersions(targetCardId) {
    if (!targetCardId || !endpoints.draftVersionsPattern) return
    window.axios.get(apiUrl(endpoints.draftVersionsPattern, targetCardId))
      .then((response) => {
        setVersions(Array.isArray(response.data?.data) ? response.data.data : [])
      })
      .catch(() => {})
  }

  function loadCollections() {
    if (!endpoints.collectionsIndex) return
    window.axios.get(endpoints.collectionsIndex)
      .then((response) => {
        const items = Array.isArray(response.data?.data) ? response.data.data : []
        setCollections(items)
        if (!selectedCollectionId && items[0]?.id) {
          setSelectedCollectionId(String(items[0].id))
        }
      })
      .catch(() => {})
  }

  React.useEffect(() => {
    if (!cardId) return
    loadVersions(cardId)
    loadCollections()
  }, [cardId])

  React.useEffect(() => {
    if (cardId || createStarted.current) return
    createStarted.current = true
    setBusy(true)

    window.axios.post(endpoints.draftStore, {
      format: card.format,
      template_id: card.template_id,
      category_id: card.category_id,
    }).then((response) => {
      const nextCard = normalizeCard(response.data.data, editorOptions)
      setCard(nextCard)
      setCardId(nextCard.id)
      setAutosaveStatus('saved')
      setAutosaveMessage('Draft created')
      lastSerialized.current = JSON.stringify(buildPayload(nextCard, tagInput))
    }).catch(() => {
      setAutosaveStatus('error')
      setAutosaveMessage('Could not create draft')
    }).finally(() => {
      setBusy(false)
    })
  }, [card, cardId, editorOptions, endpoints.draftStore, tagInput])

  React.useEffect(() => {
    if (!cardId || busy || uploading) return

    const payload = buildPayload(card, tagInput)
    const serialized = JSON.stringify(payload)
    if (serialized === lastSerialized.current) return

    setAutosaveStatus('saving')
    setAutosaveMessage('Saving draft')

    const timer = window.setTimeout(() => {
      if (autosaveAbortRef.current) {
        autosaveAbortRef.current.abort()
      }
      const controller = new AbortController()
      autosaveAbortRef.current = controller

      window.axios.post(apiUrl(endpoints.draftAutosavePattern, cardId), payload, { signal: controller.signal })
        .then(() => {
          autosaveAbortRef.current = null
          // Use the serialized form we sent — not the server response — so we
          // never overwrite state the user changed while the request was in-flight.
          lastSerialized.current = serialized
          setAutosaveStatus('saved')
          setAutosaveMessage('All changes saved')
        })
        .catch((error) => {
          if (error?.name === 'CanceledError' || error?.name === 'AbortError' || error?.code === 'ERR_CANCELED') return
          setAutosaveStatus('error')
          setAutosaveMessage('Autosave failed')
        })
    }, 900)

    return () => window.clearTimeout(timer)
  }, [busy, card, cardId, editorOptions, endpoints.draftAutosavePattern, tagInput, uploading])

  function updateCard(partial, projectPatch = null) {
    setCard((current) => {
      const next = { ...current, ...partial }
      if (projectPatch) {
        next.project_json = deepMerge(current.project_json || {}, projectPatch)
      }
      return next
    })
  }

  function updateTextField(key, value) {
    const typeMap = {
      title: 'title',
      quote_text: 'quote',
      quote_author: 'author',
      quote_source: 'source',
    }

    setCard((current) => {
      const next = { ...current, [key]: value }
      next.project_json = deepMerge(current.project_json || {}, {
        content: { [key]: value },
        text_blocks: syncTextBlocks(current.project_json?.text_blocks, typeMap[key], value),
      })
      return next
    })
  }

  function updateTextBlock(index, patch) {
    const blocks = Array.isArray(card.project_json?.text_blocks) ? [...card.project_json.text_blocks] : []
    blocks[index] = { ...blocks[index], ...patch }
    replaceTextBlocks(blocks)
  }

  function addTextBlock(type = 'body') {
    const nextBlock = {
      key: `${type}-${Date.now()}`,
      type,
      text: '',
      enabled: true,
      style: {},
    }
    replaceTextBlocks([...(Array.isArray(card.project_json?.text_blocks) ? card.project_json.text_blocks : []), nextBlock])
  }

  function removeTextBlock(index) {
    const blocks = Array.isArray(card.project_json?.text_blocks) ? card.project_json.text_blocks : []
    replaceTextBlocks(blocks.filter((_, itemIndex) => itemIndex !== index))
  }

  function moveTextBlock(index, direction) {
    const blocks = Array.isArray(card.project_json?.text_blocks) ? [...card.project_json.text_blocks] : []
    const nextIndex = index + direction

    if (nextIndex < 0 || nextIndex >= blocks.length) {
      return
    }

    const [moved] = blocks.splice(index, 1)
    blocks.splice(nextIndex, 0, moved)
    replaceTextBlocks(blocks)
  }

  function setEditorMode(mode) {
    updateCard({ editor_mode_last_used: mode }, { source_context: { editor_mode: mode } })
  }

  function handleTemplateSelect(template) {
    const layoutPreset = template.config_json?.layout || 'quote_heavy'

    updateCard(
      { template_id: template.id },
      {
        template: { id: template.id, slug: template.slug },
        layout: {
          layout: layoutPreset,
          ...(layoutPresetMap[layoutPreset] || {}),
          alignment: template.config_json?.text_align || layoutPresetMap[layoutPreset]?.alignment || 'center',
        },
        typography: {
          font_preset: template.config_json?.font_preset || card.project_json?.typography?.font_preset,
          text_color: template.config_json?.text_color || card.project_json?.typography?.text_color,
        },
        background: {
          gradient_preset: template.config_json?.gradient_preset || card.project_json?.background?.gradient_preset,
          overlay_style: template.config_json?.overlay_style || card.project_json?.background?.overlay_style,
        },
      },
    )
  }

  function handleGradientSelect(gradient) {
    updateCard({ background_type: 'gradient' }, {
      background: {
        type: 'gradient',
        gradient_preset: gradient.key,
        gradient_colors: gradient.colors,
      },
    })
  }

  function handleFontSelect(font) {
    updateCard({}, {
      typography: {
        font_preset: font.key,
      },
    })
  }

  function reloadPresets() {
    if (!endpoints.presetsIndex) return
    window.axios.get(endpoints.presetsIndex)
      .then((response) => {
        const data = response.data?.data || response.data || {}
        setCreatorPresets(data)
      })
      .catch(() => {})
  }

  function handleApplyPresetPatch(patch) {
    setCard((current) => ({
      ...current,
      project_json: deepMerge(current.project_json || {}, patch),
    }))
  }

  function fetchAiSuggestions() {
    if (!cardId || !endpoints.aiSuggestPattern) return
    const url = endpoints.aiSuggestPattern.replace('__CARD__', cardId)
    setLoadingAi(true)
    window.axios.get(url)
      .then((response) => setAiSuggestions(response.data?.suggestions || response.data || null))
      .catch(() => {})
      .finally(() => setLoadingAi(false))
  }

  function applyAiTagSuggestions(tags) {
    if (!Array.isArray(tags) || tags.length === 0) return
    const existing = tagInput ? tagInput.split(',').map((t) => t.trim()).filter(Boolean) : []
    const merged = [...new Set([...existing, ...tags])]
    setTagInput(merged.join(', '))
  }

  function requestExport(exportType) {
    if (!cardId || !endpoints.exportPattern) return
    const url = endpoints.exportPattern.replace('__CARD__', cardId)
    setRequestingExport(true)
    setExportStatus(null)
    setActiveExportType(exportType)
    const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content || ''
    window.axios.post(url, { export_type: exportType }, { headers: { 'X-CSRF-TOKEN': csrfToken } })
      .then((response) => {
        const exportData = response.data?.data || response.data
        setExportStatus(exportData)
        // Poll until ready
        if (exportData?.status === 'pending' || exportData?.status === 'processing') {
          pollExportStatus(exportData.id)
        }
      })
      .catch(() => setExportStatus({ status: 'failed' }))
      .finally(() => setRequestingExport(false))
  }

  function pollExportStatus(exportId, attempts = 0) {
    if (!endpoints.exportStatusPattern || attempts > 20) return
    const url = endpoints.exportStatusPattern.replace('__EXPORT__', exportId)
    window.setTimeout(() => {
      window.axios.get(url)
        .then((response) => {
          const data = response.data?.data || response.data
          setExportStatus(data)
          if (data?.status === 'pending' || data?.status === 'processing') {
            pollExportStatus(exportId, attempts + 1)
          }
        })
        .catch(() => {})
    }, 2500)
  }

  function handleElementMove(elementId, x, y, widthPct) {
    if (elementId.startsWith('block:')) {
      const key = elementId.slice(6)
      setCard((current) => {
        const blocks = Array.isArray(current.project_json?.text_blocks) ? [...current.project_json.text_blocks] : []
        const idx = blocks.findIndex((b) => b.key === key || b.type === key)
        if (idx === -1) return current
        const updated = [...blocks]
        updated[idx] = { ...updated[idx], pos_x: x, pos_y: y, ...(widthPct != null ? { pos_width: widthPct } : {}) }
        return { ...current, project_json: { ...current.project_json, text_blocks: updated } }
      })
    } else if (elementId.startsWith('decoration:')) {
      const idx = parseInt(elementId.slice(11), 10)
      setCard((current) => {
        const decorations = Array.isArray(current.project_json?.decorations) ? [...current.project_json.decorations] : []
        if (idx < 0 || idx >= decorations.length) return current
        const updated = [...decorations]
        updated[idx] = { ...updated[idx], pos_x: x, pos_y: y }
        return { ...current, project_json: { ...current.project_json, decorations: updated } }
      })
    }
  }

  function applyLayoutPreset(presetKey) {
    updateCard({}, {
      layout: {
        layout: presetKey,
        ...(layoutPresetMap[presetKey] || {}),
      },
    })
  }

  function addDecoration(decoration) {
    const placements = ['top-left', 'top-right', 'bottom-left', 'bottom-right']
    const current = Array.isArray(card.project_json?.decorations) ? card.project_json.decorations : []
    updateCard({}, {
      decorations: [
        ...current,
        {
          key: decoration.key,
          glyph: decoration.glyph,
          placement: placements[current.length % placements.length],
          size: 28,
        },
      ].slice(0, editorOptions.validation?.max_decorations || 6),
    })
  }

  function removeDecoration(index) {
    const current = Array.isArray(card.project_json?.decorations) ? card.project_json.decorations : []
    updateCard({}, { decorations: current.filter((_, itemIndex) => itemIndex !== index) })
  }

  function updateDecoration(index, patch) {
    const current = Array.isArray(card.project_json?.decorations) ? [...card.project_json.decorations] : []
    current[index] = { ...current[index], ...patch }
    updateCard({}, { decorations: current })
  }

  function togglePack(packId, bucket = 'pack_ids') {
    const current = Array.isArray(card.project_json?.assets?.[bucket]) ? card.project_json.assets[bucket] : []
    const numericId = Number(packId)
    const next = current.includes(numericId) ? current.filter((item) => item !== numericId) : [...current, numericId]
    updateCard({}, { assets: { [bucket]: next } })
  }

  function addAssetItem(item) {
    const current = Array.isArray(card.project_json?.assets?.items) ? card.project_json.assets.items : []
    updateCard({}, {
      assets: {
        items: [...current, {
          asset_key: item.key,
          label: item.label,
          glyph: item.glyph,
          type: item.type || 'glyph',
        }].slice(0, editorOptions.validation?.max_asset_items || 12),
      },
    })
  }

  function removeAssetItem(index) {
    const current = Array.isArray(card.project_json?.assets?.items) ? card.project_json.assets.items : []
    updateCard({}, { assets: { items: current.filter((_, itemIndex) => itemIndex !== index) } })
  }

  function manualSave() {
    if (!cardId) return
    const payload = buildPayload(card, tagInput)
    const serialized = JSON.stringify(payload)
    if (autosaveAbortRef.current) {
      autosaveAbortRef.current.abort()
      autosaveAbortRef.current = null
    }
    setBusy(true)
    setAutosaveStatus('saving')
    window.axios.patch(apiUrl(endpoints.draftUpdatePattern, cardId), payload)
      .then((response) => {
        const nextCard = normalizeCard(response.data.data, editorOptions)
        setCard((current) => mergeServerCard(current, nextCard))
        lastSerialized.current = serialized
        setAutosaveStatus('saved')
        setAutosaveMessage('Draft saved')
        loadVersions(nextCard.id)
      })
      .catch(() => {
        setAutosaveStatus('error')
        setAutosaveMessage('Save failed')
      })
      .finally(() => setBusy(false))
  }

  function renderPreview() {
    if (!cardId) return
    const payload = buildPayload(card, tagInput)
    const serialized = JSON.stringify(payload)
    if (autosaveAbortRef.current) {
      autosaveAbortRef.current.abort()
      autosaveAbortRef.current = null
    }
    setBusy(true)
    setAutosaveStatus('saving')
    setAutosaveMessage('Saving before render…')

    // Always flush the current editor state to the server before triggering the
    // render job, so the generated image matches exactly what the user sees.
    window.axios.post(apiUrl(endpoints.draftAutosavePattern, cardId), payload)
      .then(() => window.axios.post(apiUrl(endpoints.draftRenderPattern, cardId)))
      .then((response) => {
        const nextCard = normalizeCard(response.data.data, editorOptions)
        setCard((current) => mergeServerCard(current, nextCard))
        lastSerialized.current = serialized
        setAutosaveStatus('saved')
        setAutosaveMessage('Preview image rendered ✓')
        // Switch to the Publish tab so the rendered image is immediately visible.
        setActiveTab('publish')
      })
      .catch(() => {
        setAutosaveStatus('error')
        setAutosaveMessage('Render failed')
      })
      .finally(() => setBusy(false))
  }

  function publishCard() {
    if (!cardId) return
    const payload = buildPayload(card, tagInput)
    const serialized = JSON.stringify(payload)
    if (autosaveAbortRef.current) {
      autosaveAbortRef.current.abort()
      autosaveAbortRef.current = null
    }
    setBusy(true)
    window.axios.post(apiUrl(endpoints.draftPublishPattern, cardId), payload)
      .then((response) => {
        const nextCard = normalizeCard(response.data.data, editorOptions)
        setCard((current) => mergeServerCard(current, nextCard))
        lastSerialized.current = serialized
        setAutosaveStatus('saved')
        setAutosaveMessage('Card published')
        loadVersions(nextCard.id)
      })
      .catch((error) => {
        setAutosaveStatus('error')
        setAutosaveMessage(error?.response?.data?.message || 'Publish failed')
      })
      .finally(() => setBusy(false))
  }

  function deleteDraft() {
    if (!cardId || !window.confirm('Delete this draft?')) return
    setBusy(true)
    window.axios.delete(apiUrl(endpoints.draftDeletePattern, cardId))
      .then(() => {
        window.location.assign(endpoints.studioCards || '/studio/cards')
      })
      .finally(() => setBusy(false))
  }

  function uploadBackground(event) {
    const file = event.target.files?.[0]
    if (!file || !cardId) return

    const formData = new FormData()
    formData.append('background', file)

    setUploading(true)
    setActiveTab('background')
    window.axios.post(apiUrl(endpoints.draftBackgroundPattern, cardId), formData).then((response) => {
      const nextCard = normalizeCard(response.data.data, editorOptions)
      const bgInfo = response.data.background
      if (bgInfo?.processed_url) {
        nextCard.background_image = { processed_url: bgInfo.processed_url }
        nextCard.background_type = 'upload'
        if (nextCard.project_json) {
          nextCard.project_json = deepMerge(nextCard.project_json, { background: { type: 'upload' } })
        }
      }
      setCard(nextCard)
      setAutosaveStatus('saved')
      setAutosaveMessage('Background uploaded')
    }).catch(() => {
      setAutosaveStatus('error')
      setAutosaveMessage('Upload failed')
    }).finally(() => setUploading(false))
  }

  function createCollection() {
    const name = window.prompt('Collection name')
    if (!name || !endpoints.collectionsStore) return
    window.axios.post(endpoints.collectionsStore, { name })
      .then(() => loadCollections())
      .catch(() => {
        setAutosaveStatus('error')
        setAutosaveMessage('Collection could not be created')
      })
  }

  function saveToCollection() {
    if (!cardId || !endpoints.savePattern) return
    setBusy(true)
    window.axios.post(apiUrl(endpoints.savePattern, cardId), {
      collection_id: selectedCollectionId ? Number(selectedCollectionId) : undefined,
    }).then((response) => {
      setCard((current) => ({ ...current, saves_count: Number(response.data?.saves_count || current.saves_count || 0) }))
      setAutosaveStatus('saved')
      setAutosaveMessage('Saved to collection')
      loadCollections()
    }).catch(() => {
      setAutosaveStatus('error')
      setAutosaveMessage('Save to collection failed')
    }).finally(() => setBusy(false))
  }

  function restoreVersion(versionId) {
    if (!cardId || !endpoints.draftRestorePattern) return
    setBusy(true)
    window.axios.post(fillPattern(endpoints.draftRestorePattern, { CARD: cardId, VERSION: versionId }))
      .then((response) => {
        const nextCard = normalizeCard(response.data.data, editorOptions)
        setCard(nextCard)
        lastSerialized.current = JSON.stringify(buildPayload(nextCard, tagInput))
        setAutosaveStatus('saved')
        setAutosaveMessage('Version restored')
        loadVersions(nextCard.id)
      })
      .catch(() => {
        setAutosaveStatus('error')
        setAutosaveMessage('Restore failed')
      })
      .finally(() => setBusy(false))
  }

  function submitChallenge(challengeId) {
    if (!cardId || !endpoints.challengeSubmitPattern) return
    setBusy(true)
    window.axios.post(fillPattern(endpoints.challengeSubmitPattern, { CHALLENGE: challengeId, CARD: cardId }))
      .then((response) => {
        setCard((current) => ({ ...current, challenge_entries_count: Number(response.data?.challenge_entries_count || current.challenge_entries_count || 0) }))
        setAutosaveStatus('saved')
        setAutosaveMessage('Submitted to challenge')
      })
      .catch((error) => {
        setAutosaveStatus('error')
        setAutosaveMessage(error?.response?.data?.message || 'Challenge submission failed')
      })
      .finally(() => setBusy(false))
  }

  const decorations = Array.isArray(card.project_json?.decorations) ? card.project_json.decorations : []
  const textBlocks = Array.isArray(card.project_json?.text_blocks) ? card.project_json.text_blocks : []
  const backgroundType = card.project_json?.background?.type || card.background_type || 'gradient'
  const assetItems = Array.isArray(card.project_json?.assets?.items) ? card.project_json.assets.items : []
  const selectedAssetPackIds = Array.isArray(card.project_json?.assets?.pack_ids) ? card.project_json.assets.pack_ids : []
  const selectedTemplatePackIds = Array.isArray(card.project_json?.assets?.template_pack_ids) ? card.project_json.assets.template_pack_ids : []
  const currentMobileStepIndex = Math.max(0, mobileSteps.findIndex((step) => step.key === currentMobileStep))
  const currentMobileStepMeta = mobileSteps[currentMobileStepIndex] || mobileSteps[0]
  const editorMode = card.editor_mode_last_used || card.project_json?.source_context?.editor_mode || 'full'
  const advancedMode = editorMode !== 'quick'
  const currentProjectSummary = summarizeProjectSnapshot(card.project_json || {})

  const editorTabs = [
    { key: 'background', label: 'Canvas' },
    { key: 'content', label: 'Text' },
    { key: 'style', label: 'Style' },
    { key: 'publish', label: 'Publish' },
  ]

  const tabIndex = editorTabs.findIndex((t) => t.key === activeTab)

  function goToNextTab(direction) {
    const next = editorTabs[tabIndex + direction]
    if (next) {
      setActiveTab(next.key)
      window.scrollTo({ top: 0, behavior: 'smooth' })
    }
  }

  function tabBtnClasses(active) {
    return `flex-none whitespace-nowrap rounded-2xl border px-4 py-2 text-sm font-semibold transition ${
      active
        ? 'border-sky-400/30 bg-sky-500/15 text-sky-200'
        : 'border-transparent text-slate-400 hover:bg-white/[0.05] hover:text-white'
    }`
  }

  return (
    <StudioLayout title={previewMode ? 'Card Preview' : 'Card Editor'}>
      <Head title={previewMode ? 'Nova Card Preview' : 'Nova Card Editor'} />

      {/* Top action bar */}
      <section className="mb-5 flex flex-wrap items-center justify-between gap-3 rounded-[24px] border border-white/10 bg-[radial-gradient(circle_at_top_left,rgba(56,189,248,0.10),transparent_40%),linear-gradient(180deg,rgba(15,23,42,0.96),rgba(2,6,23,0.88))] px-5 py-4 shadow-[0_18px_48px_rgba(2,6,23,0.28)]">
        <div className="min-w-0">
          <p className="text-[11px] font-semibold uppercase tracking-[0.24em] text-sky-200/70">Nova Cards editor</p>
          <h2 className="mt-1 truncate text-xl font-semibold tracking-[-0.03em] text-white">{card.title || 'Untitled card'}</h2>
          <div className="mt-2 flex flex-wrap items-center gap-1.5">
            <button type="button" onClick={() => setEditorMode('quick')} className={pillClasses(editorMode === 'quick')}>Quick mode</button>
            <button type="button" onClick={() => setEditorMode('full')} className={pillClasses(editorMode === 'full')}>Advanced mode</button>
          </div>
        </div>
        <div className="flex flex-wrap items-center gap-2">
          <NovaCardAutosaveIndicator status={autosaveStatus} message={autosaveMessage} />
          <button type="button" onClick={manualSave} disabled={busy || !cardId} className="rounded-2xl border border-white/10 bg-white/[0.05] px-4 py-2 text-sm font-semibold text-white transition hover:bg-white/[0.08] disabled:opacity-60">Save</button>
          <button type="button" onClick={publishCard} disabled={busy || !cardId} className="rounded-2xl border border-emerald-300/20 bg-emerald-400/10 px-4 py-2 text-sm font-semibold text-emerald-100 transition hover:bg-emerald-400/15 disabled:opacity-60">Publish</button>
          {card.public_url ? <a href={card.public_url} className="rounded-2xl border border-white/10 bg-white/[0.05] px-4 py-2 text-sm font-semibold text-white transition hover:bg-white/[0.08]">Public page</a> : null}
        </div>
      </section>

      {/* Main grid: tabbed editor (left) + sticky preview (right) */}
      <div className="xl:grid xl:grid-cols-[minmax(0,480px)_minmax(0,1fr)] xl:items-start xl:gap-6">

        {/* Editor panel */}
        <div className="mb-6 xl:mb-0">
          {/* Tab navigation */}
          <div className="no-scrollbar -mx-1 flex gap-1 overflow-x-auto px-1 pb-2">
            {editorTabs.map((tab) => (
              <button key={tab.key} type="button" onClick={() => setActiveTab(tab.key)} className={tabBtnClasses(activeTab === tab.key)}>
                {tab.label}
              </button>
            ))}
          </div>

          {/* Tab panels */}
          <div className="mt-2 rounded-[28px] border border-white/10 bg-white/[0.04] p-5">

            {/* CANVAS TAB */}
            {activeTab === 'background' && (
              <div className="space-y-6">
                <div>
                  <div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">Canvas format</div>
                  <div className="flex flex-wrap gap-2">
                    {(editorOptions.formats || []).map((format) => (
                      <button key={format.key} type="button" onClick={() => updateCard({ format: format.key })} className={pillClasses((card.format || 'square') === format.key)}>
                        {format.label}
                      </button>
                    ))}
                  </div>
                </div>

                <div>
                  <div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">Template</div>
                  <NovaCardTemplatePicker templates={editorOptions.templates || []} selectedId={card.template_id} onSelect={handleTemplateSelect} />
                </div>

                <div>
                  <div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">Background type</div>
                  <div className="flex flex-wrap gap-2">
                    {(editorOptions.background_modes || []).map((mode) => (
                      <button key={mode.key} type="button" onClick={() => updateCard({ background_type: mode.key }, { background: { type: mode.key } })} className={pillClasses(backgroundType === mode.key)}>
                        {mode.label}
                      </button>
                    ))}
                  </div>
                </div>

                {(backgroundType === 'gradient' || backgroundType === 'template') && (
                  <div>
                    <div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">Gradient</div>
                    <NovaCardGradientPicker gradients={editorOptions.gradient_presets || []} selectedKey={card.project_json?.background?.gradient_preset} onSelect={handleGradientSelect} />
                  </div>
                )}

                {backgroundType === 'solid' && (
                  <div>
                    <div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">Solid color</div>
                    <div className="flex items-center gap-3">
                      <input type="color" value={card.project_json?.background?.solid_color || '#111827'} onChange={(event) => updateCard({ background_type: 'solid' }, { background: { type: 'solid', solid_color: event.target.value } })} className="h-12 w-16 cursor-pointer rounded-xl border border-white/10 bg-[#0d1726] p-1.5" />
                      <span className="font-mono text-sm text-white/60">{card.project_json?.background?.solid_color || '#111827'}</span>
                    </div>
                  </div>
                )}

                {backgroundType === 'upload' && (
                  <div>
                    <div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">Custom image</div>
                    {card.background_image?.processed_url ? (
                      <div className="overflow-hidden rounded-[18px] border border-white/10">
                        <img src={card.background_image.processed_url} alt="Uploaded background" className="h-44 w-full object-cover" />
                        <div className="flex items-center justify-between bg-white/[0.03] px-4 py-2.5">
                          <span className="text-xs font-semibold uppercase tracking-[0.16em] text-emerald-300">✓ Uploaded</span>
                          <label className="cursor-pointer text-xs text-slate-400 transition hover:text-white">
                            Replace image
                            <input type="file" accept="image/png,image/jpeg,image/webp" className="hidden" onChange={uploadBackground} disabled={uploading} />
                          </label>
                        </div>
                      </div>
                    ) : (
                      <label className={`flex cursor-pointer flex-col items-center gap-3 rounded-[22px] border-2 border-dashed px-6 py-10 text-center transition ${uploading ? 'border-sky-400/40 bg-sky-400/[0.04]' : 'border-white/12 bg-white/[0.02] hover:border-white/20 hover:bg-white/[0.04]'}`}>
                        <div className="text-3xl">{uploading ? '⏳' : '🖼️'}</div>
                        <div className="text-sm font-semibold text-slate-300">{uploading ? 'Uploading…' : 'Choose background image'}</div>
                        <div className="text-xs text-slate-500">JPG, PNG or WebP · Max 8 MB · Min 480×480 px</div>
                        <input type="file" accept="image/png,image/jpeg,image/webp" className="hidden" onChange={uploadBackground} disabled={uploading} />
                      </label>
                    )}
                  </div>
                )}

                <div>
                  <div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">Overlay &amp; depth</div>
                  <div className="space-y-4">
                    <div className="block text-sm text-slate-300">
                      <span className="mb-2 block">Overlay style</span>
                      <NovaSelect value={card.project_json?.background?.overlay_style || 'dark-soft'} onChange={(val) => updateCard({}, { background: { overlay_style: val } })} options={(overlayOptions || []).map((o) => ({ value: o.value, label: o.label }))} />
                    </div>
                    <label className="block text-sm text-slate-300">
                      <div className="mb-2 flex justify-between">
                        <span>Overlay opacity</span>
                        <span className="font-semibold text-white">{card.project_json?.background?.opacity || 50}%</span>
                      </div>
                      <input type="range" min="0" max="90" step="10" value={card.project_json?.background?.opacity || 50} onChange={(event) => updateCard({}, { background: { opacity: Number(event.target.value) } })} className="w-full" />
                    </label>
                    {advancedMode && (
                      <>
                        <label className="block text-sm text-slate-300">
                          <div className="mb-2 flex justify-between">
                            <span>Background blur</span>
                            <span className="font-semibold text-white">{card.project_json?.background?.blur_level || 0}px</span>
                          </div>
                          <input type="range" min="0" max="32" step="4" value={card.project_json?.background?.blur_level || 0} onChange={(event) => updateCard({}, { background: { blur_level: Number(event.target.value) } })} className="w-full" />
                        </label>
                        <div className="block text-sm text-slate-300">
                          <span className="mb-2 block">Focal position</span>
                          <NovaSelect value={card.project_json?.background?.focal_position || 'center'} onChange={(val) => updateCard({}, { background: { focal_position: val } })} options={(editorOptions.focal_positions || []).map((p) => ({ value: p.key, label: p.label }))} />
                        </div>
                      </>
                    )}
                  </div>
                </div>

                {advancedMode && (
                  <div>
                    <div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">Decorations</div>
                    <div className="grid grid-cols-3 gap-2 sm:grid-cols-5">
                      {(editorOptions.decor_presets || []).map((decor) => (
                        <button key={decor.key} type="button" onClick={() => addDecoration(decor)} className="flex flex-col items-center gap-1 rounded-[18px] border border-white/10 bg-white/[0.03] px-2 py-3 text-center transition hover:bg-white/[0.05]">
                          <span className="text-2xl">{decor.glyph}</span>
                          <span className="text-[10px] text-slate-400">{decor.label}</span>
                        </button>
                      ))}
                    </div>
                    {decorations.length > 0 && (
                      <div className="mt-3 space-y-2">
                        {decorations.map((decor, index) => (
                          <div key={`${decor.key}-${index}`} className="rounded-2xl border border-white/10 bg-white/[0.03] px-4 py-3 text-sm text-slate-200">
                            <div className="flex items-center justify-between">
                              <span className="text-base">{decor.glyph}</span>
                              <span className="flex-1 px-3 text-slate-300">{decor.key}</span>
                              <button type="button" onClick={() => removeDecoration(index)} className="text-rose-300 transition hover:text-rose-200">Remove</button>
                            </div>
                            <div className="mt-2 flex items-center gap-3">
                              <span className="text-[10px] uppercase tracking-[0.16em] text-slate-500 w-14 shrink-0">Opacity</span>
                              <input type="range" min="10" max="100" step="5"
                                value={decor.opacity ?? 85}
                                onChange={(event) => updateDecoration(index, { opacity: Number(event.target.value) })}
                                className="w-full" />
                              <span className="w-10 shrink-0 text-right font-mono text-xs text-white/70">{decor.opacity ?? 85}%</span>
                            </div>
                          </div>
                        ))}
                      </div>
                    )}
                  </div>
                )}
              </div>
            )}

            {/* TEXT TAB */}
            {activeTab === 'content' && (
              <div className="space-y-4">
                <div>
                  <div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">Font family</div>
                  <NovaCardFontPicker fonts={editorOptions.font_presets || []} selectedKey={card.project_json?.typography?.font_preset} onSelect={handleFontSelect} />
                </div>

                <div className="border-t border-white/10 pt-4">
                <label className="block text-sm text-slate-300">
                  <span className="mb-2 block">Title</span>
                  <input value={card.title || ''} onChange={(event) => updateTextField('title', event.target.value)} className="w-full rounded-2xl border border-white/10 bg-[#0d1726] px-4 py-3 text-white outline-none transition focus:border-sky-300/35" />
                </label>
                <label className="block text-sm text-slate-300">
                  <span className="mb-2 block">Quote text</span>
                  <textarea value={card.quote_text || ''} onChange={(event) => updateTextField('quote_text', event.target.value)} rows={5} className="w-full rounded-2xl border border-white/10 bg-[#0d1726] px-4 py-3 text-white outline-none transition focus:border-sky-300/35" />
                </label>
                <label className="block text-sm text-slate-300">
                  <span className="mb-2 block">Author</span>
                  <input value={card.quote_author || ''} onChange={(event) => updateTextField('quote_author', event.target.value)} className="w-full rounded-2xl border border-white/10 bg-[#0d1726] px-4 py-3 text-white outline-none transition focus:border-sky-300/35" />
                </label>
                <label className="block text-sm text-slate-300">
                  <span className="mb-2 block">Source</span>
                  <input value={card.quote_source || ''} onChange={(event) => updateTextField('quote_source', event.target.value)} className="w-full rounded-2xl border border-white/10 bg-[#0d1726] px-4 py-3 text-white outline-none transition focus:border-sky-300/35" />
                </label>

                {advancedMode && (
                  <div className="border-t border-white/10 pt-4">
                    <div className="mb-3 flex items-center justify-between gap-3">
                      <div className="text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">Text blocks</div>
                      <div className="flex gap-2">
                        <button type="button" onClick={() => addTextBlock('body')} className="rounded-2xl border border-white/10 bg-white/[0.05] px-3 py-1.5 text-xs font-semibold uppercase tracking-[0.16em] text-white">+ Body</button>
                        <button type="button" onClick={() => addTextBlock('caption')} className="rounded-2xl border border-white/10 bg-white/[0.05] px-3 py-1.5 text-xs font-semibold uppercase tracking-[0.16em] text-white">+ Caption</button>
                      </div>
                    </div>
                    <div className="space-y-3">
                      {textBlocks.map((block, index) => (
                        <div key={`${block.key || block.type}-${index}`} className="rounded-[20px] border border-white/10 bg-white/[0.03] p-3">
                          <div className="flex items-center gap-2">
                            <div className="flex flex-col gap-1">
                              <button type="button" onClick={() => moveTextBlock(index, -1)} disabled={index === 0} className="rounded border border-white/10 bg-white/[0.05] px-1.5 py-0.5 text-[10px] text-white disabled:opacity-30">▲</button>
                              <button type="button" onClick={() => moveTextBlock(index, 1)} disabled={index === textBlocks.length - 1} className="rounded border border-white/10 bg-white/[0.05] px-1.5 py-0.5 text-[10px] text-white disabled:opacity-30">▼</button>
                            </div>
                            <NovaSelect value={block.type || 'body'} onChange={(val) => updateTextBlock(index, { type: val })} searchable={false} options={[{ value: 'title', label: 'Title' }, { value: 'quote', label: 'Quote' }, { value: 'author', label: 'Author' }, { value: 'source', label: 'Source' }, { value: 'body', label: 'Body' }, { value: 'caption', label: 'Caption' }]} />
                            <input value={block.text || ''} onChange={(event) => updateTextBlock(index, { text: event.target.value, enabled: block.type === 'title' || block.type === 'quote' ? true : Boolean(event.target.value.trim()) })} className="min-w-0 flex-1 rounded-xl border border-white/10 bg-[#0d1726] px-3 py-2 text-sm text-white outline-none" />
                            <Checkbox checked={block.enabled !== false} onChange={(e) => updateTextBlock(index, { enabled: e.target.checked })} label="On" />
                            <button type="button" onClick={() => removeTextBlock(index)} disabled={block.type === 'title' || block.type === 'quote'} className="text-rose-300 transition hover:text-rose-200 disabled:opacity-30">×</button>
                          </div>
                        </div>
                      ))}
                    </div>
                  </div>
                )}
                </div>
              </div>
            )}
            {/* STYLE TAB */}
            {activeTab === 'style' && (
              <div className="space-y-6">
                <div>
                  <div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">Quote size</div>
                  <div className="flex items-center gap-3">
                    <input type="range" min="10" max="128" value={card.project_json?.typography?.quote_size || 72} onChange={(event) => updateCard({}, { typography: { quote_size: Number(event.target.value) } })} className="w-full" />
                    <span className="w-16 shrink-0 text-right font-mono text-sm font-semibold text-white">{card.project_json?.typography?.quote_size || 72}px</span>
                  </div>
                  <div className="mt-2 overflow-hidden rounded-xl border border-white/10 bg-[#0d1726] px-3 py-2">
                    <span className="font-semibold text-white/90 leading-none" style={{
                      fontFamily: (editorOptions.font_presets || []).find((f) => f.key === card.project_json?.typography?.font_preset)?.family,
                      fontSize: `${Math.min(Math.max(Number(card.project_json?.typography?.quote_size || 72) / 4, 6), 36)}px`,
                      color: card.project_json?.typography?.text_color || '#ffffff',
                    }}>Preview text</span>
                  </div>
                </div>

                <div>
                  <div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">Quote width</div>
                  <div className="flex items-center gap-3">
                    <input type="range" min="30" max="100" step="1"
                      value={card.project_json?.typography?.quote_width ?? 76}
                      onChange={(event) => updateCard({}, { typography: { quote_width: Number(event.target.value) } })}
                      className="w-full" />
                    <span className="w-16 shrink-0 text-right font-mono text-sm font-semibold text-white">{card.project_json?.typography?.quote_width ?? 76}%</span>
                  </div>
                </div>

                <div>
                  <div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">Author &amp; caption size</div>
                  <div className="flex items-center gap-3">
                    <input type="range" min="14" max="42" value={card.project_json?.typography?.author_size || 28} onChange={(event) => updateCard({}, { typography: { author_size: Number(event.target.value) } })} className="w-full" />
                    <span className="w-16 shrink-0 text-right font-mono text-sm font-semibold text-white">{card.project_json?.typography?.author_size || 28}px</span>
                  </div>
                </div>

                <div className="grid grid-cols-2 gap-4">
                  <div>
                    <div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">Text color</div>
                    <div className="flex items-center gap-2">
                      <input type="color" value={card.project_json?.typography?.text_color || '#ffffff'} onChange={(event) => updateCard({}, { typography: { text_color: event.target.value } })} className="h-10 w-12 shrink-0 cursor-pointer rounded-xl border border-white/10 bg-[#0d1726] p-1" />
                      <span className="font-mono text-xs text-white/60">{card.project_json?.typography?.text_color || '#ffffff'}</span>
                    </div>
                  </div>
                  <div>
                    <div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">Accent color</div>
                    <div className="flex items-center gap-2">
                      <input type="color" value={card.project_json?.typography?.accent_color || '#e0f2fe'} onChange={(event) => updateCard({}, { typography: { accent_color: event.target.value } })} className="h-10 w-12 shrink-0 cursor-pointer rounded-xl border border-white/10 bg-[#0d1726] p-1" />
                      <span className="font-mono text-xs text-white/60">{card.project_json?.typography?.accent_color || '#e0f2fe'}</span>
                    </div>
                  </div>
                </div>

                <div>
                  <div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">Text opacity</div>
                  <div className="flex items-center gap-3">
                    <input type="range" min="10" max="100" step="5"
                      value={card.project_json?.typography?.text_opacity ?? 100}
                      onChange={(event) => updateCard({}, { typography: { text_opacity: Number(event.target.value) } })}
                      className="w-full" />
                    <span className="w-16 shrink-0 text-right font-mono text-sm font-semibold text-white">{card.project_json?.typography?.text_opacity ?? 100}%</span>
                  </div>
                </div>

                <div>
                  <div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">Text shadow</div>
                  <div className="flex gap-2">
                    {(editorOptions.shadow_presets || [{ key: 'none', label: 'None' }, { key: 'soft', label: 'Soft' }, { key: 'strong', label: 'Strong' }]).map((preset) => {
                      const active = (card.project_json?.typography?.shadow_preset || 'soft') === preset.key
                      const shadowStyle = preset.key === 'soft' ? '0 6px 16px rgba(2,6,23,0.6)' : preset.key === 'strong' ? '0 10px 28px rgba(2,6,23,0.85)' : 'none'
                      return (
                        <button key={preset.key} type="button" onClick={() => updateCard({}, { typography: { shadow_preset: preset.key } })}
                          className={`flex-1 rounded-2xl border py-3 text-sm font-bold transition ${active ? 'border-sky-400/30 bg-sky-500/15 text-white' : 'border-white/10 bg-white/[0.03] text-slate-300 hover:bg-white/[0.05]'}`}
                          style={{ textShadow: shadowStyle }}
                        >
                          {preset.label}
                        </button>
                      )
                    })}
                  </div>
                </div>

                {advancedMode && (
                  <>
                    <div>
                      <div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">Letter spacing</div>
                      <div className="flex items-center gap-3">
                        <input type="range" min="-2" max="10" value={card.project_json?.typography?.letter_spacing || 0} onChange={(event) => updateCard({}, { typography: { letter_spacing: Number(event.target.value) } })} className="w-full" />
                        <span className="w-16 shrink-0 text-right font-mono text-sm font-semibold text-white">{card.project_json?.typography?.letter_spacing || 0}</span>
                      </div>
                    </div>
                    <div>
                      <div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">Line height</div>
                      <div className="flex gap-2">
                        {(editorOptions.line_height_presets || []).map((preset) => (
                          <button key={preset.key} type="button" onClick={() => updateCard({}, { typography: { line_height: Number(preset.value) } })}
                            className={`flex-1 rounded-2xl border py-2.5 text-sm font-semibold transition ${String(card.project_json?.typography?.line_height || 1.2) === String(preset.value) ? 'border-sky-400/30 bg-sky-500/15 text-sky-200' : 'border-white/10 bg-white/[0.03] text-slate-300 hover:bg-white/[0.05]'}`}
                          >
                            {preset.label}
                          </button>
                        ))}
                      </div>
                    </div>
                    <div>
                      <div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">Quote mark &amp; panel</div>
                      <div className="grid grid-cols-2 gap-3">
                        <div className="block text-sm text-slate-300">
                          <span className="mb-1.5 block text-xs">Quote mark style</span>
                          <NovaSelect value={card.project_json?.typography?.quote_mark_preset || 'none'} onChange={(val) => updateCard({}, { typography: { quote_mark_preset: val } })} options={(editorOptions.quote_mark_presets || []).map((p) => ({ value: p.key, label: p.label }))} />
                        </div>
                        <div className="block text-sm text-slate-300">
                          <span className="mb-1.5 block text-xs">Text panel style</span>
                          <NovaSelect value={card.project_json?.typography?.text_panel_style || 'none'} onChange={(val) => updateCard({}, { typography: { text_panel_style: val } })} options={(editorOptions.text_panel_styles || []).map((s) => ({ value: s.key, label: s.label }))} />
                        </div>
                      </div>
                    </div>
                  </>
                )}

                <div className="border-t border-white/10 pt-2">
                <div>
                  <div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">Layout preset</div>
                  <div className="flex flex-wrap gap-2">
                    {(editorOptions.layout_presets || []).map((preset) => (
                      <button key={preset.key} type="button" onClick={() => applyLayoutPreset(preset.key)} className={pillClasses((card.project_json?.layout?.layout || 'quote_heavy') === preset.key)}>
                        {preset.label}
                      </button>
                    ))}
                  </div>
                </div>

                <div>
                  <div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">Text alignment</div>
                  <div className="flex gap-2">
                    {(editorOptions.alignment_presets || [{ key: 'left', label: 'Left' }, { key: 'center', label: 'Center' }, { key: 'right', label: 'Right' }]).map((preset) => (
                      <button key={preset.key} type="button" onClick={() => updateCard({}, { layout: { alignment: preset.key } })}
                        className={`flex-1 rounded-2xl border py-2.5 text-sm font-semibold transition ${(card.project_json?.layout?.alignment || 'center') === preset.key ? 'border-sky-400/30 bg-sky-500/15 text-sky-200' : 'border-white/10 bg-white/[0.03] text-slate-300 hover:bg-white/[0.05]'}`}
                      >
                        {preset.label}
                      </button>
                    ))}
                  </div>
                </div>

                {advancedMode && (
                  <>
                    <div>
                      <div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">Vertical position</div>
                      <div className="flex flex-wrap gap-2">
                        {(editorOptions.position_presets || []).map((preset) => (
                          <button key={preset.key} type="button" onClick={() => updateCard({}, { layout: { position: preset.key } })} className={pillClasses((card.project_json?.layout?.position || 'center') === preset.key)}>
                            {preset.label}
                          </button>
                        ))}
                      </div>
                    </div>
                    <div className="grid grid-cols-2 gap-4">
                      <div>
                        <div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">Padding</div>
                        <div className="flex flex-col gap-2">
                          {(editorOptions.padding_presets || []).map((preset) => (
                            <button key={preset.key} type="button" onClick={() => updateCard({}, { layout: { padding: preset.key } })} className={pillClasses((card.project_json?.layout?.padding || 'comfortable') === preset.key)}>
                              {preset.label}
                            </button>
                          ))}
                        </div>
                      </div>
                      <div>
                        <div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">Text width</div>
                        <div className="flex flex-col gap-2">
                          {(editorOptions.max_width_presets || []).map((preset) => (
                            <button key={preset.key} type="button" onClick={() => updateCard({}, { layout: { max_width: preset.key } })} className={pillClasses((card.project_json?.layout?.max_width || 'balanced') === preset.key)}>
                              {preset.label}
                            </button>
                          ))}
                        </div>
                      </div>
                    </div>
                    <div>
                      <div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">Frame &amp; effects</div>
                      <div className="space-y-3">
                        <div className="block text-sm text-slate-300">
                          <span className="mb-1.5 block">Frame</span>
                          <NovaSelect value={card.project_json?.frame?.preset || 'none'} onChange={(val) => updateCard({}, { frame: { preset: val } })} options={(editorOptions.frame_presets || []).map((p) => ({ value: p.key, label: p.label }))} />
                        </div>
                        <div className="grid grid-cols-2 gap-3">
                          <div className="block text-sm text-slate-300">
                            <span className="mb-1.5 block text-xs">Color grade</span>
                            <NovaSelect value={card.project_json?.effects?.color_grade || 'none'} onChange={(val) => updateCard({}, { effects: { color_grade: val } })} options={(editorOptions.color_grade_presets || []).map((p) => ({ value: p.key, label: p.label }))} />
                          </div>
                          <div className="block text-sm text-slate-300">
                            <span className="mb-1.5 block text-xs">Effect</span>
                            <NovaSelect value={card.project_json?.effects?.effect_preset || 'none'} onChange={(val) => updateCard({}, { effects: { effect_preset: val } })} options={(editorOptions.effect_presets || []).map((p) => ({ value: p.key, label: p.label }))} />
                          </div>
                        </div>
                      </div>
                    </div>
                  </>
                )}

                {advancedMode && (Object.keys(creatorPresets).length > 0 || endpoints.presetsIndex) && (
                  <div>
                    <div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">Creator presets</div>
                    <NovaCardPresetPicker presets={creatorPresets} endpoints={endpoints} cardId={cardId} onApplyPatch={handleApplyPresetPatch} onPresetsChange={reloadPresets} />
                  </div>
                )}

                {advancedMode && endpoints.aiSuggestPattern && (
                  <div>
                    <div className="mb-3 flex items-center justify-between">
                      <div className="text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">AI assist</div>
                      <button type="button" disabled={loadingAi || !cardId} onClick={fetchAiSuggestions} className="rounded-full border border-sky-300/20 bg-sky-400/10 px-3 py-1.5 text-xs font-semibold text-sky-200 transition hover:bg-sky-400/15 disabled:opacity-50">
                        {loadingAi ? 'Analysing…' : 'Suggest'}
                      </button>
                    </div>
                    {aiSuggestions ? (
                      <div className="space-y-3">
                        {aiSuggestions.tags?.length > 0 && (
                          <div>
                            <div className="mb-2 text-xs font-semibold uppercase tracking-[0.18em] text-slate-500">Suggested tags</div>
                            <div className="flex flex-wrap gap-2">
                              {aiSuggestions.tags.map((tag) => (
                                <button key={tag} type="button" onClick={() => applyAiTagSuggestions([tag])} className="rounded-full border border-sky-400/20 bg-sky-400/10 px-3 py-1 text-xs font-semibold text-sky-200 transition hover:bg-sky-400/15">+ {tag}</button>
                              ))}
                            </div>
                            <button type="button" onClick={() => applyAiTagSuggestions(aiSuggestions.tags)} className="mt-1.5 text-xs text-slate-400 underline transition hover:text-white">Add all</button>
                          </div>
                        )}
                        {aiSuggestions.mood && (
                          <div className="rounded-xl border border-white/10 bg-white/[0.03] px-3 py-2.5">
                            <div className="text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-500">Mood</div>
                            <div className="text-sm text-white">{aiSuggestions.mood}</div>
                          </div>
                        )}
                        {aiSuggestions.layout_suggestion && (
                          <div className="flex items-center justify-between gap-3 rounded-xl border border-white/10 bg-white/[0.03] px-3 py-2.5">
                            <div>
                              <div className="text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-500">Layout suggestion</div>
                              <div className="text-sm text-white">{aiSuggestions.layout_suggestion}</div>
                            </div>
                            <button type="button" onClick={() => updateCard({}, { layout: { layout: aiSuggestions.layout_suggestion } })} className="rounded-full border border-white/10 bg-white/[0.05] px-3 py-1 text-xs font-semibold text-white transition hover:bg-white/[0.08]">Apply</button>
                          </div>
                        )}
                        {(aiSuggestions.readability_fixes || []).map((fix, fi) => (
                          <div key={fi} className="rounded-xl border border-amber-400/15 bg-amber-400/[0.06] px-3 py-2.5 text-sm text-amber-100">{fix}</div>
                        ))}
                      </div>
                    ) : (
                      <p className="text-sm text-slate-500">Run a suggestion pass to see AI-powered tips for this card.</p>
                    )}
                  </div>
                )}
                </div>
              </div>
            )}

            {/* PUBLISH TAB */}
            {activeTab === 'publish' && (
              <div className="space-y-5">
                <div className="block text-sm text-slate-300">
                  <span className="mb-2 block">Category</span>
                  <NovaSelect value={String(card.category_id || '')} onChange={(val) => updateCard({ category_id: val ? Number(val) : null })} placeholder="Select category" options={(editorOptions.categories || []).map((c) => ({ value: String(c.id), label: c.name }))} />
                </div>

                <div>
                  <div className="mb-2 text-sm text-slate-300">Visibility</div>
                  <div className="flex gap-2">
                    {['private', 'unlisted', 'public'].map((v) => (
                      <button key={v} type="button" onClick={() => updateCard({ visibility: v })}
                        className={`flex-1 rounded-2xl border py-2.5 text-sm font-semibold capitalize transition ${(card.visibility || 'private') === v ? 'border-sky-400/30 bg-sky-500/15 text-sky-200' : 'border-white/10 bg-white/[0.03] text-slate-300 hover:bg-white/[0.05]'}`}
                      >
                        {v}
                      </button>
                    ))}
                  </div>
                </div>

                <label className="block text-sm text-slate-300">
                  <span className="mb-2 block">Tags</span>
                  <input value={tagInput} onChange={(event) => setTagInput(event.target.value)} placeholder="motivation, calm, poetry" className="w-full rounded-2xl border border-white/10 bg-[#0d1726] px-4 py-3 text-white outline-none transition focus:border-sky-300/35" />
                </label>

                <div className="space-y-2">
                  {[
                    { key: 'allow_download', label: 'Allow download' },
                    { key: 'allow_remix', label: 'Allow remix' },
                    { key: 'allow_export', label: 'Allow export' },
                    { key: 'allow_background_reuse', label: 'Allow background reuse' },
                  ].map(({ key, label }) => (
                    <div key={key} className="flex items-center justify-between rounded-2xl border border-white/10 bg-white/[0.03] px-4 py-3 text-sm text-slate-200">
                      <span>{label}</span>
                      <Checkbox checked={key === 'allow_export' ? Boolean(card.allow_export !== false) : Boolean(card[key])} onChange={(event) => updateCard({ [key]: event.target.checked })} />
                    </div>
                  ))}
                </div>

                <label className="block text-sm text-slate-300">
                  <span className="mb-2 block">Description</span>
                  <textarea value={card.description || ''} onChange={(event) => updateCard({ description: event.target.value })} rows={3} className="w-full rounded-2xl border border-white/10 bg-[#0d1726] px-4 py-3 text-white outline-none transition focus:border-sky-300/35" />
                </label>

                {advancedMode && (
                  <div className="block text-sm text-slate-300">
                    <span className="mb-2 block">Style family</span>
                    <NovaSelect value={card.style_family || ''} onChange={(val) => updateCard({ style_family: val || null })} placeholder="None" options={(editorOptions.style_families || []).map((sf) => ({ value: sf.key, label: sf.label }))} />
                  </div>
                )}

                <div>
                  <div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">Save to collection</div>
                  <div className="flex gap-2">
                    <NovaSelect value={String(selectedCollectionId || '')} onChange={(val) => setSelectedCollectionId(val)} placeholder="Default saved cards" options={collections.map((c) => ({ value: String(c.id), label: c.name }))} />
                    <button type="button" onClick={saveToCollection} disabled={!cardId || busy} className="rounded-2xl border border-sky-300/20 bg-sky-400/10 px-4 py-3 text-sm font-semibold text-sky-100 transition hover:bg-sky-400/15 disabled:opacity-60">Save</button>
                  </div>
                  <button type="button" onClick={createCollection} className="mt-2 text-sm text-slate-400 transition hover:text-white">+ Create collection</button>
                </div>

                {(editorOptions.challenge_feed || []).length > 0 && (
                  <div>
                    <div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">Challenge entry</div>
                    <div className="space-y-2">
                      {(editorOptions.challenge_feed || []).slice(0, 4).map((challenge) => (
                        <button key={challenge.id} type="button" onClick={() => submitChallenge(challenge.id)} disabled={!cardId || busy} className="flex w-full items-center justify-between rounded-2xl border border-white/10 bg-white/[0.03] px-4 py-3 text-left text-sm text-slate-200 transition hover:bg-white/[0.05] disabled:opacity-60">
                          <span>{challenge.title}</span>
                          <span className="text-xs uppercase tracking-[0.16em] text-slate-400">{challenge.status}</span>
                        </button>
                      ))}
                    </div>
                  </div>
                )}

                <div>
                  <div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">Version history</div>
                  <div className="space-y-2">
                    {versions.length > 0 ? versions.map((version) => (
                      <div key={version.id} className="rounded-2xl border border-white/10 bg-white/[0.03] p-3 text-sm">
                        <div className="flex items-center justify-between gap-3">
                          <div>
                            <div className="font-semibold text-white">Version {version.version_number}</div>
                            <div className="text-xs text-slate-400">{version.label || 'Autosaved'}{version.created_at ? ` · ${new Date(version.created_at).toLocaleString()}` : ''}</div>
                          </div>
                          <button type="button" onClick={() => restoreVersion(version.id)} disabled={busy || !cardId} className="shrink-0 rounded-2xl border border-white/10 bg-white/[0.05] px-3 py-1.5 text-xs font-semibold uppercase tracking-[0.16em] text-white transition hover:bg-white/[0.08] disabled:opacity-60">Restore</button>
                        </div>
                        <div className="mt-2 flex flex-wrap gap-1.5 text-[11px] font-semibold uppercase tracking-[0.14em]">
                          {compareProjectSnapshots(card.project_json || {}, version.snapshot_json || {}).map((item) => (
                            <span key={`${version.id}-${item}`} className="rounded-full border border-sky-300/15 bg-sky-400/10 px-2 py-0.5 text-sky-200/80">{item}</span>
                          ))}
                        </div>
                      </div>
                    )) : (
                      <div className="rounded-2xl border border-dashed border-white/12 bg-white/[0.03] px-4 py-4 text-sm text-slate-400">Versions appear here after the first saved snapshot.</div>
                    )}
                  </div>
                </div>

                <div className="flex flex-wrap items-center justify-between gap-3 border-t border-white/10 pt-4">
                  {cardId ? <Link href={endpoints.studioCards || '/studio/cards'} className="text-sm text-slate-300 transition hover:text-white">← Back to cards</Link> : null}
                  {card.lineage?.original_card ? <div className="text-xs uppercase tracking-[0.16em] text-sky-200/70">Remix of: {card.lineage.original_card.title}</div> : null}
                  <button type="button" onClick={deleteDraft} disabled={busy || !cardId} className="rounded-2xl border border-rose-300/20 bg-rose-400/10 px-4 py-2 text-sm font-semibold text-rose-100 transition hover:bg-rose-400/15 disabled:opacity-60">Delete draft</button>
                </div>
              </div>
            )}

          </div>
        </div>

        {/* Sticky preview panel */}
        <div className="xl:sticky xl:top-5 xl:self-start xl:max-h-[calc(100vh-2.5rem)] xl:overflow-y-auto xl:pb-5">
          <section className="rounded-[32px] border border-white/10 bg-[linear-gradient(180deg,rgba(15,23,42,0.96),rgba(2,6,23,0.88))] p-5 shadow-[0_24px_70px_rgba(2,6,23,0.32)]">
            <div className="mb-3 flex items-center justify-between">
              <div className="text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-500">Live preview</div>
              <NovaCardAutosaveIndicator status={autosaveStatus} message={autosaveMessage} />
            </div>
            <NovaCardCanvasPreview card={card} fonts={editorOptions.font_presets || []} editable={true} onElementMove={handleElementMove} className="mx-auto w-full max-w-full" />

            {card.preview_url && (
              <div className="mt-5 rounded-[22px] border border-white/10 bg-white/[0.03] p-3">
                <div className="mb-2 text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400">Rendered image</div>
                <img src={`${card.preview_url}?version=${card.render_version || 0}`} alt="Rendered preview" className="w-full rounded-[18px] border border-white/10 object-cover" />
              </div>
            )}

            <div className="mt-4 flex flex-wrap gap-2">
              <button type="button" onClick={manualSave} disabled={busy || !cardId} className="flex-1 rounded-2xl border border-white/10 bg-white/[0.05] px-4 py-2.5 text-sm font-semibold text-white transition hover:bg-white/[0.08] disabled:opacity-60">Save</button>
              <button type="button" onClick={renderPreview} disabled={busy || !cardId} className="flex-1 rounded-2xl border border-amber-300/20 bg-amber-400/10 px-4 py-2.5 text-sm font-semibold text-amber-100 transition hover:bg-amber-400/15 disabled:opacity-60">Render preview</button>
              <button type="button" onClick={publishCard} disabled={busy || !cardId} className="flex-1 rounded-2xl border border-emerald-300/20 bg-emerald-400/10 px-4 py-2.5 text-sm font-semibold text-emerald-100 transition hover:bg-emerald-400/15 disabled:opacity-60">Publish</button>
            </div>

            {card.public_url && (
              <a href={card.public_url} className="mt-2 flex w-full items-center justify-center rounded-2xl border border-white/10 bg-white/[0.03] px-4 py-2.5 text-sm font-semibold text-white transition hover:bg-white/[0.05]">
                View public page →
              </a>
            )}
          </section>

          {endpoints.exportPattern && (
            <section className="mt-4 rounded-[24px] border border-white/10 bg-white/[0.04] p-4">
              <div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.2em] text-slate-400">Export</div>
              <div className="flex flex-wrap gap-2">
                {(editorOptions.export_formats || []).map((fmt) => (
                  <button key={fmt.key} type="button" disabled={requestingExport || !cardId} onClick={() => requestExport(fmt.key)}
                    className={`rounded-full border px-3 py-1.5 text-xs font-semibold uppercase tracking-[0.16em] transition disabled:opacity-50 ${activeExportType === fmt.key && exportStatus ? 'border-sky-300/30 bg-sky-400/15 text-sky-100' : 'border-white/10 bg-white/[0.03] text-slate-300 hover:bg-white/[0.05]'}`}
                  >
                    {fmt.label}
                  </button>
                ))}
              </div>
              {exportStatus && (
                <div className="mt-3 flex items-center gap-3 rounded-[18px] border border-white/10 bg-white/[0.03] p-3">
                  <div>
                    <div className="text-xs font-semibold uppercase tracking-[0.18em] text-slate-400">Export status</div>
                    <div className="text-sm font-semibold capitalize text-white">{exportStatus.status}</div>
                  </div>
                  {exportStatus.status === 'ready' && exportStatus.output_url && (
                    <a href={exportStatus.output_url} download className="ml-auto rounded-full border border-emerald-300/20 bg-emerald-400/10 px-3 py-1.5 text-xs font-semibold text-emerald-200 transition hover:bg-emerald-400/15">Download</a>
                  )}
                </div>
              )}
            </section>
          )}
        </div>

      </div>

      {/* Mobile bottom tab navigation */}
      <nav className="sticky bottom-0 z-20 mt-6 border-t border-white/10 bg-[rgba(2,6,23,0.92)] px-4 py-3 backdrop-blur xl:hidden">
        <div className="mx-auto flex max-w-7xl items-center justify-between gap-3">
          <button type="button" onClick={() => goToNextTab(-1)} disabled={tabIndex === 0} className="rounded-2xl border border-white/10 bg-white/[0.05] px-4 py-2.5 text-sm font-semibold text-white transition hover:bg-white/[0.08] disabled:opacity-50">
            Back
          </button>
          <div className="text-center">
            <div className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500">Step {tabIndex + 1} / {editorTabs.length}</div>
            <div className="mt-0.5 text-sm font-semibold text-white">{editorTabs[tabIndex]?.label}</div>
          </div>
          <button type="button" onClick={() => goToNextTab(1)} disabled={tabIndex >= editorTabs.length - 1} className="rounded-2xl border border-sky-300/20 bg-sky-400/10 px-4 py-2.5 text-sm font-semibold text-sky-100 transition hover:bg-sky-400/15 disabled:opacity-50">
            Next
          </button>
        </div>
      </nav>

    </StudioLayout>
  )
}
