<script setup>
import { computed, onMounted, reactive, ref } from 'vue'

import SocialLoginButtons from '../components/SocialLoginButtons.vue'

const form = reactive({
  username: '',
  password: '',
  captcha: '',
})

const needCaptcha = ref(false)
const captchaImage = ref('')
const captchaSession = ref('')
const loading = ref(false)
const passkeyLoading = ref(false)

const emailEnabled = ref(false)
const registerVerifyEnabled = ref(false)

const noticeType = ref('')
const noticeText = ref('')

const forgotOpen = ref(false)
const resendOpen = ref(false)

const forgotForm = reactive({
  username: '',
  captcha: '',
})
const forgotCaptchaImage = ref('')
const forgotCaptchaSession = ref('')
const forgotLoading = ref(false)
const forgotHint = ref('')
const forgotError = ref('')

const resendForm = reactive({
  email: '',
  captcha: '',
})
const resendCaptchaImage = ref('')
const resendCaptchaSession = ref('')
const resendLoading = ref(false)
const resendError = ref('')

const showResendLink = computed(() => true)
const verifyStatusLoaded = ref(false)
const socialConfig = ref({ enabled: false, providers: [] })
const socialCallbackLoading = ref(false)

const socialPendingKeys = {
  token: 'zsglpt_social_pending_bind_token',
  provider: 'zsglpt_social_pending_bind_provider',
  nickname: 'zsglpt_social_pending_bind_nickname',
  avatar: 'zsglpt_social_pending_bind_avatar_url',
}

function getCookie(name) {
  const escaped = String(name || '').replace(/([.*+?^${}()|[\]\\])/g, '\\$1')
  const match = document.cookie.match(new RegExp(`(?:^|; )${escaped}=([^;]*)`))
  return match ? decodeURIComponent(match[1]) : ''
}

class ApiError extends Error {
  constructor(message, status, data) {
    super(message || '请求失败')
    this.name = 'ApiError'
    this.response = {
      status: Number(status || 0),
      data: data || {},
    }
  }
}

async function apiRequest(path, options = {}) {
  const method = String(options.method || 'GET').toUpperCase()
  const headers = {
    ...(options.headers || {}),
  }
  const hasBody = Object.prototype.hasOwnProperty.call(options, 'body')
  if (hasBody && !headers['Content-Type']) {
    headers['Content-Type'] = 'application/json'
  }
  if (!['GET', 'HEAD', 'OPTIONS'].includes(method)) {
    const token = getCookie('csrf_token')
    if (token) {
      headers['X-CSRF-Token'] = token
    }
  }

  const response = await fetch(`/api${path}`, {
    method,
    headers,
    credentials: 'include',
    body: hasBody ? JSON.stringify(options.body ?? {}) : undefined,
  })

  let data = {}
  try {
    data = await response.json()
  } catch {
    data = {}
  }

  if (!response.ok) {
    throw new ApiError(data?.error || data?.message || `请求失败 (${response.status})`, response.status, data)
  }
  return data
}

const fetchEmailVerifyStatus = () => apiRequest('/email/verify-status')
const generateCaptcha = () => apiRequest('/generate_captcha', { method: 'POST', body: {} })
const loginRequest = (payload) => apiRequest('/login', { method: 'POST', body: payload || {} })
const passkeyLoginOptions = (payload) => apiRequest('/passkeys/login/options', { method: 'POST', body: payload || {} })
const passkeyLoginVerify = (payload) => apiRequest('/passkeys/login/verify', { method: 'POST', body: payload || {} })
const resendVerifyEmail = (payload) => apiRequest('/resend-verify-email', { method: 'POST', body: payload || {} })
const forgotPassword = (payload) => apiRequest('/forgot-password', { method: 'POST', body: payload || {} })
const fetchSocialConfig = () => apiRequest('/auth/social/config')
const socialCallbackRequest = (payload) => apiRequest('/auth/social/callback', { method: 'POST', body: payload || {} })

