/* ============================================================
 * design-system.css — 设计系统层（令牌 + 应用外壳组件）
 * ------------------------------------------------------------
 * 视觉基调：中性现代风。大面积中性灰白承载内容，品牌红只作点缀
 *           （激活态、主按钮、关键强调），描边细、留白足、层级靠
 *           字重与间距而非色块堆叠。
 *
 * 加载顺序：styles.css（卡片契约） → design-system.css（本文件） → /api/themes.css（主题）
 *          本文件在后，因此对 body / :root 等共享选择器具有更高优先级。
 *
 * 红线：本文件**只负责应用外壳**，不含任何 .card / .card-* 规则。
 *       卡片是出图契约（PNG 恒 1080 宽，由 --card-w 360px × 3 倍采样），
 *       卡片样式归属 styles.css + themes/*.css，此处绝不覆盖。
 * ============================================================ */

/* ============================================================
 * 1. 设计令牌
 * ============================================================ */
:root {
  /* ---- 中性色阶（冷灰，主承载） ---- */
  --n-0:   #ffffff;
  --n-25:  #fcfcfd;
  --n-50:  #f7f8fa;
  --n-75:  #f4f5f8;
  --n-100: #f0f2f5;
  --n-150: #e9ecf0;
  --n-200: #e2e5ea;   /* 常规描边 */
  --n-300: #d2d7de;   /* 强描边 / hover */
  --n-400: #a8afb8;   /* 占位符 / 禁用文字 */
  --n-500: #78808a;   /* 次级文字 */
  --n-600: #575e67;
  --n-700: #3d434a;
  --n-800: #262a30;
  --n-900: #14171b;   /* 主文字 */

  /* ---- 语义别名（兼容既有代码与各页内联样式引用） ---- */
  --bg-app:        var(--n-50);
  --bg-panel:      var(--n-0);
  --bg-soft:       var(--n-100);
  --bg-hover:      var(--n-75);
  --border:        var(--n-200);
  --border-strong: var(--n-300);
  --text:          var(--n-900);
  --text-soft:     var(--n-500);
  --text-mute:     var(--n-400);

  /* ---- 品牌色（点缀用，不铺面） ---- */
  --brand:        #ff2e4d;
  --brand-strong: #e11d3c;
  --brand-soft:   #fff1f3;
  --brand-line:   #ffd6dc;
  --brand-ring:   rgba(255, 46, 77, .20);
  --btn-hover-bg: #f7f8fa;          /* 旧变量兼容（原为 #fff5f7 粉色） */
  --btn-hover-border: var(--n-300);
  --btn-hover-fg: var(--n-900);

  /* ---- 语义色 ---- */
  --ok:          #12855a;  --ok-soft:     #e8f6ef;
  --warn:        #b7791f;  --warn-soft:   #fdf6e7;
  --danger:      #d0342c;  --danger-soft: #fdeceb;

  /* ---- 字体 ---- */
  --font-ui: "Noto Sans SC", "Noto Color Emoji", "PingFang SC", "Microsoft YaHei", "微软雅黑", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --font-mono: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace, "Noto Sans SC";

  /* ---- 字号阶梯 ---- */
  --fs-11: 11px;
  --fs-12: 12px;
  --fs-13: 13px;
  --fs-14: 14px;
  --fs-16: 16px;
  --fs-20: 20px;
  --fs-26: 26px;

  /* ---- 间距阶梯（4 的倍数） ---- */
  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
  --sp-5: 20px; --sp-6: 24px; --sp-8: 32px; --sp-10: 40px;

  /* ---- 圆角 ---- */
  --r-xs: 4px; --r-sm: 6px; --r-md: 10px; --r-lg: 14px; --r-pill: 999px;

  /* ---- 阴影（克制，靠描边与留白分层） ---- */
  --sh-1: 0 1px 2px rgba(16, 20, 28, .05);
  --sh-2: 0 2px 8px rgba(16, 20, 28, .06);
  --sh-3: 0 12px 32px rgba(16, 20, 28, .12);

  /* ---- 交互 ---- */
  --t-fast: 150ms cubic-bezier(.4, 0, .2, 1);
  --ring: 0 0 0 3px var(--brand-ring);
  --ctl-h: 32px;        /* 控件统一高度 */
  --ctl-h-sm: 28px;

  /* ---- 三栏骨架（桌面基准，媒体查询内收窄） ---- */
  --pane-input: 320px;
  --pane-copy:  380px;
  --row-title:  52px;   /* 三栏标题行统一高度 → 内容区自然对齐 */
  --row-tools:  48px;   /* 三栏工具行统一高度 */
}

/* ============================================================
 * 2. 基础
 * ============================================================ */
html, body { height: 100%; }

body {
  font-family: var(--font-ui);
  background: var(--bg-app);
  color: var(--text);
  display: flex;
  flex-direction: column;
  height: 100vh;
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* 文档型页面（我的 API / 开发者文档）：允许整页滚动，不锁定视口。
   三栏工作台（首页）才需要 100vh + 内部滚动。 */
body.doc-page { display: block; height: auto; min-height: 100vh; overflow: auto; }

/* ---- 外壳自洽基线（不要删） ----
   本文件会被「我的 API / 开发者文档」单独引入，而这两个页面**不加载 styles.css**
   的全局 reset（`* { margin: 0; padding: 0 }`）。所以本文件涉及的外壳元素必须自己
   把浏览器默认外边距归零，否则 <h1>/<p> 会带上 UA 默认 margin，把页头撑高
   （实测：我的 API 页头 64px vs 首页 56px）。放在组件规则之前，便于后面的
   组件规则按需覆盖。 */
.app-header h1, .app-header p, .app-header nav,
.empty-state h1, .empty-state h2, .empty-state h3, .empty-state p,
.modal-box h1, .modal-box h2, .modal-box h3, .modal-box p { margin: 0; }

/* 无障碍：键盘焦点必须可见（WCAG AA） */
:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  * { transition-duration: .01ms !important; animation-duration: .01ms !important; }
}

