/* 基础可复用组件样式，对应 Figma「组件」frame 里的 BUTTON / TAG / CARD / CHAT / CHART / TAB */

/* ---------- BUTTON ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-5);
  border-radius: var(--radius-md);
  font-size: var(--font-size-sm);
  font-weight: 500;
  border: none;
  cursor: pointer;
  transition: opacity 0.15s ease;
}
.btn:hover { opacity: 0.85; }
.btn:active { opacity: 0.7; }

.btn--primary {
  background: var(--color-text-title);
  color: #fff;
}
.btn--secondary {
  background: var(--color-bg-subtle);
  color: var(--color-text-secondary);
}
.btn--danger {
  background: var(--color-danger-bg);
  color: var(--color-danger);
}
.btn--ghost {
  background: transparent;
  color: var(--color-text-muted);
  border: 1px solid var(--color-border);
}
.btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
/* 用户反馈第13轮：主按钮（#1D2129 那个深色按钮）禁用态要单独调整成整体opacity 30% */
.btn--primary:disabled {
  opacity: 0.3;
}

/* ---------- TAG ---------- */
.tag {
  display: inline-flex;
  align-items: center;
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-pill);
  font-size: var(--font-size-xs);
  background: var(--color-bg-subtle);
  color: var(--color-text-secondary);
  border: 1px solid transparent;
  cursor: pointer;
  user-select: none;
}
.tag.is-active {
  background: var(--color-brand-bg-alt);
  color: var(--color-brand-strong);
  border-color: var(--color-brand);
}
.tag-group {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

/* ---------- CARD（文件/附件卡，三态：默认简历 / 已删除 / 本地附件） ---------- */
.file-card {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  border: 1px solid var(--color-border);
  background: var(--color-bg-panel);
  cursor: pointer;
  max-width: 320px;
}
.file-card__icon {
  width: 32px;
  height: 32px;
  border-radius: var(--radius-sm);
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-size: 14px;
}
.file-card__body {
  min-width: 0;
}
.file-card__name {
  font-size: var(--font-size-sm);
  color: var(--color-text-title);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.file-card__meta {
  font-size: var(--font-size-xs);
  color: var(--color-text-muted);
}

/* 状态=默认，文件类型=简历 */
.file-card--resume .file-card__icon { background: var(--color-brand); }

/* 状态=禁用 已删除 */
.file-card--deleted {
  opacity: 0.5;
  cursor: not-allowed;
}
.file-card--deleted .file-card__icon { background: var(--color-text-disabled); }
.file-card--deleted .file-card__name { text-decoration: line-through; }

/* 状态=默认，文件类型=本地附件（如：用户上传的原始简历PDF） */
.file-card--local .file-card__icon { background: var(--color-accent-blue); }

/* 用户反馈第25轮：对话模块外层不许滚动，滚动只发生在 #chat-thread 内部。
   之前 .module-view 是 overflow-y:auto，而输入框光晕（.chat-inputbar__glow，高 235%、
   向下溢出）会把 scrollHeight 顶出去 60 多px——在 iPad 上就表现为整块能被拖着晃。
   输入框本身在 flex 列的最后一格，天然常驻底部；离屏幕底 32px =
   .module-view 的 24px padding + .chat-inputbar-wrap 的 8px 下外边距，光晕跟着一起走。 */
#view-chat {
  overflow: hidden;
}

/* ---------- CHAT 气泡 ---------- */
/* 用户反馈第25/26轮：对话流里气泡和卡片的宽度要对齐——
   之前气泡是 70%（≈515px）、.qcard 是固定 480px，两种块对不齐。
   第25轮先统一成"正文列满宽"，第26轮用户定死为 515px：
   **卡片固定 515 宽**（不随内容缩），**气泡最多 515**（短消息仍然贴着文字走）。 */
.chat-msg {
  display: flex;
  margin-bottom: var(--space-4);
  max-width: var(--chat-block-max);
}
.chat-msg--ai {
  align-self: flex-start;
}
.chat-msg--user {
  align-self: flex-end;
  margin-left: auto;
}
.chat-msg__bubble {
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  font-size: var(--font-size-sm);
  line-height: 1.6;
}
.chat-msg--ai .chat-msg__bubble {
  background: var(--color-bg-panel);
  border: 1px solid var(--color-border);
  color: var(--color-text-title);
}

/* AI 气泡要给左侧的小光球（.ai-orb）留定位上下文；光球本身定位到气泡外面的空槽里，
   空槽是 #chat-thread 的 padding-left 撑出来的（见 chat.js），所以不会撑出横向滚动条 */
.chat-msg--ai {
  position: relative;
}

/* ---------- 气泡出现动效（第24轮） ----------
   用户气泡：发出去的东西，弹得明显一点，从右下角冒出来
   AI 气泡：外框只要"轻微"弹一下，主要戏份在里面的逐字显示上 */
.chat-msg--user .chat-msg__bubble {
  animation: bubblePopUser 0.34s cubic-bezier(0.34, 1.56, 0.64, 1) both;
  transform-origin: 100% 100%;
}
.chat-msg--ai .chat-msg__bubble {
  animation: bubblePopAI 0.26s cubic-bezier(0.34, 1.4, 0.64, 1) both;
  transform-origin: 0 100%;
}
@keyframes bubblePopUser {
  0%   { opacity: 0; transform: scale(0.7) translateY(8px); }
  55%  { opacity: 1; transform: scale(1.05) translateY(0); }
  75%  { transform: scale(0.98); }
  100% { opacity: 1; transform: scale(1); }
}
@keyframes bubblePopAI {
  0%   { opacity: 0; transform: scale(0.94) translateY(4px); }
  60%  { opacity: 1; transform: scale(1.02) translateY(0); }
  100% { opacity: 1; transform: scale(1); }
}

/* ---------- AI 逐字显示 ----------
   实现上不是"把文字一个个塞进 DOM"，而是**一开始就把整段文字放进去**、每个字包一层
   .tw-char 且 opacity:0，再逐个点亮。这样有两个好处：
   1) textContent 从第一帧起就是完整文案——测试、搜索、定位条取文案都不用等动画
   2) 气泡尺寸一开始就定死，文字是"在原地一个个浮现"，不会边打字边把布局往下顶 */
.tw-char {
  opacity: 0;
}
.tw-char.is-on {
  opacity: 1;
}

/* 系统开了"减弱动态效果"就全部退化成静态，iPad 上跟随系统设置 */
@media (prefers-reduced-motion: reduce) {
  .chat-msg__bubble,
  .decor-orb,
  .decor-orb::before,
  .decor-texture--scroll::before,
  .decor-texture--scroll::after,
  .ai-orb {
    animation: none !important;
  }
  .tw-char { opacity: 1 !important; }
}
.chat-msg--user .chat-msg__bubble {
  /* 用户反馈第13轮：用户发送的气泡背景改成 #F7F8FA（原来是深色块+白字） */
  background: #F7F8FA;
  color: var(--color-text-title);
}

/* ---------- CHART 诊断评分环 ---------- */
.score-ring {
  --ring-size: 96px;
  --ring-value: 0; /* 0-100，JS 动态设置 */
  width: var(--ring-size);
  height: var(--ring-size);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  flex-shrink: 0;
  background: conic-gradient(
    var(--color-brand) calc(var(--ring-value) * 1%),
    var(--color-bg-subtle) 0
  );
}
.score-ring::before {
  content: "";
  position: absolute;
  /* 用户指定：环变粗，inset 8px → 4px */
  inset: 4px;
  background: var(--color-bg-panel);
  border-radius: 50%;
  z-index: 1;
}
.score-ring__value {
  position: relative;
  z-index: 1;
  font-size: var(--font-size-lg);
  font-weight: 700;
  color: var(--color-brand-strong);
}
.score-ring__value {
  z-index: 2;
}
/* 加载态：分数显示 0（设计稿 Component 74 里那个 0 是 #C4C4C4），
   圆环是一圈**流动的灰色渐变**——静态参考 Component 74 的
   #FFFFFF → #DDDDDD(36%) → #F4F4F4(64%) → #EBEBEB(99%)，
   实际做成持续旋转，视觉上像加载骨架屏在"涌动"。
   旋转层单独放在 ::after 上（不是转整个元素），否则中间的数字会跟着一起转。 */
.score-ring.is-loading .score-ring__value {
  color: var(--color-text-disabled);
}
.score-ring.is-loading {
  background: none;
}
.score-ring.is-loading::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  border-radius: 50%;
  background: conic-gradient(
    from 0deg,
    #FFFFFF 0%,
    #DDDDDD 36%,
    #F4F4F4 64%,
    #EBEBEB 99%,
    #FFFFFF 100%
  );
  animation: ringFlow 1.1s linear infinite;
}
@keyframes ringFlow {
  to { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
  .score-ring.is-loading::after { animation: none; }
}

/* ---------- 诊断检查项（逐个出现 → 转圈 → 打勾） ---------- */
.diag-checks {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.diag-check {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--font-size-sm);
  color: var(--color-text-title);
  /* 还没轮到的项：不占位、不可见（用 height:0 而不是 display:none，
     这样出现时是"长出来"而不是整块跳一下） */
  opacity: 0;
  max-height: 0;
  overflow: hidden;
  transition: opacity 0.12s ease, max-height 0.12s ease;
}
.diag-check.is-loading,
.diag-check.is-done {
  opacity: 1;
  max-height: 28px;
}
.diag-check__mark {
  position: relative;
  width: 14px;
  height: 14px;
  flex-shrink: 0;
}
/* 加载态：品牌色的旋转缺口圆环 */
.diag-check.is-loading .diag-check__mark {
  border: 2px solid var(--color-brand-bg);
  border-top-color: var(--color-brand);
  border-radius: 50%;
  animation: diagSpin 0.5s linear infinite;
}
@keyframes diagSpin {
  to { transform: rotate(360deg); }
}
/* 完成态：品牌色对勾（用两条边旋转 45° 画出来，不额外引一个图标） */
.diag-check.is-done .diag-check__mark::after {
  content: "";
  position: absolute;
  left: 4px;
  top: 0;
  width: 5px;
  height: 10px;
  border: solid var(--color-brand);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
@media (prefers-reduced-motion: reduce) {
  .diag-check.is-loading .diag-check__mark { animation: none; }
}

/* ---------- TAB ---------- */
.tab-group {
  display: inline-flex;
  background: var(--color-bg-subtle);
  border-radius: var(--radius-pill);
  padding: 2px;
  gap: 2px;
}
.tab-item {
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-pill);
  font-size: var(--font-size-xs);
  color: var(--color-text-secondary);
  cursor: pointer;
  border: none;
  background: transparent;
}
.tab-item.is-active {
  background: var(--color-bg-panel);
  color: var(--color-text-title);
  font-weight: 600;
  box-shadow: var(--shadow-card);
}

/* ---------- 通用卡片容器 ---------- */
.panel {
  background: var(--color-bg-panel);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-xl);
  padding: var(--space-5);
}