function base64UrlToUint8Array(base64url) {
  const value = String(base64url || '')
  const padding = '='.repeat((4 - (value.length % 4)) % 4)
  const base64 = (value + padding).replace(/-/g, '+').replace(/_/g, '/')
  const raw = window.atob(base64)
  const bytes = new Uint8Array(raw.length)
  for (let i = 0; i < raw.length; i += 1) {
    bytes[i] = raw.charCodeAt(i)
  }
  return bytes
}

function uint8ArrayToBase64Url(input) {
  const bytes = input instanceof ArrayBuffer ? new Uint8Array(input) : new Uint8Array(input || [])
  let binary = ''
  for (let i = 0; i < bytes.length; i += 1) {
    binary += String.fromCharCode(bytes[i])
  }
  return window
    .btoa(binary)
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=+$/g, '')
}

function normalizePublicKeyOptions(options) {
  if (!options || typeof options !== 'object') {
    throw new Error('Passkey参数无效')
  }
  return options.publicKey && typeof options.publicKey === 'object' ? options.publicKey : options
}

function toRequestOptions(rawOptions) {
  const options = normalizePublicKeyOptions(rawOptions)
  const normalized = {
    ...options,
    challenge: base64UrlToUint8Array(options.challenge),
  }
  if (Array.isArray(options.allowCredentials)) {
    normalized.allowCredentials = options.allowCredentials.map((item) => ({
      ...item,
      id: base64UrlToUint8Array(item.id),
    }))
  }
  return normalized
}

function serializeCredential(credential) {
  const response = credential?.response || {}
  const output = {
    id: credential?.id,
    rawId: uint8ArrayToBase64Url(credential?.rawId),
    type: credential?.type,
    authenticatorAttachment: credential?.authenticatorAttachment || undefined,
    response: {},
  }
  if (response.clientDataJSON) output.response.clientDataJSON = uint8ArrayToBase64Url(response.clientDataJSON)
  if (response.authenticatorData) output.response.authenticatorData = uint8ArrayToBase64Url(response.authenticatorData)
  if (response.signature) output.response.signature = uint8ArrayToBase64Url(response.signature)
  if (response.userHandle) {
    output.response.userHandle = uint8ArrayToBase64Url(response.userHandle)
  } else {
    output.response.userHandle = null
  }
  return output
}

function isPasskeyAvailable() {
  return typeof window !== 'undefined' && window.isSecureContext && !!window.PublicKeyCredential && !!navigator.credentials
}

async function authenticateWithPasskey(rawOptions) {
  const publicKey = toRequestOptions(rawOptions)
  const credential = await navigator.credentials.get({ publicKey })
  return serializeCredential(credential)
}

async function loadVerifyStatus() {
  if (verifyStatusLoaded.value) return
  try {
    const status = await fetchEmailVerifyStatus()
    emailEnabled.value = Boolean(status?.email_enabled)
    registerVerifyEnabled.value = Boolean(status?.register_verify_enabled)
  } catch {
    emailEnabled.value = false
    registerVerifyEnabled.value = false
  } finally {
    verifyStatusLoaded.value = true
  }
}

function setNotice(type, text) {
  noticeType.value = String(type || '')
  noticeText.value = String(text || '')
}

function clearNotice() {
  noticeType.value = ''
  noticeText.value = ''
}

async function refreshLoginCaptcha() {
  try {
    const data = await generateCaptcha()
    captchaSession.value = data?.session_id || ''
    captchaImage.value = data?.captcha_image || ''
    form.captcha = ''
  } catch {
    captchaSession.value = ''
    captchaImage.value = ''
  }
}

async function refreshEmailResetCaptcha() {
  try {
    const data = await generateCaptcha()
    forgotCaptchaSession.value = data?.session_id || ''
    forgotCaptchaImage.value = data?.captcha_image || ''
    forgotForm.captcha = ''
  } catch {
    forgotCaptchaSession.value = ''
    forgotCaptchaImage.value = ''
  }
}

