<script setup>
import { ref, computed, onUnmounted } from "vue";
import { useRouter } from "vue-router";
import axios from "axios";

import {
  GRS_API_KEY,
  EXAM_API_URL,
  EXAM_MODEL,
  EXAM_TEMPERATURE,
  EXAM_MAX_TOKENS,
  IMAGE_MAX_SIZE_MB,
  IMAGE_ALLOWED_TYPES_EXAM,
} from "@/config/index.js";

const router = useRouter();

const API_KEY = GRS_API_KEY;
const API_URL = EXAM_API_URL;

const SYSTEM_PROMPT = `你是一位专业的英语教师，请对以下英语试题进行深度解析。
请严格按照以下格式输出，每个部分用对应标题开头：

## 题干理解
（分析题目类型、题干含义、关键信息点）

## 考点识别
（识别本题考查的语法点、词汇点、语言技能等核心考点）

## 解题思路
（详细说明解题步骤和方法，帮助学生理解解题过程，思路讲解需符合中学生思维逻辑）

## 正确答案
（给出正确答案，如有选项请标明选项字母）

## 详细解析
（对答案进行深入解释，分析干扰项，总结规律和技巧）`;

// ── 状态 ──
const activeTab = ref("text"); // 'text' | 'image'
const status = ref("idle"); // 'idle' | 'analyzing' | 'done' | 'error'
const textInput = ref("");
const imageFile = ref(null); // { dataUrl, base64 }
const isDragOver = ref(false);
const fileInputRef = ref(null);
const errorMsg = ref("");
const rawContent = ref("");
const cancelTokenSource = ref(null);

// ── 五维度定义 ──
const DIMENSIONS = [
  { key: "understanding", title: "题干理解", icon: "book" },
  { key: "keypoints", title: "考点识别", icon: "target" },
  { key: "approach", title: "解题思路", icon: "lightbulb" },
  { key: "answer", title: "正确答案", icon: "check" },
  { key: "explanation", title: "详细解析", icon: "detail" },
];

// ── 解析流式内容为五个维度块 ──
const analysisBlocks = computed(() => {
  const raw = rawContent.value;
  const result = {};
  const titles = ["题干理解", "考点识别", "解题思路", "正确答案", "详细解析"];
  const keys = [
    "understanding",
    "keypoints",
    "approach",
    "answer",
    "explanation",
  ];

  titles.forEach((title, i) => {
    const startReg = new RegExp(`##\\s*${title}`);
    const nextTitles = titles
      .slice(i + 1)
      .map((t) => `##\\s*${t}`)
      .join("|");
    const endReg = nextTitles ? new RegExp(nextTitles) : null;

    const startMatch = raw.match(startReg);
    if (!startMatch) {
      result[keys[i]] = "";
      return;
    }

    const startIdx = startMatch.index + startMatch[0].length;
    let endIdx = raw.length;
    if (endReg) {
      const endMatch = raw.slice(startIdx).match(endReg);
      if (endMatch) endIdx = startIdx + endMatch.index;
    }
    result[keys[i]] = raw.slice(startIdx, endIdx).trim();
  });
  return result;
});

// 当前正在生成的维度（最后一个有内容的）
const activeBlockKey = computed(() => {
  if (status.value !== "analyzing") return null;
  const keys = [
    "understanding",
    "keypoints",
    "approach",
    "answer",
    "explanation",
  ];
  let last = null;
  for (const k of keys) {
    if (analysisBlocks.value[k] !== undefined && analysisBlocks.value[k] !== "")
      last = k;
  }
  return last;
});

// ── 图片处理 ──
const readFileAsBase64 = (file) =>
  new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = (e) => {
      const dataUrl = e.target.result;
      resolve({ dataUrl, base64: dataUrl.split(",")[1] });
    };
    reader.onerror = reject;
    reader.readAsDataURL(file);
  });

const handleFile = async (file) => {
  if (!file) return;
  const allowed = IMAGE_ALLOWED_TYPES_EXAM;
  if (!allowed.includes(file.type)) {
    errorMsg.value = "仅支持 JPG / PNG / WebP 格式的图片";
    return;
  }
  if (file.size > IMAGE_MAX_SIZE_MB * 1024 * 1024) {
    errorMsg.value = `图片大小不能超过 ${IMAGE_MAX_SIZE_MB}MB`;
    return;
  }
  errorMsg.value = "";
  const { dataUrl, base64 } = await readFileAsBase64(file);
  imageFile.value = { dataUrl, base64, mimeType: file.type };
};