/* ---------- 对话底部常驻输入框（设计稿 frame「输入框状态」node 4757:13403）----------
   设计稿是一张 851×134、圆角16、纯白、无描边无阴影的两行卡片：
     第一行文本输入，第二行 麦克风 / ＋上传附件 …… 发送按钮。
   卡片本身不做视觉分隔，靠背后那圈彩色光晕跟页面底色拉开关系。 */
.chat-inputbar-wrap {
  position: relative;
  flex-shrink: 0;
  width: 100%;
  max-width: 760px;
  margin: var(--space-4) auto var(--space-2);
}
/* 侧栏展开时主区域窄了 174px，760px 的输入框（外加两侧各溢出 52px 的光晕）会顶到
   右侧定位条上，所以这种情况下收到 85% */
#app:not(.is-sidebar-collapsed) .chat-inputbar-wrap {
  max-width: 646px;
}

/* 光晕：设计稿导出的切图（.decor-input-glow 在 css/decor.css 里内联为 data URI）。
   之前是用 4 个 CSS 模糊色块按 Figma 那 4 个高斯模糊矩形的参数复刻的，用户看下来
   "效果不好"——CSS 的 filter:blur 跟 Figma 的高斯模糊在边缘衰减和颜色混合上并不一致，
   叠 4 层之后差异被放大，所以改用切图。切图本身带透明通道，直接铺在卡片底下即可。
   inset 用负值让光晕从卡片四周溢出；pointer-events:none，纯装饰不挡点击。 */
