<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import {
  getAllDictTypes,
  createDictType,
  updateDictType,
  deleteDictType,
  getDictDataByType,
  createDictData,
  updateDictData,
  deleteDictData,
  type SysDictType,
  type SysDictData,
  type CreateDictTypeDto,
  type UpdateDictTypeDto,
  type CreateDictDataDto,
  type UpdateDictDataDto,
} from '@/api/dict'
import AppLayout from '@/components/AppLayout.vue'
import { useToast } from '@/composables/useToast'

const toast = useToast()

// ==================== 字典类型 ====================
const dictTypes = ref<SysDictType[]>([])
const typesLoading = ref(false)

// 类型表单弹窗
const showTypeModal = ref(false)
const typeFormLoading = ref(false)
const editingType = ref<SysDictType | null>(null)
const typeForm = ref<CreateDictTypeDto>({ dictName: '', dictType: '', status: '0', remark: '' })
const typeFormErrors = ref<Record<string, string>>({})

// 类型删除
const showTypeDeleteModal = ref(false)
const typeDeleteTarget = ref<SysDictType | null>(null)
const typeDeleteLoading = ref(false)

// ==================== 字典数据 ====================
const selectedType = ref<SysDictType | null>(null)
const dictData = ref<SysDictData[]>([])
const dataLoading = ref(false)

// 数据表单弹窗
const showDataModal = ref(false)
const dataFormLoading = ref(false)
const editingData = ref<SysDictData | null>(null)
const dataForm = ref<CreateDictDataDto>({
  dictSort: 0,
  dictLabel: '',
  dictValue: '',
  dictType: '',
  cssClass: '',
  listClass: '',
  isDefault: 'N',
  status: '0',
})
const dataFormErrors = ref<Record<string, string>>({})

// 数据删除
const showDataDeleteModal = ref(false)
const dataDeleteTarget = ref<SysDictData | null>(null)
const dataDeleteLoading = ref(false)

// ==================== 类型状态映射 ====================
const statusMap: Record<string, string> = { '0': '正常', '1': '关闭' }

// ==================== 字典类型 API ====================
async function fetchDictTypes() {
  typesLoading.value = true
  try {
    dictTypes.value = await getAllDictTypes()
  } catch (e: any) {
    dictTypes.value = []
    const msg = e?.response?.data?.message || e?.message || '获取字典类型失败'
    toast.error(msg)
  } finally {
    typesLoading.value = false
  }
}

// --- 类型弹窗 ---
function openTypeCreate() {
  editingType.value = null
  typeForm.value = { dictName: '', dictType: '', status: '0', remark: '' }
  typeFormErrors.value = {}
  showTypeModal.value = true
}

function openTypeEdit(tp: SysDictType) {
  editingType.value = tp
  typeForm.value = {
    dictName: tp.dictName,
    dictType: tp.dictType,
    status: tp.status,
    remark: tp.remark || '',
  }
  typeFormErrors.value = {}
  showTypeModal.value = true
}

function validateTypeForm(): boolean {
  const errs: Record<string, string> = {}
  if (!typeForm.value.dictName.trim()) errs.dictName = '字典名称不能为空'
  if (!editingType.value && !typeForm.value.dictType.trim()) errs.dictType = '字典类型编码不能为空'
  typeFormErrors.value = errs
  return Object.keys(errs).length === 0
}

async function handleTypeSubmit() {
  if (!validateTypeForm()) return
  typeFormLoading.value = true
  try {
    if (editingType.value) {
      const dto: UpdateDictTypeDto = {
        dictName: typeForm.value.dictName,
        status: typeForm.value.status,
        remark: typeForm.value.remark,
      }
      await updateDictType(editingType.value.id, dto)
      toast.success('字典类型更新成功')
    } else {
      await createDictType(typeForm.value)
      toast.success('字典类型创建成功')
    }
    showTypeModal.value = false
    fetchDictTypes()
  } catch (e: any) {
    const msg = e?.response?.data?.message || e?.message || '操作失败'
    toast.error(msg)
  } finally {
    typeFormLoading.value = false
  }
}

// --- 类型删除 ---
function openTypeDelete(tp: SysDictType) {
  typeDeleteTarget.value = tp
  showTypeDeleteModal.value = true
}

