<script setup lang="ts">
import type { CategoryNode } from './CategoryTreeNode.vue'
import type { CardDetail } from './CardDetailModal.vue'
import { request } from '~/utils/http/factory'
import { cardTypeRegistry, CARD_TYPE_OPTIONS, type CardType } from '~/config/cardTypes'

const ASPECT_PRESETS = [
  { label: '竖图 3:4', value: 0.75 },
  { label: '方形 1:1', value: 1.0 },
  { label: '横图 4:3', value: 1.33 },
  { label: '宽屏 16:9', value: 1.78 },
  { label: '超宽 2.35:1', value: 2.35 },
]

const props = defineProps<{
  mode: 'create' | 'edit'
  card: CardDetail | null
  categories: CategoryNode[]
  visible: boolean
}>()

const emit = defineEmits<{
  'update:visible': [v: boolean]
  saved: [cardData: any]
}>()

// ── Tag fetching ──

interface TagItem { id: number; name: string; slug: string }
const allTags = ref<TagItem[]>([])
const tagsLoaded = ref(false)

async function loadTags() {
  if (tagsLoaded.value) return
  try {
    const raw = await request<{ code: number; data: { list: TagItem[] } }>('/api/tags')
    allTags.value = raw.data.list
    tagsLoaded.value = true
  } catch { /* ignore */ }
}

// ── Form state ──

const formType = ref<string>('image-text')
const title = ref('')
const description = ref('')
const content = ref('')
const aspectRatio = ref<number | null>(1.33)
const categoryId = ref<string | null>(null)
const imageUrl = ref('')
const selectedTagIds = ref<number[]>([])

// ── Article fetching ──

interface ArticleItem { id: number; title: string }
const allArticles = ref<ArticleItem[]>([])
const articlesLoaded = ref(false)
const selectedArticleIds = ref<number[]>([])
const articleSearch = ref('')
const showArticlePicker = ref(false)

async function loadArticles() {
  if (articlesLoaded.value) return
  try {
    const raw = await request<{ code: number; data: { items: ArticleItem[] } }>(
      '/api/articles?pageSize=200',
    )
    allArticles.value = raw.data.items
    articlesLoaded.value = true
  } catch { /* ignore */ }
}

const availableArticles = computed(() =>
  allArticles.value.filter(
    (a) =>
      !selectedArticleIds.value.includes(a.id) &&
      a.title.toLowerCase().includes(articleSearch.value.toLowerCase()),
  ),
)

async function fetchCardArticles(cardId: number) {
  try {
    const raw = await request<{
      code: number
      data: { articles?: { id: number; title: string }[] }
    }>(`/api/cards/${cardId}`)
    if (raw.data?.articles) {
      selectedArticleIds.value = raw.data.articles.map((a) => a.id)
    }
  } catch { /* ignore */ }
}

const saving = ref(false)
const error = ref('')

// ── Computed ──

const isEdit = computed(() => props.mode === 'edit')

const titleText = computed(() => isEdit.value ? '编辑卡片' : '新增卡片')

const formFields = computed(() => cardTypeRegistry[formType.value as CardType]?.formFields ?? {})
const needsImage = computed(() => formFields.value.image ?? false)
const needsTags = computed(() => formFields.value.tags ?? false)
const needsDescription = computed(() => formFields.value.description ?? false)
const needsBody = computed(() => formFields.value.body ?? false)

// Flatten category tree for select
interface FlatCat { id: string; name: string; depth: number }
const flatCategories = computed<FlatCat[]>(() => {
  const result: FlatCat[] = [{ id: '', name: '无分类', depth: 0 }]
  function walk(nodes: CategoryNode[], depth: number) {
    for (const n of nodes) {
      if (n.id === '__uncategorized__') continue
      result.push({ id: n.id, name: n.name, depth })
      if (n.children) walk(n.children, depth + 1)
    }
  }
  walk(props.categories, 1)
  return result
})

const availableTags = computed(() =>
  allTags.value.filter(t => !selectedTagIds.value.includes(t.id))
)

// ── Init ──