.chat-inputbar__glow {
  position: absolute;
  left: 50%;
  top: 50%;
  /* 尺寸按设计稿量出来的比例给：光晕大约是卡片宽的 1.22 倍、高的 2 倍，
     卡片并不在光晕的几何中心而是偏下，所以竖直方向再往上偏一点。
     这里直接给宽高百分比（背景 100% 100% 拉伸），没有用 aspect-ratio 锁比例——
     切图本身是一团柔和的渐变，轻微拉伸看不出来，但锁死比例反而会让上下留白不够。 */
  /* 竖直偏移是算出来的，不是试出来的：切图里那条**完全不透明**的实心色条占了图高的
     30.3%~51.8%（用 alpha>=250 量的），设计稿里它是藏在卡片背后的。卡片高度占光晕高度的
     1/2.35≈42.5%，translateY 的百分比正好等于"卡片中心落在光晕高度的百分之几"，
     取 45% 时卡片覆盖 23.7%~66.3%，刚好把那条色条整个盖住，同时下方留出更多黄色扩散——
     跟设计稿一致。 */
  width: 130%;
  height: 235%;
  transform: translate(-50%, -45%);
  pointer-events: none;
  z-index: 0;
}

.chat-inputbar {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding: 20px 22px;
  background: var(--color-bg-panel);
  border-radius: var(--radius-xl);
}

.chat-inputbar__input {
  width: 100%;
  border: none;
  outline: none;
  background: transparent;
  font-size: var(--font-size-sm);
  font-family: inherit;
  color: var(--color-text-title);
  line-height: var(--line-height-normal);
}
.chat-inputbar__input::placeholder {
  color: var(--color-text-muted);
}

.chat-inputbar__tools {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

/* 麦克风：设计稿里是裸图标（20×20），没有圆形底和描边 */
.chat-mic-btn {
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  border: none;
  background: transparent;
  border-radius: var(--radius-md);
  color: var(--color-text-title);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.15s ease, color 0.15s ease;
}
.chat-mic-btn:hover {
  background: var(--color-bg-subtle);
}
.chat-mic-btn.is-listening {
  color: var(--color-brand);
  background: var(--color-brand-bg);
  animation: micPulse 1s ease-in-out infinite;
}
@keyframes micPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(247, 89, 64, 0.35); }
  50% { box-shadow: 0 0 0 6px rgba(247, 89, 64, 0); }
}

/* ＋上传附件：设计稿里的「＋」是两根 15×2 / 2×15 的圆角小方条，这里用伪元素画 */
.chat-upload-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-2);
  border: none;
  background: transparent;
  border-radius: var(--radius-md);
  color: var(--color-text-title);
  font-family: inherit;
  font-size: var(--font-size-sm);
  cursor: pointer;
  transition: background 0.15s ease;
}
.chat-upload-btn:hover {
  background: var(--color-bg-subtle);
}
.chat-upload-btn__plus {
  position: relative;
  width: 15px;
  height: 15px;
  flex-shrink: 0;
}
.chat-upload-btn__plus::before,
.chat-upload-btn__plus::after {
  content: "";
  position: absolute;
  background: currentColor;
  border-radius: var(--radius-pill);
}
.chat-upload-btn__plus::before {
  left: 0; top: 6.5px; width: 15px; height: 2px;
}
.chat-upload-btn__plus::after {
  left: 6.5px; top: 0; width: 2px; height: 15px;
}