/* 视觉隐藏但可被读屏器读取 */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ============================================================
 * 3. 页头（首页 / 设置页共用一套）
 * ============================================================ */
.app-header {
  flex: 0 0 auto;
  min-height: 56px;
  padding: 0 var(--sp-4);
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  background: var(--n-0);
  border-bottom: 1px solid var(--border);
  position: relative;
  z-index: 20;
}

.brand { display: flex; align-items: center; gap: 10px; min-width: 0; flex: 0 0 auto; }

.brand .logo {
  width: 30px; height: 30px;
  flex: 0 0 auto;
  border-radius: 9px;
  display: flex; align-items: center; justify-content: center;
  background: var(--brand-soft);
  border: 1px solid var(--brand-line);
  font-size: 15px; line-height: 1;
}

.brand-text { min-width: 0; }
.brand-text h1 {
  font-size: 14.5px; font-weight: 700; line-height: 1.25;
  letter-spacing: -.01em; white-space: nowrap;
}
.brand-text p {
  font-size: var(--fs-11); color: var(--text-soft); line-height: 1.3;
  margin-top: 1px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* 页头不再放置 logo 旁的入口按钮（.app-nav 已移除）。
   各功能页（我的设置 / 我的 API / 开发者文档）直达网址即可，
   页头只保留品牌 + 右侧用户区，避免入口分散注意力。 */

.header-actions { display: flex; align-items: center; gap: var(--sp-2); margin-left: auto; flex: 0 0 auto; }
.user-area { display: flex; align-items: center; gap: var(--sp-2); }

/* 头像入口 + 悬停竖排菜单 */
.avatar-wrap { position: relative; display: inline-flex; align-items: center; }
.avatar-btn {
  width: 34px; height: 34px; border-radius: 50%; padding: 0;
  border: 1px solid var(--border); background: var(--n-50);
  cursor: pointer; overflow: hidden;
  display: flex; align-items: center; justify-content: center;
  transition: border-color var(--t-fast), box-shadow var(--t-fast);
}
.avatar-btn:hover { border-color: var(--n-300); box-shadow: var(--sh-1); }
.avatar-btn .avatar-img { width: 100%; height: 100%; object-fit: cover; display: block; }
.avatar-btn .avatar-default {
  width: 100%; height: 100%;
  display: flex; align-items: center; justify-content: center;
  font-size: 15px; font-weight: 700; color: var(--brand); background: #fff;
}
.avatar-menu {
  position: absolute; top: calc(100% + 8px); right: 0; min-width: 176px;
  background: var(--n-0); border: 1px solid var(--border); border-radius: var(--r-md);
  box-shadow: var(--sh-3); padding: 6px; z-index: 60;
  display: none; flex-direction: column; gap: 2px;
}
.avatar-wrap:hover .avatar-menu, .avatar-menu.open { display: flex; }
.avatar-menu-item {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  padding: 8px 10px; border: none; background: transparent; border-radius: var(--r-sm);
  font-family: inherit; font-size: var(--fs-13); color: var(--text);
  cursor: pointer; text-decoration: none; white-space: nowrap;
  transition: background var(--t-fast), color var(--t-fast);
}
.avatar-menu-item:hover { background: var(--n-75); color: var(--text); }
.avatar-menu-item.danger:hover { background: var(--danger-soft); color: var(--danger); }
.avatar-menu-sep { height: 1px; background: var(--border); margin: 4px 2px; }
.avatar-menu-points { padding: 6px 10px; font-size: var(--fs-12); color: var(--text-soft); }
.avatar-menu-points b { color: var(--brand); font-size: var(--fs-13); font-weight: 700; }

/* ============================================================
 * 4. 按钮体系
 * ============================================================ */
.btn {
  height: var(--ctl-h);
  padding: 0 14px;
  border-radius: var(--r-sm);
  border: 1px solid var(--border);
  background: var(--n-0);
  color: var(--text);
  font-family: inherit;
  font-size: var(--fs-13);
  font-weight: 500;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--t-fast), border-color var(--t-fast), color var(--t-fast), box-shadow var(--t-fast);
}
.btn:hover  { background: var(--n-50); border-color: var(--n-300); }
.btn:active { background: var(--n-100); }

.btn.ghost { background: var(--n-0); }
.btn.ghost:hover { background: var(--n-50); border-color: var(--n-300); }

