<template>
  <div class="login-page">
    <!-- 左侧品牌展示区 -->
    <div class="login-left" :style="bgStyle">
      <div class="left-overlay" />
      <!-- 装饰网格线 -->
      <div class="left-grid" />
      <!-- 浮动装饰圆点 -->
      <div class="dot dot-1" />
      <div class="dot dot-2" />
      <div class="dot dot-3" />
      <div class="left-content">
        <!-- 品牌 logo -->
        <div class="brand">
          <img src="/logo.png" class="brand-logo-img" alt="Websopy" />
          <div class="brand-site-name">websopy</div>
        </div>

        <!-- 中央文案 -->
        <div class="hero-text">
          <div class="hero-tag">{{ $t('login.aiPlatform') }}</div>
          <h1 class="hero-title">{{ config?.loginTitle || $t('login.buildNextGen') }}</h1>
          <p class="hero-desc">{{ $t('login.lowcodeAccess') }}<br />{{ $t('login.fromIdeaToOnline') }}</p>
          <div class="hero-stats">
            <div class="stat-item">
              <span class="stat-num">10000+</span>
              <span class="stat-label">{{ $t('login.developers') }}</span>
            </div>
            <div class="stat-divider" />
            <div class="stat-item">
              <span class="stat-num">500+</span>
              <span class="stat-label">{{ $t('login.aiApps') }}</span>
            </div>
            <div class="stat-divider" />
            <div class="stat-item">
              <span class="stat-num">99.9%</span>
              <span class="stat-label">{{ $t('login.uptime') }}</span>
            </div>
          </div>
        </div>

        <!-- 底部 -->
        <div class="left-footer">
          <span>{{ $t('login.copyright') }}</span>
        </div>
      </div>
    </div>

    <!-- 右侧登录表单区 -->
    <div class="login-right">
      <div class="form-wrapper">
        <!-- 移动端 logo（仅小屏显示） -->
        <div class="mobile-brand">
          <img :src="config?.sysLogo || defaultLogo" class="mobile-logo" alt="logo" />
          <span class="mobile-brand-name">{{ config?.siteName || $t('login.aiAppPlatform') }}</span>
        </div>

        <!-- 登录卡片头部 -->
        <div class="form-header">
          <h2 class="form-title">{{ $t('login.welcomeBack') }}</h2>
          <p class="form-subtitle">{{ $t('login.loginToContinue') }}</p>
        </div>

        <!-- 切换标签 -->
        <div class="login-tabs">
          <button
            class="login-tab"
            :class="{ active: loginType === 'scan' }"
            @click="setLoginType('scan')"
          >
            <QrcodeOutlined />
            {{ $t('login.scanLogin') }}
          </button>
          <button
            class="login-tab"
            :class="{ active: loginType === 'sms' }"
            @click="setLoginType('sms')"
          >
            <MobileOutlined />
            {{ $t('login.phoneLogin') }}
          </button>
        </div>

        <!-- 表单区域 -->
        <a-form ref="formRef" :model="form" :rules="rules" class="login-form">
          <!-- 手机号登录 -->
          <template v-if="loginType === 'sms'">
            <a-form-item name="phone">
              <a-input
                v-model:value="form.phone"
                size="large"
                allow-clear
                :maxlength="11"
                :placeholder="$t('login.phonePlaceholder')"
                class="form-input"
              >
                <template #prefix>
                  <span class="phone-prefix">+86</span>
                  <span class="phone-prefix-divider" />
                </template>
              </a-input>
            </a-form-item>

            <a-form-item name="smsCode">
              <div class="captcha-row">
                <a-input
                  v-model:value="form.smsCode"
                  size="large"
                  allow-clear
                  :maxlength="6"
                  :placeholder="$t('login.smsCodePlaceholder')"
                  class="form-input"
                  @press-enter="submitSms"
                />
                <button
                  class="sms-btn"
                  :disabled="countdown > 0"
                  :class="{ disabled: countdown > 0 }"
                  @click.prevent="openImgCodeModal"
                >
                  <span v-if="countdown <= 0">{{ $t('login.sendCode') }}</span>
                  <span v-else>{{ countdown }}{{ $t('login.resendAfter') }}</span>
                </button>
              </div>
            </a-form-item>

            <!-- 注册协议和隐私政策 -->
            <div class="agreement-row">
              <a-checkbox v-model:checked="form.agreement">
                <span class="agreement-text">
                  {{ $t('login.agreeTerms') }}
                  <NuxtLink to="/agreement" target="_blank" class="agreement-link" @click.stop>{{ $t('login.registerAgreement') }}</NuxtLink>
                  {{ $t('common.and') || '和' }}
                  <NuxtLink to="/privacy" target="_blank" class="agreement-link" @click.stop>{{ $t('login.privacyPolicy') }}</NuxtLink>
                </span>
              </a-checkbox>
            </div>

            <a-form-item>
              <a-button
                block
                size="large"
                type="primary"
                :loading="loading"
                class="submit-btn"
                @click="submitSms"
              >
                {{ loading ? $t('login.loggingIn') : $t('login.loginNow') }}
              </a-button>
            </a-form-item>
          </template>

          <!-- 扫码登录 -->
          <template v-else>
            <QrLogin @login-success="onQrLoginSuccess" @login-error="onQrLoginError" />
          </template>
        </a-form>

        <!-- 底部切换扫码 -->
        <div class="form-footer">
          <button class="switch-scan-btn" @click="toggleScan">
            <QrcodeOutlined v-if="loginType !== 'scan'" />
            <MobileOutlined v-else />
            {{ loginType === 'scan' ? $t('login.switchToPhone') : $t('login.switchToScan') }}
          </button>
        </div>
      </div>
    </div>

    <!-- 图形验证码弹窗（发送短信用） -->
    <a-modal v-model:open="imgCodeModalOpen" :width="360" :footer="null" :title="$t('login.securityVerify')">
      <p class="modal-tip">{{ $t('login.completeVerifyFirst') }}</p>
      <div class="captcha-row modal-captcha">
        <a-input
          v-model:value="imgCode"
          size="large"
          allow-clear
          :maxlength="5"
          :placeholder="$t('login.enterImgCode')"
          @press-enter="sendSmsCode"
        />
        <button class="captcha-img-btn" @click.prevent="changeCaptcha" :title="$t('login.clickRefresh')">
          <img alt="captcha" :src="captcha" />
        </button>
      </div>
      <a-button block size="large" type="primary" :loading="sendingSms" class="submit-btn" @click="sendSmsCode">
        {{ $t('login.sendCode') }}
      </a-button>
    </a-modal>

    <!-- 选择账号弹窗 -->
    <a-modal v-model:open="selectUserOpen" :width="520" :footer="null" :title="$t('login.selectAccount')">
      <a-list item-layout="horizontal" :data-source="admins">
        <template #renderItem="{ item }">
          <a-list-item class="list-item" @click="selectUser(item)">
            <a-list-item-meta :description="`${$t('login.tenantId')}: ${item.tenantId}`">
              <template #title>{{ item.tenantName || item.username }}</template>
              <template #avatar>
                <a-avatar :src="item.avatar" />
              </template>
            </a-list-item-meta>
            <template #actions><RightOutlined /></template>
          </a-list-item>
        </template>
      </a-list>
    </a-modal>
  </div>