const formReady = ref(false)

watch(() => props.visible, (v) => {
  if (!v) {
    formReady.value = false
    return
  }
  error.value = ''
  saving.value = false

  if (props.mode === 'edit' && props.card) {
    formType.value = props.card.type
    title.value = props.card.title
    description.value = props.card.description ?? ''
    content.value = (props.card as any).content ?? ''
    aspectRatio.value = props.card.aspectRatio
    categoryId.value = props.card.categoryId ?? null
    imageUrl.value = props.card.image ?? ''
    selectedTagIds.value = []
    selectedArticleIds.value = []
    fetchCardArticles(props.card.id)
  } else {
    formType.value = 'image-text'
    title.value = ''
    description.value = ''
    content.value = ''
    aspectRatio.value = 1.33
    categoryId.value = null
    imageUrl.value = ''
    selectedTagIds.value = []
    selectedArticleIds.value = []
  }

  loadTags()
  loadArticles()
  // 等表单字段写入后再渲染 ByteMD 编辑器
  nextTick(() => {
    formReady.value = true
  })
})

// ── File upload ──

async function onFileUpload(e: Event) {
  const file = (e.target as HTMLInputElement).files?.[0]
  if (!file) return
  const form = new FormData()
  form.append('file', file)
  try {
    const raw = await $fetch<{ code: number; data: { url: string } }>('/api/file/upload', {
      method: 'POST',
      body: form,
    })
    imageUrl.value = raw.data[0].url
  } catch (e) {
    console.error('[CardFormModal] 图片上传失败:', e)
    error.value = '图片上传失败'
  }
}

// ── Tag toggle ──

let articleBlurTimer: ReturnType<typeof setTimeout> | null = null

function onArticleFocus() {
  if (articleBlurTimer) clearTimeout(articleBlurTimer)
  showArticlePicker.value = true
  loadArticles()
}

function onArticleBlur() {
  articleBlurTimer = setTimeout(() => {
    showArticlePicker.value = false
  }, 200)
}

function addArticleId(id: number) {
  selectedArticleIds.value.push(id)
  articleSearch.value = ''
}

function toggleTag(tagId: number) {
  const idx = selectedTagIds.value.indexOf(tagId)
  if (idx >= 0) {
    selectedTagIds.value.splice(idx, 1)
  } else {
    selectedTagIds.value.push(tagId)
  }
}

// ── Submit ──

async function onSubmit() {
  error.value = ''

  // 仅标题为必填
  if (!title.value.trim()) { error.value = '请输入标题'; return }

  saving.value = true

  const body: Record<string, unknown> = {
    type: formType.value,
    title: title.value.trim(),
    description: description.value.trim() || null,
    content: content.value.trim() || null,
    aspectRatio: aspectRatio.value,
    categoryId: categoryId.value || null,
  }

  // 封面图（仅当有内容时传输）
  if (imageUrl.value.trim()) {
    body.images = [{ url: imageUrl.value.trim(), sortOrder: 0 }]
  }

  if (needsTags.value) {
    body.tagIds = [...selectedTagIds.value]
  }

  body.articleIds = selectedArticleIds.value.length > 0 ? [...selectedArticleIds.value] : null

  try {
    let res: any
    if (isEdit.value && props.card) {
      res = await request(`/api/cards/${props.card.id}`, { method: 'PUT', body })
    } else {
      res = await request('/api/cards', { method: 'POST', body })
    }
    emit('saved', res.data)
    emit('update:visible', false)
  } catch (err: any) {
    error.value = err?.data?.message ?? err?.message ?? '保存失败'
  } finally {
    saving.value = false
  }
}
</script>