/* 发送按钮：设计稿 92×40、圆角8、#1D2129 白字；
   禁用态不是换浅色，而是整个按钮 opacity 10%（Figma 里就是这么做的） */
.chat-send-btn {
  width: 92px;
  height: 40px;
  padding: 0;
  border-radius: var(--radius-md);
  flex-shrink: 0;
  transition: opacity 0.15s ease;
}
.chat-send-btn.is-empty {
  opacity: 0.1;
}

/* 问答卡片里的"支持语音输入"小字提示 */
.qcard__voice-hint {
  font-size: 11px;
  color: var(--color-text-disabled);
  margin-bottom: var(--space-2);
  display: flex;
  align-items: center;
  gap: 4px;
}

/* 时间选择器：仿 Ant Design 月份选择器交互——触发按钮 + 悬浮面板（年份切换 + 月份九宫格） */
.date-picker {
  display: inline-block;
}
.date-picker__trigger {
  min-width: 96px;
  padding: 7px 10px;
  border: 1px solid var(--color-border);
  border-radius: 6px;
  font-size: var(--font-size-xs);
  font-family: inherit;
  background: var(--color-bg-panel);
  color: var(--color-text-title);
  cursor: pointer;
  text-align: left;
}
.date-picker__trigger:hover {
  border-color: var(--color-brand);
}
.date-picker__trigger.is-placeholder {
  color: var(--color-text-muted);
}
.date-picker__panel {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  z-index: 30;
  width: 216px;
  background: var(--color-bg-panel);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-panel);
  padding: 10px;
}
.date-picker__panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 8px;
  font-size: var(--font-size-xs);
  font-weight: 600;
  color: var(--color-text-title);
}
.date-picker__panel-head button {
  border: none;
  background: var(--color-bg-subtle);
  width: 24px;
  height: 24px;
  border-radius: 50%;
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
  color: var(--color-text-secondary);
}
.date-picker__panel-head button:hover {
  background: var(--color-brand-bg-alt);
  color: var(--color-brand-strong);
}
.date-picker__month-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 6px;
}
.date-picker__month-grid button {
  padding: 6px 0;
  border: 1px solid transparent;
  background: var(--color-bg-subtle);
  border-radius: 6px;
  font-size: 12px;
  color: var(--color-text-secondary);
  cursor: pointer;
}
.date-picker__month-grid button:hover {
  border-color: var(--color-brand);
}
.date-picker__month-grid button.is-active {
  background: var(--color-brand-bg-alt);
  color: var(--color-brand-strong);
  border-color: var(--color-brand);
  font-weight: 600;
}
.date-picker__present-btn {
  width: 100%;
  margin-top: 8px;
  padding: 6px 0;
  border: 1px solid var(--color-border);
  border-radius: 6px;
  background: var(--color-bg-panel);
  font-size: 12px;
  color: var(--color-text-secondary);
  cursor: pointer;
}
.date-picker__present-btn.is-active {
  background: var(--color-brand-bg-alt);
  color: var(--color-brand-strong);
  border-color: var(--color-brand);
}
/* ---------- 结构化提问卡（单字段 / 表单） ---------- */
.qcard {
  background: var(--color-bg-panel);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  /* 固定宽度，不是上限——卡片内容再少也保持 515，跟气泡的右边界对齐；
     max-width:100% 只是兜底，防止窄屏下顶出正文列 */
  width: var(--chat-block-max);
  max-width: 100%;
  margin-bottom: var(--space-4);
}
.qcard__question {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
  color: var(--color-text-title);
}
.qcard__fields {
  display: flex;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}
.qcard__field {
  flex: 1;
}
.qcard__field label {
  display: block;
  font-size: var(--font-size-xs);
  color: var(--color-text-muted);
  margin-bottom: var(--space-1);
}
.qcard__field input {
  width: 100%;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  font-size: var(--font-size-sm);
  font-family: inherit;
}
.qcard__actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-2);
}

/* ==================== 快速定位条（Desktop-14 +「定位条交互说明」） ====================
   [FIGMA-精确] 设计稿里定位条不是贴着右边缘的一整条灰栏，而是一根**浮起来的白色圆角药丸**：
   21px 宽、圆角约 10、纯白，离右边缘还有一段距离，刻度带在药丸内部上下居中。
   之前做成了"28px 宽 + 左描边 + 浅灰底"的贴边栏，整个形态就不对。 */
.locator-rail {
  position: relative;
  flex-shrink: 0;
  width: 69px;              /* 药丸 21 + 两侧各 24 的留白，留白同时也是手指的滑动区 */
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: ns-resize;
}
.locator-rail__strip {
  width: 21px;
  max-height: calc(100% - 48px);
  padding: 8px 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 26px;                /* [FIGMA-精确] 刻度间距 28px（2px 高 + 26px 间隙） */
  overflow-y: auto;
  background: var(--color-bg-panel);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  scrollbar-width: none;
}
.locator-rail__strip::-webkit-scrollbar { display: none; }

