<script setup lang="ts">
import { computed, onUnmounted, ref } from 'vue'
import { throttle } from 'lodash'
import { AnchorLocations, BezierConnector } from '@jsplumb/browser-ui'

import SvgIcon from '@/components/SvgIcon/index.vue'
import { getActionTypeDisplay, getAgentMapIcon } from '@/layout/components/config.ts'
import { type ConnectArg, Jsplumb } from '@/layout/components/Main/TaskTemplate/utils.ts'
import variables from '@/styles/variables.module.scss'
import { type IRawStepTask, useAgentsStore } from '@/stores'
import api from '@/api'
import ProcessCard from '../TaskProcess/ProcessCard.vue'
import ExecutePlan from './ExecutePlan.vue'

const emit = defineEmits<{
  (e: 'refreshLine'): void
  (el: 'setCurrentTask', task: IRawStepTask): void
}>()

const agentsStore = useAgentsStore()
const drawerVisible = ref(false)
const collaborationProcess = computed(() => {
  return agentsStore.agentRawPlan.data?.['Collaboration Process'] ?? []
})

// 编辑逻辑
const editMode = ref(false) //全局编辑开关
const editMap = reactive<Record<string, boolean>>({}) //行级编辑状态
const editBuffer = reactive<Record<string, string | undefined>>({}) //临时输入

function getProcessDescription(stepId: string, processId: string) {
  const step = collaborationProcess.value.find(s => s.Id === stepId)
  if (step) {
    const process = step.TaskProcess.find(p => p.ID === processId)
    return process?.Description || ''
  }
  return ''
}

function save() {
  Object.keys(editMap).forEach(key => {
    if (editMap[key]) {
      const [stepId, processId] = key.split('-')
      const value = editBuffer[key]
      // 确保 value 是字符串类型
      if (value !== undefined && value !== null) {
        // @ts-ignore - TypeScript 无法正确推断类型，但运行时是安全的
        handleSaveEdit(stepId, processId, value)
      }
    }
  })
  editMode.value = false
}

function handleOpenEdit(stepId: string, processId: string) {
  if (!editMode.value) return
  const key = `${stepId}-${processId}`
  editMap[key] = true
  editBuffer[key] = getProcessDescription(stepId, processId)
}

function handleSaveEdit(stepId: string, processId: string, value: string) {
  const key = `${stepId}-${processId}`
  const step = collaborationProcess.value.find(s => s.Id === stepId)
  if (step) {
    const process = step.TaskProcess.find(p => p.ID === processId)
    if (process) {
      process.Description = value
    }
  }
  editMap[key] = false
  ElMessage.success('已保存（前端内存）')
}
const jsplumb = new Jsplumb('task-results-main', {
  connector: {
    type: BezierConnector.type,
    options: { curviness: 30, stub: 10 }
  }
})

// 操作折叠面板时要实时的刷新连线
let timer: ReturnType<typeof setInterval> | null = null
function handleCollapse() {
  if (timer) {
    clearInterval(timer)
  }
  timer = setInterval(() => {
    jsplumb.repaintEverything()
    emit('refreshLine')
  }, 1) as ReturnType<typeof setInterval>

  // 默认三秒后已经完全打开
  const timer1 = setTimeout(() => {
    if (timer) {
      clearInterval(timer)
      timer = null
    }
  }, 3000)

  onUnmounted(() => {
    if (timer) {
      clearInterval(timer)
    }
    if (timer1) {
      clearInterval(timer1)
    }
  })
}