async function refreshResendCaptcha() {
  try {
    const data = await generateCaptcha()
    resendCaptchaSession.value = data?.session_id || ''
    resendCaptchaImage.value = data?.captcha_image || ''
    resendForm.captcha = ''
  } catch {
    resendCaptchaSession.value = ''
    resendCaptchaImage.value = ''
  }
}

function redirectAfterLogin() {
  const urlParams = new URLSearchParams(window.location.search || '')
  const next = String(urlParams.get('next') || '').trim()
  const safeNext = next && next.startsWith('/') && !next.startsWith('//') && !next.startsWith('/\\') ? next : ''
  window.setTimeout(() => {
    window.location.href = safeNext || '/app'
  }, 300)
}

function currentLoginRedirectUri() {
  const url = new URL(window.location.href)
  url.pathname = '/login'
  url.search = ''
  url.hash = ''
  return url.toString()
}

async function loadSocialConfig() {
  try {
    const data = await fetchSocialConfig()
    socialConfig.value = {
      enabled: Boolean(data?.enabled),
      providers: Array.isArray(data?.providers) ? data.providers : [],
    }
  } catch {
    socialConfig.value = { enabled: false, providers: [] }
  }
}

function savePendingSocialBind(data) {
  try {
    window.sessionStorage.setItem(socialPendingKeys.token, data?.bind_token || '')
    window.sessionStorage.setItem(socialPendingKeys.provider, data?.provider || '')
    window.sessionStorage.setItem(socialPendingKeys.nickname, data?.nickname || '')
    window.sessionStorage.setItem(socialPendingKeys.avatar, data?.avatar_url || '')
  } catch {
    // ignore storage failures
  }
}

async function handleSocialCallback() {
  const params = new URLSearchParams(window.location.search || '')
  const provider = String(params.get('provider') || params.get('type') || '').trim()
  const code = String(params.get('code') || '').trim()
  if (!provider || !code) return false

  socialCallbackLoading.value = true
  setNotice('success', '正在完成快捷登录...')
  try {
    const data = await socialCallbackRequest({ provider, code, mode: 'login' })
    if (data?.requires_register && data?.bind_token) {
      savePendingSocialBind(data)
      setNotice('success', '请完成注册后继续')
      window.setTimeout(() => {
        window.location.href = '/register'
      }, 500)
      return true
    }
    if (data?.success || data?.bound) {
      setNotice('success', '快捷登录成功，正在跳转...')
      redirectAfterLogin()
      return true
    }
    setNotice('error', '快捷登录失败')
  } catch (e) {
    const data = e?.response?.data
    setNotice('error', data?.error || data?.message || '快捷登录失败')
  } finally {
    socialCallbackLoading.value = false
  }
  return true
}

async function onSubmit() {
  clearNotice()

  if (!form.username.trim() || !form.password.trim()) {
    setNotice('error', '用户名和密码不能为空')
    return
  }
  if (needCaptcha.value && !form.captcha.trim()) {
    setNotice('error', '请输入验证码')
    return
  }

  loading.value = true
  try {
    const username = form.username.trim()
    await loginRequest({
      username,
      password: form.password,
      captcha_session: captchaSession.value,
      captcha: form.captcha.trim(),
      need_captcha: needCaptcha.value,
    })
    setNotice('success', '登录成功，正在跳转...')
    redirectAfterLogin()
  } catch (e) {
    const status = e?.response?.status
    const data = e?.response?.data
    const message = data?.error || data?.message || '登录失败'

    setNotice('error', message)

    if (data?.need_captcha) {
      needCaptcha.value = true
      await refreshLoginCaptcha()
    } else if (needCaptcha.value && status === 400) {
      await refreshLoginCaptcha()
    }
  } finally {
    loading.value = false
  }
}