/* 普通刻度：10×2 的 #D9D9D9 小横条 */
.locator-tick {
  width: 10px;
  height: 2px;
  flex-shrink: 0;
  border-radius: var(--radius-pill);
  background: var(--color-placeholder);
  cursor: pointer;
  transition: background 0.15s ease, height 0.15s ease;
}
.locator-tick:hover {
  background: var(--color-brand);
}
/* 当前所在的那一轮：10×6 的品牌色粗条（设计稿里只有一根） */
.locator-tick.is-current {
  height: 6px;
  border-radius: 2.5px;
  background: var(--color-brand);
}
/* 关键节点：17px 的深色 ph 实心图标（原来是 emoji + 圆形描边） */
.locator-tick--key {
  width: 17px;
  height: 17px;
  border-radius: 0;
  background: transparent;
  color: var(--color-text-title);
  display: flex;
  align-items: center;
  justify-content: center;
}
.locator-tick--key:hover {
  background: transparent;
  color: var(--color-brand);
}

/* ---------- 定位条浮层（Desktop-14） ---------- */
.locator-panel {
  display: none;
  position: absolute;
  /* 用户反馈第25轮：浮层上下居中于屏幕（原来是顶部对齐 top:0） */
  top: 50%;
  transform: translateY(-50%);
  right: 69px;
  width: 279px;
  max-height: 78vh;
  flex-direction: column;
  overflow: hidden;
  background: var(--color-bg-panel);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-panel);
  z-index: 20;
}
.locator-panel__head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 10px;
  flex-shrink: 0;
}
/* [FIGMA-精确] 胶囊 tab：选中＝深色实心白字；未选中＝白底 + #F0F0F0 描边 */
.locator-tab {
  width: 83px;
  height: 31px;
  flex-shrink: 0;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill);
  background: var(--color-bg-panel);
  color: var(--color-text-title);
  font-family: inherit;
  font-size: var(--font-size-xs);
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}
.locator-tab.is-active {
  background: var(--color-text-title);
  border-color: var(--color-text-title);
  color: var(--color-text-inverse);
}
/* 右上角 19×19、圆角5、深色描边的小方按钮，里面两个斜向小箭头 */
/* 用户反馈第25轮：右上角这颗按钮从"两个斜箭头（展开）"改成叉（关闭）。
   设计稿的 icon frame 里没有 X，图形是按 Phosphor 的画法补出来的（见 icons.js 的 "x"）。 */
.locator-panel__close {
  margin-left: auto;
  width: 19px;
  height: 19px;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 1px solid var(--color-text-title);
  border-radius: 5px;
  background: var(--color-bg-panel);
  color: var(--color-text-title);
  cursor: pointer;
}

.locator-panel__body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 6px 10px 12px;
}
.locator-panel__empty {
  padding: var(--space-3);
  color: var(--color-text-muted);
  font-size: var(--font-size-xs);
}

/* 一条记录：16px 圆头像 + 一行 12.5px 文字，没有时间戳 */
.locator-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 6px;
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: background 0.12s ease;
}
.locator-row:hover,
.locator-row.is-active {
  background: var(--color-bg-page);
}
/* 用户反馈第25轮：普通记录的头像改用「对话AI图标」那颗小光球（图在 decor.css 里，
   跟 .ai-orb 共用同一串 data URI）。用户自己发言的那一行是另一种头像，见 --user。 */
.locator-row__avatar {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--color-text-inverse);
}
/* 关键节点在列表里是方形深色图标块，不是圆头像 */
.locator-row__avatar--key {
  border-radius: var(--radius-xs);
  background-image: none;
  background-color: var(--color-text-title);
}
/* 用户发言/选择那一行：直接用顶栏那张用户头像（图在 decor.css 里，和 .decor-avatar
   共用同一串 data URI），跟 AI 那颗光球一眼区分开 */
.locator-row__avatar--user {
  background-color: transparent;
}
/* 用户那行整体缩进一点、颜色浅一点，读起来是"这一轮里我说了什么" */
.locator-row--user .locator-row__text {
  color: var(--color-text-secondary);
}
.locator-row--user {
  padding-left: 16px;
}
.locator-row__text {
  flex: 1;
  min-width: 0;
  font-size: var(--font-size-xs);
  color: var(--color-text-title);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* 一问一答共用的包裹容器（chat.js ensureRoundWrapper）：默认不带任何视觉样式，
   只有定位条联动高亮时才会显形，这样"问"和"答"才能作为一个整体一起被色块包住 */
.chat-round-group {
  display: flex;
  flex-direction: column;
  border-radius: var(--radius-md);
  transition: background-color 0.15s ease, padding 0.15s ease, margin 0.15s ease;
}

/* 定位条/浮层联动高亮：鼠标悬浮时的即时提示 + 点击定位跳转后的短暂闪烁。
   用户反馈第13轮：改成填充色块(#F9F9F9)把整轮"问+答"包进去，而不是给单个气泡描边，
   参考Desktop-14"定位条交互说明"——意思是这一问一答有因果关系，是关联在一起的 */
/* 用户反馈第15轮：对话流底色从纯白改成 --color-bg-page 之后，原来的浅灰高亮块在灰底上
   看不出来了，所以高亮反过来做——高亮态是"浮起来的白卡片+阴影"，对比度比之前更强 */
.locator-highlight-hover {
  background-color: var(--color-bg-panel);
  box-shadow: var(--shadow-card);
  border-radius: var(--radius-lg);
  padding: 8px 12px;
  margin: 0 -12px;
}
.locator-highlight-flash {
  animation: locatorFlash 1.6s ease;
  border-radius: var(--radius-lg);
  padding: 8px 12px;
  margin: 0 -12px;
}
@keyframes locatorFlash {
  0% { background-color: var(--color-bg-panel); box-shadow: var(--shadow-card); }
  100% { background-color: transparent; box-shadow: none; }
}

/* ---------- 产物关系图：画布缩放/拖动 + 复位按钮 + 小地图（Desktop-15/16/21） ---------- */
.canvas-viewport {
  touch-action: none;
  cursor: grab;
  -webkit-user-select: none;
  user-select: none;
}
.canvas-viewport:active {
  cursor: grabbing;
}
/* 复位按钮：贴在小地图右侧，底边跟小地图对齐。
   小地图是 left:16 / bottom:16 / 宽 198（见 buildMinimap），所以这里 left = 16+198+12 */
.canvas-reset-btn {
  position: absolute;
  left: 226px;
  bottom: 16px;
  z-index: 5;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  height: 44px;
  padding: 0 var(--space-4);
  background: var(--color-bg-panel);
  box-shadow: var(--shadow-card);
  font-size: var(--font-size-xs);
}
.canvas-minimap {
  position: absolute;
  left: 16px;
  bottom: 16px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-bg-panel);
  overflow: hidden;
  z-index: 4;
  box-shadow: var(--shadow-card);
}