</template>

<script setup lang="ts">
import { computed, onMounted, onUnmounted, reactive, ref } from 'vue'
import { message, type FormInstance } from 'ant-design-vue'
import {
  MobileOutlined,
  QrcodeOutlined,
  RightOutlined
} from '@ant-design/icons-vue'
import QrLogin from '@/components/QrLogin.vue'
import { configWebsiteField, type Config } from '@/api/cms/cmsWebsiteField'
import { loginBySms, sendSmsCaptcha, getCaptcha } from '@/api/passport/login'
import type { LoginParam } from '@/api/passport/login/model'
import { listAdminsByPhoneAll } from '@/api/system/user'
import type { User } from '@/api/system/user/model'
import { getUserInfo } from '@/api/layout'
import { TEMPLATE_ID } from '@/config/setting'
import { setToken } from '@/utils/token-util'
import type { QrCodeStatusResponse } from '@/api/passport/qrLogin'
const { t } = useI18n()

definePageMeta({ layout: false })

const route = useRoute()

const defaultLogo = 'https://oss.wsdns.cn/20240822/0252ad4ed46449cdafe12f8d3d96c2ea.svg'
const config = ref<Config>()
const loading = ref(false)
const loginType = ref<'scan' | 'sms'>('scan')

const captcha = ref('')
const captchaText = ref('')