// 创建内部连线
function createInternalLine(id?: string) {
  const arr: ConnectArg[] = []
  jsplumb.reset()
  collaborationProcess.value.forEach(item => {
    // 创建左侧流程与产出的连线
    arr.push({
      sourceId: `task-results-${item.Id}-0`,
      targetId: `task-results-${item.Id}-1`,
      anchor: [AnchorLocations.Left, AnchorLocations.Left]
    })
    collaborationProcess.value.forEach(jitem => {
      // 创建左侧产出与上一步流程的连线
      if (item.InputObject_List!.includes(jitem.OutputObject ?? '')) {
        arr.push({
          sourceId: `task-results-${jitem.Id}-1`,
          targetId: `task-results-${item.Id}-0`,
          anchor: [AnchorLocations.Left, AnchorLocations.Left],
          config: {
            type: 'output'
          }
        })
      }
      // 创建右侧任务程序与InputObject字段的连线
      jitem.TaskProcess.forEach(i => {
        if (i.ImportantInput?.includes(`InputObject:${item.OutputObject}`)) {
          const color = getActionTypeDisplay(i.ActionType)?.color ?? ''
          const sourceId = `task-results-${item.Id}-1`
          const targetId = `task-results-${jitem.Id}-0-${i.ID}`
          arr.push({
            sourceId,
            targetId,
            anchor: [AnchorLocations.Right, AnchorLocations.Right],
            config: {
              stops: [
                [0, color],
                [1, color]
              ],
              transparent: targetId !== id
            }
          })
        }
      })
    })

    // 创建右侧TaskProcess内部连线
    item.TaskProcess?.forEach(i => {
      if (!i.ImportantInput?.length) {
        return
      }
      item.TaskProcess?.forEach(i2 => {
        if (i.ImportantInput.includes(`ActionResult:${i2.ID}`)) {
          const color = getActionTypeDisplay(i.ActionType)?.color ?? ''
          const sourceId = `task-results-${item.Id}-0-${i2.ID}`
          const targetId = `task-results-${item.Id}-0-${i.ID}`
          arr.push({
            sourceId,
            targetId,
            anchor: [AnchorLocations.Right, AnchorLocations.Right],
            config: {
              stops: [
                [0, color],
                [1, color]
              ],
              transparent: targetId !== id
            }
          })
        }
      })
    })
  })

  jsplumb.connects(arr)
  jsplumb.repaintEverything()
}

const loading = ref(false)

// 额外产物编辑状态
const editingOutputId = ref<string | null>(null)
const editingOutputContent = ref('')

// 额外产物内容存储
const additionalOutputContents = ref<Record<string, string>>({})

async function handleRun() {
  try {
    loading.value = true
    const d = await api.executePlan(agentsStore.agentRawPlan.data!)
    agentsStore.setExecutePlan(d)
  } finally {
    loading.value = false
  }
}

// 查看任务过程
async function handleTaskProcess() {
  drawerVisible.value = true
}

// 开始编辑额外产物内容
function startOutputEditing(output: string) {
  editingOutputId.value = output
  editingOutputContent.value = getAdditionalOutputContent(output) || ''
}

// 保存额外产物内容
function saveOutputEditing() {
  if (editingOutputId.value && editingOutputContent.value.trim()) {
    additionalOutputContents.value[editingOutputId.value] = editingOutputContent.value.trim()
  }
  editingOutputId.value = null
  editingOutputContent.value = ''
}

// 取消编辑额外产物内容
function cancelOutputEditing() {
  editingOutputId.value = null
  editingOutputContent.value = ''
}

// 获取额外产物内容
function getAdditionalOutputContent(output: string) {
  return additionalOutputContents.value[output] || ''
}

// 处理额外产物的键盘事件
function handleOutputKeydown(event: KeyboardEvent) {
  if (event.key === 'Enter') {
    event.preventDefault()
    saveOutputEditing()
  } else if (event.key === 'Escape') {
    editingOutputId.value = null
    editingOutputContent.value = ''
  }
}

// 添加滚动状态标识
const isScrolling = ref(false)
let scrollTimer: ReturnType<typeof setTimeout> | null = null

// 修改滚动处理函数
function handleScroll() {
  isScrolling.value = true
  emit('refreshLine')
  // 清除之前的定时器
  if (scrollTimer) {
    clearTimeout(scrollTimer)
  }
  jsplumb.repaintEverything()

  // 设置滚动结束检测
  scrollTimer = setTimeout(() => {
    isScrolling.value = false
  }, 300) as ReturnType<typeof setTimeout>
}

// 修改鼠标事件处理函数
const handleMouseEnter = throttle(id => {
  if (!isScrolling.value) {
    createInternalLine(id)
  }
}, 100)

const handleMouseLeave = throttle(() => {
  if (!isScrolling.value) {
    createInternalLine()
  }
}, 100)

function clear() {
  jsplumb.reset()
}

// ========== 按钮交互状态管理 ==========
const buttonHoverState = ref<'process' | 'execute' | null>(null)
let buttonHoverTimer: ReturnType<typeof setTimeout> | null = null
const handleProcessMouseEnter = () => {
  if (buttonHoverTimer) {
    clearTimeout(buttonHoverTimer)
    buttonHoverTimer = null
  }
  buttonHoverState.value = 'process'
}