/* 产物列表收起后的展开入口（Desktop-15 的 Group 14624）：
   43×44、圆角16、白底、一层很散的投影，里面是 ph:list-bullets-fill。
   设计稿里它浮在画布左上角（距画布左边 25px、顶栏下方 18px） */
.canvas-list-btn {
  position: absolute;
  left: 25px;
  top: 18px;
  z-index: 6;
  width: 44px;
  height: 44px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: var(--radius-xl);
  background: var(--color-bg-panel);
  color: var(--color-text-title);
  box-shadow: var(--shadow-panel);
  cursor: pointer;
  transition: background 0.15s ease;
}
.canvas-list-btn:hover {
  background: var(--color-bg-subtle);
}
.canvas-minimap__viewport {
  position: absolute;
  border: 1.5px solid var(--color-accent-blue);
  background: rgba(80, 121, 242, 0.12);
  border-radius: var(--radius-xs);
  pointer-events: none;
}

/* ==================== 职位推荐（Desktop-9） ==================== */

/* 左侧筛选面板：通顶白底 + 右侧分割线，不是一张带圆角描边的卡片 */
.job-filters {
  width: 232px;
  flex-shrink: 0;
  overflow-y: auto;
  padding: var(--space-4) var(--space-4) var(--space-6);
  background: var(--color-bg-panel);
  border-right: 1px solid var(--color-border);
}
.job-filters__label {
  font-size: var(--font-size-xs);
  color: var(--color-text-title);
  font-weight: 500;
  margin: var(--space-4) 0 var(--space-2);
}
.job-filters__label:first-child {
  margin-top: 0;
}
.job-filters__title {
  font-size: var(--font-size-sm);
  font-weight: 600;
  color: var(--color-text-title);
  margin: var(--space-5) 0 var(--space-3);
}
/* 设计稿里「重置」是一条满宽的深色按钮，不是普通次要按钮 */
.job-filters__reset {
  width: 100%;
  padding: var(--space-2) 0;
}

.job-select {
  width: 100%;
  padding: 9px 10px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-bg-panel);
  color: var(--color-text-title);
  font-family: inherit;
  font-size: var(--font-size-xs);
  cursor: pointer;
}

/* 筛选项 chip：设计稿里是圆角8的矩形两列排布，不是圆到底的 pill
   （pill 在这一页只用于顶部分类 tab） */
.job-chip-group {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-2);
}
.job-chip {
  padding: 9px 6px;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  background: var(--color-bg-subtle);
  color: var(--color-text-secondary);
  font-family: inherit;
  font-size: var(--font-size-xs);
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}
.job-chip:hover {
  background: var(--color-border-strong);
}
.job-chip.is-active {
  background: var(--color-brand-bg);
  color: var(--color-brand);
  border-color: var(--color-brand);
}

/* 顶部分类 tab：选中＝深色实心胶囊白字，未选中＝纯文字无底色 */
.job-tabs {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  flex-shrink: 0;
}
.job-tab {
  padding: 7px 16px;
  border: none;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--color-text-secondary);
  font-family: inherit;
  font-size: var(--font-size-xs);
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.15s ease, color 0.15s ease;
}
.job-tab:hover {
  background: var(--color-bg-subtle);
}
.job-tab.is-active {
  background: var(--color-text-title);
  color: var(--color-text-inverse);
  font-weight: 500;
}
.job-tab--more {
  color: var(--color-text-muted);
  letter-spacing: 1px;
}