async function handleTypeDelete() {
  if (!typeDeleteTarget.value) return
  typeDeleteLoading.value = true
  try {
    await deleteDictType(typeDeleteTarget.value.id)
    toast.success('字典类型已删除')
    showTypeDeleteModal.value = false
    if (selectedType.value?.id === typeDeleteTarget.value.id) {
      selectedType.value = null
      dictData.value = []
    }
    fetchDictTypes()
  } catch (e: any) {
    const msg = e?.response?.data?.message || e?.message || '删除失败'
    toast.error(msg)
  } finally {
    typeDeleteLoading.value = false
  }
}

// ==================== 字典数据 API ====================
function selectType(tp: SysDictType) {
  selectedType.value = tp
  fetchDictData(tp.dictType)
}

async function fetchDictData(dictTypeCode: string) {
  dataLoading.value = true
  try {
    dictData.value = await getDictDataByType(dictTypeCode)
  } catch {
    dictData.value = []
  } finally {
    dataLoading.value = false
  }
}

// --- 数据弹窗 ---
function openDataCreate() {
  if (!selectedType.value) {
    toast.error('请先选择一个字典类型')
    return
  }
  editingData.value = null
  dataForm.value = {
    dictSort: 0,
    dictLabel: '',
    dictValue: '',
    dictType: selectedType.value.dictType,
    cssClass: '',
    listClass: '',
    isDefault: 'N',
    status: '0',
  }
  dataFormErrors.value = {}
  showDataModal.value = true
}

function openDataEdit(item: SysDictData) {
  editingData.value = item
  dataForm.value = {
    dictSort: item.dictSort,
    dictLabel: item.dictLabel,
    dictValue: item.dictValue,
    dictType: item.dictType,
    cssClass: item.cssClass || '',
    listClass: item.listClass || '',
    isDefault: item.isDefault,
    status: item.status,
  }
  dataFormErrors.value = {}
  showDataModal.value = true
}

function validateDataForm(): boolean {
  const errs: Record<string, string> = {}
  if (!dataForm.value.dictLabel.trim()) errs.dictLabel = '字典标签不能为空'
  if (!dataForm.value.dictValue.trim()) errs.dictValue = '字典键值不能为空'
  dataFormErrors.value = errs
  return Object.keys(errs).length === 0
}

async function handleDataSubmit() {
  if (!validateDataForm()) return
  dataFormLoading.value = true
  try {
    if (editingData.value) {
      const dto: UpdateDictDataDto = {
        dictSort: dataForm.value.dictSort,
        dictLabel: dataForm.value.dictLabel,
        dictValue: dataForm.value.dictValue,
        cssClass: dataForm.value.cssClass || undefined,
        listClass: dataForm.value.listClass || undefined,
        isDefault: dataForm.value.isDefault,
        status: dataForm.value.status,
      }
      await updateDictData(editingData.value.id, dto)
      toast.success('字典数据更新成功')
    } else {
      await createDictData(dataForm.value)
      toast.success('字典数据创建成功')
    }
    showDataModal.value = false
    if (selectedType.value) fetchDictData(selectedType.value.dictType)
  } catch (e: any) {
    const msg = e?.response?.data?.message || e?.message || '操作失败'
    toast.error(msg)
  } finally {
    dataFormLoading.value = false
  }
}

// --- 数据删除 ---
function openDataDelete(item: SysDictData) {
  dataDeleteTarget.value = item
  showDataDeleteModal.value = true
}

async function handleDataDelete() {
  if (!dataDeleteTarget.value) return
  dataDeleteLoading.value = true
  try {
    await deleteDictData(dataDeleteTarget.value.id)
    toast.success('字典数据已删除')
    showDataDeleteModal.value = false
    if (selectedType.value) fetchDictData(selectedType.value.dictType)
  } catch (e: any) {
    const msg = e?.response?.data?.message || e?.message || '删除失败'
    toast.error(msg)
  } finally {
    dataDeleteLoading.value = false
  }
}

// ==================== 工具函数 ====================
function formatTime(t: string | null): string {
  if (!t) return '--'
  return new Date(t).toLocaleString('zh-CN')
}

