<script setup lang="ts">
import {
  DeleteOutlined,
  EditOutlined,
  LeftOutlined,
  RobotOutlined,
  SaveOutlined,
  ThunderboltOutlined,
} from '@ant-design/icons-vue'
import { ApiClientError } from '@geo/http-client'
import type { BrandLibrarySummary, QuestionCandidate, QuestionSource } from '@geo/shared-types'
import { useMutation, useQuery, useQueryClient } from '@tanstack/vue-query'
import { message } from 'ant-design-vue'
import { computed, onBeforeUnmount, reactive, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRoute, useRouter } from 'vue-router'

import AiWaitingModal from '@/components/AiWaitingModal.vue'
import { brandsApi } from '@/lib/api'
import {
  formatError,
  getQuestionMaterializeErrorMessage,
  isDuplicateQuestionSkipReason,
} from '@/lib/errors'

type CreateMode = 'combination' | 'ai' | 'batch'

interface CandidateRow extends QuestionCandidate {
  id: string
  selected: boolean
}

const AI_SEED_TOPIC_MIN_LENGTH = 2

const queryClient = useQueryClient()
const route = useRoute()
const router = useRouter()
const { t } = useI18n()

const currentStep = ref(0)
const createMode = ref<CreateMode>('combination')
const candidateRows = ref<CandidateRow[]>([])
const editingCandidateId = ref<string | null>(null)
const pageNotice = ref('')
const aiCandidateModalOpen = ref(false)
const aiCandidateProgress = ref(12)
const aiCandidateStageIndex = ref(0)
const aiWaitingKind = ref<'candidate' | 'fill'>('candidate')
const aiSeedTopicError = ref('')

let aiCandidateProgressTimer: number | null = null
let aiCandidateCloseTimer: number | null = null

const vFocus = {
  mounted(el: HTMLElement) {
    const input =
      el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement
        ? el
        : el.querySelector<HTMLInputElement | HTMLTextAreaElement>('input, textarea')
    if (input) {
      input.focus()
      const len = input.value.length
      input.setSelectionRange(len, len)
    }
  },
}

const expansionForm = reactive({
  region: [''],
  prefix: [''],
  core: [''],
  industry: [''],
  suffix: [''],
})

const aiForm = reactive({
  seed_topics: [] as string[],
})

const aiTopicTokenSeparators = [',', '，', ';', '；', '\n']

const batchForm = reactive({
  text: '',
})

const brandId = computed(() => {
  const value = Number(route.params.brandId)
  return Number.isNaN(value) || value <= 0 ? 0 : value
})

const brandQuery = useQuery({
  queryKey: computed(() => ['brands', 'detail', brandId.value]),
  enabled: computed(() => brandId.value > 0),
  queryFn: () => brandsApi.detail(brandId.value),
})

const brandLibrarySummaryQuery = useQuery({
  queryKey: ['brands', 'library-summary'],
  queryFn: () => brandsApi.getLibrarySummary(),
})

const questionsQuery = useQuery({
  queryKey: computed(() => ['brands', brandId.value, 'questions', 'all']),
  enabled: computed(() => brandId.value > 0),
  queryFn: () => brandsApi.listQuestions(brandId.value),
})

const brandLibrarySummary = computed<BrandLibrarySummary | null>(
  () => brandLibrarySummaryQuery.data.value ?? null,
)

const currentQuestions = computed(() => questionsQuery.data.value ?? [])
const maxQuestions = computed(
  () =>
    brandLibrarySummary.value?.max_questions ??
    brandLibrarySummary.value?.max_questions_per_brand ??
    0,
)
const usedQuestions = computed(
  () => brandLibrarySummary.value?.used_questions ?? currentQuestions.value.length,
)
const remainingQuestions = computed(() => {
  if (typeof brandLibrarySummary.value?.remaining_questions === 'number') {
    return brandLibrarySummary.value.remaining_questions
  }
  if (!maxQuestions.value) {
    return 0
  }
  return Math.max(maxQuestions.value - usedQuestions.value, 0)
})
const selectedQuestionCount = computed(() => selectedCandidates.value.length)
const displayUsedQuestions = computed(() => usedQuestions.value + selectedQuestionCount.value)
const displayRemainingQuestions = computed(() => {
  if (!maxQuestions.value) {
    return 0
  }
  return Math.max(maxQuestions.value - displayUsedQuestions.value, 0)
})
const canSelectMoreCandidates = computed(() => {
  if (!maxQuestions.value) {
    return true
  }
  return displayUsedQuestions.value < maxQuestions.value
})

const questionLimitReached = computed(() => {
  if (!maxQuestions.value) {
    return false
  }
  return usedQuestions.value >= maxQuestions.value
})

const existingQuestionSet = computed(() => {
  const keys = new Set<string>()
  for (const question of currentQuestions.value) {
    keys.add(normalizeQuestionKey(question.question_text))
  }
  return keys
})

const selectedCandidates = computed(() => candidateRows.value.filter((item) => item.selected))
const selectedCandidatesAllExistInLibrary = computed(() => {
  if (!selectedCandidates.value.length) {
    return false
  }
  return selectedCandidates.value.every((item) =>
    existingQuestionSet.value.has(normalizeQuestionKey(item.text)),
  )
})
const candidateStats = computed(() => {
  const total = candidateRows.value.length
  const selected = selectedCandidates.value.length
  const tooShort = candidateRows.value.filter((item) => item.too_short).length
  const duplicate = candidateRows.value.filter((item) => item.duplicate).length
  return { total, selected, tooShort, duplicate }
})

const expansionToolTotal = computed(() => {
  const core = splitLines(expansionForm.core).length
  const industry = splitLines(expansionForm.industry).length
  if (!core || !industry) {
    return 0
  }
  return (
    combinationPartCount(expansionForm.region) *
    combinationPartCount(expansionForm.prefix) *
    core *
    industry *
    combinationPartCount(expansionForm.suffix)
  )
})

const expansionToolPreviewCount = computed(() => Math.min(expansionToolTotal.value, 500))

const createModeSource = computed<QuestionSource>(() => {
  if (createMode.value === 'ai') {
    return 'ai_distill'
  }
  if (createMode.value === 'batch') {
    return 'manual'
  }
  return 'combination'
})