/* 表格：横线+竖线组成的真正网格 */
.job-table {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  border-top: 1px solid var(--color-border);
}
/* 筛选面板收起时（Desktop-25）表格通宽，这里不需要额外规则——面板不渲染，flex 自然铺满 */
.job-table__head,
.job-table__row {
  display: grid;
  align-items: center;
}
.job-table__head > div,
.job-table__row > div {
  padding: 0 var(--space-3);
  border-right: 1px solid var(--color-border);
  min-width: 0;
}
.job-table__head > div:last-child,
.job-table__row > div:last-child {
  border-right: none;
}
.job-table__head {
  flex-shrink: 0;
  padding: 11px 0;
  color: var(--color-text-muted);
  font-size: var(--font-size-xs);
  border-bottom: 1px solid var(--color-border);
}
.job-table__body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}
.job-table__row {
  padding: 9px 0;
  border-bottom: 1px solid var(--color-border);
  font-size: var(--font-size-xs);
  color: var(--color-text-title);
  cursor: pointer;
  transition: background 0.12s ease;
}
.job-table__row:hover {
  background: var(--color-bg-subtle);
}
.job-cell--ellipsis {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* 公司规模标签：[FIGMA-精确] 38×20、圆角6、文字 #1D2129，底色按规模分三组 */
.job-size-tag {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 38px;
  height: 20px;
  padding: 0 6px;
  border-radius: var(--radius-sm);
  color: var(--color-text-title);
  font-size: var(--font-size-xs);
  white-space: nowrap;
}
.job-size-tag--warn { background: var(--color-warning-bg); }
.job-size-tag--ok { background: var(--color-success-bg); }
.job-size-tag--purple { background: var(--color-purple-bg); }

/* 职位单元格：小圆角方块公司标识 + 职位名/公司名两行 */
.job-title-cell {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
/* [FIGMA-精确] 19×19、圆角约4、实色底白字 */
.job-logo {
  width: 19px;
  height: 19px;
  flex-shrink: 0;
  border-radius: var(--radius-xs);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 600;
  color: var(--color-text-inverse);
}
/* 设计稿的职位列只有职位名一行（没有公司名第二行），所以行更矮、一屏能放下 18 条 */
.job-title-cell__name {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* 人岗匹配度：五个小菱形（设计稿不是五角星） */
.job-diamonds {
  display: flex;
  gap: 4px;
}
.job-diamond {
  width: 7px;
  height: 7px;
  transform: rotate(45deg);
  border-radius: 1px;
  background: var(--color-bg-subtle);
  border: 1px solid var(--color-border-strong);
}
.job-diamond.is-on {
  background: var(--color-accent-blue-50);
  border-color: var(--color-accent-blue-50);
}

/* 公司信用分：绿色短横条 + 数字 */
.job-credit {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-variant-numeric: tabular-nums;
}
.job-credit__bar {
  width: 22px;
  height: 3px;
  border-radius: var(--radius-pill);
  background: var(--color-success);
  flex-shrink: 0;
}

/* ==================== 经历库（Desktop-10） ==================== */

.lib-intro {
  position: relative;
  z-index: 1;
  max-width: 720px;
  margin-bottom: var(--space-4);
  font-size: var(--font-size-sm);
  line-height: var(--line-height-loose);
  color: var(--color-text-title);
}

/* 4 列卡片。分栏是手动指定的（见 library.js render 的注释），
   不用 CSS multi-column，否则"基本信息+教育经历"会被自动拆到不同列 */
/* 卡片网格层级压在"新建经历"下拉之下，否则菜单会被后面的卡片盖住 */
.lib-grid {
  position: relative;
  z-index: 0;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-5);
  align-items: start;
}
.lib-col {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  min-width: 0;
}

.lib-card {
  background: var(--color-bg-panel);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-card);
}
/* 标题条：浅色底 + 右侧铅笔。[FIGMA-精确] Desktop-10 里**每张卡的底色都不一样**：
   基本信息 #FDF9E3（奶油）／工作经历 #EEFCF5（薄荷）／项目经历 #F7F6FE（薰衣草）／
   实习经历 #FEF7F6（浅粉）／教育经历 #F4F9FE（浅蓝）。之前只做了"基本信息奶油、其余浅灰"，
   把这套配色抹平了。 */
.lib-card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: 14px 16px;
  background: var(--color-bg-subtle);
  font-size: var(--font-size-sm);
  font-weight: 600;
  color: var(--color-text-title);
}
.lib-card__head--cream { background: var(--color-warning-bg-soft); }
.lib-card__head--mint { background: var(--color-success-bg-soft); }
.lib-card__head--lavender { background: var(--color-purple-bg-soft); }
.lib-card__head--pink { background: var(--color-brand-bg-soft); }
.lib-card__head--blue { background: var(--color-accent-blue-bg); }
.lib-card__edit {
  flex-shrink: 0;
  width: 24px;
  height: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: transparent;
  border-radius: var(--radius-xs);
  color: var(--color-text-muted);
  cursor: pointer;
}
.lib-card__edit:hover {
  background: rgba(0, 0, 0, 0.05);
  color: var(--color-text-title);
}
.lib-card__body {
  padding: 16px;
}

/* 基本信息里的头像块 */
.lib-portrait {
  background: var(--color-bg-subtle);
  border-radius: var(--radius-md);
  padding: 20px 0 14px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}
.lib-portrait__photo {
  width: 64px;
  height: 64px;
  border-radius: 50%;
}
.lib-portrait__name {
  font-size: var(--font-size-sm);
  color: var(--color-text-title);
}

/* 标签/值两列，行之间没有分割线（设计稿如此） */
.lib-field {
  display: flex;
  gap: var(--space-3);
  padding: 6px 0;
  font-size: var(--font-size-xs);
}
.lib-field__label {
  width: 68px;
  flex-shrink: 0;
  color: var(--color-text-muted);
}
.lib-field__value {
  flex: 1;
  min-width: 0;
  color: var(--color-text-title);
  word-break: break-all;
}