<template>
  <BoDialog
    :show="visible"
    :mask-can-close="!saving"
    :style="{ width: '85vw', maxWidth: '1400px' }"
    @update:show="(v: boolean) => emit('update:visible', v)"
  >
    <div class="card-form">
      <!-- Header -->
      <div class="form-header">
        <h2 class="form-title">{{ titleText }}</h2>
        <button
          type="button"
          class="form-close"
          :disabled="saving"
          @click="emit('update:visible', false)"
        >
          <Icon name="lucide:x" />
        </button>
      </div>

      <!-- Body -->
      <div class="form-body">
        <!-- Error: full width -->
        <div v-if="error" class="form-error">
          <Icon name="lucide:alert-circle" class="form-error-icon" />
          <span>{{ error }}</span>
        </div>

        <div class="form-columns">
          <!-- ═══ 左栏：基础信息 ═══ -->
          <div class="form-left">
            <!-- ── 卡片类型 ── -->
            <div class="form-group">
              <div class="form-group-label">卡片类型</div>
              <div class="type-grid">
                <button
                  v-for="ct in CARD_TYPE_OPTIONS"
                  :key="ct.value"
                  type="button"
                  class="type-card"
                  :class="{ active: formType === ct.value }"
                  :disabled="saving"
                  @click="formType = ct.value"
                >
                  <Icon :name="ct.icon" class="type-card-icon" />
                  <span class="type-card-label">{{ ct.label }}</span>
                </button>
              </div>
            </div>

            <!-- ── 标题 ── -->
            <div class="form-group">
              <div class="form-group-label">标题 <span class="field-required">*</span></div>
              <div class="field-header">
                <span class="field-count">{{ title.length }}/60</span>
              </div>
              <input
                v-model="title"
                type="text"
                class="field-input"
                placeholder="输入卡片标题"
                :disabled="saving"
                maxlength="60"
                @keyup.enter="onSubmit"
              />
            </div>

            <!-- ── 分类 ── -->
            <div class="form-group">
              <div class="form-group-label">分类</div>
              <select v-model="categoryId" class="field-select" :disabled="saving">
                <option
                  v-for="fc in flatCategories"
                  :key="fc.id"
                  :value="fc.id || null"
                >
                  {{ '\u00A0\u00A0'.repeat(fc.depth) }}{{ fc.name }}
                </option>
              </select>
            </div>

            <!-- ── 描述 ── -->
            <div class="form-group">
              <div class="field-header">
                <label class="form-group-label" style="margin-bottom:0">描述</label>
                <span class="field-count">{{ description.length }}/200</span>
              </div>
              <textarea
                v-model="description"
                class="field-textarea"
                rows="3"
                placeholder="简短的描述…"
                :disabled="saving"
                maxlength="200"
              ></textarea>
            </div>

            <!-- ── 宽高比 ── -->
            <div class="form-group">
              <div class="form-group-label">宽高比</div>
              <div class="ratio-bar">
                <button
                  v-for="r in ASPECT_PRESETS"
                  :key="r.value"
                  type="button"
                  class="ratio-btn"
                  :class="{ active: aspectRatio === r.value }"
                  :disabled="saving"
                  @click="aspectRatio = r.value"
                >
                  {{ r.label }}
                </button>
              </div>
            </div>
          </div>

          <!-- ═══ 右栏：媒体 & 关联 ═══ -->
          <div class="form-right">
            <!-- ── 封面图 ── -->
            <div class="form-group">
              <div class="form-group-label">封面图</div>

              <div class="image-upload-row">
                <label class="image-dropzone">
                  <Icon name="lucide:image-plus" class="dropzone-icon" />
                  <span class="dropzone-text">点击上传</span>
                  <input
                    type="file"
                    accept="image/*"
                    :disabled="saving"
                    hidden
                    @change="onFileUpload"
                  />
                </label>
                <input
                  v-model="imageUrl"
                  type="url"
                  class="field-input"
                  placeholder="或粘贴图片 URL…"
                  :disabled="saving"
                />
              </div>

              <div v-if="imageUrl" class="image-preview-block">
                <img
                  :src="imageUrl"
                  alt="预览"
                  class="image-preview-img"
                  @error="(e) => { (e.target as HTMLImageElement).style.display = 'none' }"
                />
                <button
                  type="button"
                  class="image-preview-clear"
                  :disabled="saving"
                  @click="imageUrl = ''"
                >
                  <Icon name="lucide:x" />
                </button>
              </div>
            </div>

            <!-- ── 正文（Markdown） ── -->
            <div class="form-group">
              <div class="field-header">
                <label class="form-group-label" style="margin-bottom:0">正文（Markdown）</label>
                <span class="field-count">{{ content.length }}/50000</span>
              </div>
              <BoMdEditor
                v-if="formReady"
                :key="props.card?.id ?? 'new'"
                v-model="content"
                placeholder="支持 Markdown 语法…"
                :disabled="saving"
                :maxlength="50000"
                height="400px"
              />
            </div>

            <!-- ── 标签 ── -->
            <div class="form-group">
              <div class="form-group-label">标签</div>

              <div v-if="selectedTagIds.length > 0" class="chip-row">
                <span
                  v-for="tid in selectedTagIds"
                  :key="tid"
                  class="chip chip-active"
                >
                  {{ allTags.find(t => t.id === tid)?.name ?? `#${tid}` }}
                  <button
                    type="button"
                    class="chip-remove"
                    :disabled="saving"
                    @click="toggleTag(tid)"
                  >
                    <Icon name="lucide:x" />
                  </button>
                </span>
              </div>

              <div v-if="availableTags.length > 0" class="tag-pool">
                <button
                  v-for="tag in availableTags"
                  :key="tag.id"
                  type="button"
                  class="tag-chip"
                  :disabled="saving"
                  @click="toggleTag(tag.id)"
                >
                  {{ tag.name }}
                </button>
              </div>

              <span v-if="allTags.length === 0 && tagsLoaded" class="hint-text">
                暂无可用标签，请先在标签管理中创建
              </span>
            </div>

            <!-- ── 关联文章 ── -->
            <div class="form-group">
              <div class="form-group-label">关联文章</div>

              <div v-if="selectedArticleIds.length > 0" class="chip-row">
                <span
                  v-for="aid in selectedArticleIds"
                  :key="aid"
                  class="chip chip-article"
                >
                  {{ allArticles.find(a => a.id === aid)?.title ?? `文章 #${aid}` }}
                  <button
                    type="button"
                    class="chip-remove"
                    :disabled="saving"
                    @click="selectedArticleIds = selectedArticleIds.filter(id => id !== aid)"
                  >
                    <Icon name="lucide:x" />
                  </button>
                </span>
              </div>

              <div class="article-picker">
                <div class="article-search-wrap">
                  <Icon name="lucide:search" class="article-search-icon" />
                  <input
                    v-model="articleSearch"
                    type="text"
                    class="field-input"
                    placeholder="搜索文章然后点击添加…"
                    :disabled="saving"
                    @focus="onArticleFocus"
                    @blur="onArticleBlur"
                  />
                </div>
                <div v-if="showArticlePicker && availableArticles.length > 0" class="article-dropdown">
                  <button
                    v-for="a in availableArticles.slice(0, 12)"
                    :key="a.id"
                    type="button"
                    class="article-option"
                    :disabled="saving"
                    @mousedown.prevent="addArticleId(a.id)"
                  >
                    {{ a.title }}
                  </button>
                </div>
                <div
                  v-else-if="showArticlePicker && articleSearch && availableArticles.length === 0"
                  class="article-dropdown"
                >
                  <span class="article-empty">未找到匹配文章</span>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>

      <!-- Footer -->
      <div class="form-footer">
        <button
          type="button"
          class="btn btn-secondary"
          :disabled="saving"
          @click="emit('update:visible', false)"
        >
          取消
        </button>
        <button
          type="button"
          class="btn btn-primary"
          :disabled="saving"
          @click="onSubmit"
        >
          <Icon v-if="saving" name="lucide:loader-2" class="btn-spinner" />
          {{ saving ? '保存中…' : (isEdit ? '保存修改' : '创建卡片') }}
        </button>
      </div>
    </div>
  </BoDialog>