const aiCandidateStages = computed(() => [
  aiWaitingKind.value === 'fill'
    ? t('brands.questions.aiFill.waitingStages.region')
    : t('brands.questions.ai.waitingStages.context'),
  aiWaitingKind.value === 'fill'
    ? t('brands.questions.aiFill.waitingStages.generate')
    : t('brands.questions.ai.waitingStages.generate'),
  aiWaitingKind.value === 'fill'
    ? t('brands.questions.aiFill.waitingStages.refine')
    : t('brands.questions.ai.waitingStages.refine'),
])

const aiWaitingTitle = computed(() =>
  aiWaitingKind.value === 'fill'
    ? t('brands.questions.aiFill.waitingTitle')
    : t('brands.questions.ai.waitingTitle'),
)

const isPageLoading = computed(
  () =>
    brandQuery.isPending.value ||
    brandLibrarySummaryQuery.isPending.value ||
    questionsQuery.isPending.value,
)

const isGenerating = computed(
  () =>
    previewMutations.combination.isPending.value ||
    previewMutations.ai.isPending.value ||
    combinationFillMutation.isPending.value,
)

const previewMutations = {
  combination: useMutation({
    mutationFn: () =>
      brandsApi.previewQuestionCombination(brandId.value, {
        region: splitLines(expansionForm.region),
        prefix: splitLines(expansionForm.prefix),
        core: splitLines(expansionForm.core),
        industry: splitLines(expansionForm.industry),
        suffix: splitLines(expansionForm.suffix),
        max_items: 500,
      }),
    onSuccess: (result) => {
      candidateRows.value = toCandidateRows(result.candidates)
      pageNotice.value = result.truncated ? t('brands.questions.messages.truncated') : ''
      currentStep.value = 1
    },
    onError: (error) => {
      pageNotice.value = formatError(error)
    },
  }),
  ai: useMutation({
    mutationFn: async () => {
      const topics = normalizedAiSeedTopics.value
      const results = await Promise.all(
        topics.map((topic) =>
          brandsApi.distillQuestions(brandId.value, {
            seed_topic: topic,
          }),
        ),
      )
      return mergeQuestionCandidateResults(results)
    },
    onSuccess: (result) => {
      candidateRows.value = toCandidateRows(result.candidates)
      if (result.cache_hit) {
        pageNotice.value = t('brands.questions.messages.cacheHit')
      } else if (result.ai_points_charged) {
        pageNotice.value = t('brands.questions.messages.aiCharged', {
          count: result.ai_points_charged,
        })
      } else {
        pageNotice.value = ''
      }
      currentStep.value = 1
    },
    onError: (error) => {
      handleAiPreviewError(error)
    },
  }),
}

const combinationFillMutation = useMutation({
  mutationFn: () =>
    brandsApi.fillQuestionCombination(brandId.value, {
      region: splitLines(expansionForm.region),
      prefix: splitLines(expansionForm.prefix),
      core: splitLines(expansionForm.core),
      industry: splitLines(expansionForm.industry),
      suffix: splitLines(expansionForm.suffix),
    }),
  onSuccess: (result) => {
    fillExpansionColumn('region', result.region)
    fillExpansionColumn('prefix', result.prefix)
    fillExpansionColumn('core', result.core)
    fillExpansionColumn('industry', result.industry)
    fillExpansionColumn('suffix', result.suffix)
    pageNotice.value = ''
    message.success(
      result.ai_points_charged
        ? t('brands.questions.aiFill.charged', { count: result.ai_points_charged })
        : t('brands.questions.aiFill.success'),
    )
  },
  onError: (error) => {
    pageNotice.value = formatError(error)
  },
})

const materializeMutation = useMutation({
  mutationFn: () =>
    brandsApi.materializeQuestions(brandId.value, {
      source: createModeSource.value,
      questions: selectedCandidates.value.map((item) => ({ text: item.text.trim() })),
    }),
  onSuccess: async (result) => {
    if (result.created_questions > 0) {
      message.success(
        t('brands.questions.messages.partialSaved', {
          created: result.created_questions,
          skipped: result.skipped_questions.length,
        }),
      )
      await queryClient.invalidateQueries({ queryKey: ['brands'] })
      await router.push({
        name: 'brands',
        query: { brand_id: String(brandId.value) },
      })
      return
    }
    pageNotice.value =
      result.skipped_questions.length > 0 &&
      result.skipped_questions.every((item) => isDuplicateQuestionSkipReason(item.reason))
        ? t('brands.questions.errors.duplicateExisting')
        : t('brands.questions.errors.noValidQuestions')
  },
  onError: (error) => {
    const materializeMessage = getQuestionMaterializeErrorMessage(error, {
      duplicate: t('brands.questions.errors.duplicateExisting'),
      noValid: t('brands.questions.errors.noValidQuestions'),
    })
    if (materializeMessage) {
      pageNotice.value = materializeMessage
      return
    }
    pageNotice.value = formatError(error)
  },
})

function splitLines(value: string | string[]): string[] {
  if (Array.isArray(value)) {
    return value.map(normalizeSearchTermText).filter(Boolean)
  }
  return value
    .split(/\r?\n|,/)
    .map(normalizeSearchTermText)
    .filter(Boolean)
}

function normalizedTopicParts(value: string): string[] {
  return value
    .split(/\r?\n|,|，|;|；/)
    .map((item) => item.trim())
    .filter(Boolean)
}

const normalizedAiSeedTopics = computed(() => {
  const seen = new Set<string>()
  const topics: string[] = []
  for (const rawTopic of aiForm.seed_topics) {
    for (const topic of normalizedTopicParts(rawTopic)) {
      const key = topic.toLowerCase()
      if (seen.has(key)) {
        continue
      }
      seen.add(key)
      topics.push(topic)
    }
  }
  return topics
})

const tooShortAiSeedTopics = computed(() =>
  normalizedAiSeedTopics.value.filter(
    (topic) => countSearchTermChars(topic) < AI_SEED_TOPIC_MIN_LENGTH,
  ),
)

function updateAiSeedTopics(value: string[]): void {
  clearAiSeedTopicError()
  aiForm.seed_topics.splice(0, aiForm.seed_topics.length, ...dedupeTopicParts(value))
}