const handleExecuteMouseEnter = () => {
  if (buttonHoverTimer) {
    clearTimeout(buttonHoverTimer)
    buttonHoverTimer = null
  }
  if (agentsStore.agentRawPlan.data) {
    buttonHoverState.value = 'execute'
  }
}

const handleButtonMouseLeave = () => {
  // 添加防抖，防止快速切换时的抖动
  if (buttonHoverTimer) {
    clearTimeout(buttonHoverTimer)
  }
  buttonHoverTimer = setTimeout(() => {
    buttonHoverState.value = null
  }, 50) // 适当减少延迟时间
}

// 添加离开组件时的清理
onUnmounted(() => {
  if (buttonHoverTimer) {
    clearTimeout(buttonHoverTimer)
  }
})
// 计算按钮类名
const processBtnClass = computed(() => {
  return buttonHoverState.value === 'process' ? 'ellipse' : 'circle'
})

const executeBtnClass = computed(() => {
  // 鼠标悬停在过程按钮上时，执行按钮变圆形
  if (buttonHoverState.value === 'process') {
    return 'circle'
  }
  // 其他情况：如果有任务数据就显示椭圆形，否则显示圆形
  return agentsStore.agentRawPlan.data ? 'ellipse' : 'circle'
})

// 计算按钮是否显示文字
const showProcessText = computed(() => {
  return buttonHoverState.value === 'process'
})

const showExecuteText = computed(() => {
  // 鼠标悬停在过程按钮上时，执行按钮不显示文字
  if (buttonHoverState.value === 'process') return false
  // 其他情况：如果有任务数据就显示文字，否则不显示
  return agentsStore.agentRawPlan.data
})

// 计算按钮标题
const processBtnTitle = computed(() => {
  return buttonHoverState.value === 'process' ? '任务过程' : '点击查看任务过程'
})

const executeBtnTitle = computed(() => {
  return showExecuteText.value ? '任务执行' : '点击运行'
})

defineExpose({
  createInternalLine,
  clear
})
</script>