// ==================== 初始化 ====================
onMounted(fetchDictTypes)
</script>

<template>
  <AppLayout>
    <div class="dict-page">
      <!-- ========== 左侧：字典类型 ========== -->
      <div class="dict-panel dict-types-panel">
        <div class="panel-header">
          <h3><i class="fas fa-tags"></i> 字典类型</h3>
          <button class="btn btn-sm btn-primary" @click="openTypeCreate">
            <i class="fas fa-plus"></i> 新增
          </button>
        </div>

        <div class="type-list" v-if="!typesLoading">
          <div v-if="dictTypes.length === 0" class="empty-list">暂无字典类型</div>
          <div
            v-for="tp in dictTypes"
            :key="tp.id"
            class="type-item"
            :class="{ selected: selectedType?.id === tp.id }"
            @click="selectType(tp)"
          >
            <div class="type-item-left">
              <div class="type-name">{{ tp.dictName }}</div>
              <div class="type-code">{{ tp.dictType }}</div>
            </div>
            <div class="type-item-right" @click.stop>
              <button class="btn-icon" title="编辑" @click="openTypeEdit(tp)">
                <i class="fas fa-edit"></i>
              </button>
              <button class="btn-icon btn-icon-danger" title="删除" @click="openTypeDelete(tp)">
                <i class="fas fa-trash"></i>
              </button>
            </div>
          </div>
        </div>
        <div v-else class="loading-state">
          <i class="fas fa-spinner fa-spin"></i> 加载中...
        </div>
      </div>

      <!-- ========== 右侧：字典数据 ========== -->
      <div class="dict-panel dict-data-panel">
        <div class="panel-header">
          <h3>
            <i class="fas fa-list"></i>
            {{ selectedType ? selectedType.dictName + ' - 数据项' : '字典数据' }}
          </h3>
          <button class="btn btn-sm btn-primary" @click="openDataCreate" :disabled="!selectedType">
            <i class="fas fa-plus"></i> 新增
          </button>
        </div>

        <div class="data-table-wrapper" v-if="!dataLoading">
          <table class="data-table" v-if="dictData.length > 0">
            <thead>
              <tr>
                <th style="width: 60px">ID</th>
                <th style="width: 140px">标签</th>
                <th style="width: 120px">键值</th>
                <th style="width: 70px">排序</th>
                <th style="width: 70px">默认</th>
                <th style="width: 70px">状态</th>
                <th style="width: 100px">创建时间</th>
                <th style="width: 130px">操作</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="item in dictData" :key="item.id">
                <td>{{ item.id }}</td>
                <td class="text-bold">{{ item.dictLabel }}</td>
                <td class="text-mono">{{ item.dictValue }}</td>
                <td>{{ item.dictSort }}</td>
                <td>
                  <span class="default-tag" :class="item.isDefault === 'Y' ? 'is-default' : ''">
                    {{ item.isDefault === 'Y' ? '是' : '否' }}
                  </span>
                </td>
                <td>
                  <span class="status-badge" :class="item.status === '0' ? 'active' : 'inactive'">
                    {{ statusMap[item.status] || item.status }}
                  </span>
                </td>
                <td class="text-sm">{{ formatTime(item.createdAt) }}</td>
                <td class="action-cell">
                  <button class="btn btn-sm btn-outline" @click="openDataEdit(item)">
                    <i class="fas fa-edit"></i>
                  </button>
                  <button class="btn btn-sm btn-danger" @click="openDataDelete(item)">
                    <i class="fas fa-trash"></i>
                  </button>
                </td>
              </tr>
            </tbody>
          </table>
          <div v-else class="empty-list">
            {{ selectedType ? '该类型下暂无数据项' : '请先在左侧选择一个字典类型' }}
          </div>
        </div>
        <div v-else class="loading-state">
          <i class="fas fa-spinner fa-spin"></i> 加载中...
        </div>
      </div>
    </div>

    <!-- ========== 字典类型弹窗 ========== -->
    <Teleport to="body">
      <div class="modal-overlay" v-if="showTypeModal" @click.self="showTypeModal = false">
        <div class="modal-box modal-sm">
          <div class="modal-header">
            <h3>
              <i :class="editingType ? 'fas fa-edit' : 'fas fa-plus'"></i>
              {{ editingType ? '编辑字典类型' : '新增字典类型' }}
            </h3>
            <button class="modal-close" @click="showTypeModal = false">&times;</button>
          </div>
          <div class="modal-body">
            <div class="form-group">
              <label>字典名称 <span class="required">*</span></label>
              <input v-model="typeForm.dictName" class="form-input" placeholder="如：性别" />
              <span class="field-error" v-if="typeFormErrors.dictName">{{ typeFormErrors.dictName }}</span>
            </div>
            <div class="form-group" v-if="!editingType">
              <label>字典类型编码 <span class="required">*</span></label>
              <input v-model="typeForm.dictType" class="form-input" placeholder="如：sys_user_sex" />
              <span class="field-error" v-if="typeFormErrors.dictType">{{ typeFormErrors.dictType }}</span>
            </div>
            <div class="form-group">
              <label>状态</label>
              <select v-model="typeForm.status" class="form-input">
                <option value="0">正常</option>
                <option value="1">关闭</option>
              </select>
            </div>
            <div class="form-group">
              <label>备注</label>
              <input v-model="typeForm.remark" class="form-input" placeholder="可选备注信息" />
            </div>
          </div>
          <div class="modal-footer">
            <button class="btn btn-outline" @click="showTypeModal = false">取消</button>
            <button class="btn btn-primary" :disabled="typeFormLoading" @click="handleTypeSubmit">
              <i class="fas fa-spinner fa-spin" v-if="typeFormLoading"></i>
              {{ typeFormLoading ? '保存中...' : '保存' }}
            </button>
          </div>
        </div>
      </div>
    </Teleport>

    <!-- ========== 字典类型删除弹窗 ========== -->
    <Teleport to="body">
      <div class="modal-overlay" v-if="showTypeDeleteModal" @click.self="showTypeDeleteModal = false">
        <div class="modal-box modal-sm">
          <div class="modal-header">
            <h3><i class="fas fa-exclamation-triangle" style="color: #e74c3c"></i> 确认删除</h3>
            <button class="modal-close" @click="showTypeDeleteModal = false">&times;</button>
          </div>
          <div class="modal-body">
            <p class="modal-desc">
              确定要删除字典类型 <strong>{{ typeDeleteTarget?.dictName }}</strong> 吗？
            </p>
            <p class="modal-hint">删除类型会同时影响关联的字典数据。</p>
          </div>
          <div class="modal-footer">
            <button class="btn btn-outline" @click="showTypeDeleteModal = false">取消</button>
            <button class="btn btn-danger" :disabled="typeDeleteLoading" @click="handleTypeDelete">
              <i class="fas fa-spinner fa-spin" v-if="typeDeleteLoading"></i>
              {{ typeDeleteLoading ? '删除中...' : '确认删除' }}
            </button>
          </div>
        </div>
      </div>
    </Teleport>

    <!-- ========== 字典数据弹窗 ========== -->
    <Teleport to="body">
      <div class="modal-overlay" v-if="showDataModal" @click.self="showDataModal = false">
        <div class="modal-box modal-sm">
          <div class="modal-header">
            <h3>
              <i :class="editingData ? 'fas fa-edit' : 'fas fa-plus'"></i>
              {{ editingData ? '编辑字典数据' : '新增字典数据' }}
            </h3>
            <button class="modal-close" @click="showDataModal = false">&times;</button>
          </div>
          <div class="modal-body">
            <div class="form-group">
              <label>字典标签 <span class="required">*</span></label>
              <input v-model="dataForm.dictLabel" class="form-input" placeholder="如：男" />
              <span class="field-error" v-if="dataFormErrors.dictLabel">{{ dataFormErrors.dictLabel }}</span>
            </div>
            <div class="form-group">
              <label>字典键值 <span class="required">*</span></label>
              <input v-model="dataForm.dictValue" class="form-input" placeholder="如：0" />
              <span class="field-error" v-if="dataFormErrors.dictValue">{{ dataFormErrors.dictValue }}</span>
            </div>
            <div class="form-row">
              <div class="form-group">
                <label>排序</label>
                <input v-model.number="dataForm.dictSort" type="number" class="form-input" />
              </div>
              <div class="form-group">
                <label>是否默认</label>
                <select v-model="dataForm.isDefault" class="form-input">
                  <option value="N">否</option>
                  <option value="Y">是</option>
                </select>
              </div>
            </div>
            <div class="form-group">
              <label>状态</label>
              <select v-model="dataForm.status" class="form-input">
                <option value="0">正常</option>
                <option value="1">关闭</option>
              </select>
            </div>
          </div>
          <div class="modal-footer">
            <button class="btn btn-outline" @click="showDataModal = false">取消</button>
            <button class="btn btn-primary" :disabled="dataFormLoading" @click="handleDataSubmit">
              <i class="fas fa-spinner fa-spin" v-if="dataFormLoading"></i>
              {{ dataFormLoading ? '保存中...' : '保存' }}
            </button>
          </div>
        </div>
      </div>
    </Teleport>

    <!-- ========== 字典数据删除弹窗 ========== -->
    <Teleport to="body">
      <div class="modal-overlay" v-if="showDataDeleteModal" @click.self="showDataDeleteModal = false">
        <div class="modal-box modal-sm">
          <div class="modal-header">
            <h3><i class="fas fa-exclamation-triangle" style="color: #e74c3c"></i> 确认删除</h3>
            <button class="modal-close" @click="showDataDeleteModal = false">&times;</button>
          </div>
          <div class="modal-body">
            <p class="modal-desc">
              确定要删除字典数据 <strong>{{ dataDeleteTarget?.dictLabel }}</strong> 吗？
            </p>
          </div>
          <div class="modal-footer">
            <button class="btn btn-outline" @click="showDataDeleteModal = false">取消</button>
            <button class="btn btn-danger" :disabled="dataDeleteLoading" @click="handleDataDelete">
              <i class="fas fa-spinner fa-spin" v-if="dataDeleteLoading"></i>
              {{ dataDeleteLoading ? '删除中...' : '确认删除' }}
            </button>
          </div>
        </div>
      </div>
    </Teleport>
  </AppLayout>