function dedupeTopicParts(values: string[]): string[] {
  const seen = new Set<string>()
  const topics: string[] = []
  for (const value of values) {
    for (const topic of normalizedTopicParts(value)) {
      const key = topic.toLowerCase()
      if (seen.has(key)) {
        continue
      }
      seen.add(key)
      topics.push(topic)
    }
  }
  return topics
}

function mergeQuestionCandidateResults(
  results: Awaited<ReturnType<typeof brandsApi.distillQuestions>>[],
) {
  const seen = new Set<string>()
  const candidates: QuestionCandidate[] = []
  let aiPointsCharged = 0
  let cacheHit = false
  let truncated = false

  for (const result of results) {
    aiPointsCharged += result.ai_points_charged ?? 0
    cacheHit = cacheHit || Boolean(result.cache_hit)
    truncated = truncated || Boolean(result.truncated)

    for (const candidate of result.candidates) {
      const key = normalizeQuestionKey(candidate.text)
      if (!key || seen.has(key)) {
        continue
      }
      seen.add(key)
      candidates.push(candidate)
    }
  }

  return {
    candidates,
    ai_points_charged: aiPointsCharged || undefined,
    cache_hit: cacheHit,
    truncated,
  }
}

type ColumnKey = 'region' | 'prefix' | 'core' | 'industry' | 'suffix'

function handleInput(column: ColumnKey, index: number) {
  const arr = expansionForm[column]
  if (index === arr.length - 1 && arr[index].trim() !== '') {
    arr.push('')
  }
  while (arr.length > 1 && arr[arr.length - 1].trim() === '' && arr[arr.length - 2].trim() === '') {
    arr.pop()
  }
}

function handleEnter(column: ColumnKey, index: number) {
  const arr = expansionForm[column]
  if (arr[index].trim() === '') {
    return
  }
  if (index === arr.length - 1) {
    arr.push('')
  } else {
    arr.splice(index + 1, 0, '')
  }
  setTimeout(() => {
    const inputs = document.querySelectorAll(
      `.word-column[data-col="${column}"] .word-column__input`,
    ) as NodeListOf<HTMLInputElement>
    if (inputs[index + 1]) {
      inputs[index + 1].focus()
    }
  }, 0)
}

function handleBackspace(column: ColumnKey, index: number, event: KeyboardEvent) {
  const arr = expansionForm[column]
  if (arr[index] === '' && arr.length > 1) {
    event.preventDefault()
    arr.splice(index, 1)
    setTimeout(() => {
      const inputs = document.querySelectorAll(
        `.word-column[data-col="${column}"] .word-column__input`,
      ) as NodeListOf<HTMLInputElement>
      const target = inputs[index - 1 >= 0 ? index - 1 : 0]
      if (target) {
        target.focus()
      }
    }, 0)
  }
}

function handleBlur(column: ColumnKey, index: number) {
  setTimeout(() => {
    const arr = expansionForm[column]
    if (arr[index] !== undefined && arr[index].trim() === '' && index !== arr.length - 1) {
      arr.splice(index, 1)
    }
  }, 150)
}

function removeItem(column: ColumnKey, index: number) {
  const arr = expansionForm[column]
  if (arr.length > 1) {
    arr.splice(index, 1)
  } else {
    arr[0] = ''
  }
}

function fillExpansionColumn(column: ColumnKey, values: string[]): void {
  const cleaned = splitLines(values).slice(0, 4)
  expansionForm[column].splice(0, expansionForm[column].length, ...cleaned, '')
}

function combinationPartCount(value: string | string[]): number {
  const count = splitLines(value).length
  return count > 0 ? count : 1
}

function normalizeQuestionKey(value: string): string {
  return normalizeSearchTermText(value).toLowerCase()
}

function normalizeSearchTermText(value: string): string {
  return value
    .trim()
    .replace(/[\p{P}]/gu, '')
    .replace(/\s+/g, '')
}

function countSearchTermChars(value: string): number {
  return Array.from(normalizeSearchTermText(value)).length
}

function searchTermLooksValid(value: string): boolean {
  const text = normalizeSearchTermText(value)
  if (text.length < 4) {
    return false
  }
  return /[\p{L}\p{N}]/u.test(text)
}

function toCandidateRows(candidates: QuestionCandidate[]): CandidateRow[] {
  return candidates
    .map((candidate, index) => {
      const text = normalizeSearchTermText(candidate.text)
      const tooShort = text.length < 4
      const invalid = !searchTermLooksValid(text)
      return {
        ...candidate,
        text,
        too_short: candidate.too_short || tooShort,
        suggest_skip: candidate.suggest_skip || tooShort || invalid,
        id: `${Date.now()}-${index}-${text}`,
        selected: false,
      }
    })
    .filter((candidate) => candidate.text)
}

function buildLocalCandidates(texts: string[], source: QuestionSource): CandidateRow[] {
  const seen = new Set<string>()
  const rows: CandidateRow[] = []
  for (const raw of texts) {
    const text = normalizeSearchTermText(raw)
    if (!text) {
      continue
    }
    const key = normalizeQuestionKey(text)
    const duplicateInBatch = seen.has(key)
    seen.add(key)
    const tooShort = text.length < 4
    const duplicate = existingQuestionSet.value.has(key) || duplicateInBatch
    const invalid = !searchTermLooksValid(text)
    rows.push({
      id: `${Date.now()}-${rows.length}-${text}`,
      text,
      layer: 'L2',
      intent: 'informational',
      source,
      too_short: tooShort,
      duplicate,
      suggest_skip: tooShort || duplicate || invalid,
      selected: false,
    })
  }
  return rows
}

function changeCreateMode(mode: CreateMode): void {
  if (createMode.value === mode) {
    return
  }
  clearAiSeedTopicError()
  createMode.value = mode
  currentStep.value = 0
  candidateRows.value = []
  pageNotice.value = ''
}

function setAiSeedTopicError(value: string): void {
  aiSeedTopicError.value = value
  pageNotice.value = value
}

function clearAiSeedTopicError(): void {
  if (aiSeedTopicError.value && pageNotice.value === aiSeedTopicError.value) {
    pageNotice.value = ''
  }
  aiSeedTopicError.value = ''
}