.btn.primary {
  background: var(--brand); border-color: var(--brand); color: #fff;
  font-weight: 600; box-shadow: var(--sh-1);
}
.btn.primary:hover  { background: var(--brand-strong); border-color: var(--brand-strong); color: #fff; }
.btn.primary:active { background: var(--brand-strong); }

.btn.sm { height: var(--ctl-h-sm); padding: 0 10px; font-size: var(--fs-12); }
.btn.sm.icon { width: var(--ctl-h-sm); padding: 0; font-size: var(--fs-13); line-height: 1; }
.btn.block { width: 100%; }

.btn:disabled { opacity: .5; cursor: not-allowed; box-shadow: none; }
.btn:disabled:hover { background: var(--n-0); border-color: var(--border); color: var(--text); }
.btn.primary:disabled:hover { background: var(--brand); border-color: var(--brand); color: #fff; }

/* ============================================================
 * 5. 工作区骨架（输入 / 预览 / 文案）
 * ============================================================ */
.workspace {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  position: relative;          /* 三栏宽度拖拽手柄的定位上下文 */
  /* 三栏宽度：拖拽过就用 app.js 写的内联值 --pane-*-cur，否则回落到响应式阶梯。
     内联变量优先级高于媒体查询，所以"没拖过"时 --pane-input / --pane-copy
     的阶梯照常生效，拖拽值只在用户主动调过之后接管。
     图片区（中间列）恒为剩余空间；「图片区 ≥ 360px」由 app.js 的 clampDrag()
     每帧钳制左右两栏来实现 —— 故意不写成 minmax(360px, 1fr)：那样在
     窗口被缩到极窄时会直接产生横向溢出，比"钳不住"更糟。 */
  grid-template-columns:
    var(--pane-input-cur, var(--pane-input))
    minmax(0, 1fr)
    var(--pane-copy-cur, var(--pane-copy));
}

.workspace > section {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  background: var(--n-0);
}
.workspace > section + section { border-left: 1px solid var(--border); }

/* ---- 三栏宽度拖拽手柄（仅三栏档 ≥1024px 可见） ----
   手柄是 .workspace 的绝对定位子项，不占栅格；位置用纯 CSS 吸附在分隔线上
   （左柄贴输入栏右缘、右柄贴文案栏左缘），因此拖拽时 JS 只改两个宽度变量，
   完全不需要计算坐标、也不需要读布局。
   命中区 9px（中线左右各 4.5px）是经验值：再宽会吃掉 textarea 右边缘的
   滚动条点击区，再窄则不好抓。默认完全透明，只有 hover / 聚焦 / 拖拽中
   才点亮 2px 品牌色中线 —— 不拖的时候视觉上等于不存在。 */
.workspace-resizer {
  position: absolute;
  top: 0; bottom: 0;
  width: 9px;
  z-index: 6;
  display: none;
  cursor: col-resize;
  touch-action: none;          /* 触屏横向拖拽不触发页面滚动 */
  background: transparent;
  border: 0;
  padding: 0;
}
/* 握把：默认只在分隔线中部露出一小段浅灰（24px），像一个可抓的把手 ——
   完全透明的话用户根本不会发现这里能拖；hover / 聚焦 / 拖拽中再拉通整条并点亮品牌色。 */
.workspace-resizer::after {
  content: '';
  position: absolute;
  top: 50%; left: 50%;
  width: 2px; height: 24px;
  transform: translate(-50%, -50%);
  border-radius: 1px;
  background: var(--n-300);
  transition: height .15s, background .15s;
}
.workspace-resizer:hover::after,
.workspace-resizer:focus-visible::after,
.workspace-resizer.is-dragging::after { height: 100%; background: var(--brand); }
.workspace-resizer:focus-visible { outline: none; }
#rzLeft  { left:  calc(var(--pane-input-cur, var(--pane-input)) - 4.5px); }
#rzRight { right: calc(var(--pane-copy-cur,  var(--pane-copy))  - 4.5px); }

@media (min-width: 1024px) { .workspace-resizer { display: block; } }

/* 拖拽中：全局锁定横向光标 + 禁止选中（否则横拖会连带选中 textarea 内容） */
body.is-resizing,
body.is-resizing * { cursor: col-resize !important; }
body.is-resizing { user-select: none; -webkit-user-select: none; }

/* ---- 标题行：三栏统一高度，内容区起点自然对齐 ---- */
.pane-title {
  flex: 0 0 auto;
  min-height: var(--row-title);
  padding: 8px var(--sp-4);
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex-wrap: wrap;
  border-bottom: 1px solid var(--border);
}
/* 标题主体：允许收缩（窄栏时提示文字优雅截断），保证标题行永远单行、三栏等高。
   ⚠️ flex-basis 必须是 0，不能是 auto：auto 会让换行判定用"内容全宽"，
   于是提示文字那 60~70px 会把整行挤到第二行 —— 视口 >1119px 时提示还在
   （≤1119 才被下面的媒体查询隐藏），一旦输入栏被拖窄到约 260px，
   标题行就从 52px 涨到 72px，三栏等高契约当场被拖破。
   basis 归 0 后换行只按「标题文字 + 按钮」的真实最小宽判定，
   提示在窄列里被压缩/省略号，宽列照常完整显示 —— 与视口和列宽都解耦。 */
.title-main {
  display: flex; align-items: baseline; gap: 8px;
  flex: 1 1 0; min-width: 0; white-space: nowrap;
}
.pane-title-text { font-size: var(--fs-13); font-weight: 600; color: var(--text); flex: 0 0 auto; }
.pane-title .hint {
  font-size: var(--fs-11); font-weight: 400; color: var(--text-soft);
  flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis;
}
/* 文案栏标题行要同时放下「AI 优化 / 复制文案 / 📋 复制标题」三个文字按钮。
   提示「可直接编辑」会把它挤到第二行（1440 栏宽 380px 时就需要 372px > 可用 348px），
   而这条信息在正文 placeholder（「…可直接编辑…」）里已经写了，隐藏不丢信息。 */
.xhs-pane .pane-title .hint { display: none; }

/* 标题行右侧控件：始终靠右；空间不足换行时也保持右对齐（不再出现孤悬左侧） */
.title-controls {
  display: flex; align-items: center; gap: 10px;
  flex: 1 1 auto; min-width: 0;
  justify-content: flex-end;
}

/* ---- 工具行：三栏统一高度 ---- */
.pane-tools {
  flex: 0 0 auto;
  min-height: var(--row-tools);
  padding: 8px var(--sp-4);
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  background: var(--n-25);
  border-bottom: 1px solid var(--border);
}

/* 行内小标签 */
.field-inline {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: var(--fs-12); color: var(--text-soft); white-space: nowrap;
}

/* ============================================================
 * 6. 分段控件（比例 3:4 / 3:5 / 1:1）
 * ============================================================ */
.ratio-group {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 2px;
  background: var(--n-100);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  flex: 0 0 auto;
  overflow: visible;
}
.ratio-group button {
  border: 0;
  height: 26px;
  padding: 0 10px;
  border-radius: var(--r-xs);
  background: transparent;
  color: var(--text-soft);
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 500;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--t-fast), color var(--t-fast), box-shadow var(--t-fast);
}
.ratio-group button + button { border-left: 0; }
.ratio-group button:hover { background: rgba(255, 255, 255, .75); color: var(--text); }
.ratio-group button.active {
  background: var(--n-0); color: var(--brand); font-weight: 600; box-shadow: var(--sh-1);
}

/* 工具行内的比例/字号：比例控件默认高 32px（2 内边距 + 26 按钮 + 2 边框），
   而工具行内容区只有 31px（48 − 上下各 8 内边距 − 1 下边框）→ 会把工具行撑到 49px，
   与左右两栏差 1px、破坏三栏等高契约。把内边距收到 1px 即为 30px。
   字号块加 flex:0 0 auto，避免被主题 rail 挤压后文字被裁。 */
.pane-tools .ratio-group { padding: 1px; }
.pane-tools .field-inline { flex: 0 0 auto; }

/* 工具行的"组"：每组 = 描述文字 + 控件，是一个**不可分割的整体**。
   组内 nowrap，换行只发生在组与组之间 —— 否则页面变窄时会出现
   「主题」二字留在上一行、主题条被挤到下一行（描述文字与控件分家）。
   组的直接子项：.tool-label（描述文字）/ 控件 / 数值。 */
.pane-tools .tool-group {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  min-width: 0;
  white-space: nowrap;
}
.pane-tools .tool-label { flex: 0 0 auto; }
/* 主题组吃掉整行剩余空间，让主题条尽可能长；换行时整组（标签+条）一起走 */
.pane-tools .tool-group-theme { flex: 1 1 0; min-width: 0; }
.pane-tools .tool-group-theme .theme-rail { flex: 1 1 0; min-width: 0; }
/* 字号组：桌面档滑块固定宽；手机档会拉满整行（见 §11 响应式阶梯） */
.pane-tools .font-group { gap: 6px; }
.pane-tools .font-group #fontRange { flex: 0 0 auto; }

/* ============================================================
 * 7. 字号滑块
 * ============================================================ */
#fontRange {
  -webkit-appearance: none;
  appearance: none;
  width: 92px;
  height: 3px;
  border-radius: var(--r-pill);
  background: var(--n-200);
  outline: none;
  cursor: pointer;
  vertical-align: middle;
}
#fontRange::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 14px; height: 14px;
  border-radius: 50%;
  background: #fff;
  border: 2px solid var(--brand);
  box-shadow: var(--sh-1);
  cursor: pointer;
}
#fontRange::-moz-range-thumb {
  width: 12px; height: 12px; border-radius: 50%;
  background: #fff; border: 2px solid var(--brand);
  cursor: pointer;
}
.font-val {
  font-size: 12.5px; color: var(--text); min-width: 40px;
  text-align: right; font-variant-numeric: tabular-nums;
}