<template>
  <div
    class="h-full flex flex-col relative"
    id="task-results"
    :class="{ 'is-running': agentsStore.executePlan.length > 0 }"
  >
    <!--  标题与执行按钮 -->
    <div class="text-[18px] font-bold mb-[18px] flex justify-between items-center px-[20px]">
      <span class="text-[var(--color-text-title-header)]">执行结果</span>
      <div
        class="flex items-center gap-[14px] task-button-group"
        @mouseleave="handleButtonMouseLeave"
      >
        <!-- 任务过程按钮 -->
        <el-button
          :class="processBtnClass"
          :color="variables.tertiary"
          :title="processBtnTitle"
          @mouseenter="handleProcessMouseEnter"
          @click="handleTaskProcess"
        >
          <svg-icon icon-class="process" />
          <span v-if="showProcessText" class="btn-text">任务过程</span>
        </el-button>

        <!-- 任务执行按钮 -->
        <el-popover
          :disabled="Boolean(agentsStore.agentRawPlan.data)"
          title="请先输入要执行的任务"
          :visible="showPopover"
          @hide="showPopover = false"
        >
          <template #reference>
            <el-button
              :class="executeBtnClass"
              :color="variables.tertiary"
              :title="executeBtnTitle"
              :disabled="!agentsStore.agentRawPlan.data"
              @mouseenter="handleExecuteMouseEnter"
              @click="handleRun"
            >
              <svg-icon icon-class="action" />
              <span v-if="showExecuteText" class="btn-text">任务执行</span>
            </el-button>
          </template>
        </el-popover>

        <el-drawer
          v-model="drawerVisible"
          title="任务过程"
          direction="rtl"
          size="30%"
          :destroy-on-close="false"
        >
          <!-- 头部工具栏 -->
          <template #header>
            <div class="drawer-header">
              <span class="title">任务过程</span>
              <!-- <el-button v-if="!editMode" text icon="Edit" @click="editMode = true" />
              <el-button v-else text icon="Check" @click="save" /> -->
            </div>
          </template>
          <el-scrollbar height="calc(100vh - 120px)">
            <el-empty v-if="!collaborationProcess.length" description="暂无任务过程" />
            <div v-else class="process-list">
              <!-- 使用ProcessCard组件显示每个AgentSelection -->
              <ProcessCard
                v-for="step in collaborationProcess"
                :key="step.Id"
                :step="step"
                @open-edit="handleOpenEdit"
                @save-edit="handleSaveEdit"
              />
            </div>
          </el-scrollbar>
        </el-drawer>
      </div>
    </div>
    <!-- 内容 -->
    <div
      v-loading="agentsStore.agentRawPlan.loading"
      class="flex-1 overflow-auto relative"
      @scroll="handleScroll"
    >
      <div id="task-results-main" class="px-[40px] relative">
        <!-- 原有的流程和产物 -->
        <div v-for="item in collaborationProcess" :key="item.Id" class="card-item">
          <el-card
            class="card-item w-full relative"
            :class="agentsStore.currentTask?.StepName === item.StepName ? 'active-card' : ''"
            :shadow="true"
            :id="`task-results-${item.Id}-0`"
            @click="emit('setCurrentTask', item)"
          >
            <div class="text-[18px] mb-[15px]">{{ item.StepName }}</div>
            <!--  折叠面板 -->
            <el-collapse @change="handleCollapse">
              <el-collapse-item
                v-for="item1 in item.TaskProcess"
                :key="`task-results-${item.Id}-${item1.ID}`"
                :name="`task-results-${item.Id}-${item1.ID}`"
                :disabled="Boolean(!agentsStore.executePlan.length || loading)"
                @mouseenter="() => handleMouseEnter(`task-results-${item.Id}-0-${item1.ID}`)"
                @mouseleave="handleMouseLeave"
              >
                <template v-if="loading" #icon>
                  <SvgIcon icon-class="loading" size="20px" class="animate-spin" />
                </template>
                <template v-else-if="!agentsStore.executePlan.length" #icon>
                  <span></span>
                </template>
                <template #title>
                  <!-- 运行之前背景颜色是var(--color-bg-detail-list),运行之后背景颜色是var(--color-bg-detail-list-run) -->
                  <div
                    class="flex items-center gap-[15px] rounded-[20px]"
                    :class="{
                      'bg-[var(--color-bg-detail-list)]': !agentsStore.executePlan.length,
                      'bg-[var(--color-bg-detail-list-run)]': agentsStore.executePlan.length
                    }"
                  >
                    <!--  右侧链接点 -->
                    <div
                      class="absolute right-0 top-1/2 transform -translate-y-1/2"
                      :id="`task-results-${item.Id}-0-${item1.ID}`"
                    ></div>
                    <div
                      class="w-[41px] h-[41px] rounded-full flex items-center justify-center"
                      :style="{ background: getAgentMapIcon(item1.AgentName).color }"
                    >
                      <svg-icon
                        :icon-class="getAgentMapIcon(item1.AgentName).icon"
                        color="#fff"
                        size="24px"
                      />
                    </div>
                    <div class="text-[16px]">
                      <span
                        :class="{
                          'text-[var(--color-text-result-detail)]': !agentsStore.executePlan.length,
                          'text-[var(--color-text-result-detail-run)]':
                            agentsStore.executePlan.length
                        }"
                        >{{ item1.AgentName }}:&nbsp; &nbsp;</span
                      >
                      <span :style="{ color: getActionTypeDisplay(item1.ActionType)?.color }">
                        {{ getActionTypeDisplay(item1.ActionType)?.name }}
                      </span>
                    </div>
                  </div>
                </template>
                <ExecutePlan
                  :action-id="item1.ID"
                  :node-id="item.StepName"
                  :execute-plans="agentsStore.executePlan"
                />
              </el-collapse-item>
            </el-collapse>
          </el-card>

          <el-card
            class="card-item w-full relative output-object-card"
            :shadow="true"
            :class="agentsStore.currentTask?.StepName === item.StepName ? 'active-card' : ''"
            :id="`task-results-${item.Id}-1`"
            @click="emit('setCurrentTask', item)"
          >
            <!--            <div class="text-[18px]">{{ item.OutputObject }}</div>-->
            <el-collapse @change="handleCollapse">
              <el-collapse-item
                class="output-object"
                :disabled="Boolean(!agentsStore.executePlan.length || loading)"
              >
                <template v-if="loading" #icon>
                  <SvgIcon icon-class="loading" size="20px" class="animate-spin" />
                </template>
                <template v-else-if="!agentsStore.executePlan.length" #icon>
                  <span></span>
                </template>
                <template #title>
                  <div
                    class="text-[18px]"
                    :class="{
                      'text-[var(--color-text-result-detail)]': !agentsStore.executePlan.length,
                      'text-[var(--color-text-result-detail-run)]': agentsStore.executePlan.length
                    }"
                  >
                    {{ item.OutputObject }}
                  </div>
                </template>
                <ExecutePlan
                  :node-id="item.OutputObject"
                  :execute-plans="agentsStore.executePlan"
                />
              </el-collapse-item>
            </el-collapse>
          </el-card>
        </div>

        <!-- 额外产物的编辑卡片 -->
        <div
          v-for="(output, index) in agentsStore.additionalOutputs"
          :key="`additional-output-${index}`"
          class="card-item"
        >
          <!-- 空的流程卡片位置 -->
          <div class="w-full"></div>

          <!-- 额外产物的编辑卡片 -->
          <el-card
            class="card-item w-full relative output-object-card additional-output-card"
            :shadow="false"
            :id="`additional-output-results-${index}`"
          >
            <!-- 产物名称行 -->
            <div class="text-[18px] mb-3">
              {{ output }}
            </div>

            <!-- 编辑区域行 -->
            <div class="additional-output-editor">
              <div v-if="editingOutputId === output" class="w-full">
                <!-- 编辑状态：输入框 + 按钮 -->
                <div class="flex flex-col gap-3">
                  <el-input
                    v-model="editingOutputContent"
                    type="textarea"
                    :autosize="{ minRows: 3, maxRows: 6 }"
                    placeholder="请输入产物内容"
                    @keydown="handleOutputKeydown"
                    class="output-editor"
                    size="small"
                  />
                  <div class="flex justify-end gap-2">
                    <el-button @click="saveOutputEditing" type="primary" size="small" class="px-3">
                      √
                    </el-button>
                    <el-button @click="cancelOutputEditing" size="small" class="px-3">
                      ×
                    </el-button>
                  </div>
                </div>
              </div>
              <div v-else class="w-full">
                <!-- 非编辑状态：折叠区域 + 编辑按钮 -->
                <div
                  class="flex items-center justify-between p-3 bg-[var(--color-bg-quinary)] rounded-[8px]"
                >
                  <div
                    class="text-[14px] text-[var(--color-text-secondary)] output-content-display"
                  >
                    {{ getAdditionalOutputContent(output) || '暂无内容，点击编辑' }}
                  </div>
                  <el-button
                    @click="startOutputEditing(output)"
                    size="small"
                    type="primary"
                    plain
                    class="flex items-center gap-1"
                  >
                    <svg-icon icon-class="action" size="12px" />
                    <span>编辑</span>
                  </el-button>
                </div>
              </div>
            </div>
          </el-card>
        </div>
      </div>
    </div>
  </div>