</template>

<style scoped>
/* ═══════════════════════════════════════════════════════════════════
   Card Form — Human-centered redesign
   ═══════════════════════════════════════════════════════════════════ */

.card-form {
  display: flex;
  flex-direction: column;
  background: var(--color-canvas, #faf9f5);
  border-radius: 16px;
}

/* ── Header ─────────────────────────────────────────────────────── */

.form-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 20px 28px 0;
  flex-shrink: 0;
}

.form-title {
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 500;
  color: var(--color-ink);
  margin: 0;
  letter-spacing: -0.3px;
}

.form-close {
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: none;
  border-radius: 8px;
  color: var(--color-muted);
  cursor: pointer;
  transition: all 0.12s ease;
}
.form-close :deep(svg) { width: 18px; height: 18px; }
.form-close:hover { background: var(--color-surface-soft); color: var(--color-ink); }
.form-close:disabled { opacity: 0.4; cursor: not-allowed; }

/* ── Body ───────────────────────────────────────────────────────── */

.form-body {
  padding: 20px 28px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  max-height: calc(85vh - 130px);
  overflow-y: auto;
}

/* ── Two-column grid ────────────────────────────────────────────── */

.form-columns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 28px;
}

.form-left,
.form-right {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-width: 0;
}

/* ── Error ──────────────────────────────────────────────────────── */