</template>

<style scoped>
/* ==================== 布局 ==================== */
.dict-page {
  display: flex;
  gap: 20px;
  height: calc(100vh - 140px);
}

.dict-panel {
  background: #ffffff;
  border-radius: 12px;
  border: 1px solid #e9edf4;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.dict-types-panel {
  width: 320px;
  flex-shrink: 0;
}

.dict-data-panel {
  flex: 1;
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px 20px;
  border-bottom: 1px solid #f0f3f8;
}

.panel-header h3 {
  font-size: 15px;
  color: #1e293b;
  display: flex;
  align-items: center;
  gap: 8px;
}

/* ==================== 类型列表 ==================== */
.type-list {
  flex: 1;
  overflow-y: auto;
}

.type-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 20px;
  border-bottom: 1px solid #f0f3f8;
  cursor: pointer;
  transition: background 0.15s;
}

.type-item:hover {
  background: #f8f9fc;
}

.type-item.selected {
  background: #eef2ff;
  border-left: 3px solid #4f7cff;
}

.type-item-left {
  min-width: 0;
}

.type-name {
  font-weight: 600;
  color: #1e293b;
  font-size: 14px;
  margin-bottom: 2px;
}

.type-code {
  font-size: 12px;
  color: #8b9ab0;
  font-family: 'Consolas', 'Monaco', monospace;
}