function formatQuotedTerms(terms: string[]): string {
  return terms
    .slice(0, 3)
    .map((term) => `“${term}”`)
    .join('、')
}

function buildAiSeedTopicTooShortMessage(terms: string[]): string {
  const visibleTerms = formatQuotedTerms(terms)
  const hiddenCount = Math.max(terms.length - 3, 0)
  if (hiddenCount > 0) {
    return t('brands.questions.errors.aiSeedTopicTooShortWithMore', {
      terms: visibleTerms,
      count: hiddenCount,
    })
  }
  return t('brands.questions.errors.aiSeedTopicTooShort', {
    terms: visibleTerms,
  })
}

function isAiSeedTopicValidationError(error: unknown): boolean {
  return (
    error instanceof ApiClientError &&
    (error.message === 'invalid_seed_topic' ||
      (error.message === 'invalid_params' && /SeedTopic|seed_topic/.test(error.detail ?? '')))
  )
}

function handleAiPreviewError(error: unknown): void {
  const notice = formatError(error)
  if (isAiSeedTopicValidationError(error)) {
    setAiSeedTopicError(notice)
    return
  }
  pageNotice.value = notice
}

function clearAiCandidateProgressTimer(): void {
  if (aiCandidateProgressTimer === null) {
    return
  }
  window.clearInterval(aiCandidateProgressTimer)
  aiCandidateProgressTimer = null
}

function clearAiCandidateCloseTimer(): void {
  if (aiCandidateCloseTimer === null) {
    return
  }
  window.clearTimeout(aiCandidateCloseTimer)
  aiCandidateCloseTimer = null
}

function beginAiCandidateTask(kind: 'candidate' | 'fill' = 'candidate'): void {
  clearAiCandidateProgressTimer()
  clearAiCandidateCloseTimer()
  aiWaitingKind.value = kind
  aiCandidateModalOpen.value = true
  aiCandidateProgress.value = 12
  aiCandidateStageIndex.value = 0

  aiCandidateProgressTimer = window.setInterval(() => {
    const nextProgress = Math.min(92, aiCandidateProgress.value + 8)
    aiCandidateProgress.value = nextProgress
    aiCandidateStageIndex.value = nextProgress >= 66 ? 2 : nextProgress >= 38 ? 1 : 0
  }, 700)
}

async function fillCombinationWords(): Promise<void> {
  pageNotice.value = ''
  beginAiCandidateTask('fill')
  try {
    await combinationFillMutation.mutateAsync()
  } finally {
    finishAiCandidateTask()
  }
}

function finishAiCandidateTask(): void {
  clearAiCandidateProgressTimer()
  aiCandidateProgress.value = 100
  aiCandidateStageIndex.value = aiCandidateStages.value.length - 1
  aiCandidateCloseTimer = window.setTimeout(() => {
    aiCandidateModalOpen.value = false
    aiCandidateCloseTimer = null
  }, 180)
}

onBeforeUnmount(() => {
  clearAiCandidateProgressTimer()
  clearAiCandidateCloseTimer()
})

async function generateCandidates(): Promise<void> {
  pageNotice.value = ''
  clearAiSeedTopicError()
  if (questionLimitReached.value) {
    pageNotice.value = t('brands.questions.messages.limitReached', {
      limit: maxQuestions.value,
    })
    return
  }

  if (createMode.value === 'batch') {
    candidateRows.value = buildLocalCandidates(splitLines(batchForm.text), 'manual')
    pageNotice.value = candidateRows.value.length ? '' : t('brands.questions.errors.emptyInput')
    if (candidateRows.value.length) {
      currentStep.value = 1
    }
    return
  }

  if (createMode.value === 'combination') {
    if (!splitLines(expansionForm.core).length) {
      pageNotice.value = t('brands.questions.combination.coreRequired')
      return
    }
    if (!splitLines(expansionForm.industry).length) {
      pageNotice.value = t('brands.questions.combination.industryRequired')
      return
    }
    await previewMutations.combination.mutateAsync()
    return
  }

  if (!normalizedAiSeedTopics.value.length) {
    pageNotice.value = t('brands.questions.errors.emptyInput')
    return
  }
  if (tooShortAiSeedTopics.value.length) {
    setAiSeedTopicError(buildAiSeedTopicTooShortMessage(tooShortAiSeedTopics.value))
    return
  }
  beginAiCandidateTask('candidate')
  try {
    await previewMutations.ai.mutateAsync()
  } finally {
    finishAiCandidateTask()
  }
}

function removeCandidate(id: string): void {
  candidateRows.value = candidateRows.value.filter((item) => item.id !== id)
}

function refreshCandidate(row: CandidateRow): void {
  row.text = normalizeSearchTermText(row.text)
  const key = normalizeQuestionKey(row.text)
  const duplicateInBatch = candidateRows.value.some(
    (item) => item.id !== row.id && normalizeQuestionKey(item.text) === key,
  )
  row.too_short = row.text.length < 4
  row.duplicate = Boolean(key) && (existingQuestionSet.value.has(key) || duplicateInBatch)
  row.suggest_skip = row.too_short || row.duplicate || !searchTermLooksValid(row.text)
}

function refreshAllCandidates(): void {
  candidateRows.value.forEach(refreshCandidate)
}

function startEditingCandidate(id: string): void {
  editingCandidateId.value = id
}

function stopEditingCandidate(row: CandidateRow): void {
  editingCandidateId.value = null
  refreshCandidate(row)
}

function updateCandidateSelection(row: CandidateRow, checked: boolean): void {
  refreshCandidate(row)
  if (checked && !row.selected && !canSelectMoreCandidates.value) {
    pageNotice.value = t('brands.questions.messages.selectionLimitReached', {
      limit: maxQuestions.value,
    })
    return
  }
  pageNotice.value = ''
  row.selected = checked
}

function isCandidateSelectionDisabled(row: CandidateRow): boolean {
  return !row.selected && !canSelectMoreCandidates.value
}