</template>

<style scoped lang="scss">
#task-results.is-running {
  --color-bg-detail-list: var(--color-bg-detail-list-run); // 直接指向 100 % 版本
}
#task-results {
  :deep(.el-collapse) {
    border: none;
    border-radius: 20px;
    .el-collapse-item + .el-collapse-item {
      margin-top: 10px;
    }

    .el-collapse-item__header {
      border: none;
      background: var(--color-bg-detail-list-run);
      min-height: 41px;
      line-height: 41px;
      border-radius: 20px;
      transition: border-radius 1ms;
      position: relative;

      .el-collapse-item__title {
        background: var(--color-bg-detail-list);
        border-radius: 20px;
      }

      .el-icon {
        font-size: 20px;
        font-weight: 900;
        background: var(--color-bg-icon-rotate);
        border-radius: 50px;
        color: #d8d8d8;
      }

      &.is-active {
        border-bottom-left-radius: 0;
        border-bottom-right-radius: 0;
      }
    }

    .output-object {
      .el-collapse-item__header {
        background: none;

        .el-collapse-item__title {
          background: none;
        }
      }

      .el-collapse-item__wrap {
        background: none;

        .card-item {
          background: var(--color-bg-detail);
          padding: 25px;
          padding-top: 10px;
          border-radius: 7px;
        }
      }
    }

    .el-collapse-item__wrap {
      border: none;
      background: var(--color-bg-detail-list);
      border-bottom-left-radius: 20px;
      border-bottom-right-radius: 20px;
    }
  }

  :deep(.el-card) {
    .el-card__body {
      padding-right: 40px;
      background-color: var(--color-bg-detail);
      &:hover {
        background-color: var(--color-card-bg-result-hover);
      }
    }
  }

  .output-object-card {
    :deep(.el-card__body) {
      padding-top: 0;
      padding-bottom: 0;
      padding-right: 0;
    }
  }

  .active-card {
    background: linear-gradient(var(--color-bg-tertiary), var(--color-bg-tertiary)) padding-box,
      linear-gradient(to right, #00c8d2, #315ab4) border-box;
  }

  .card-item + .card-item {
    margin-top: 10px;
  }

  .additional-output-card {
    border: 1px dashed #dcdfe6;
    opacity: 0.9;
    box-shadow: var(--color-agent-list-hover-shadow);

    &:hover {
      border-color: #409eff;
      opacity: 1;
    }

    :deep(.el-card__body) {
      padding: 20px;
    }

    // 编辑区域样式调整
    .el-collapse {
      border: none;

      .el-collapse-item {
        .el-collapse-item__header {
          background: var(--color-bg-detail);
          min-height: 36px;
          line-height: 36px;
          border-radius: 8px;

          .el-collapse-item__title {
            background: transparent;
            font-size: 14px;
            padding-left: 0;
          }

          .el-icon {
            font-size: 16px;
          }
        }

        .el-collapse-item__wrap {
          background: var(--color-bg-detail);
          border-radius: 0 0 8px 8px;
        }
      }
    }
  }

  // 额外产物编辑区域样式
  .additional-output-editor {
    .output-editor {
      :deep(.el-textarea__inner) {
        font-size: 14px;
        color: var(--color-text-primary);
        background: var(--color-bg-detail);
        border: 1px solid #dcdfe6;
        resize: none;
        padding: 12px;
      }
    }

    .output-content-display {
      word-break: break-word;
      white-space: pre-wrap;
      transition: all 0.2s ease;
      line-height: 1.5;
      min-height: 20px;

      &:hover {
        background-color: rgba(0, 0, 0, 0.05);
        border-color: #409eff;
        opacity: 1;
      }
    }

    // 编辑按钮样式
    .el-button {
      font-weight: bold;
      font-size: 16px;
      border-radius: 4px;

      &.el-button--small {
        padding: 4px 12px;
      }
    }
  }

  // ========== 新增：按钮交互样式 ==========
  .task-button-group {
    .el-button {
      display: inline-flex !important;
      align-items: center !important;
      justify-content: center !important;
      transition: all 0.35s cubic-bezier(0.175, 0.885, 0.32, 1.275) !important;
      overflow: hidden !important;
      white-space: nowrap !important;
      border: none !important;
      color: var(--color-text-primary) !important;
      position: relative;
      background-color: var(--color-bg-tertiary);
      &:hover {
        transform: translateY(-2px) !important;
        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important;
        filter: brightness(1.1) !important;
      }

      &.is-disabled {
        opacity: 0.5;
        cursor: not-allowed !important;

        &:hover {
          transform: none !important;
          box-shadow: none !important;
          filter: none !important;
        }
      }
    }

    // 圆形状态
    .circle {
      width: 40px !important;
      height: 40px !important;
      min-width: 40px !important;
      padding: 0 !important;
      border-radius: 50% !important;

      .btn-text {
        display: none !important;
      }
    }

    // 椭圆形状态
    .ellipse {
      height: 40px !important;
      border-radius: 20px !important;
      padding: 0 16px !important;
      gap: 8px;

      .btn-text {
        display: inline-block !important;
        font-size: 14px;
        font-weight: 500;
        margin-left: 4px;
        opacity: 1;
        animation: fadeIn 0.3s ease forwards;
      }
    }

    .btn-text {
      font-size: 14px;
      font-weight: 500;
      margin-left: 4px;
      opacity: 0;
      animation: fadeIn 0.3s ease forwards;
    }

    @keyframes fadeIn {
      from {
        opacity: 0;
        transform: translateX(-5px);
      }
      to {
        opacity: 1;
        transform: translateX(0);
      }
    }
  }
}
</style>