const imgCodeModalOpen = ref(false)
const imgCode = ref('')
const sendingSms = ref(false)

const countdown = ref(0)
let countdownTimer: ReturnType<typeof setInterval> | null = null

const selectUserOpen = ref(false)
const admins = ref<User[]>([])

const formRef = ref<FormInstance>()
const form = reactive<LoginParam & { smsCode?: string; agreement?: boolean }>({
  phone: '',
  smsCode: '',
  remember: true,
  agreement: false
})

const phoneReg = /^1[3-9]\d{9}$/
const rules = reactive({
  phone: [
    { required: true, message: t('login.enterPhone'), type: 'string' },
    { pattern: phoneReg, message: t('login.phoneFormatError'), trigger: 'blur' }
  ],
  smsCode: [{ required: true, message: t('login.enterSmsCode'), type: 'string' }]
})

const bgStyle = computed(() => {
  const bg = config.value?.loginBgImg
  if (!bg) return {}
  return { backgroundImage: `url(${bg})` }
})

function setLoginType(type: 'scan' | 'sms') {
  loginType.value = type
}

function toggleScan() {
  loginType.value = loginType.value === 'scan' ? 'sms' : 'scan'
}

function stopCountdown() {
  if (countdownTimer) clearInterval(countdownTimer)
  countdownTimer = null
  countdown.value = 0
}

async function changeCaptcha() {
  try {
    const data = await getCaptcha()
    captcha.value = data.base64
    captchaText.value = data.text
  } catch (e: unknown) {
    message.error(e instanceof Error ? e.message : t('login.loginFailed'))
  }
}

function openImgCodeModal() {
  if (!form.phone) return message.error(t('login.enterPhone'))
  imgCode.value = ''
  changeCaptcha()
  imgCodeModalOpen.value = true
}

async function sendSmsCode() {
  if (!imgCode.value) return message.error(t('login.enterImgCode'))
  if (captchaText.value && imgCode.value.toLowerCase() !== captchaText.value.toLowerCase()) {
    return message.error(t('login.imgCodeIncorrect'))
  }

  sendingSms.value = true
  try {
    await sendSmsCaptcha({ phone: form.phone })
    message.success(t('login.smsSentSuccess'))
    imgCodeModalOpen.value = false
    countdown.value = 30
    stopCountdown()
    countdown.value = 30
    countdownTimer = setInterval(() => {
      countdown.value -= 1
      if (countdown.value <= 0) stopCountdown()
    }, 1000)
  } catch (e: unknown) {
    message.error(e instanceof Error ? e.message : t('login.sendFailed'))
  } finally {
    sendingSms.value = false
  }
}

async function goAfterLogin() {
  const from = typeof route.query.from === 'string' ? route.query.from : ''
  await navigateTo(from || '/')
}

function persistUserId(userId: unknown) {
  if (!import.meta.client) return
  const n = typeof userId === 'number' ? userId : Number(userId)
  if (Number.isFinite(n) && n > 0) localStorage.setItem('UserId', String(n))
}

async function ensureUserIdPersisted(seed?: unknown) {
  if (!import.meta.client) return
  persistUserId(seed)
  try {
    if (localStorage.getItem('UserId')) return
  } catch {
    // ignore
  }

  try {
    const me = await getUserInfo()
    persistUserId(me.userId)
  } catch {
    // ignore (don't block login redirect)
  }
}

async function submitSms() {
  if (!formRef.value) return
  // 校验协议勾选
  if (!form.agreement) {
    return message.error(t('login.agreeRequired'))
  }
  loading.value = true
  try {
    await formRef.value.validate()
    const msg = await loginBySms({
      phone: form.phone,
      code: String(form.smsCode || '').toLowerCase(),
      tenantId: form.tenantId,
      remember: !!form.remember
    })

    if (msg === '请选择登录用户') {
      selectUserOpen.value = true
      admins.value = await listAdminsByPhoneAll({
        phone: form.phone,
        templateId: Number(TEMPLATE_ID)
      })
      return
    }

    message.success(msg || t('login.loginSuccess'))
    await ensureUserIdPersisted()
    await goAfterLogin()
  } catch (e: unknown) {
    message.error(e instanceof Error ? e.message : t('login.loginFailed'))
  } finally {
    loading.value = false
  }
}