async function saveCandidates(): Promise<void> {
  refreshAllCandidates()
  if (!selectedCandidates.value.length) {
    pageNotice.value = t('brands.questions.errors.noSelection')
    return
  }
  if (selectedCandidatesAllExistInLibrary.value) {
    pageNotice.value = t('brands.questions.errors.duplicateExisting')
    return
  }
  await materializeMutation.mutateAsync()
}

function backToBrands(): void {
  void router.push({
    name: 'brands',
    query: brandId.value ? { brand_id: String(brandId.value) } : undefined,
  })
}
</script>

<template>
  <div class="wizard-page">
    <div v-if="isPageLoading" class="wizard-page__loading">
      <a-skeleton active :paragraph="{ rows: 12 }" />
    </div>

    <template v-else>
      <header class="wizard-page__header">
        <div class="header-left">
          <a-button type="text" shape="circle" @click="backToBrands">
            <template #icon><LeftOutlined /></template>
          </a-button>
          <div class="header-title-box">
            <h2 class="header-title">
              {{ t('brands.questions.page.title') }}
              <span class="header-accent">{{ brandQuery.data.value?.name }}</span>
            </h2>
            <p class="header-eyebrow">{{ t('brands.questions.page.subtitle') }}</p>
          </div>
        </div>
      </header>

      <main class="wizard-page__main">
        <div class="wizard-steps-container">
          <a-steps :current="currentStep" class="wizard-steps" size="small">
            <a-step :title="t('brands.questions.page.steps.configure')" />
            <a-step :title="t('brands.questions.page.steps.preview')" />
          </a-steps>
        </div>

        <a-alert
          v-if="pageNotice"
          class="page-alert"
          type="warning"
          show-icon
          :message="pageNotice"
        />

        <section v-if="currentStep === 0" class="wizard-section">
          <div class="wizard-card">
            <div class="wizard-card__header">
              <div>
                <h3>{{ t('brands.questions.page.methodTitle') }}</h3>
                <p>{{ t('brands.questions.page.methodHint') }}</p>
              </div>
            </div>

            <div class="method-grid">
              <button
                type="button"
                class="method-card"
                :class="{ 'method-card--active': createMode === 'combination' }"
                @click="changeCreateMode('combination')"
              >
                <strong>{{ t('brands.questions.modal.methods.combination') }}</strong>
                <span>{{ t('brands.questions.page.methods.combination') }}</span>
              </button>
              <button
                type="button"
                class="method-card"
                :class="{ 'method-card--active': createMode === 'ai' }"
                @click="changeCreateMode('ai')"
              >
                <strong>{{ t('brands.questions.modal.methods.aiDistill') }}</strong>
                <span>{{ t('brands.questions.page.methods.ai') }}</span>
              </button>
              <button
                type="button"
                class="method-card"
                :class="{ 'method-card--active': createMode === 'batch' }"
                @click="changeCreateMode('batch')"
              >
                <strong>{{ t('brands.questions.modal.methods.manualBatch') }}</strong>
                <span>{{ t('brands.questions.page.methods.batch') }}</span>
              </button>
            </div>
          </div>

          <div v-if="createMode === 'combination'" class="wizard-card">
            <div class="wizard-card__header">
              <div>
                <h3>{{ t('brands.questions.modal.methods.combination') }}</h3>
                <p>{{ t('brands.questions.page.expansionHint') }}</p>
              </div>
              <div class="tool-header-actions">
                <a-button
                  class="tool-ai-fill"
                  :loading="combinationFillMutation.isPending.value"
                  :disabled="previewMutations.combination.isPending.value"
                  @click="fillCombinationWords"
                >
                  <template #icon><RobotOutlined /></template>
                  {{ t('brands.questions.aiFill.button') }}
                </a-button>
                <div class="tool-estimate">
                  {{
                    t('brands.questions.combination.estimate', { count: expansionToolPreviewCount })
                  }}
                </div>
              </div>
            </div>

            <div class="expansion-tool">
              <div class="word-column" data-col="region">
                <div class="word-column__head">
                  <strong>{{ t('brands.questions.combination.region') }}</strong>
                  <span>
                    {{
                      splitLines(expansionForm.region).length ||
                      t('brands.questions.combination.defaultWord')
                    }}
                  </span>
                </div>
                <div class="word-column__list">
                  <div
                    v-for="(item, index) in expansionForm.region"
                    :key="index"
                    class="word-column__item"
                  >
                    <span class="word-column__dot"></span>
                    <input
                      v-model="expansionForm.region[index]"
                      class="word-column__input"
                      placeholder="输入词语"
                      @input="handleInput('region', index)"
                      @keydown.enter.prevent="handleEnter('region', index)"
                      @keydown.backspace="handleBackspace('region', index, $event)"
                      @blur="handleBlur('region', index)"
                    />
                    <button
                      v-if="index < expansionForm.region.length - 1"
                      class="word-column__remove"
                      tabindex="-1"
                      @click="removeItem('region', index)"
                    >
                      ×
                    </button>
                  </div>
                </div>
              </div>
              <div class="word-column" data-col="prefix">
                <div class="word-column__head">
                  <strong>{{ t('brands.questions.combination.prefix') }}</strong>
                  <span>
                    {{
                      splitLines(expansionForm.prefix).length ||
                      t('brands.questions.combination.defaultWord')
                    }}
                  </span>
                </div>
                <div class="word-column__list">
                  <div
                    v-for="(item, index) in expansionForm.prefix"
                    :key="index"
                    class="word-column__item"
                  >
                    <span class="word-column__dot"></span>
                    <input
                      v-model="expansionForm.prefix[index]"
                      class="word-column__input"
                      placeholder="输入词语"
                      @input="handleInput('prefix', index)"
                      @keydown.enter.prevent="handleEnter('prefix', index)"
                      @keydown.backspace="handleBackspace('prefix', index, $event)"
                      @blur="handleBlur('prefix', index)"
                    />
                    <button
                      v-if="index < expansionForm.prefix.length - 1"
                      class="word-column__remove"
                      tabindex="-1"
                      @click="removeItem('prefix', index)"
                    >
                      ×
                    </button>
                  </div>
                </div>
              </div>
              <div class="word-column word-column--required" data-col="core">
                <div class="word-column__head">
                  <strong>{{ t('brands.questions.combination.core') }}</strong>
                  <span>{{ splitLines(expansionForm.core).length }}</span>
                </div>
                <div class="word-column__list">
                  <div
                    v-for="(item, index) in expansionForm.core"
                    :key="index"
                    class="word-column__item"
                  >
                    <span class="word-column__dot"></span>
                    <input
                      v-model="expansionForm.core[index]"
                      class="word-column__input"
                      placeholder="输入词语"
                      @input="handleInput('core', index)"
                      @keydown.enter.prevent="handleEnter('core', index)"
                      @keydown.backspace="handleBackspace('core', index, $event)"
                      @blur="handleBlur('core', index)"
                    />
                    <button
                      v-if="index < expansionForm.core.length - 1"
                      class="word-column__remove"
                      tabindex="-1"
                      @click="removeItem('core', index)"
                    >
                      ×
                    </button>
                  </div>
                </div>
              </div>
              <div class="word-column word-column--required" data-col="industry">
                <div class="word-column__head">
                  <strong>{{ t('brands.questions.combination.industry') }}</strong>
                  <span>{{ splitLines(expansionForm.industry).length }}</span>
                </div>
                <div class="word-column__list">
                  <div
                    v-for="(item, index) in expansionForm.industry"
                    :key="index"
                    class="word-column__item"
                  >
                    <span class="word-column__dot"></span>
                    <input
                      v-model="expansionForm.industry[index]"
                      class="word-column__input"
                      placeholder="输入词语"
                      @input="handleInput('industry', index)"
                      @keydown.enter.prevent="handleEnter('industry', index)"
                      @keydown.backspace="handleBackspace('industry', index, $event)"
                      @blur="handleBlur('industry', index)"
                    />
                    <button
                      v-if="index < expansionForm.industry.length - 1"
                      class="word-column__remove"
                      tabindex="-1"
                      @click="removeItem('industry', index)"
                    >
                      ×
                    </button>
                  </div>
                </div>
              </div>
              <div class="word-column" data-col="suffix">
                <div class="word-column__head">
                  <strong>{{ t('brands.questions.combination.suffix') }}</strong>
                  <span>
                    {{
                      splitLines(expansionForm.suffix).length ||
                      t('brands.questions.combination.defaultWord')
                    }}
                  </span>
                </div>
                <div class="word-column__list">
                  <div
                    v-for="(item, index) in expansionForm.suffix"
                    :key="index"
                    class="word-column__item"
                  >
                    <span class="word-column__dot"></span>
                    <input
                      v-model="expansionForm.suffix[index]"
                      class="word-column__input"
                      placeholder="输入词语"
                      @input="handleInput('suffix', index)"
                      @keydown.enter.prevent="handleEnter('suffix', index)"
                      @keydown.backspace="handleBackspace('suffix', index, $event)"
                      @blur="handleBlur('suffix', index)"
                    />
                    <button
                      v-if="index < expansionForm.suffix.length - 1"
                      class="word-column__remove"
                      tabindex="-1"
                      @click="removeItem('suffix', index)"
                    >
                      ×
                    </button>
                  </div>
                </div>
              </div>
            </div>
          </div>

          <div v-else-if="createMode === 'ai'" class="wizard-card">
            <div class="field-item field-item--compact">
              <label>{{ t('brands.questions.ai.seedTopic') }}</label>
              <a-select
                class="seed-topic-select"
                mode="tags"
                :value="aiForm.seed_topics"
                :status="aiSeedTopicError ? 'error' : undefined"
                :placeholder="t('brands.questions.ai.seedTopicPlaceholder')"
                :token-separators="aiTopicTokenSeparators"
                :options="[]"
                @change="(value: string[]) => updateAiSeedTopics(value)"
              />
              <p v-if="aiSeedTopicError" class="field-item__error">
                {{ aiSeedTopicError }}
              </p>
              <p class="field-item__hint">
                {{ t('brands.questions.ai.seedTopicHint') }}
              </p>
            </div>
          </div>

          <div v-else class="wizard-card">
            <div class="wizard-card__header">
              <div>
                <h3>{{ t('brands.questions.modal.methods.manualBatch') }}</h3>
                <p>{{ t('brands.questions.page.batchHint') }}</p>
              </div>
            </div>
            <div class="field-item">
              <label>{{ t('brands.questions.batch.label') }}</label>
              <a-textarea v-model:value="batchForm.text" :rows="12" />
            </div>
          </div>
        </section>

        <section v-else class="wizard-section">
          <div class="wizard-card">
            <div class="wizard-card__header">
              <div>
                <h3>{{ t('brands.questions.modal.preview') }}</h3>
                <p>
                  {{
                    t('brands.questions.modal.summary', {
                      total: candidateStats.total,
                      selected: candidateStats.selected,
                      tooShort: candidateStats.tooShort,
                      duplicate: candidateStats.duplicate,
                    })
                  }}
                </p>
              </div>
            </div>

            <div class="candidate-list">
              <div v-if="!candidateRows.length" class="candidate-empty">
                {{ t('brands.questions.emptyCandidates') }}
              </div>
              <div v-for="row in candidateRows" :key="row.id" class="candidate-row">
                <a-checkbox
                  :checked="row.selected"
                  :disabled="isCandidateSelectionDisabled(row)"
                  @change="
                    (event: Event) =>
                      updateCandidateSelection(row, (event.target as HTMLInputElement).checked)
                  "
                />
                <a-textarea
                  v-if="editingCandidateId === row.id"
                  v-focus
                  v-model:value="row.text"
                  class="candidate-row__editor"
                  auto-size
                  @blur="stopEditingCandidate(row)"
                  @keydown.enter="stopEditingCandidate(row)"
                />
                <span v-else class="candidate-row__text" @dblclick="startEditingCandidate(row.id)">
                  {{ row.text }}
                </span>
                <a-tag v-if="editingCandidateId !== row.id && row.duplicate" color="orange">
                  {{ t('brands.questions.badges.duplicate') }}
                </a-tag>
                <a-tag v-else-if="editingCandidateId !== row.id && row.too_short" color="red">
                  {{ t('brands.questions.badges.tooShort') }}
                </a-tag>
                <div v-if="editingCandidateId !== row.id" class="candidate-row__actions">
                  <a-button
                    type="text"
                    shape="circle"
                    class="action-btn-edit"
                    @click="startEditingCandidate(row.id)"
                  >
                    <EditOutlined />
                  </a-button>
                  <a-button type="text" shape="circle" danger @click="removeCandidate(row.id)">
                    <DeleteOutlined />
                  </a-button>
                </div>
              </div>
            </div>
          </div>
        </section>
      </main>

      <footer class="wizard-page__footer">
        <div class="footer-meta">
          {{
            t('brands.questions.usage', {
              used: displayUsedQuestions,
              total: maxQuestions || '--',
            })
          }}
          <span>{{ t('brands.questions.remaining', { count: displayRemainingQuestions }) }}</span>
        </div>
        <div class="footer-actions">
          <a-button @click="backToBrands">{{ t('common.cancel') }}</a-button>
          <a-button v-if="currentStep > 0" @click="currentStep = 0">
            {{ t('common.previous') }}
          </a-button>
          <a-button
            v-if="currentStep === 0"
            type="primary"
            :loading="isGenerating"
            @click="generateCandidates"
          >
            <template #icon><ThunderboltOutlined /></template>
            {{ t('brands.questions.generate') }}
          </a-button>
          <a-button
            v-else
            type="primary"
            :disabled="!selectedCandidates.length"
            :loading="materializeMutation.isPending.value"
            @click="saveCandidates"
          >
            <template #icon><SaveOutlined /></template>
            {{ t('brands.questions.modal.save', { count: selectedCandidates.length }) }}
          </a-button>
        </div>
      </footer>
    </template>

    <AiWaitingModal
      :open="aiCandidateModalOpen"
      :title="aiWaitingTitle"
      :progress="aiCandidateProgress"
      :stage="aiCandidateStages[aiCandidateStageIndex]"
    />
  </div>