/* 字号步进按钮（− / +）：只在手机档出现。
   触屏上拖动 range 的精度很差，一步 5% 的点按比拖动更可控，
   且与滑块配合能覆盖"微调"和"大范围"两种用法。
   桌面档隐藏：横向空间紧张，且鼠标拖动本身足够精确。 */
.font-step {
  display: none;
  width: 26px; height: 26px;
  align-items: center; justify-content: center;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--r-xs);
  background: var(--n-0);
  color: var(--text);
  font-family: inherit;
  font-size: 15px;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
  -webkit-user-select: none; user-select: none;
  touch-action: manipulation;
  transition: background var(--t-fast), border-color var(--t-fast);
}
.font-step:hover { background: var(--n-50); border-color: var(--n-300); }
.font-step:active { background: var(--n-100); }

/* ============================================================
 * 8. 主题选择器（横向滚动单行 + 两端箭头）
 * ------------------------------------------------------------
 * 14 个主题原本铺 3 行吃掉 102px 纵向空间，现压成一行 28px，
 * 省出的高度全部让给卡片预览。
 * ============================================================ */
.theme-rail {
  position: relative;
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 1 1 auto;
  min-width: 0;
}
.rail-scroll {
  display: flex;
  gap: 6px;
  flex: 1 1 auto;
  min-width: 0;
  overflow-x: auto;
  overflow-y: hidden;
  /* 垂直内边距只有 1px：按钮本身 28px（--ctl-h-sm）+ 上下各 1px = 30px，
     恰好放进 --row-tools 48px（48 − 16 内边距 − 1 下边框 = 31px 可容纳）。
     这里若写 2px，中栏工具行会被撑到 49px，与左右两栏差 1px、破坏三栏对齐。 */
  padding: 1px 2px;
  scroll-behavior: smooth;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.rail-scroll::-webkit-scrollbar { display: none; }

.rail-btn {
  flex: 0 0 auto;
  width: 24px; height: 24px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--n-0);
  color: var(--text-soft);
  font-size: var(--fs-12);
  line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer;
  transition: background var(--t-fast), border-color var(--t-fast), color var(--t-fast), opacity var(--t-fast);
}
.rail-btn:hover:not([disabled]) { background: var(--n-50); border-color: var(--n-300); color: var(--text); }
.rail-btn[disabled] { opacity: .3; cursor: default; }