.form-error {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  background: rgba(198, 69, 69, 0.07);
  color: var(--color-error, #c64545);
  border-radius: 10px;
  font-size: 13px;
  font-weight: 500;
}
.form-error-icon { width: 16px; height: 16px; flex-shrink: 0; }

/* ── Form group ─────────────────────────────────────────────────── */

.form-group {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.form-group-label {
  font-size: 13px;
  font-weight: 600;
  color: var(--color-muted);
  text-transform: uppercase;
  letter-spacing: 0.6px;
}

/* ── Type grid ──────────────────────────────────────────────────── */

.type-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
}

.type-grid :last-child:nth-child(4),
.type-grid :last-child:nth-child(5) {
  /* Let the last row of 2 items stay 3-column sized */
}

.type-card {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 16px;
  border-radius: 12px;
  border: 1.5px solid var(--color-hairline);
  background: var(--color-canvas);
  cursor: pointer;
  transition: all 0.18s ease;
  font-family: var(--font-body);
}
.type-card-icon {
  width: 22px;
  height: 22px;
  color: var(--color-muted);
  flex-shrink: 0;
  transition: color 0.18s ease;
}
.type-card-label {
  font-size: 14px;
  font-weight: 500;
  color: var(--color-body);
  transition: color 0.18s ease;
}
.type-card:hover {
  border-color: var(--color-primary);
  background: rgba(204, 120, 92, 0.04);
}
.type-card:hover .type-card-icon,
.type-card:hover .type-card-label {
  color: var(--color-primary);
}
.type-card.active {
  border-color: var(--color-primary);
  background: rgba(204, 120, 92, 0.08);
  box-shadow: 0 0 0 3px rgba(204, 120, 92, 0.10);
}
.type-card.active .type-card-icon,
.type-card.active .type-card-label {
  color: var(--color-primary);
}
.type-card:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* ── Fields ─────────────────────────────────────────────────────── */

.form-field {
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.field-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.field-label {
  font-size: 14px;
  font-weight: 500;
  color: var(--color-body);
}
.field-required {
  color: var(--color-error, #c64545);
  font-weight: 600;
}
.field-count {
  font-size: 11px;
  color: var(--color-muted-soft);
}

.field-input,
.field-select,
.field-textarea {
  width: 100%;
  padding: 10px 14px;
  font-size: 14px;
  color: var(--color-ink);
  background: var(--color-canvas);
  border: 1px solid var(--color-hairline);
  border-radius: 10px;
  outline: none;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
  font-family: var(--font-body);
  box-sizing: border-box;
}
.field-input:focus,
.field-select:focus,
.field-textarea:focus {
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px rgba(204, 120, 92, 0.12);
}

.field-select {
  cursor: pointer;
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%236c6a64' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
  padding-right: 40px;
}

.field-textarea {
  resize: vertical;
  min-height: 90px;
  line-height: 1.6;
}

/* ── Image upload ───────────────────────────────────────────────── */

.image-upload-row {
  display: flex;
  gap: 10px;
}

.image-dropzone {
  width: 100px;
  height: 42px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  border: 1.5px dashed var(--color-hairline);
  border-radius: 10px;
  cursor: pointer;
  color: var(--color-muted);
  transition: all 0.15s ease;
  flex-shrink: 0;
  background: var(--color-canvas);
}
.image-dropzone:hover {
  border-color: var(--color-primary);
  color: var(--color-primary);
  background: rgba(204, 120, 92, 0.04);
}
.dropzone-icon { width: 20px; height: 20px; }
.dropzone-text { font-size: 11px; font-weight: 500; }

.image-upload-row .field-input {
  flex: 1;
}

.image-preview-block {
  position: relative;
  border-radius: 10px;
  overflow: hidden;
  border: 1px solid var(--color-hairline);
  background: var(--color-surface-soft);
}
.image-preview-img {
  width: 100%;
  max-height: 320px;
  object-fit: cover;
  display: block;
}
.image-preview-clear {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: rgba(20, 20, 19, 0.45);
  backdrop-filter: blur(6px);
  border-radius: 50%;
  color: #fff;
  cursor: pointer;
  transition: background 0.15s;
}
.image-preview-clear :deep(svg) { width: 14px; height: 14px; }
.image-preview-clear:hover { background: rgba(20, 20, 19, 0.7); }

/* ── Multi-image ────────────────────────────────────────────────── */

.multi-image-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.multi-image-row {
  display: flex;
  gap: 8px;
  align-items: center;
}

.multi-image-index {
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 600;
  color: var(--color-muted);
  background: var(--color-surface-soft);
  border-radius: 8px;
  flex-shrink: 0;
}

.multi-image-row .field-input {
  flex: 1;
}

.multi-image-remove {
  width: 34px;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--color-hairline);
  background: var(--color-canvas);
  border-radius: 8px;
  color: var(--color-muted);
  cursor: pointer;
  flex-shrink: 0;
  transition: all 0.12s ease;
}
.multi-image-remove :deep(svg) { width: 15px; height: 15px; }
.multi-image-remove:hover { border-color: var(--color-error); color: var(--color-error); }

.add-row-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 14px;
  font-size: 13px;
  font-weight: 500;
  border-radius: 8px;
  border: 1.5px dashed var(--color-hairline);
  background: none;
  color: var(--color-muted);
  cursor: pointer;
  transition: all 0.15s ease;
  align-self: flex-start;
}
.add-row-btn :deep(svg) { width: 15px; height: 15px; }
.add-row-btn:hover { border-color: var(--color-primary); color: var(--color-primary); }

.image-previews-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));
  gap: 8px;
}