.type-item-right {
  display: flex;
  gap: 4px;
  flex-shrink: 0;
}

.btn-icon {
  background: none;
  border: none;
  color: #8b9ab0;
  cursor: pointer;
  padding: 4px 6px;
  border-radius: 4px;
  font-size: 13px;
  transition: all 0.15s;
}

.btn-icon:hover {
  color: #4f7cff;
  background: #eef2ff;
}

.btn-icon-danger:hover {
  color: #e74c3c;
  background: #fde8e8;
}

.empty-list {
  padding: 40px 20px;
  text-align: center;
  color: #8b9ab0;
  font-size: 14px;
}

/* ==================== 数据表格 ==================== */
.data-table-wrapper {
  flex: 1;
  overflow: auto;
}

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}

.data-table th {
  background: #f8f9fc;
  color: #5a6a85;
  font-weight: 600;
  text-align: left;
  padding: 10px 14px;
  border-bottom: 1px solid #e9edf4;
  white-space: nowrap;
  position: sticky;
  top: 0;
}

.data-table td {
  padding: 10px 14px;
  border-bottom: 1px solid #f0f3f8;
  color: #1e293b;
}

.data-table tbody tr:hover {
  background: #f8f9fc;
}

.text-bold {
  font-weight: 600;
}

.text-mono {
  font-family: 'Consolas', 'Monaco', monospace;
  font-size: 13px;
}