.theme-group { display: flex; flex: 0 0 auto; gap: 6px; }
.theme-group button {
  flex: 0 0 auto;
  height: var(--ctl-h-sm);
  padding: 0 12px;
  border-radius: var(--r-pill);
  border: 1px solid var(--border);
  background: var(--n-0);
  color: var(--text-soft);
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer;
  transition: background var(--t-fast), border-color var(--t-fast), color var(--t-fast);
}
.theme-group button:hover { background: var(--n-50); border-color: var(--n-300); color: var(--text); }
/* 激活态用品牌色「淡底 + 同色字」，避免大面积红块压迫卡片预览 */
.theme-group button.active {
  background: var(--brand-soft);
  border-color: var(--brand-line);
  color: var(--brand);
  font-weight: 600;
}

/* ============================================================
 * 9. 输入区
 * ============================================================ */
.et-import-row { display: flex; align-items: center; gap: var(--sp-2); flex: 1 1 auto; min-width: 0; }

#mdImportUrl,
.ui-input {
  flex: 1 1 auto;
  min-width: 0;
  height: 30px;
  padding: 0 10px;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--n-0);
  color: var(--text);
  font-family: inherit;
  font-size: var(--fs-12);
  outline: none;
  transition: border-color var(--t-fast), box-shadow var(--t-fast);
}
#mdImportUrl:focus,
.ui-input:focus { border-color: var(--brand); box-shadow: var(--ring); }
#mdImportUrl::placeholder,
.ui-input::placeholder { color: var(--text-mute); }

.et-actions {
  display: flex; align-items: center; justify-content: flex-end;
  gap: var(--sp-2); flex: 0 0 auto;
}
.et-tip { font-size: var(--fs-11); color: var(--text-mute); white-space: nowrap; }

#mdInput {
  flex: 1 1 auto;
  min-height: 0;
  border: 0;
  resize: none;
  padding: var(--sp-4);
  font-family: var(--font-mono);
  font-size: 13px;
  line-height: 1.72;
  color: var(--text);
  background: var(--n-0);
  outline: none;
  tab-size: 2;
}
#mdInput::placeholder { color: var(--text-mute); }

/* ============================================================
 * 10. 卡片预览区（舞台）
 * ------------------------------------------------------------
 * ⚠️ 卡片宽度由 styles.css 的 --card-w（360px）决定，是出图契约：
 *    导出按 1080 / 360 = 3 倍采样，改宽度会让预览排版与导出图不一致。
 *    因此预览区变宽后**不放大卡片**，而是：≥792px 可用宽时自动并排
 *    两列，让一屏能看到更多张。
 * ============================================================ */
.preview-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  /* 左右内边距随图片区宽度自适应：可用宽 ≥ 392px（= 卡片 360 + 左右各 16）时
     维持 16px；被拖窄进 360–392px 区间时线性收到 0。
     这正是「图片区最低 360px」的落地方式 —— 图片区 360px 时内边距为 0，
     360px 的卡片刚好完整落位，不会裁切、也不会缩放（缩放会破坏出图契约）。
     百分比 padding 基于包含块（.preview-pane）宽度，而该宽度不受自身 padding
     影响，所以这里不存在循环依赖。 */
  padding: var(--sp-6) clamp(0px, calc((100% - var(--card-w)) / 2), var(--sp-4));
  display: grid;
  grid-template-columns: repeat(auto-fit, 360px);
  justify-content: center;
  justify-items: center;
  align-content: start;
  gap: var(--sp-6);
  background: var(--n-100);
}

/* ============================================================
 * 11. 空态
 * ============================================================ */
.empty-state {
  grid-column: 1 / -1;
  width: 100%;
  max-width: 420px;
  margin: var(--sp-8) auto;
  padding: var(--sp-8) var(--sp-6);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  text-align: center;
  background: rgba(255, 255, 255, .72);
  border: 1px dashed var(--n-300);
  border-radius: var(--r-lg);
}
.empty-state .es-icon {
  width: 40px; height: 40px;
  border-radius: var(--r-md);
  background: var(--n-0);
  border: 1px solid var(--border);
  display: flex; align-items: center; justify-content: center;
  color: var(--text-mute);
  font-size: 17px;
  margin-bottom: 2px;
}
.empty-state h3 { font-size: 13.5px; font-weight: 600; color: var(--text); }
.empty-state p  { font-size: 12.5px; line-height: 1.65; color: var(--text-soft); margin: 0; }
.empty-state .btn { margin-top: var(--sp-2); }
/* 兼容旧类名（若仍有残留引用） */
.empty-tip { grid-column: 1 / -1; text-align: center; color: var(--text-soft); font-size: var(--fs-13); padding: var(--sp-8) 0; }

/* ============================================================
 * 12. 小红书文案区
 * ============================================================ */