async function onPasskeyLogin() {
  clearNotice()

  const username = form.username.trim()
  if (!isPasskeyAvailable()) {
    setNotice('error', '当前浏览器或环境不支持Passkey（需 HTTPS）')
    return
  }

  passkeyLoading.value = true
  try {
    const optionsRes = await passkeyLoginOptions(username ? { username } : {})
    const credential = await authenticateWithPasskey(optionsRes?.publicKey || {})
    await passkeyLoginVerify(username ? { username, credential } : { credential })
    setNotice('success', 'Passkey 登录成功，正在跳转...')
    redirectAfterLogin()
  } catch (e) {
    const data = e?.response?.data
    const message =
      data?.error ||
      (e?.name === 'NotAllowedError' ? 'Passkey验证未完成（可能取消、超时或设备未响应）' : e?.message || 'Passkey登录失败')
    setNotice('error', message)
  } finally {
    passkeyLoading.value = false
  }
}

async function openForgot() {
  await loadVerifyStatus()
  forgotOpen.value = true
  forgotHint.value = ''
  forgotError.value = ''
  forgotForm.username = ''
  forgotForm.captcha = ''
  await refreshEmailResetCaptcha()
}

async function submitForgot() {
  forgotError.value = ''
  forgotHint.value = ''

  if (!emailEnabled.value) {
    forgotError.value = '邮件功能未启用，请联系管理员重置密码。'
    return
  }

  const username = forgotForm.username.trim()
  if (!username) {
    forgotError.value = '请输入用户名'
    return
  }
  if (!forgotForm.captcha.trim()) {
    forgotError.value = '请输入验证码'
    return
  }

  forgotLoading.value = true
  try {
    const res = await forgotPassword({
      username,
      captcha_session: forgotCaptchaSession.value,
      captcha: forgotForm.captcha.trim(),
    })
    setNotice('success', res?.message || '已发送重置邮件')
    forgotOpen.value = false
  } catch (e) {
    const data = e?.response?.data
    const message = data?.error || '发送失败'
    if (data?.code === 'email_not_bound') {
      forgotHint.value = message
    } else {
      forgotError.value = message
    }
    await refreshEmailResetCaptcha()
  } finally {
    forgotLoading.value = false
  }
}

async function openResend() {
  await loadVerifyStatus()
  if (!registerVerifyEnabled.value) {
    setNotice('error', '当前未启用注册邮箱验证，无需重发验证邮件。')
    return
  }
  resendOpen.value = true
  resendForm.email = ''
  resendForm.captcha = ''
  resendError.value = ''
  await refreshResendCaptcha()
}

async function submitResend() {
  resendError.value = ''

  const email = resendForm.email.trim()
  if (!email) {
    resendError.value = '请输入邮箱'
    return
  }
  if (!resendForm.captcha.trim()) {
    resendError.value = '请输入验证码'
    return
  }

  resendLoading.value = true
  try {
    const res = await resendVerifyEmail({
      email,
      captcha_session: resendCaptchaSession.value,
      captcha: resendForm.captcha.trim(),
    })
    setNotice('success', res?.message || '验证邮件已发送，请查收')
    resendOpen.value = false
  } catch (e) {
    const data = e?.response?.data
    resendError.value = data?.error || '发送失败'
    await refreshResendCaptcha()
  } finally {
    resendLoading.value = false
  }
}

function goRegister() {
  window.location.href = '/register'
}

onMounted(async () => {
  await loadSocialConfig()
  await handleSocialCallback()
  if (needCaptcha.value) {
    await refreshLoginCaptcha()
  }
})
</script>