const onFileChange = (e) => handleFile(e.target.files[0]);
const onDrop = (e) => {
  isDragOver.value = false;
  handleFile(e.dataTransfer.files[0]);
};
const resetImage = () => {
  imageFile.value = null;
  if (fileInputRef.value) fileInputRef.value.value = "";
};

// ── 构建请求体（OpenAI 兼容格式）──
const buildRequestBody = (imageBase64 = null, mimeType = "image/jpeg") => {
  const userContent = [];

  if (activeTab.value === "text") {
    userContent.push({ type: "text", text: textInput.value });
  } else {
    userContent.push({ type: "text", text: "请分析图片中的英语试题" });
    if (imageBase64) {
      userContent.push({
        type: "image_url",
        image_url: { url: `data:${mimeType};base64,${imageBase64}` },
      });
    }
  }

  return {
    model: EXAM_MODEL,
    messages: [
      { role: "system", content: SYSTEM_PROMPT },
      { role: "user", content: userContent },
    ],
    temperature: EXAM_TEMPERATURE,
    max_tokens: EXAM_MAX_TOKENS,
    stream: true,
  };
};

// ── 提取 SSE delta 文本（OpenAI 兼容格式）──
const extractTextDelta = (chunk) => {
  let result = "";
  const lines = chunk.split("\n");
  for (const line of lines) {
    if (!line.startsWith("data: ")) continue;
    const jsonStr = line.slice(6).trim();
    if (!jsonStr || jsonStr === "[DONE]") continue;
    try {
      const data = JSON.parse(jsonStr);
      const text = data?.choices?.[0]?.delta?.content;
      if (text) result += text;
    } catch {
      // 忽略解析失败的行
    }
  }
  return result;
};

// ── 过滤 <think>...</think> 思考块 ──
const filterThinkBlocks = (text) => {
  // 移除完整的 think 块
  let filtered = text.replace(/<think>[\s\S]*?<\/think>/g, "");
  // 如果 think 块还未闭合（流式中），移除从 <think> 开始到末尾的内容
  filtered = filtered.replace(/<think>[\s\S]*$/, "");
  return filtered;
};

// ── 开始分析 ──
const canAnalyze = computed(() => {
  if (status.value === "analyzing") return false;
  if (activeTab.value === "text") return textInput.value.trim().length > 0;
  return imageFile.value !== null;
});