</template>

<style scoped>
.wizard-page {
  display: flex;
  flex-direction: column;
  min-height: calc(100vh - 120px);
  background: #ffffff;
}

.wizard-page__loading {
  padding: 40px;
}

.wizard-page__header {
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 32px;
  background: #ffffff;
  border-bottom: 1px solid #edf2f7;
}

.header-left {
  display: flex;
  align-items: center;
  gap: 16px;
}

.header-title-box {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.header-title {
  margin: 0;
  color: #111827;
  font-size: 20px;
  font-weight: 800;
}

.header-accent {
  margin-left: 8px;
  color: #1677ff;
  font-size: 14px;
  font-weight: 600;
}

.header-eyebrow {
  margin: 0;
  color: #6d7c94;
  font-size: 13px;
}

.wizard-page__main {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 1160px;
  margin: 0 auto;
  padding: 24px 32px 64px;
}

.wizard-steps-container {
  padding: 0 0 24px;
  border-bottom: 1px solid #edf2f7;
}

.page-alert {
  margin-top: 20px;
}

.wizard-section {
  display: flex;
  flex-direction: column;
}

.wizard-card {
  padding: 32px 0;
  border-bottom: 1px solid #edf2f7;
}

.wizard-section > .wizard-card:last-child {
  border-bottom: none;
}

.wizard-card__header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 24px;
}