<template>
  <div class="login-page">
    <div class="login-container">
      <div class="login-header">
        <span class="login-badge">用户登录</span>
        <h1>用户登录系统</h1>
        <p>知识管理平台</p>
      </div>

      <div v-if="noticeText" class="notice" :class="noticeType === 'success' ? 'is-success' : 'is-error'">
        {{ noticeText }}
      </div>

      <div class="form-group">
        <label for="username">用户账号</label>
        <input
          id="username"
          v-model="form.username"
          class="text-input"
          placeholder="请输入用户名"
          autocomplete="username"
        />
      </div>

      <div class="form-group">
        <label for="password">密码</label>
        <input
          id="password"
          v-model="form.password"
          class="text-input"
          type="password"
          placeholder="请输入密码"
          autocomplete="current-password"
          @keyup.enter="onSubmit"
        />
      </div>

      <div v-if="needCaptcha" class="form-group">
        <label for="captcha">验证码</label>
        <div class="captcha-row">
          <input
            id="captcha"
            v-model="form.captcha"
            class="text-input captcha-input"
            placeholder="请输入验证码"
            @keyup.enter="onSubmit"
          />
          <img
            v-if="captchaImage"
            class="captcha-img"
            :src="captchaImage"
            alt="验证码"
            title="点击刷新"
            @click="refreshLoginCaptcha"
          />
          <button type="button" class="captcha-refresh" @click="refreshLoginCaptcha">刷新</button>
        </div>
      </div>

      <button type="button" class="btn-login" :disabled="loading || socialCallbackLoading" @click="onSubmit">
        {{ loading || socialCallbackLoading ? '登录中...' : '登录系统' }}
      </button>
      <button type="button" class="btn-passkey" :disabled="passkeyLoading" @click="onPasskeyLogin">
        {{ passkeyLoading ? 'Passkey验证中...' : '使用 Passkey 登录' }}
      </button>

      <div v-if="socialConfig.enabled" class="social-login-area">
        <div class="divider"><span>快捷登录</span></div>
        <SocialLoginButtons
          :providers="socialConfig.providers"
          mode="login"
          :redirect-uri="currentLoginRedirectUri()"
          block
          @error="(message) => setNotice('error', message)"
        />
      </div>

      <div class="action-links">
        <button type="button" class="link-btn" @click="openForgot">忘记密码？</button>
        <button v-if="showResendLink" type="button" class="link-btn" @click="openResend">重发验证邮件</button>
      </div>

      <div class="register-row">
        <span>还没有账号？</span>
        <button type="button" class="link-btn" @click="goRegister">立即注册</button>
      </div>
    </div>

    <div v-if="forgotOpen" class="modal-mask" @click.self="forgotOpen = false">
      <section class="modal-card">
        <div class="modal-head">
          <h3>找回密码</h3>
          <button type="button" class="modal-close" @click="forgotOpen = false">关闭</button>
        </div>

        <p class="modal-tip" :class="{ warn: !emailEnabled }">
          {{
            emailEnabled
              ? '输入用户名并完成验证码，我们将向该账号绑定的邮箱发送重置链接。'
              : '邮件功能未启用，无法通过邮箱找回密码。'
          }}
        </p>

        <p v-if="forgotHint" class="modal-tip warn">{{ forgotHint }}</p>
        <p v-if="forgotError" class="modal-tip error">{{ forgotError }}</p>

        <div class="form-group">
          <label for="forgot-username">用户名</label>
          <input id="forgot-username" v-model="forgotForm.username" class="text-input" placeholder="请输入用户名" />
        </div>

        <div class="form-group">
          <label for="forgot-captcha">验证码</label>
          <div class="captcha-row">
            <input
              id="forgot-captcha"
              v-model="forgotForm.captcha"
              class="text-input captcha-input"
              placeholder="请输入验证码"
            />
            <img
              v-if="forgotCaptchaImage"
              class="captcha-img"
              :src="forgotCaptchaImage"
              alt="验证码"
              title="点击刷新"
              @click="refreshEmailResetCaptcha"
            />
            <button type="button" class="captcha-refresh" @click="refreshEmailResetCaptcha">刷新</button>
          </div>
        </div>

        <div class="modal-actions">
          <button type="button" class="btn-ghost" @click="forgotOpen = false">取消</button>
          <button type="button" class="btn-login" :disabled="forgotLoading || !emailEnabled" @click="submitForgot">
            {{ forgotLoading ? '发送中...' : '发送重置邮件' }}
          </button>
        </div>
      </section>
    </div>

    <div v-if="resendOpen" class="modal-mask" @click.self="resendOpen = false">
      <section class="modal-card">
        <div class="modal-head">
          <h3>重发验证邮件</h3>
          <button type="button" class="modal-close" @click="resendOpen = false">关闭</button>
        </div>

        <p class="modal-tip">用于注册邮箱验证：请输入邮箱并完成验证码。</p>
        <p v-if="resendError" class="modal-tip error">{{ resendError }}</p>

        <div class="form-group">
          <label for="resend-email">邮箱</label>
          <input id="resend-email" v-model="resendForm.email" class="text-input" placeholder="name@example.com" />
        </div>

        <div class="form-group">
          <label for="resend-captcha">验证码</label>
          <div class="captcha-row">
            <input
              id="resend-captcha"
              v-model="resendForm.captcha"
              class="text-input captcha-input"
              placeholder="请输入验证码"
            />
            <img
              v-if="resendCaptchaImage"
              class="captcha-img"
              :src="resendCaptchaImage"
              alt="验证码"
              title="点击刷新"
              @click="refreshResendCaptcha"
            />
            <button type="button" class="captcha-refresh" @click="refreshResendCaptcha">刷新</button>
          </div>
        </div>

        <div class="modal-actions">
          <button type="button" class="btn-ghost" @click="resendOpen = false">取消</button>
          <button type="button" class="btn-login" :disabled="resendLoading" @click="submitResend">
            {{ resendLoading ? '发送中...' : '发送' }}
          </button>
        </div>
      </section>
    </div>
  </div>