const startAnalysis = async () => {
  if (!canAnalyze.value) return;
  status.value = "analyzing";
  errorMsg.value = "";
  rawContent.value = "";

  const imageBase64 =
    activeTab.value === "image" ? imageFile.value?.base64 : null;
  const mimeType =
    activeTab.value === "image" ? imageFile.value?.mimeType : "image/jpeg";

  cancelTokenSource.value = axios.CancelToken.source();
  let lastLength = 0;

  await axios
    .post(API_URL, buildRequestBody(imageBase64, mimeType), {
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${API_KEY}`,
      },
      responseType: "text",
      cancelToken: cancelTokenSource.value.token,
      onDownloadProgress: (event) => {
        const text = (event.target || event.event?.target)?.responseText || "";
        if (!text) return;
        const newChunk = text.slice(lastLength);
        lastLength = text.length;
        const delta = extractTextDelta(newChunk);
        if (delta) {
          rawContent.value = filterThinkBlocks(rawContent.value + delta);
        }
      },
    })
    .then(() => {
      status.value = "done";
    })
    .catch((err) => {
      if (axios.isCancel(err)) return;
      console.error(err);
      errorMsg.value =
        err?.response?.data?.error?.message ||
        err.message ||
        "请求失败，请稍后重试";
      status.value = "error";
    });
};

const resetAll = () => {
  status.value = "idle";
  rawContent.value = "";
  errorMsg.value = "";
};

// ── 基础 Markdown 渲染（加粗、换行）──
const renderContent = (text) => {
  return text
    .replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>")
    .replace(/\n/g, "<br>");
};

const goBack = () => router.back();

onUnmounted(() => {
  if (cancelTokenSource.value)
    cancelTokenSource.value.cancel("组件卸载，取消请求");
});
</script>

<template>
  <div class="page-container">
    <!-- Nav Bar -->
    <div class="nav-bar">
      <button class="back-btn" @click="goBack">
        <svg
          width="20"
          height="20"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2.2"
          stroke-linecap="round"
          stroke-linejoin="round"
        >
          <polyline points="15 18 9 12 15 6" />
        </svg>
        <span>返回</span>
      </button>
      <h1 class="nav-title">英语试题 AI 分析</h1>
      <div style="width: 72px"></div>
    </div>

    <!-- Error Banner -->
    <div v-if="errorMsg" class="error-banner">
      <span>{{ errorMsg }}</span>
      <button class="error-close" @click="errorMsg = ''">✕</button>
    </div>

    <!-- Main Layout: Left + Right -->
    <div class="main-layout">
      <!-- Left Panel: Input -->
      <div class="left-panel">
        <!-- Input Card -->
        <div class="input-card">
          <!-- Tabs -->
          <div class="tabs">
            <button
              class="tab-btn"
              :class="{ active: activeTab === 'text' }"
              @click="activeTab = 'text'"
            >
              <svg
                width="16"
                height="16"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
              >
                <path
                  d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"
                />
                <polyline points="14 2 14 8 20 8" />
                <line x1="16" y1="13" x2="8" y2="13" />
                <line x1="16" y1="17" x2="8" y2="17" />
                <polyline points="10 9 9 9 8 9" />
              </svg>
              文本输入
            </button>
            <button
              class="tab-btn"
              :class="{ active: activeTab === 'image' }"
              @click="activeTab = 'image'"
            >
              <svg
                width="16"
                height="16"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
              >
                <rect x="3" y="3" width="18" height="18" rx="3" />
                <circle cx="8.5" cy="8.5" r="1.5" />
                <polyline points="21 15 16 10 5 21" />
              </svg>
              图片上传
            </button>
          </div>

          <!-- Text Input -->
          <div v-if="activeTab === 'text'" class="tab-content">
            <div class="text-input-header">
              <button
                class="example-btn"
                @click="
                  textInput =
                    'A ______ is a small, clever animal with a red coat.\nA：fox\nB：foxes\nC：fox\'s\nD：foxes\''
                "
              >
                示例试题
              </button>
            </div>
            <textarea
              v-model="textInput"
              class="text-area"
              placeholder="请粘贴英语试题文本，例如：&#10;&#10;Choose the best answer:&#10;The teacher asked the students _____ they had finished their homework.&#10;A. that  B. if  C. what  D. whether"
              :disabled="status === 'analyzing'"
            ></textarea>
            <div class="char-count">{{ textInput.length }} 字符</div>
          </div>

          <!-- Image Upload -->
          <div v-else class="tab-content">
            <div
              v-if="!imageFile"
              class="upload-zone"
              :class="{ 'drag-over': isDragOver }"
              @click="fileInputRef.click()"
              @dragover.prevent="isDragOver = true"
              @dragleave.prevent="isDragOver = false"
              @drop.prevent="onDrop"
            >
              <input
                ref="fileInputRef"
                type="file"
                accept="image/jpeg,image/jpg,image/png,image/webp"
                style="display: none"
                @change="onFileChange"
              />
              <div class="upload-icon-wrap">
                <svg
                  width="36"
                  height="36"
                  viewBox="0 0 24 24"
                  fill="none"
                  stroke="currentColor"
                  stroke-width="1.5"
                  stroke-linecap="round"
                  stroke-linejoin="round"
                >
                  <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
                  <polyline points="17 8 12 3 7 8" />
                  <line x1="12" y1="3" x2="12" y2="15" />
                </svg>
              </div>
              <p class="upload-title">点击或拖拽上传试题图片</p>
              <p class="upload-hint">支持 JPG、PNG、WebP，最大 10MB</p>
            </div>
            <div v-else class="image-preview-wrap">
              <div class="image-preview-header">
                <span class="preview-label">已上传图片</span>
                <button
                  class="reupload-btn"
                  @click="resetImage"
                  :disabled="status === 'analyzing'"
                >
                  重新上传
                </button>
              </div>
              <img :src="imageFile.dataUrl" class="preview-img" />
            </div>
          </div>
        </div>

        <!-- Analyze Button -->
        <div class="btn-wrap">
          <button
            class="analyze-btn"
            :disabled="!canAnalyze"
            @click="startAnalysis"
          >
            <span v-if="status === 'analyzing'" class="btn-spinner"></span>
            <svg
              v-else
              width="20"
              height="20"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
            >
              <circle cx="11" cy="11" r="8" />
              <path d="m21 21-4.35-4.35" />
            </svg>
            <span>{{
              status === "analyzing" ? "分析中..." : "AI 深度分析"
            }}</span>
          </button>
          <button
            v-if="status === 'done' || status === 'error'"
            class="reset-btn"
            @click="resetAll"
          >
            重新分析
          </button>
        </div>
      </div>

      <!-- Right Panel: Result -->
      <div class="right-panel">
        <!-- Empty State -->
        <div v-if="status === 'idle'" class="empty-state">
          <div class="empty-icon">
            <svg
              width="48"
              height="48"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="1.2"
              stroke-linecap="round"
              stroke-linejoin="round"
            >
              <circle cx="11" cy="11" r="8" />
              <path d="m21 21-4.35-4.35" />
            </svg>
          </div>
          <p class="empty-title">等待分析</p>
          <p class="empty-hint">在左侧输入试题内容，点击「AI 深度分析」开始</p>
        </div>

        <!-- Result Area -->
        <div
          v-else-if="status === 'analyzing' || status === 'done'"
          class="result-area"
        >
          <div class="result-header">
            <div class="result-title-wrap">
              <div
                class="result-dot"
                :class="{ pulse: status === 'analyzing' }"
              ></div>
              <span class="result-title">{{
                status === "analyzing" ? "AI 正在分析..." : "分析完成"
              }}</span>
            </div>
          </div>

          <div
            v-for="(dim, idx) in DIMENSIONS"
            :key="dim.key"
            class="dim-card"
            :class="{
              'is-active': activeBlockKey === dim.key,
              'has-content': analysisBlocks[dim.key],
            }"
            :style="{ animationDelay: `${idx * 0.08}s` }"
          >
            <div class="dim-header">
              <!-- Book icon -->
              <svg
                v-if="dim.icon === 'book'"
                class="dim-icon"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
              >
                <path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20" />
                <path
                  d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"
                />
              </svg>
              <!-- Target icon -->
              <svg
                v-else-if="dim.icon === 'target'"
                class="dim-icon"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
              >
                <circle cx="12" cy="12" r="10" />
                <circle cx="12" cy="12" r="6" />
                <circle cx="12" cy="12" r="2" />
              </svg>
              <!-- Lightbulb icon -->
              <svg
                v-else-if="dim.icon === 'lightbulb'"
                class="dim-icon"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
              >
                <line x1="9" y1="18" x2="15" y2="18" />
                <line x1="10" y1="22" x2="14" y2="22" />
                <path
                  d="M15.09 14c.18-.98.65-1.74 1.41-2.5A4.65 4.65 0 0 0 18 8 6 6 0 0 0 6 8c0 1 .23 2.23 1.5 3.5A4.61 4.61 0 0 1 8.91 14"
                />
              </svg>
              <!-- Check icon -->
              <svg
                v-else-if="dim.icon === 'check'"
                class="dim-icon"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
              >
                <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" />
                <polyline points="22 4 12 14.01 9 11.01" />
              </svg>
              <!-- Detail icon -->
              <svg
                v-else
                class="dim-icon"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
              >
                <circle cx="12" cy="12" r="10" />
                <line x1="12" y1="8" x2="12" y2="12" />
                <line x1="12" y1="16" x2="12.01" y2="16" />
              </svg>
              <span class="dim-title">{{ dim.title }}</span>
            </div>
            <div class="dim-body">
              <div
                v-if="analysisBlocks[dim.key]"
                class="dim-content"
                v-html="renderContent(analysisBlocks[dim.key])"
              ></div>
              <div v-else-if="status === 'analyzing'" class="dim-placeholder">
                <span class="placeholder-dot"></span>
                <span class="placeholder-dot"></span>
                <span class="placeholder-dot"></span>
              </div>
              <span v-if="activeBlockKey === dim.key" class="cursor-blink"
                >|</span
              >
            </div>
          </div>
        </div>

        <!-- Error State -->
        <div v-else-if="status === 'error'" class="empty-state error-state">
          <div class="empty-icon error-icon">
            <svg
              width="48"
              height="48"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="1.2"
              stroke-linecap="round"
              stroke-linejoin="round"
            >
              <circle cx="12" cy="12" r="10" />
              <line x1="12" y1="8" x2="12" y2="12" />
              <line x1="12" y1="16" x2="12.01" y2="16" />
            </svg>
          </div>
          <p class="empty-title">分析失败</p>
          <p class="empty-hint">请检查网络连接后重试</p>
        </div>
      </div>
    </div>
  </div>
</template>

<style scoped>
.page-container {
  height: 100vh;
  overflow: hidden;
  background: var(--bg);
  display: flex;
  flex-direction: column;
  font-family: "Inter", "PingFang SC", -apple-system, sans-serif;
}

/* Nav */
.nav-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1rem 1.5rem;
  background: rgba(255, 255, 255, 0.03);
  border-bottom: 1px solid var(--card-border);
  backdrop-filter: blur(12px);
  position: sticky;
  top: 0;
  z-index: 10;
}
.back-btn {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  background: none;
  border: none;
  color: var(--text-secondary);
  cursor: pointer;
  font-size: 0.95rem;
  padding: 0.4rem 0.75rem;
  border-radius: 8px;
  transition: all 0.2s;
}
.back-btn:hover {
  background: rgba(255, 255, 255, 0.07);
  color: var(--text-primary);
}
.nav-title {
  font-size: 1.2rem;
  font-weight: 600;
  margin: 0;
  background: linear-gradient(135deg, #14b8a6, #0d9488);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}

/* Main Layout */
.main-layout {
  flex: 1;
  min-height: 0;
  display: flex;
  overflow: hidden;
}

/* Left Panel */
.left-panel {
  width: 420px;
  min-width: 320px;
  max-width: 480px;
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding: 1.5rem;
  border-right: 1px solid var(--card-border);
  overflow-y: auto;
  flex-shrink: 0;
}

/* Right Panel */
.right-panel {
  flex: 1;
  overflow-y: auto;
  padding: 1.5rem;
  display: flex;
  flex-direction: column;
}

/* 自定义滚动条 */
.right-panel::-webkit-scrollbar {
  width: 8px;
}

.right-panel::-webkit-scrollbar-track {
  background: rgba(0, 0, 0, 0.1);
  border-radius: 4px;
  margin: 4px 0;
}

.right-panel::-webkit-scrollbar-thumb {
  background: rgba(20, 184, 166, 0.3);
  border-radius: 4px;
  transition: background 0.2s;
}

.right-panel::-webkit-scrollbar-thumb:hover {
  background: rgba(20, 184, 166, 0.5);
}

.right-panel::-webkit-scrollbar-thumb:active {
  background: rgba(20, 184, 166, 0.7);
}

/* Empty State */
.empty-state {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
  color: var(--text-secondary);
  opacity: 0.5;
}
.empty-icon {
  color: var(--text-secondary);
  opacity: 0.4;
}
.empty-title {
  font-size: 1rem;
  font-weight: 600;
  margin: 0;
  color: var(--text-primary);
}
.empty-hint {
  font-size: 0.875rem;
  margin: 0;
  text-align: center;
}
.error-state {
  opacity: 1;
}
.error-icon {
  color: #f87171;
  opacity: 1;
}
.error-state .empty-title {
  color: #fca5a5;
}

/* Error Banner */
.error-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: rgba(239, 68, 68, 0.12);
  border: 1px solid rgba(239, 68, 68, 0.3);
  border-bottom: 1px solid rgba(239, 68, 68, 0.3);
  padding: 0.75rem 1.5rem;
  color: #fca5a5;
  font-size: 0.9rem;
  animation: fadeInUp 0.2s ease;
  flex-shrink: 0;
}
.error-close {
  background: none;
  border: none;
  color: #fca5a5;
  cursor: pointer;
  font-size: 1rem;
  opacity: 0.7;
  transition: opacity 0.2s;
}
.error-close:hover {
  opacity: 1;
}

/* Input Card */
.input-card {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid var(--card-border);
  border-radius: 20px;
  overflow: hidden;
  backdrop-filter: blur(12px);
}

/* Tabs */
.tabs {
  display: flex;
  border-bottom: 1px solid var(--card-border);
}
.tab-btn {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 1rem;
  background: none;
  border: none;
  color: var(--text-secondary);
  font-size: 0.95rem;
  cursor: pointer;
  transition: all 0.2s;
  position: relative;
}
.tab-btn::after {
  content: "";
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 2px;
  background: linear-gradient(135deg, #14b8a6, #0d9488);
  transform: scaleX(0);
  transition: transform 0.25s;
}
.tab-btn.active {
  color: #14b8a6;
}
.tab-btn.active::after {
  transform: scaleX(1);
}
.tab-btn:hover:not(.active) {
  color: var(--text-primary);
  background: rgba(255, 255, 255, 0.03);
}

/* Tab Content */
.tab-content {
  padding: 1.25rem;
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.text-area {
  width: 100%;
  flex: 1;
  min-height: 260px;
  background: rgba(0, 0, 0, 0.2);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 12px;
  color: var(--text-primary);
  font-size: 0.95rem;
  line-height: 1.7;
  padding: 1rem;
  resize: none;
  outline: none;
  transition: border-color 0.2s, box-shadow 0.2s;
  box-sizing: border-box;
  font-family: inherit;
}
.text-area::placeholder {
  color: var(--text-secondary);
  opacity: 0.6;
}
.text-area:focus {
  border-color: rgba(20, 184, 166, 0.5);
  box-shadow: 0 0 0 3px rgba(20, 184, 166, 0.1);
}
.text-area:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.char-count {
  text-align: right;
  font-size: 0.8rem;
  color: var(--text-secondary);
  margin-top: 0.5rem;
  opacity: 0.6;
}

/* Text Input Header */
.text-input-header {
  display: flex;
  align-items: center;
  margin-bottom: 0.75rem;
}
.example-btn {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  background: rgba(20, 184, 166, 0.1);
  border: 1px solid rgba(20, 184, 166, 0.25);
  color: #14b8a6;
  border-radius: 8px;
  padding: 0.35rem 0.85rem;
  font-size: 0.85rem;
  cursor: pointer;
  transition: all 0.2s;
}
.example-btn:hover {
  background: rgba(20, 184, 166, 0.2);
  transform: translateY(-1px);
}

/* Upload Zone */
.upload-zone {
  border: 2px dashed rgba(20, 184, 166, 0.3);
  border-radius: 16px;
  padding: 3rem 2rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
  cursor: pointer;
  transition: all 0.25s;
  text-align: center;
}
.upload-zone:hover,
.upload-zone.drag-over {
  border-color: #14b8a6;
  background: rgba(20, 184, 166, 0.06);
  transform: translateY(-2px);
  box-shadow: 0 8px 32px rgba(20, 184, 166, 0.1);
}
.upload-icon-wrap {
  width: 72px;
  height: 72px;
  border-radius: 50%;
  background: rgba(20, 184, 166, 0.1);
  border: 1px solid rgba(20, 184, 166, 0.25);
  display: flex;
  align-items: center;
  justify-content: center;
  color: #14b8a6;
  transition: all 0.25s;
}
.upload-zone:hover .upload-icon-wrap,
.upload-zone.drag-over .upload-icon-wrap {
  background: rgba(20, 184, 166, 0.18);
  transform: scale(1.08);
}
.upload-title {
  font-size: 1rem;
  font-weight: 500;
  color: var(--text-primary);
  margin: 0;
}
.upload-hint {
  font-size: 0.85rem;
  color: var(--text-secondary);
  margin: 0;
}

/* Image Preview */
.image-preview-wrap {
  background: rgba(0, 0, 0, 0.15);
  border: 1px solid var(--card-border);
  border-radius: 12px;
  overflow: hidden;
}
.image-preview-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.75rem 1rem;
  border-bottom: 1px solid var(--card-border);
}
.preview-label {
  font-size: 0.875rem;
  color: var(--text-secondary);
}
.reupload-btn {
  background: rgba(20, 184, 166, 0.1);
  border: 1px solid rgba(20, 184, 166, 0.25);
  color: #14b8a6;
  border-radius: 8px;
  padding: 0.3rem 0.75rem;
  font-size: 0.85rem;
  cursor: pointer;
  transition: all 0.2s;
}
.reupload-btn:hover:not(:disabled) {
  background: rgba(20, 184, 166, 0.2);
}
.reupload-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
.preview-img {
  width: 100%;
  max-height: 360px;
  object-fit: contain;
  display: block;
  background: rgba(0, 0, 0, 0.2);
}

/* Analyze Button */
.btn-wrap {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1rem;
}
.analyze-btn {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  background: linear-gradient(135deg, #14b8a6, #0d9488);
  border: none;
  color: white;
  font-size: 1rem;
  font-weight: 600;
  padding: 0.875rem 2.5rem;
  border-radius: 50px;
  cursor: pointer;
  transition: all 0.25s;
  box-shadow: 0 4px 20px rgba(20, 184, 166, 0.35);
}
.analyze-btn:hover:not(:disabled) {
  transform: translateY(-2px);
  box-shadow: 0 8px 28px rgba(20, 184, 166, 0.5);
}
.analyze-btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  transform: none;
}
.reset-btn {
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.12);
  color: var(--text-secondary);
  font-size: 0.9rem;
  padding: 0.875rem 1.5rem;
  border-radius: 50px;
  cursor: pointer;
  transition: all 0.2s;
}
.reset-btn:hover {
  background: rgba(255, 255, 255, 0.1);
  color: var(--text-primary);
}
.btn-spinner {
  width: 18px;
  height: 18px;
  border: 2px solid rgba(255, 255, 255, 0.35);
  border-top-color: white;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

/* Result Area */
.result-area {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.result-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.result-title-wrap {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}
.result-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #14b8a6;
}
.result-dot.pulse {
  animation: pulse 1.2s ease-in-out infinite;
}
.result-title {
  font-size: 1rem;
  font-weight: 600;
  color: var(--text-primary);
}

/* Dimension Cards */
.dim-card {
  background: rgba(255, 255, 255, 0.02);
  border: 1px solid var(--card-border);
  border-radius: 16px;
  overflow: hidden;
  transition: border-color 0.3s, box-shadow 0.3s;
  animation: fadeInUp 0.4s ease both;
}
.dim-card.is-active {
  border-color: rgba(20, 184, 166, 0.4);
  box-shadow: 0 0 24px rgba(20, 184, 166, 0.08);
}
.dim-card.has-content {
  border-color: rgba(255, 255, 255, 0.1);
}
.dim-header {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.875rem 1.25rem;
  border-bottom: 1px solid var(--card-border);
  background: rgba(20, 184, 166, 0.04);
}
.dim-icon {
  width: 18px;
  height: 18px;
  color: #14b8a6;
  flex-shrink: 0;
}
.dim-title {
  font-size: 0.95rem;
  font-weight: 600;
  color: #14b8a6;
}
.dim-body {
  padding: 1rem 1.25rem;
  min-height: 48px;
  position: relative;
}
.dim-content {
  font-size: 0.95rem;
  color: var(--text-primary);
  line-height: 1.8;
}
.dim-content :deep(strong) {
  color: #14b8a6;
  font-weight: 600;
}

/* Placeholder dots */
.dim-placeholder {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0.5rem 0;
}
.placeholder-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: rgba(20, 184, 166, 0.4);
  animation: bounce 1.2s ease-in-out infinite;
}
.placeholder-dot:nth-child(2) {
  animation-delay: 0.2s;
}
.placeholder-dot:nth-child(3) {
  animation-delay: 0.4s;
}

/* Cursor blink */
.cursor-blink {
  display: inline-block;
  color: #14b8a6;
  font-weight: 300;
  animation: blink 0.8s step-end infinite;
  margin-left: 1px;
}

/* Animations */
@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}
@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translateY(12px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
@keyframes pulse {
  0%,
  100% {
    opacity: 1;
    transform: scale(1);
  }
  50% {
    opacity: 0.5;
    transform: scale(0.75);
  }
}
@keyframes bounce {
  0%,
  80%,
  100% {
    transform: translateY(0);
  }
  40% {
    transform: translateY(-6px);
  }
}
@keyframes blink {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0;
  }
}

@media (max-width: 768px) {
  .main-layout {
    flex-direction: column;
    height: auto;
    overflow: visible;
  }
  .left-panel {
    width: 100%;
    max-width: 100%;
    border-right: none;
    border-bottom: 1px solid var(--card-border);
    overflow-y: visible;
  }
  .right-panel {
    padding: 1.25rem 1rem;
  }
  .upload-zone {
    padding: 2rem 1rem;
  }
  .analyze-btn {
    padding: 0.875rem 1.75rem;
  }
}
</style>