.wizard-card__header h3 {
  display: flex;
  align-items: center;
  margin: 0;
  color: #111827;
  font-size: 16px;
  font-weight: 800;
}

.wizard-card__header h3::before {
  display: inline-block;
  width: 4px;
  height: 16px;
  margin-right: 12px;
  background: #111827;
  border-radius: 2px;
  content: '';
}

.wizard-card__header p {
  margin: 6px 0 0 16px;
  color: #667085;
  font-size: 13px;
  line-height: 1.7;
}

.method-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 12px;
}

.method-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-height: 112px;
  padding: 18px;
  text-align: left;
  background: #ffffff;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  cursor: pointer;
  transition:
    border-color 0.18s ease,
    box-shadow 0.18s ease,
    transform 0.18s ease;
}

.method-card:hover {
  border-color: #94a3b8;
  transform: translateY(-1px);
  box-shadow: 0 16px 30px rgba(15, 23, 42, 0.06);
}

.method-card--active {
  border-color: #2563eb;
  background: #f8fbff;
  box-shadow: inset 0 0 0 1px rgba(37, 99, 235, 0.12);
}

.method-card strong {
  color: #111827;
  font-size: 15px;
  font-weight: 800;
}

.method-card span {
  color: #64748b;
  font-size: 13px;
  line-height: 1.6;
}

.field-item {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.field-item--compact {
  margin-top: 20px;
}

.field-item > label {
  display: inline-flex;
  align-items: center;
  margin-bottom: 12px;
  color: #1f2937;
  font-size: 15px;
  font-weight: 700;
}

.field-item > label::after {
  content: '：';
}

.field-item__hint {
  margin: 0;
  color: #64748b;
  font-size: 13px;
  line-height: 1.6;
}

.field-item__error {
  margin: 0;
  color: #b42318;
  font-size: 13px;
  line-height: 1.6;
}

.seed-topic-select {
  width: 100%;
}

.seed-topic-select :deep(.ant-select-selector) {
  min-height: 52px;
  padding: 8px 12px;
  border-color: #d9d9d9 !important;
  border-radius: 8px;
}

.seed-topic-select :deep(.ant-select-selection-overflow) {
  gap: 6px;
}

.seed-topic-select :deep(.ant-select-selection-item) {
  display: inline-flex;
  align-items: center;
  max-width: 100%;
  min-height: 28px;
  color: #1e293b;
  background: #f1f5f9;
  border-color: #dbe3ee;
  border-radius: 6px;
}

.tool-header-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 10px;
}

.tool-ai-fill {
  background: #1677ff !important;
  border: none !important;
  color: #ffffff !important;
  font-weight: 600;
  border-radius: 8px;
  height: 40px;
  box-shadow: 0 2px 8px rgba(22, 119, 255, 0.15);
  transition: all 0.2s ease;
  padding: 0 16px;
}

.tool-ai-fill:hover:not([disabled]) {
  background: #0958d9 !important;
  box-shadow: 0 4px 12px rgba(22, 119, 255, 0.2);
  transform: translateY(-1px);
}