async function selectUser(user: User) {
  form.tenantId = user.tenantId
  selectUserOpen.value = false
  await submitSms()
}

async function onQrLoginSuccess(payload: QrCodeStatusResponse) {
  const accessToken = payload.accessToken || payload.access_token
  if (accessToken) setToken(String(accessToken), true)
  if (payload.tenantId && import.meta.client) localStorage.setItem('TenantId', String(payload.tenantId))
  const seedUserId =
    typeof payload.userInfo === 'object' && payload.userInfo && 'userId' in payload.userInfo
      ? (payload.userInfo as { userId?: unknown }).userId
      : undefined
  await ensureUserIdPersisted(seedUserId)
  message.success(t('login.scanLoginSuccess'))
  await goAfterLogin()
}

function onQrLoginError(error: string) {
  message.error(error || t('login.scanLoginFailed'))
}

onMounted(async () => {
  try {
    config.value = await configWebsiteField({ lang: 'zh-CN' })
  } catch {
    // ignore config errors
  }
  changeCaptcha()

  if (typeof route.query.loginPhone === 'string') {
    form.phone = route.query.loginPhone
    loginType.value = 'sms'
  }
})

onUnmounted(() => {
  stopCountdown()
})
</script>

<style scoped>
/* ===== 整体布局 ===== */
.login-page {
  display: flex;
  min-height: 100vh;
  background: #f7f8fa;
}

/* ===== 左侧品牌区 ===== */
.login-left {
  flex: 1;
  position: relative;
  background: linear-gradient(150deg, #0f0c29 0%, #1a1a4e 30%, #24243e 60%, #302b63 100%);
  background-size: cover;
  background-position: center;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  min-height: 100vh;
}

.left-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(150deg, rgba(10, 10, 40, 0.75) 0%, rgba(30, 20, 80, 0.55) 100%);
}

/* 装饰网格 */
.left-grid {
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(rgba(255,255,255,0.04) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,0.04) 1px, transparent 1px);
  background-size: 48px 48px;
  pointer-events: none;
}

/* 浮动装饰圆点 */
.dot {
  position: absolute;
  border-radius: 50%;
  pointer-events: none;
}
.dot-1 {
  width: 420px;
  height: 420px;
  background: radial-gradient(circle, rgba(99, 102, 241, 0.22) 0%, transparent 65%);
  top: -120px;
  right: -80px;
}
.dot-2 {
  width: 320px;
  height: 320px;
  background: radial-gradient(circle, rgba(167, 139, 250, 0.18) 0%, transparent 65%);
  bottom: 60px;
  left: -60px;
}
.dot-3 {
  width: 180px;
  height: 180px;
  background: radial-gradient(circle, rgba(236, 72, 153, 0.14) 0%, transparent 65%);
  top: 45%;
  left: 55%;
}

/* 光晕大圆 */
.login-left::before {
  content: '';
  position: absolute;
  width: 560px;
  height: 560px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.06);
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  pointer-events: none;
}
.login-left::after {
  content: '';
  position: absolute;
  width: 380px;
  height: 380px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.08);
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  pointer-events: none;
}

.left-content {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 100vh;
  padding: 48px 56px;
}

/* 品牌区 */
.brand {
  display: flex;
  align-items: center;
  gap: 0;
}
/* 品牌 logo 图片 */
.brand-logo-img {
  height: 22px;
  width: auto;
  object-fit: contain;
}
/* site-name 渐变文字，与导航栏保持一致 */
.brand-site-name {
  color: #fff;
  font-family: 'Alimama FangYuanTi VF,sans-serif', sans-serif;
  font-size: 18px;
  font-weight: 700;
  letter-spacing: 0.04em;
  white-space: nowrap;
  line-height: 1;
  background: linear-gradient(135deg, #ffffff 0%, #a5c8ff 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  margin-left: 8px;
}

/* Hero 文案 */
.hero-text {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 48px 0 36px;
}

.hero-tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 14px;
  border-radius: 20px;
  background: rgba(99, 102, 241, 0.2);
  border: 1px solid rgba(99, 102, 241, 0.35);
  color: #a5b4fc;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.5px;
  margin-bottom: 24px;
  width: fit-content;
}