.image-preview-thumb {
  aspect-ratio: 1;
  border-radius: 8px;
  overflow: hidden;
  background: var(--color-surface-soft);
  border: 1px solid var(--color-hairline);
}
.image-preview-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ── Aspect ratio ───────────────────────────────────────────────── */

.ratio-bar {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}

.ratio-btn {
  padding: 8px 16px;
  font-size: 13px;
  font-weight: 500;
  border-radius: 10px;
  border: 1.5px solid var(--color-hairline);
  background: var(--color-canvas);
  color: var(--color-body);
  cursor: pointer;
  transition: all 0.15s ease;
  font-family: var(--font-body);
}
.ratio-btn:hover {
  border-color: var(--color-muted);
}
.ratio-btn.active {
  border-color: var(--color-primary);
  background: rgba(204, 120, 92, 0.08);
  color: var(--color-primary);
}
.ratio-btn:disabled { opacity: 0.4; cursor: not-allowed; }

/* ── Chips ──────────────────────────────────────────────────────── */

.chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 5px 12px;
  font-size: 13px;
  font-weight: 500;
  border-radius: 9999px;
  border: none;
}

.chip-active {
  background: var(--color-primary);
  color: #fff;
}
.chip-article {
  background: var(--color-accent-teal, #5db8a6);
  color: #fff;
}

.chip-remove {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  border: none;
  background: rgba(255, 255, 255, 0.22);
  border-radius: 50%;
  cursor: pointer;
  color: inherit;
  padding: 0;
}
.chip-remove :deep(svg) { width: 10px; height: 10px; }
.chip-remove:hover { background: rgba(255, 255, 255, 0.4); }

/* ── Tag pool ───────────────────────────────────────────────────── */

.tag-pool {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.tag-chip {
  padding: 5px 14px;
  font-size: 13px;
  font-weight: 500;
  border-radius: 9999px;
  border: 1px solid var(--color-hairline);
  background: var(--color-canvas);
  color: var(--color-body);
  cursor: pointer;
  transition: all 0.12s ease;
  font-family: var(--font-body);
}
.tag-chip:hover {
  border-color: var(--color-primary);
  color: var(--color-primary);
  background: rgba(204, 120, 92, 0.05);
}
.tag-chip:disabled { opacity: 0.4; cursor: not-allowed; }

/* ── Article picker ─────────────────────────────────────────────── */

.article-picker {
  position: relative;
}

.article-search-wrap {
  position: relative;
}
.article-search-icon {
  position: absolute;
  left: 12px;
  top: 50%;
  transform: translateY(-50%);
  width: 15px;
  height: 15px;
  color: var(--color-muted-soft);
  pointer-events: none;
}
.article-search-wrap .field-input {
  padding-left: 36px;
}

.article-dropdown {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  margin-top: 4px;
  background: #fff;
  border: 1px solid var(--color-hairline);
  border-radius: 10px;
  max-height: 200px;
  overflow-y: auto;
  z-index: 10;
  box-shadow: 0 6px 20px rgba(20, 20, 19, 0.10);
}

.article-option {
  display: block;
  width: 100%;
  padding: 9px 14px;
  border: none;
  background: none;
  font-size: 13px;
  color: var(--color-body);
  text-align: left;
  cursor: pointer;
  transition: background 0.1s;
  font-family: var(--font-body);
}
.article-option:hover {
  background: var(--color-surface-soft);
}

.article-empty {
  display: block;
  padding: 10px 14px;
  font-size: 13px;
  color: var(--color-muted);
}

/* ── Hint ───────────────────────────────────────────────────────── */

.hint-text {
  font-size: 12px;
  color: var(--color-muted-soft);
  line-height: 1.4;
}

/* ── Footer ─────────────────────────────────────────────────────── */

.form-footer {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  padding: 14px 28px;
  border-top: 1px solid var(--color-hairline);
  background: var(--color-surface-soft);
  flex-shrink: 0;
}

.btn {
  height: 40px;
  padding: 0 20px;
  font-size: 14px;
  font-weight: 500;
  border-radius: 10px;
  cursor: pointer;
  transition: all 0.15s ease;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-body);
}
.btn:disabled { opacity: 0.5; cursor: not-allowed; }

.btn-secondary {
  color: var(--color-body);
  background: var(--color-canvas);
  border: 1px solid var(--color-hairline);
}
.btn-secondary:hover:not(:disabled) {
  background: #fff;
  border-color: var(--color-muted);
}

.btn-primary {
  color: #fff;
  background: var(--color-primary);
  border: none;
}
.btn-primary:hover:not(:disabled) {
  background: var(--color-primary-active, #a9583e);
}

.btn-spinner {
  animation: spin 0.8s linear infinite;
  width: 16px;
  height: 16px;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

/* ── Responsive: single column on mobile ────────────────────────── */

@media (max-width: 767.98px) {
  .form-columns {
    grid-template-columns: 1fr;
    gap: 16px;
  }

  .form-body {
    padding: 16px;
    max-height: calc(85vh - 120px);
  }

  .form-header {
    padding: 16px 16px 0;
  }

  .form-footer {
    padding: 12px 16px;
    flex-direction: column;
    gap: 8px;
  }

  .form-footer .btn {
    width: 100%;
    justify-content: center;
  }
}
</style>