</template>

<style scoped>
.login-page {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  position: relative;
  background: linear-gradient(135deg, #eef2ff 0%, #f6f7fb 45%, #ecfeff 100%);
}

.login-page::before {
  content: '';
  position: fixed;
  inset: 0;
  background:
    radial-gradient(800px 500px at 15% 20%, rgba(59, 130, 246, 0.18), transparent 60%),
    radial-gradient(700px 420px at 85% 70%, rgba(124, 58, 237, 0.16), transparent 55%);
  pointer-events: none;
}

.login-container {
  width: 100%;
  max-width: 420px;
  background: #fff;
  border-radius: 16px;
  box-shadow: 0 18px 60px rgba(17, 24, 39, 0.15);
  border: 1px solid rgba(17, 24, 39, 0.08);
  padding: 36px 30px;
  position: relative;
  z-index: 1;
}

.login-header {
  text-align: center;
  margin-bottom: 26px;
}

.login-badge {
  display: inline-block;
  background: rgba(59, 130, 246, 0.1);
  color: #1d4ed8;
  padding: 6px 14px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 700;
  margin-bottom: 14px;
}

.login-header h1 {
  font-size: 24px;
  color: #111827;
  margin: 0 0 10px;
  letter-spacing: 0.2px;
}

.login-header p {
  margin: 0;
  color: #6b7280;
  font-size: 14px;
}

.notice {
  margin-bottom: 14px;
  padding: 10px 12px;
  border-radius: 10px;
  font-size: 13px;
  font-weight: 600;
}

.notice.is-error {
  color: #b91c1c;
  background: #fee2e2;
  border: 1px solid #fecaca;
}

.notice.is-success {
  color: #065f46;
  background: #d1fae5;
  border: 1px solid #a7f3d0;
}

.form-group {
  margin-bottom: 16px;
}

.form-group label {
  display: block;
  margin-bottom: 8px;
  color: #111827;
  font-weight: 700;
  font-size: 13px;
}

.text-input {
  width: 100%;
  height: 44px;
  border-radius: 10px;
  border: 1px solid rgba(17, 24, 39, 0.18);
  padding: 0 12px;
  font-size: 14px;
  color: #111827;
  background: rgba(255, 255, 255, 0.92);
  outline: none;
  transition: border-color 0.18s, box-shadow 0.18s;
  box-sizing: border-box;
}

.text-input:focus {
  border-color: rgba(59, 130, 246, 0.8);
  box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.14);
}