.hero-title {
  font-size: 46px;
  font-weight: 800;
  color: #fff;
  line-height: 1.18;
  margin-bottom: 22px;
  letter-spacing: -1px;
  white-space: pre-line;
}
.hero-desc {
  font-size: 15px;
  color: rgba(255, 255, 255, 0.55);
  margin-bottom: 44px;
  line-height: 1.8;
}

/* 统计数字 */
.hero-stats {
  display: flex;
  align-items: center;
  gap: 0;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 14px;
  padding: 20px 28px;
  backdrop-filter: blur(8px);
  width: fit-content;
}
.stat-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 0 28px;
}
.stat-item:first-child {
  padding-left: 0;
}
.stat-item:last-child {
  padding-right: 0;
}
.stat-num {
  font-size: 22px;
  font-weight: 800;
  color: #fff;
  letter-spacing: -0.5px;
  line-height: 1;
}
.stat-label {
  font-size: 12px;
  color: rgba(255, 255, 255, 0.45);
}
.stat-divider {
  width: 1px;
  height: 36px;
  background: rgba(255, 255, 255, 0.12);
}

.left-footer {
  color: rgba(255, 255, 255, 0.25);
  font-size: 12px;
}

/* ===== 右侧表单区 ===== */
.login-right {
  width: 65%;
  min-width: 380px;
  background: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 64px 48px;
  box-shadow: -1px 0 0 0 #f0f0f0;
}

.form-wrapper {
  width: 100%;
  max-width: 400px;
  box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
  padding: 40px;
}

/* 移动端品牌 */
.mobile-brand {
  display: none;
  align-items: center;
  gap: 10px;
  margin-bottom: 40px;
}
.mobile-logo {
  width: 34px;
  height: 34px;
  border-radius: 8px;
}
.mobile-brand-name {
  font-size: 18px;
  font-weight: 700;
  color: #111;
}

/* 表单头部 */
.form-header {
  margin-bottom: 36px;
}
.form-title {
  font-size: 28px;
  font-weight: 800;
  color: #0d0d0d;
  margin: 0 0 10px;
  letter-spacing: -0.5px;
}
.form-subtitle {
  font-size: 14px;
  color: #8c8c8c;
  margin: 0;
  line-height: 1.5;
}

/* 切换 Tab */
.login-tabs {
  display: flex;
  gap: 0;
  margin-bottom: 32px;
  border-bottom: 1.5px solid #f0f0f0;
}
.login-tab {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 10px 0 12px;
  border: none;
  border-bottom: 2px solid transparent;
  background: transparent;
  font-size: 14px;
  font-weight: 500;
  color: #a0a0a0;
  cursor: pointer;
  transition: all 0.2s ease;
  outline: none;
  margin-bottom: -1.5px;
}
.login-tab.active {
  color: #0d0d0d;
  border-bottom-color: #6366f1;
  font-weight: 600;
}
.login-tab:hover:not(.active) {
  color: #555;
}

/* 表单输入框 */
.login-form :deep(.ant-input-affix-wrapper),
.login-form :deep(.ant-input),
.login-form :deep(.ant-input-password) {
  border-radius: 10px;
  border-color: #ebebeb;
  background: #fafafa;
  transition: all 0.2s;
  padding-top: 6px;
  padding-bottom: 6px;
  align-items: center;
}
.login-form :deep(.ant-input-affix-wrapper:focus),
.login-form :deep(.ant-input-affix-wrapper-focused) {
  border-color: #6366f1;
  background: #fff;
  box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
}
.login-form :deep(.ant-form-item) {
  margin-bottom: 18px;
}

.input-icon {
  color: #bfbfbf;
  font-size: 15px;
}

/* 验证码行 */
.captcha-row {
  display: flex;
  gap: 10px;
  align-items: center;
}
.captcha-row :deep(.ant-input-affix-wrapper),
.captcha-row :deep(.ant-input) {
  flex: 1;
  min-width: 0;
  border-radius: 10px !important;
}