/* ---- 标题行：输入框独占整行 ----
   小红书标题上限 20 字，20 个中文字在 12.5px 下约需 250px。旧版把「标题」标签、
   字符计数、「复制标题」按钮全塞在同一行（吃掉 158px），输入框只剩 169px 可用
   （≈13 字），第 14 字起就被截断（实测 20 字溢出 101px）。
   现改为：① 输入框吃满整行；② 计数以浮层贴在框内右端（预留 44px，不占行内宽度）；
   ③ 「复制标题」上移到面板标题行，与「AI 优化 / 复制文案」并排。
   实测可用文字宽：1440 下 293px（23 字）、1280 下 273px（22 字），20 字有富余。 */
.title-row { display: flex; align-items: center; gap: var(--sp-2); flex: 1 1 auto; min-width: 0; }
.title-field { position: relative; flex: 1 1 auto; min-width: 0; display: flex; align-items: center; }

.xhs-title-input {
  flex: 1 1 auto;
  width: 100%;
  min-width: 0;
  height: 30px;
  padding: 0 44px 0 10px;      /* 右侧 44px 让位给框内计数浮层 */
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--n-0);
  color: var(--text);
  font-family: inherit;
  font-size: 12.5px;
  outline: none;
  transition: border-color var(--t-fast), box-shadow var(--t-fast);
}
.xhs-title-input:focus { border-color: var(--brand); box-shadow: var(--ring); }
.xhs-title-input::placeholder { color: var(--text-mute); }
.xhs-title-input.title-over,
.xhs-title-input.title-over:focus {
  border-color: var(--danger); background: var(--danger-soft); color: var(--danger);
}

/* 字符计数：绝对定位浮在输入框内右端，不参与行内宽度分配 */
.title-count {
  position: absolute; right: 10px; top: 50%;
  transform: translateY(-50%);
  pointer-events: none;
  font-size: var(--fs-11);
  color: var(--text-soft);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.title-count.title-over { color: var(--danger); font-weight: 700; }

.xhs-actions { display: flex; align-items: center; justify-content: flex-end; gap: 6px; flex: 0 0 auto; }

.xhs-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: var(--sp-5) var(--sp-5) var(--sp-8);
  background: var(--n-0);
  font-family: var(--font-ui);
  font-size: 14px;
  line-height: 1.9;
  color: var(--n-800);
}
.xhs-preview { outline: none; min-height: 60px; }
.xhs-preview.is-empty:before {
  content: attr(data-placeholder);
  color: var(--text-mute);
  pointer-events: none;
}
.xhs-line { margin: 0 0 4px; word-break: break-word; overflow-wrap: anywhere; }
.xhs-para { margin: 0 0 12px; word-break: break-word; overflow-wrap: anywhere; }
.xhs-para:last-child { margin-bottom: 0; }

/* ============================================================
 * 13. 移动端 Tab（< 1000px 时取代三栏）
 * ============================================================ */
.mobile-tabs {
  display: none;
  flex: 0 0 auto;
  gap: 4px;
  padding: 8px var(--sp-3);
  background: var(--n-0);
  border-bottom: 1px solid var(--border);
}
.mobile-tabs button {
  flex: 1 1 0;
  height: var(--ctl-h);
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: var(--n-100);
  color: var(--text-soft);
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 500;
  cursor: pointer;
  transition: background var(--t-fast), color var(--t-fast), box-shadow var(--t-fast);
}
.mobile-tabs button[aria-selected="true"] {
  background: var(--n-0);
  border-color: var(--border);
  color: var(--text);
  font-weight: 600;
  box-shadow: var(--sh-1);
}

/* ============================================================
 * 14. 弹窗 / Toast
 * ============================================================ */
.modal {
  position: fixed; inset: 0;
  background: rgba(20, 23, 27, .42);
  display: flex; align-items: center; justify-content: center;
  z-index: 9999; padding: var(--sp-6);
  animation: modalFade .15s ease-out;
}
.modal.hidden { display: none; }
.modal-box {
  position: relative;
  width: 100%; max-width: 380px;
  background: var(--n-0);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-3);
  padding: var(--sp-6) var(--sp-6) var(--sp-5);
  animation: modalZoom .18s ease-out;
}
.modal-box h3 {
  font-size: var(--fs-16); font-weight: 700;
  margin-bottom: var(--sp-3); text-align: center; color: var(--text);
}
.modal-close {
  position: absolute; top: 8px; right: 10px;
  width: 28px; height: 28px;
  border: 0; background: transparent;
  font-size: 20px; line-height: 1;
  color: var(--text-mute); cursor: pointer; border-radius: var(--r-sm);
  transition: background var(--t-fast), color var(--t-fast);
}
.modal-close:hover { background: var(--n-100); color: var(--text); }
.modal-box label {
  display: block; font-size: var(--fs-13); font-weight: 600;
  margin: 12px 0 6px; color: var(--text);
}
.modal-box input {
  width: 100%; height: 38px; padding: 0 12px;
  border: 1px solid var(--border); border-radius: var(--r-sm);
  font-family: inherit; font-size: var(--fs-14);
  background: var(--n-0); color: var(--text);
  outline: none;
  transition: border-color var(--t-fast), box-shadow var(--t-fast);
}
.modal-box input:focus { border-color: var(--brand); box-shadow: var(--ring); }
.auth-tabs {
  display: flex; gap: 3px;
  background: var(--n-100); border-radius: var(--r-sm);
  padding: 3px; margin-bottom: var(--sp-3);
}
.auth-tab {
  flex: 1 1 50%;
  padding: 7px 0;
  border: 0; background: transparent;
  font-family: inherit; font-size: var(--fs-13); font-weight: 600;
  color: var(--text-soft); cursor: pointer;
  border-radius: var(--r-xs);
  transition: background var(--t-fast), color var(--t-fast), box-shadow var(--t-fast);
}
.auth-tab:hover { color: var(--text); }
.auth-tab.active { background: var(--n-0); color: var(--brand); box-shadow: var(--sh-1); }
#inviteWrap { margin-top: 2px; }
#authSubmit { margin-top: var(--sp-4); }