/* 列表型分类卡里的每一条 */
.lib-item {
  padding: 14px 0;
  border-bottom: 1px solid var(--color-border);
  cursor: pointer;
}
.lib-item:first-child {
  padding-top: 0;
}
.lib-item:last-child {
  border-bottom: none;
  padding-bottom: 0;
}
.lib-item__name {
  font-size: var(--font-size-sm);
  font-weight: 600;
  color: var(--color-text-title);
}
.lib-item__meta {
  margin-top: 4px;
  font-size: var(--font-size-xs);
  color: var(--color-text-secondary);
}
.lib-item__desc {
  margin-top: 6px;
  font-size: var(--font-size-xs);
  line-height: var(--line-height-normal);
  color: var(--color-text-muted);
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.lib-empty {
  font-size: var(--font-size-xs);
  color: var(--color-text-muted);
  padding: var(--space-3) 0;
}


/* 顶栏里各模块自己的控件槽 */
.topbar__lead {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-shrink: 0;
}
/* [FIGMA-精确] Desktop-25 里标题右边的「筛选」chip：81×34、圆角8 */
.topbar-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  height: 34px;
  padding: 0 var(--space-3);
  border: none;
  border-radius: var(--radius-md);
  background: var(--color-bg-subtle);
  color: var(--color-text-title);
  font-family: inherit;
  font-size: var(--font-size-xs);
  cursor: pointer;
}
.topbar-chip:hover {
  background: var(--color-border-strong);
}
/* 用户反馈第27轮：展开按钮里的箭头缩到原来的 70%（20→14），"筛选"两个字不变 */
.topbar-chip svg {
  width: 14px;
  height: 14px;
}
/* 第28轮：箭头和"筛选"严格上下居中。同 .job-filters__label--row，
   line-height:1 让文字盒等于 em 盒，居中结果不再受字体行高影响 */
.topbar-chip {
  line-height: 1;
}

/* ==================== 双层小方按钮（Desktop-21 的 Group 14622） ====================
   设计稿里是"灰色底板 + 白色面板"两层叠出来的小按钮：
   外层 32×32 圆角10 填 #F0F0F0，内层 28×28 圆角8.5 白色带一层很散的投影，
   里面是 16px 的 ph:arrow-line-left-bold（#87909C）。
   这里整体按 1.25 倍放大（外 40 / 内 36 / 图标 20），因为原尺寸的 16px 图标违反了
   本项目"可点图标 ≥20px"的触摸端规范（见 HANDOFF 常驻技术要求），
   构成关系和配色完全照搬，只是尺寸放大。 */
.plate-btn {
  width: 40px;
  height: 40px;
  padding: 0;
  border: none;
  border-radius: var(--radius-lg);
  background: var(--color-border);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.plate-btn__face {
  width: 36px;
  height: 36px;
  border-radius: 10px;
  background: var(--color-bg-panel);
  box-shadow: var(--shadow-card);
  color: var(--color-text-muted);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: color 0.15s ease;
}
.plate-btn:hover .plate-btn__face {
  color: var(--color-text-title);
}

/* 筛选面板右上角的收起按钮 */
.job-filters {
  position: relative;
}
/* 用户反馈第28轮：收起按钮跟"针对简历"同处一行 flex，垂直居中由 align-items 保证，
   不再用 absolute + 写死 top（字号/行高/padding 一变就错位）。
   负 margin-right 是把它推回原来那个视觉位置（原 right:12px vs 面板 padding 16px）。 */
.job-filters__label--row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  /* line-height:1 把文字盒收成 em 盒。align-items:center 对齐的是**盒子**，
     默认 line-height:normal 的行盒高度由字体的 ascent/descent 决定——
     webfont 没加载出来时换成系统字体，行盒高度会变，居中就跟着漂。
     收成 em 盒之后，对齐结果只跟字号有关，跟哪套字体无关。 */
  line-height: 1;
}
.job-filters__collapse {
  flex-shrink: 0;
  margin-right: -4px;
  z-index: 2;
}
/* 用户反馈第27轮：这颗收起按钮整体缩到原来的 50%（外 40→20 / 内 36→18 / 图标 20→10），
   圆角同比缩。只改筛选栏这一颗，产物列表那颗 .canvas-collapse-btn 保持原大小。
   注意它仍然带 .tap-target——44×44 的热区是 ::after 撑的，跟视觉尺寸无关，
   所以视觉缩小不影响"触控热区 ≥44×44"那条常驻要求（"图标 ≥20px"是用户在这里主动放宽的）。 */
.job-filters__collapse.plate-btn {
  width: 20px;
  height: 20px;
  border-radius: 6px;
}
.job-filters__collapse .plate-btn__face {
  width: 18px;
  height: 18px;
  border-radius: 5px;
}
.job-filters__collapse .plate-btn__face svg {
  width: 10px;
  height: 10px;
}
/* 产物列表展开态的收起按钮：贴在列表/画布分割线上，靠画布这一侧 */
.canvas-collapse-btn {
  position: absolute;
  left: 12px;
  top: 16px;
  z-index: 6;
}
/* 给收起按钮让出位置，第一个分组标题不要顶到它 */
/* 第28轮删掉了这里原来的 .job-filters__label:first-child { padding-right: 48px }。
   那 48px 是当年为"绝对定位悬在右上角的收起按钮"给文字让位用的；现在按钮进了同一行 flex，
   这条 padding 反而会把按钮往左推 48px（负 margin 抵掉 4 之后正好差 44），
   表现为 space-between 好像失效了。留个碑，别再加回来。 */