.text-sm {
  font-size: 12px;
  color: #8b9ab0;
}

.action-cell {
  display: flex;
  gap: 4px;
}

.default-tag {
  display: inline-block;
  padding: 1px 8px;
  border-radius: 10px;
  font-size: 12px;
  background: #f0f3f8;
  color: #8b9ab0;
}

.default-tag.is-default {
  background: #e8f5e9;
  color: #2e7d32;
  font-weight: 600;
}

.status-badge {
  display: inline-block;
  padding: 2px 10px;
  border-radius: 12px;
  font-size: 12px;
  font-weight: 600;
}

.status-badge.active {
  background: #e8f5e9;
  color: #2e7d32;
}

.status-badge.inactive {
  background: #fce4e4;
  color: #c62828;
}

.loading-state {
  padding: 60px;
  text-align: center;
  color: #8b9ab0;
  font-size: 15px;
}

/* ==================== 按钮 ==================== */
.btn {
  padding: 8px 16px;
  border-radius: 8px;
  border: none;
  font-size: 14px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-weight: 500;
  transition: all 0.2s;
}

.btn:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.btn-primary {
  background: #4f7cff;
  color: #ffffff;
}

.btn-primary:hover:not(:disabled) {
  background: #3b5fd9;
}

.btn-outline {
  background: #ffffff;
  color: #4a5568;
  border: 1px solid #dde2eb;
}

.btn-outline:hover:not(:disabled) {
  background: #f0f3f8;
}

.btn-danger {
  background: #e74c3c;
  color: #ffffff;
}

.btn-danger:hover:not(:disabled) {
  background: #c0392b;
}

.btn-sm {
  padding: 5px 10px;
  font-size: 12px;
  border-radius: 6px;
}

/* ==================== 弹窗 ==================== */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1000;
}

.modal-box {
  background: #ffffff;
  border-radius: 14px;
  width: 480px;
  max-width: 90vw;
  max-height: 85vh;
  overflow-y: auto;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15);
}

.modal-box.modal-sm {
  width: 400px;
}

.modal-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 20px 24px 16px;
  border-bottom: 1px solid #f0f3f8;
}

.modal-header h3 {
  font-size: 17px;
  color: #1e293b;
  display: flex;
  align-items: center;
  gap: 8px;
}

.modal-close {
  background: none;
  border: none;
  font-size: 24px;
  color: #8b9ab0;
  cursor: pointer;
  line-height: 1;
}

.modal-body {
  padding: 20px 24px;
}

.modal-footer {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  padding: 16px 24px;
  border-top: 1px solid #f0f3f8;
}

.modal-desc {
  color: #5a6a85;
  line-height: 1.6;
  margin-bottom: 8px;
}

.modal-hint {
  font-size: 13px;
  color: #8b9ab0;
}

/* ==================== 表单 ==================== */
.form-group {
  margin-bottom: 14px;
}

.form-group label {
  display: block;
  margin-bottom: 6px;
  font-size: 13px;
  font-weight: 600;
  color: #5a6a85;
}

.required {
  color: #e74c3c;
}

.form-input {
  width: 100%;
  padding: 9px 14px;
  border: 1px solid #dde2eb;
  border-radius: 8px;
  font-size: 14px;
  outline: none;
  color: #1e293b;
  transition: border-color 0.2s;
  box-sizing: border-box;
}

.form-input:focus {
  border-color: #4f7cff;
}

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

.field-error {
  display: block;
  margin-top: 4px;
  font-size: 12px;
  color: #e74c3c;
}
</style>