@keyframes modalFade { from { opacity: 0; } to { opacity: 1; } }
@keyframes modalZoom { from { opacity: 0; transform: scale(.96); } to { opacity: 1; transform: scale(1); } }

.toast {
  position: fixed; left: 50%; bottom: 36px;
  transform: translateX(-50%) translateY(16px);
  background: var(--n-800);
  color: #fff;
  font-family: inherit;
  font-size: var(--fs-13);
  padding: 10px 18px;
  border-radius: var(--r-pill);
  box-shadow: var(--sh-3);
  opacity: 0; pointer-events: none;
  transition: opacity .2s, transform .2s;
  z-index: 10000;
  max-width: 80vw; text-align: center;
}
.toast.show { opacity: 1; pointer-events: auto; transform: translateX(-50%) translateY(0); }

.hidden { display: none !important; }
.msg { font-size: var(--fs-13); }
.msg.ok  { color: var(--ok); }
.msg.err { color: var(--danger); }

/* ============================================================
 * 15. 响应式阶梯
 * ------------------------------------------------------------
 * 三栏能成立的下限：输入+文案最小 560px，预览区需 ≥ 408px
 * （卡片 360 + 左右各 24 内边距）→ 1024px。低于此宽度改用 Tab
 * 切换单栏，避免卡片被压扁/裁切（旧版在 1024px 下三栏各 341px，
 * 可用宽 309px < 卡片 360px，卡片被裁）。
 * ============================================================ */
@media (max-width: 1439px) {
  :root { --pane-input: 300px; --pane-copy: 360px; }
}
@media (max-width: 1279px) {
  :root { --pane-input: 280px; --pane-copy: 320px; }
}
@media (max-width: 1119px) {
  :root { --pane-input: 260px; --pane-copy: 300px; }
  .brand-text p { display: none; }
}

/* 1024–1279 是三栏收窄档（文案栏收到 300 ~ 320px）。输入框让出 44px 计数浮层后，
   可用文字宽只剩 213 ~ 233px（17 ~ 18 字），装不下 20 字 → 该区间隐藏字符计数，
   让输入框吃满整行。超限提示由输入框自身的红色描边 + 底色承担，信息不丢失。
   ≥1280 宽度够用，计数照常显示；≤1023 已切单栏、宽度充裕，同样显示。 */
@media (min-width: 1024px) and (max-width: 1279px) {
  .title-count { display: none; }
  .xhs-title-input { padding-right: 10px; }
}

/* 1024–1119 是收窄档里的最窄一档（文案栏仅 300px）：即使去掉计数，可用宽仍差 3px
   （247px < 250px）。把输入框左右内边距同时收到 6px，可用 255px（≈20.4 字），
   20 字完整可见。 */
@media (min-width: 1024px) and (max-width: 1119px) {
  .xhs-title-input { padding-left: 6px; padding-right: 6px; }
}
@media (max-width: 1023px) {
  .mobile-tabs { display: flex; }

  .workspace { display: block; overflow: hidden; }
  .workspace > section { display: none; height: 100%; }
  .workspace > section.is-active { display: flex; }
  .workspace > section + section { border-left: 0; }

  .preview-scroll { grid-template-columns: minmax(0, 1fr); grid-column-gap: 0; }
}

@media (max-width: 640px) {
  .app-header { padding: 0 var(--sp-3); }
  .brand-text h1 { font-size: var(--fs-13); }
  /* 标题行按钮**恒右对齐**。原先这里是 justify-content: flex-start，
     导致 640 以下「载入示例 / 打包下载 / AI 优化…」全部贴在标题文字右侧、
     行尾留一大片空白（640 文案栏实测留白 442px）。
     .title-controls 本身是 flex:1 1 auto，改成 flex-end 后即使换行也仍贴右边缘。 */
  .title-controls { justify-content: flex-end; }
  .pane-title { padding: 8px var(--sp-3); }
  .pane-tools { padding: 8px var(--sp-3); }
  #mdInput { padding: var(--sp-3); }
  .xhs-scroll { padding: var(--sp-4); }

  /* 手机档「字号」：滑块独占一整行并拉满，同时露出 ± 步进按钮。
     触屏可拖长度 64px → 约 200–230px，拇指 14px → 22px，
     再配一步 5% 的点按，微调不必再靠拖。
     选择器多带一层 .pane-tools 提高特异性：否则会被后面 @media(max-width:1279px)
     里的 .pane-tools .tool-group{gap:5px} 覆盖掉这里的 gap。 */
  .preview-pane .pane-tools .font-group { flex: 1 1 100%; gap: 8px; }
  .preview-pane .pane-tools .font-group #fontRange {
    flex: 1 1 auto; width: auto; min-width: 90px; height: 6px;
  }
  .preview-pane .pane-tools .font-group #fontRange::-webkit-slider-thumb { width: 22px; height: 22px; }
  .preview-pane .pane-tools .font-group #fontRange::-moz-range-thumb { width: 20px; height: 20px; }
  .font-step { display: inline-flex; flex: 0 0 auto; }

  /* 手机上「比例」组同样独占整行，并让分段控件铺满：
     否则第一行只有一个 124px 的比例控件、右侧空 240px 很突兀。
     铺满后三个选项各约 110–195px，触控目标也更大。 */
  .preview-pane .pane-tools .tool-group:not(.font-group):not(.tool-group-theme) { flex: 1 1 100%; }
  .preview-pane .pane-tools .ratio-group { flex: 1 1 0; }
  .preview-pane .pane-tools .ratio-group button { flex: 1 1 0; }
}