/* 图形验证码按钮 */
.captcha-img-btn {
  width: 148px;
  height: 48px;
  flex-shrink: 0;
  border: 1px solid #ebebeb;
  border-radius: 10px;
  overflow: hidden;
  background: #fafafa;
  cursor: pointer;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: border-color 0.2s;
}
.captcha-img-btn:hover {
  border-color: #6366f1;
}
.captcha-img-btn img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.captcha-placeholder {
  font-size: 12px;
  color: #bfbfbf;
}

/* 发送验证码按钮 */
.sms-btn {
  flex-shrink: 0;
  padding: 0 18px;
  height: 48px;
  border: 1px solid #6366f1;
  border-radius: 10px;
  background: transparent;
  color: #6366f1;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  white-space: nowrap;
  transition: all 0.2s;
  outline: none;
}
.sms-btn:hover:not(.disabled) {
  background: #6366f1;
  color: #fff;
}
.sms-btn.disabled {
  border-color: #e0e0e0;
  color: #bfbfbf;
  cursor: not-allowed;
}

/* 手机号前缀 +86（prefix 模式） */
.phone-prefix {
  font-size: 14px;
  color: #555;
  font-weight: 500;
  margin-right: 2px;
}
.phone-prefix-divider {
  display: inline-block;
  width: 1px;
  height: 14px;
  background: #d9d9d9;
  margin-left: 8px;
  vertical-align: middle;
}

/* 记住登录 */
.form-options {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 24px;
  margin-top: -4px;
}

/* 提交按钮 */
.submit-btn.ant-btn-primary {
  background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%) !important;
  border: none !important;
  border-radius: 10px !important;
  height: 48px !important;
  font-size: 15px !important;
  font-weight: 600 !important;
  letter-spacing: 0.3px !important;
  box-shadow: 0 4px 16px rgba(99, 102, 241, 0.28) !important;
  transition: all 0.22s !important;
}
.submit-btn.ant-btn-primary:hover:not(:disabled) {
  transform: translateY(-1px) !important;
  box-shadow: 0 8px 24px rgba(99, 102, 241, 0.38) !important;
}

/* 扫码切换 */
.form-footer {
  margin-top: 28px;
  text-align: center;
}
.switch-scan-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 18px;
  border: 1px solid #ebebeb;
  border-radius: 20px;
  background: transparent;
  color: #8c8c8c;
  font-size: 13px;
  cursor: pointer;
  transition: all 0.2s;
  outline: none;
}
.switch-scan-btn:hover {
  border-color: #6366f1;
  color: #6366f1;
  background: rgba(99, 102, 241, 0.04);
}

/* 弹窗 */
.modal-tip {
  font-size: 13px;
  color: #8c8c8c;
  margin-bottom: 16px;
}
.modal-captcha {
  margin-bottom: 16px;
}

/* 账号列表 */
.list-item {
  cursor: pointer;
  transition: background 0.15s;
  padding: 8px 12px;
  border-radius: 8px;
}
.list-item:hover {
  background: #f5f5f5;
}

/* 注册协议和隐私政策勾选 */
.agreement-row {
  margin-bottom: 20px;
  line-height: 1.6;
}
.agreement-row :deep(.ant-checkbox-wrapper) {
  font-size: 13px;
  color: #666;
  align-items: flex-start;
}
.agreement-row :deep(.ant-checkbox) {
  margin-top: 2px;
}
.agreement-text {
  font-size: 13px;
  color: #666;
  line-height: 1.6;
}
.agreement-link {
  color: #6366f1;
  text-decoration: none;
  font-size: 13px;
}
.agreement-link:hover {
  text-decoration: underline;
}

/* ===== 响应式 ===== */
@media (max-width: 900px) {
  .login-left {
    display: none;
  }
  .login-right {
    width: 100%;
    min-width: unset;
    padding: 48px 32px;
    min-height: 100vh;
    box-shadow: none;
  }
  .mobile-brand {
    display: flex;
  }
  .form-wrapper {
    max-width: 400px;
    margin: 0 auto;
  }
}
</style>