.tool-ai-fill[disabled],
.tool-ai-fill.ant-btn-loading {
  background: #f1f5f9 !important;
  border: 1px solid #e2e8f0 !important;
  color: #94a3b8 !important;
  box-shadow: none !important;
  transform: none !important;
}

.tool-ai-fill :deep(.anticon) {
  font-size: 16px;
}

.tool-estimate {
  padding: 8px 12px;
  color: #475569;
  font-size: 13px;
  text-align: right;
  background: #f8fafc;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
}

.expansion-tool {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 16px;
}

.word-column {
  display: flex;
  flex-direction: column;
  background: #f8fafc;
  border: 1px solid #e2e8f0;
  border-radius: 8px;
  overflow: hidden;
  transition: all 0.2s ease;
}

.word-column:focus-within {
  background: #ffffff;
  border-color: #1677ff;
  box-shadow:
    0 4px 12px rgba(0, 0, 0, 0.05),
    0 0 0 2px rgba(22, 119, 255, 0.1);
}

.word-column__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 14px 16px;
  background: #f1f5f9;
  border-bottom: 1px solid #e2e8f0;
  transition: background 0.2s ease;
}

.word-column:focus-within .word-column__head {
  background: #ffffff;
}

.word-column__head strong {
  color: #334155;
  font-size: 14px;
  font-weight: 700;
}

.word-column__head span {
  color: #64748b;
  font-size: 12px;
}

.word-column--required .word-column__head {
  background: #f0f7ff;
  border-bottom-color: #dbeafe;
}

.word-column--required:focus-within .word-column__head {
  background: #ffffff;
  border-bottom-color: #e2e8f0;
}

.word-column--required .word-column__head strong {
  color: #1d4ed8;
}

.word-column--required .word-column__head span {
  color: #3b82f6;
}

.word-column__list {
  display: flex;
  flex-direction: column;
  padding: 8px 0;
  overflow-y: auto;
  min-height: 280px;
  max-height: 400px;
}

.word-column__list::-webkit-scrollbar {
  width: 4px;
}

.word-column__list::-webkit-scrollbar-thumb {
  background: #cbd5e1;
  border-radius: 4px;
}

.word-column__item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 16px;
  position: relative;
}

.word-column__item:hover .word-column__remove {
  opacity: 1;
}

.word-column__dot {
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background-color: #cbd5e1;
  flex-shrink: 0;
  transition: background-color 0.2s;
}

.word-column__item:focus-within .word-column__dot {
  background-color: #1677ff;
}

.word-column__input {
  flex: 1;
  min-width: 0;
  border: none;
  background: transparent;
  color: #1e293b;
  font-size: 14px;
  padding: 4px 0;
  outline: none;
}

.word-column__input::placeholder {
  color: #94a3b8;
}

.word-column__remove {
  opacity: 0;
  background: transparent;
  border: none;
  color: #94a3b8;
  font-size: 16px;
  cursor: pointer;
  padding: 0 4px;
  transition:
    opacity 0.2s,
    color 0.2s;
  display: flex;
  align-items: center;
  justify-content: center;
}

.word-column__remove:hover {
  color: #ef4444;
}

.candidate-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 12px;
  align-items: stretch;
  padding: 12px;
  background: #f8fafc;
  border: 1px solid #e2e8f0;
  border-radius: 8px;
}

.candidate-empty {
  grid-column: 1 / -1;
  padding: 32px;
  color: #94a3b8;
  text-align: center;
  background: #ffffff;
  border: 1px dashed #d8e0ec;
  border-radius: 8px;
}

.candidate-row {
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr) auto auto;
  gap: 12px;
  align-items: center;
  min-height: 58px;
  padding: 12px 16px;
  background: #ffffff;
  border: 1px solid #e6edf5;
  border-radius: 8px;
  transition:
    background-color 0.2s ease,
    border-color 0.2s ease,
    box-shadow 0.2s ease;
}

.candidate-row:hover {
  background-color: #fbfdff;
  border-color: #b7d6ff;
  box-shadow: 0 8px 18px rgba(15, 23, 42, 0.04);
}

.candidate-row__text {
  padding: 4px 11px;
  font-size: 14px;
  color: #1e293b;
  cursor: pointer;
  white-space: pre-wrap;
  word-break: break-all;
}

.candidate-row__editor {
  width: 100%;
  min-width: 0;
  min-height: 44px;
  line-height: 1.6;
  white-space: pre-wrap;
  resize: none;
}

.candidate-row__editor :deep(.ant-input) {
  width: 100%;
  min-height: 44px;
  line-height: 1.6;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  resize: none;
}

.candidate-row__actions {
  display: flex;
  gap: 4px;
  opacity: 0;
  transition: opacity 0.2s;
}

.candidate-row:hover .candidate-row__actions,
.candidate-row:focus-within .candidate-row__actions {
  opacity: 1;
}

.action-btn-edit:hover {
  color: #52c41a !important;
  background: #f6ffed !important;
}

.wizard-page__footer {
  position: sticky;
  bottom: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 24px;
  margin-top: auto;
  background: rgba(255, 255, 255, 0.9);
  border-top: 1px solid rgba(20, 44, 88, 0.08);
  backdrop-filter: blur(14px);
}

.footer-meta {
  color: #475569;
  font-size: 13px;
}

.footer-meta span {
  margin-left: 12px;
  color: #64748b;
}

.footer-actions {
  display: flex;
  gap: 12px;
}

@media (max-width: 1024px) {
  .method-grid,
  .expansion-tool {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .word-column {
    border-bottom: 1px solid #ece7f7;
  }
}

@media (max-width: 768px) {
  .wizard-page__header {
    padding: 14px 16px;
  }

  .wizard-page__main {
    padding: 18px 16px 48px;
  }

  .method-grid,
  .expansion-tool {
    grid-template-columns: 1fr;
  }

  .wizard-card__header {
    flex-direction: column;
  }

  .candidate-row {
    grid-template-columns: 24px minmax(0, 1fr) auto auto;
  }

  .wizard-page__footer {
    flex-direction: column;
    gap: 12px;
    align-items: stretch;
  }

  .footer-actions {
    justify-content: flex-end;
  }
}
</style>