/* ============================================================
 * 11. 标题行 / 工具行的响应式阶梯
 * ------------------------------------------------------------
 * 分工（两行各司其职，任何宽度都不换行）：
 *   标题行 = 页面标识 + 主操作「打包下载」
 *   工具行 = 设置项（比例 / 字号 / 主题）
 * 目标：≥1024 三栏并排时，三栏标题行恒 52px、工具行恒 48px（内容区起点严格对齐）；
 *      ≤1023 单栏 Tab 模式无等高约束，只保证不换行、不裁切。
 * ============================================================ */

/* 窄档（1024–1279 三栏收窄档，以及 ≤1023 单栏）：标题行按钮与字号控件同步收紧。
   文案栏在这两档只有 300–320px：三个文字按钮用正常尺寸需 234px + 标题主体 65px = 299px
   > 可用 288px → 会把标题行挤成两行（实测 76px）。收紧字号/内边距/间距后约 190px，
   合计 255px，稳定单行。 */
@media (max-width: 1279px) {
  /* 标题行主体与右侧控件之间的间距：12px → 8px，为 300px 的文案栏再挤出 4px */
  .pane-title { gap: 8px; }
  .pane-title .title-controls { gap: 4px; }

  /* 文案栏在 300–320px 档要一行放下三个文字按钮：字号收到 11px、内边距 6px 后
     合计约 182px，加标题主体 65px + 间距 8px = 255px，稳在 268–288px 可用宽之内
     （收到 11.5px/8px 时实测需 288.2px，1024 档可用仅 268px，仍会换行）。 */
  .xhs-pane .title-controls .btn.sm { font-size: var(--fs-11); padding: 0 6px; }

  /* 预览栏工具行要同处一行放下 比例 + 字号 + 主题（现在还多了「比例」标签）。
     1024 档是最紧的三栏档：内容区仅 431px。新增标签约需 29px，从这些地方让出：
       分段按钮内边距 8→6（省 12）、组间距 8→6（省 4）、组内 gap 6→5（省 2）、
       滑块 64→56（省 8）、数值 min-width 34→30（省 4）→ 合计约 30px。 */
  .pane-tools { gap: 6px; }
  .pane-tools .tool-group { gap: 5px; }
  .pane-tools .ratio-group button { padding: 0 6px; font-size: 12px; }
  .pane-tools .field-inline { font-size: 11.5px; }
  #fontRange { width: 56px; }
  .font-val { min-width: 30px; font-size: 11.5px; }
}

/* 三栏最窄档（1024–1279）：「字号」的数值改成**浮层**（叠在滑块右端上方），
   横向不再占位与占 gap，多出来的约 35px 全部给主题条
   （约 147px → 约 182px，可多显示约 1 个主题 chip）。
   垂直方向不撑高工具行：.font-group 居中后上方有 7px 余量，
   浮层再向上探入 .pane-tools 的 8px 上内边距，仍在 48px 行高之内。 */
@media (min-width: 1024px) and (max-width: 1279px) {
  .pane-tools .font-group { position: relative; }
  .pane-tools .font-group #fontVal {
    position: absolute;
    right: 0;
    bottom: 100%;
    min-width: 0;
    padding-bottom: 1px;
    font-size: 11px;
    line-height: 1;
    color: var(--text-soft);
    pointer-events: none;
  }
}

/* 1024–1119 是最窄的三栏档（编辑栏仅 260px）：标题行里的次要提示「## 即一张卡」
   会把它挤成两行（实测 244.3px > 可用 228px）。该提示在输入框 placeholder
   （「· ## 二级标题 会切成一张卡片」）里已有，隐藏不丢信息，隐藏后约 173px 稳在一行。
   注：.title-main 的 flex-basis 改成 0 之后，这行**已经不会**因为提示而换行了；
   这条规则现在只是"最窄档省点地方"的额外优化，留着无害。 */
@media (max-width: 1119px) {
  .editor-pane .pane-title .hint { display: none; }
}

/* 单栏模式（≤1023）：工具行允许换行。极窄屏下 比例+字号+主题 一行摆不开，
   让**整个主题组**（「主题」标签 + 主题条）落到第二行整行展开 ——
   比被压成一条缝可用得多，也不会再出现"标签留在上一行、主题条掉下去"。
   关键：换行单位现在是 .tool-group（不再是 rail 本身），min-width 要算上
   标签宽度（23px）与组内 gap（5px），并把 flex-basis 显式设为 0。
   默认 auto 时基准尺寸 = 内容宽度（14 个主题 chip ≈ 1000px），
   开了 wrap 后即使 900px 宽屏也会被判定"放不下"而独占一行。 */
@media (max-width: 1023px) {
  .preview-pane .pane-tools { flex-wrap: wrap; }
  .preview-pane .tool-group-theme { flex: 1 1 0; min-width: 228px; }
}