.btn-login {
  width: 100%;
  height: 44px;
  border: none;
  border-radius: 10px;
  background: linear-gradient(135deg, #2563eb 0%, #7c3aed 100%);
  color: #fff;
  font-size: 14px;
  font-weight: 800;
  cursor: pointer;
  transition: transform 0.15s, filter 0.15s;
}

.btn-passkey {
  width: 100%;
  height: 42px;
  margin-top: 10px;
  border-radius: 10px;
  border: 1px solid rgba(17, 24, 39, 0.14);
  background: #f8fafc;
  color: #0f172a;
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
}

.btn-passkey:hover:not(:disabled) {
  background: #f1f5f9;
}

.social-login-area {
  margin-top: 14px;
}

.divider {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 14px 0 10px;
  color: #64748b;
  font-size: 12px;
  font-weight: 700;
}

.divider::before,
.divider::after {
  content: '';
  height: 1px;
  flex: 1;
  background: rgba(17, 24, 39, 0.12);
}

.btn-passkey:disabled,
.btn-login:disabled,
.btn-ghost:disabled,
.captcha-refresh:disabled {
  cursor: not-allowed;
  opacity: 0.72;
}

.btn-login:hover:not(:disabled) {
  transform: translateY(-1px);
  filter: brightness(1.02);
}

.action-links {
  margin-top: 14px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  flex-wrap: wrap;
}

.link-btn {
  border: none;
  background: none;
  color: #2563eb;
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
  padding: 0;
}

.link-btn:hover {
  text-decoration: underline;
}

.register-row {
  margin-top: 16px;
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 8px;
  color: #6b7280;
  font-size: 13px;
}

.captcha-row {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
}

.captcha-input {
  flex: 1;
  min-width: 0;
}

.captcha-img {
  height: 44px;
  border: 1px solid rgba(17, 24, 39, 0.14);
  border-radius: 8px;
  cursor: pointer;
  user-select: none;
}

.captcha-refresh {
  height: 42px;
  padding: 0 12px;
  border: 1px solid rgba(17, 24, 39, 0.14);
  border-radius: 10px;
  background: #f8fafc;
  color: #111827;
  font-size: 13px;
  cursor: pointer;
}

.captcha-refresh:hover {
  background: #f1f5f9;
}

.modal-mask {
  position: fixed;
  inset: 0;
  background: rgba(15, 23, 42, 0.45);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  z-index: 20;
}

.modal-card {
  width: min(560px, 96vw);
  border-radius: 14px;
  background: #fff;
  border: 1px solid rgba(17, 24, 39, 0.08);
  box-shadow: 0 16px 42px rgba(15, 23, 42, 0.28);
  padding: 16px;
}

.modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.modal-head h3 {
  margin: 0;
  font-size: 16px;
  font-weight: 800;
  color: #0f172a;
}

.modal-close {
  height: 30px;
  padding: 0 10px;
  border-radius: 8px;
  border: 1px solid rgba(17, 24, 39, 0.16);
  background: #fff;
  color: #334155;
  cursor: pointer;
}

.modal-tip {
  margin: 12px 0;
  padding: 10px;
  border-radius: 10px;
  background: #eff6ff;
  border: 1px solid #bfdbfe;
  color: #1e3a8a;
  font-size: 13px;
}

.modal-tip.warn {
  background: #fffbeb;
  border-color: #fde68a;
  color: #92400e;
}

.modal-tip.error {
  background: #fef2f2;
  border-color: #fecaca;
  color: #991b1b;
}

.modal-actions {
  margin-top: 14px;
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}

.btn-ghost {
  min-width: 86px;
  height: 40px;
  border-radius: 10px;
  border: 1px solid rgba(17, 24, 39, 0.2);
  background: #fff;
  color: #334155;
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
}

@media (max-width: 480px) {
  .login-page {
    align-items: flex-start;
    padding: 16px 10px 10px;
  }

  .login-container {
    max-width: 100%;
    padding: 26px 18px;
    border-radius: 14px;
  }

  .login-header h1 {
    font-size: 22px;
  }

  .captcha-img {
    height: 42px;
  }

  .captcha-refresh {
    height: 40px;
    padding: 0 10px;
  }

  .modal-card {
    padding: 14px;
  }
}
</style>
