/* ==========================================================================
   销售管理系统 · 主题层
   在 Bootstrap 5 之上做的一层覆盖：设计令牌、中文字体栈、导航骨架与业务组件。
   命名约定：--sms-* 为自有令牌，其余为 Bootstrap 变量对齐或组件类。
   ========================================================================== */

:root {
  /* 主色：比纯亮蓝更深、饱和度收一点，长时间看不刺眼 */
  --sms-primary: #2f54d8;
  --sms-primary-hover: #2444b6;
  --sms-primary-soft: #eef2fe;
  /* 比 soft 深一档的主色底。soft 淡到跟白底快分不出来，只适合当悬停底色；
     要当**一块看得出颜色的信息条背景**得用这个。 */
  --sms-primary-tint: #e3ebfd;
  --sms-primary-ring: rgba(47, 84, 216, .16);

  /* 中性色阶 */
  --sms-ink: #0d1524;
  --sms-ink-2: #3b4657;
  --sms-muted: #6b7688;
  --sms-faint: #9aa4b6;
  --sms-line: #e6ebf4;
  --sms-line-2: #eff2f8;
  --sms-bg: #f4f6fb;
  --sms-surface: #ffffff;
  --sms-surface-2: #fafbfe;

  /* 语义色 */
  --sms-ok: #0e9f6e;
  --sms-ok-soft: #e6f6ef;
  --sms-warn: #c2740a;
  --sms-warn-soft: #fdf3e4;
  --sms-danger: #d92d20;
  --sms-danger-soft: #fdecea;
  --sms-info-soft: #eef2fe;
  --sms-violet: #7a5af8;
  --sms-violet-soft: #f3f0ff;
  /* 比 soft 深一档的紫底。「正在汇报」那一块只靠底色标记，soft 太浅（几乎就是白的，
     当初只是给描边打底用的），单独用看不出来。跟 --sms-primary-tint 一个重量级。 */
  --sms-violet-tint: #e7e0fd;

  /* 圆角 */
  --r-xs: 6px;
  --r-sm: 8px;
  --r-md: 10px;
  --r-lg: 14px;
  --r-xl: 18px;

  /* 阴影：多层低透明度，营造纸面质感 */
  --sh-xs: 0 1px 2px rgba(13, 21, 36, .05);
  --sh-sm: 0 1px 2px rgba(13, 21, 36, .04), 0 2px 6px -2px rgba(13, 21, 36, .06);
  --sh-md: 0 2px 4px -2px rgba(13, 21, 36, .06), 0 10px 24px -10px rgba(13, 21, 36, .14);
  --sh-lg: 0 4px 10px -4px rgba(13, 21, 36, .06), 0 24px 48px -20px rgba(13, 21, 36, .2);

  --ease: cubic-bezier(.4, 0, .2, 1);
  --t: .16s var(--ease);

  /* 侧栏 */
  --sms-sidebar-w: 234px;
  --sms-sidebar-ink: #a7b3c7;
  --sms-sidebar-ink-2: #6f7d94;

  /* Bootstrap 变量对齐 */
  --bs-primary: var(--sms-primary);
  --bs-body-color: var(--sms-ink-2);
  --bs-body-bg: var(--sms-bg);
  --bs-border-color: var(--sms-line);
  --bs-link-color: var(--sms-primary);
  --bs-link-hover-color: var(--sms-primary-hover);
  --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", "PingFang SC",
      "HarmonyOS Sans SC", "Microsoft YaHei UI", "Microsoft YaHei",
      "Hiragino Sans GB", "Source Han Sans SC", "Noto Sans CJK SC",
      Roboto, "Helvetica Neue", Arial, sans-serif;
}

/* ------------------------------------------------------------------ 基础 */
*, *::before, *::after { box-sizing: border-box; }
* { -webkit-tap-highlight-color: transparent; }
html { -webkit-text-size-adjust: 100%; }

body {
  font-family: var(--bs-font-sans-serif);
  background: var(--sms-bg);
  color: var(--sms-ink-2);
  font-size: 14px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4, h5, h6 { color: var(--sms-ink); letter-spacing: -.012em; }
a { text-decoration: none; }
::selection { background: rgba(47, 84, 216, .16); }

/* 细滚动条 */
* { scrollbar-width: thin; scrollbar-color: #ccd4e2 transparent; }
*::-webkit-scrollbar { width: 9px; height: 9px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
  background: #ccd4e2; border-radius: 99px;
  border: 2.5px solid transparent; background-clip: content-box;
}
*::-webkit-scrollbar-thumb:hover { background: #b0bcd0; background-clip: content-box; }

/* 图标（SVG sprite）：viewBox 24 → 实际尺寸更小，描边要按比例放大才不发虚 */
.ico {
  width: 17px; height: 17px; flex: 0 0 auto;
  fill: none; stroke: currentColor;
  stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round;
  vertical-align: -.14em;
}
.ico.sm { width: 15px; height: 15px; stroke-width: 2.1; }
.ico.lg { width: 20px; height: 20px; stroke-width: 1.75; }

/* ---------------------------------------------------------------- 骨架 */
.app-shell { display: flex; min-height: 100vh; }

.sidebar {
  width: var(--sms-sidebar-w);
  flex: 0 0 var(--sms-sidebar-w);
  background: linear-gradient(180deg, #0e1626 0%, #0a1120 100%);
  color: var(--sms-sidebar-ink);
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
  border-right: 1px solid rgba(255, 255, 255, .05);
}
.sidebar::-webkit-scrollbar { width: 6px; }
.sidebar::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, .1); background-clip: content-box; }

.sidebar .brand {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 19px 18px 17px;
  color: #fff;
  font-weight: 600;
  font-size: 15px;
  letter-spacing: .02em;
}
.sidebar .brand .logo {
  width: 31px; height: 31px;
  display: grid; place-items: center;
  border-radius: 9px;
  background: linear-gradient(140deg, #4a6ee8, #2f54d8 60%, #2444b6);
  box-shadow: 0 4px 12px -3px rgba(47, 84, 216, .7), inset 0 1px 0 rgba(255, 255, 255, .22);
  color: #fff;
}
/* 站标是**实心剪影**，不是描边线条。`.ico` 那套是 fill:none + stroke，
   细线在 17px 下必然糊成一团黑（favicon 32px 就是这个毛病），
   所以这一处单独把描边关掉、改成填充。 */
.sidebar .brand .logo .ico { width: 17px; height: 17px; fill: currentColor; stroke: none; }

.sidebar .nav { padding-bottom: 12px; }

.sidebar .nav-group {
  padding: 18px 20px 7px;
  font-size: 11px;
  letter-spacing: .1em;
  color: var(--sms-sidebar-ink-2);
  font-weight: 600;
}

.sidebar .nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 1px 10px;
  padding: 8.5px 11px;
  border-radius: var(--r-sm);
  color: var(--sms-sidebar-ink);
  font-size: 13.5px;
  transition: background var(--t), color var(--t), box-shadow var(--t);
}
.sidebar .nav-item:hover { background: rgba(255, 255, 255, .06); color: #eef2f9; }
.sidebar .nav-item.active {
  background: linear-gradient(90deg, rgba(47, 84, 216, .34), rgba(47, 84, 216, .12));
  color: #fff;
  font-weight: 500;
  box-shadow: inset 0 0 0 1px rgba(122, 156, 255, .2);
}
.sidebar .nav-item .ico { opacity: .82; }
.sidebar .nav-item.active .ico { opacity: 1; }

.sidebar .sidebar-foot {
  margin-top: auto;
  padding: 15px 20px;
  font-size: 11.5px;
  letter-spacing: .02em;
  color: #55617a;
  border-top: 1px solid rgba(255, 255, 255, .05);
}

.main-area { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }

.topbar {
  position: sticky; top: 0; z-index: 1020;
  display: flex; align-items: center; gap: 12px;
  padding: 10px 26px;
  min-height: 58px;
  background: rgba(255, 255, 255, .84);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid var(--sms-line);
}
.topbar .crumb {
  font-size: 15.5px; font-weight: 600; color: var(--sms-ink);
  letter-spacing: -.01em;
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 9px;
}
.topbar .crumb .sub { font-weight: 400; color: var(--sms-muted); font-size: 12.5px; letter-spacing: 0; }
.topbar .spacer { flex: 1; }

.user-chip {
  display: flex; align-items: center; gap: 9px;
  padding: 4px 11px 4px 4px;
  border-radius: 999px;
  border: 1px solid var(--sms-line);
  background: #fff;
  transition: border-color var(--t), box-shadow var(--t), background var(--t);
  /* 顶栏是 flex，胶囊以前是个"能屈能伸"的 flex item：面包屑一长它就被压缩，
     头像是圆的却被压成椭圆、名字挤成两行。这里把整个胶囊钉住不参与收缩，
     要挤也是挤面包屑那边。 */
  flex: 0 0 auto;
}
.user-chip:hover { border-color: #d3dbe9; box-shadow: var(--sh-sm); }
.user-chip .avatar {
  /* 圆形靠的是宽高相等；一旦被 flex 收缩就变成椭圆，所以宽度不许缩 */
  flex: 0 0 29px;
  width: 29px; height: 29px; border-radius: 50%;
  display: grid; place-items: center;
  background: linear-gradient(140deg, #e8eeff, #dbe5fd);
  color: var(--sms-primary-hover);
  font-size: 12.5px; font-weight: 600;
}
/* 头像的通用外观（顶栏那个小圆、个人资料页那个大圆共用）。**没传头像图时显示姓名首字**，
   所以这个框必须是「能放字也能放图」的 —— 传了图就用 <img> 铺满（object-fit: cover
   保证不是圆形变椭圆；上传时已经被裁成正方形了，这里是第二道保险）。 */
.avatar {
  flex: 0 0 auto; display: grid; place-items: center;
  border-radius: 50%; overflow: hidden;
  background: linear-gradient(140deg, #e8eeff, #dbe5fd);
  color: var(--sms-primary-hover);
  font-weight: 600;
}
.avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.avatar-lg { width: 64px; height: 64px; font-size: 24px; }

.user-chip .meta { line-height: 1.2; min-width: 0; }
/* 万一名字还是很长（顶栏被挤到极限），让它走省略号 —— 换行会把胶囊撑成两行高 */
.user-chip .meta .n {
  font-size: 13px; font-weight: 600; color: var(--sms-ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.user-chip .meta .r { font-size: 11px; color: var(--sms-muted); white-space: nowrap; }

.dropdown-menu {
  border: 1px solid var(--sms-line);
  border-radius: var(--r-md);
  box-shadow: var(--sh-md);
  padding: 5px;
  font-size: 13.5px;
}
.dropdown-menu .dropdown-item { border-radius: var(--r-xs); padding: 7px 11px; }
.dropdown-menu .dropdown-item:active { background: var(--sms-primary-soft); color: var(--sms-primary-hover); }

.page { padding: 22px 26px 48px; width: 100%; max-width: 1580px; margin: 0 auto; }

.section-title { font-size: 14.5px; font-weight: 600; color: var(--sms-ink); margin: 0; letter-spacing: -.005em; }

/* ---------------------------------------------------------------- 卡片 */
.card {
  border: 1px solid var(--sms-line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-sm);
  background: var(--sms-surface);
}
.card > .card-header {
  background: linear-gradient(180deg, #fdfeff, #fafbfe);
  border-bottom: 1px solid var(--sms-line);
  padding: 13px 18px;
  min-height: 51px;
  font-weight: 600;
  color: var(--sms-ink);
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  border-radius: calc(var(--r-lg) - 1px) calc(var(--r-lg) - 1px) 0 0;
}
.card > .card-header .hint { font-weight: 400; font-size: 12.5px; color: var(--sms-muted); }
.card > .card-body { padding: 18px; }
/* 用后代选择器：表单页的 footer 包在 <form> 里，不是 .card 的直接子元素。
   卡片不设 overflow:hidden（会裁掉下拉菜单），所以圆角必须落在 header/footer 自己身上。 */
.card .card-footer {
  background: linear-gradient(180deg, #fafbfe, #fdfeff);
  border-top: 1px solid var(--sms-line);
  padding: 13px 18px;
  border-radius: 0 0 calc(var(--r-lg) - 1px) calc(var(--r-lg) - 1px);
}

/* ---------------------------------------------------------------- 按钮 */
.btn {
  border-radius: var(--r-sm);
  font-size: 13.5px;
  font-weight: 500;
  padding: 7px 15px;
  transition: background var(--t), border-color var(--t), color var(--t),
              box-shadow var(--t), transform var(--t);
}
.btn:active { transform: translateY(.5px); }
.btn:focus-visible { outline: 0; box-shadow: 0 0 0 3.5px var(--sms-primary-ring); }

.btn-primary {
  --bs-btn-bg: var(--sms-primary);
  --bs-btn-border-color: var(--sms-primary);
  --bs-btn-color: #fff;
  --bs-btn-hover-bg: var(--sms-primary-hover);
  --bs-btn-hover-border-color: var(--sms-primary-hover);
  --bs-btn-hover-color: #fff;
  --bs-btn-active-bg: var(--sms-primary-hover);
  --bs-btn-active-border-color: var(--sms-primary-hover);
  --bs-btn-active-color: #fff;
  --bs-btn-disabled-bg: #9aabe8;
  --bs-btn-disabled-border-color: #9aabe8;
  --bs-btn-disabled-color: #fff;
  color: #fff;
  background-image: linear-gradient(180deg, rgba(255, 255, 255, .15), rgba(255, 255, 255, 0));
  box-shadow: 0 1px 2px rgba(47, 84, 216, .22), inset 0 1px 0 rgba(255, 255, 255, .16);
}
.btn-primary:hover { box-shadow: 0 4px 12px -3px rgba(47, 84, 216, .45), inset 0 1px 0 rgba(255, 255, 255, .16); }

.btn-outline-secondary {
  --bs-btn-color: var(--sms-ink-2);
  --bs-btn-border-color: var(--sms-line);
  --bs-btn-hover-bg: #f2f5fb;
  --bs-btn-hover-border-color: #d5dceb;
  --bs-btn-hover-color: var(--sms-ink);
  --bs-btn-active-bg: #eaeffa;
  --bs-btn-active-border-color: #d5dceb;
  background: #fff;
  box-shadow: var(--sh-xs);
}
.btn-outline-secondary:hover { box-shadow: var(--sh-sm); }

/* 删除 / 停用这类危险操作，以前是"没框的红字"，跟旁边有框的「编辑」并排时
   一半像按钮一半像链接。现在跟其他操作一样做成有框按钮，靠**颜色**区分危险，
   而不是靠"看起来不像能点的"。Bootstrap 自带的 .btn-outline-danger 用它自己的红
   （#dc3545），跟本系统的 --sms-danger 不是一个色，所以照上面两套的写法覆盖变量。 */
.btn-outline-danger {
  --bs-btn-color: var(--sms-danger);
  --bs-btn-border-color: #f3c6c2;
  --bs-btn-hover-bg: var(--sms-danger-soft);
  --bs-btn-hover-border-color: #eeaaa4;
  --bs-btn-hover-color: var(--sms-danger);
  --bs-btn-active-bg: var(--sms-danger-soft);
  --bs-btn-active-border-color: #eeaaa4;
  --bs-btn-active-color: var(--sms-danger);
  background: #fff;
  box-shadow: var(--sh-xs);
}
.btn-outline-danger:hover { box-shadow: var(--sh-sm); }

/* 「查询」是全站统一的实心蓝小按钮 —— 一排浅色筛选框里只有它是实心的，
   一眼就能找到。以前人员管理页是全尺寸实心、其余页面是描边，两种长相。 */
.btn-query { min-width: 76px; }

.btn-ghost {
  background: transparent; border: 1px solid transparent;
  color: var(--sms-muted); box-shadow: none;
}
.btn-ghost:hover { background: #eef2f9; color: var(--sms-ink); border-color: transparent; }

.btn-sm { font-size: 12.5px; padding: 5px 11px; border-radius: 7px; }

/* ---------------------------------------------------------------- 表单 */
.form-label {
  font-size: 12.5px; font-weight: 500; color: var(--sms-ink-2);
  margin-bottom: 6px; letter-spacing: .005em;
}
.form-label .req { color: var(--sms-danger); margin-left: 2px; }

.form-control, .form-select {
  border: 1px solid var(--sms-line);
  border-radius: var(--r-sm);
  font-size: 13.5px;
  padding: 8px 12px;
  color: var(--sms-ink-2);
  background-color: #fff;
  box-shadow: 0 1px 1px rgba(13, 21, 36, .02);
  transition: border-color var(--t), box-shadow var(--t);
}
/* 必须单独留出右侧内边距：上面的简写 padding 会盖掉 Bootstrap 给箭头预留的
   padding-right，箭头就压到文字上了。箭头换成与周网格一致的浅色 chevron。 */
.form-select {
  --bs-form-select-bg-img: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236b7688' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9.5l6 6 6-6'/%3E%3C/svg%3E");
  padding-right: 33px;
  background-size: 13px 13px;
}
.form-control::placeholder { color: var(--sms-faint); }
.form-control:hover:not(:focus), .form-select:hover:not(:focus) { border-color: #d3dbe9; }
.form-control:focus, .form-select:focus {
  border-color: var(--sms-primary);
  box-shadow: 0 0 0 3.5px var(--sms-primary-ring);
}
.form-control.is-invalid, .form-select.is-invalid { border-color: var(--sms-danger); }
.form-control.is-invalid:focus, .form-select.is-invalid:focus {
  box-shadow: 0 0 0 3.5px rgba(217, 45, 32, .14);
}
.form-text { font-size: 12px; color: var(--sms-muted); }
.invalid-feedback { font-size: 12px; }
.form-check-input:checked { background-color: var(--sms-primary); border-color: var(--sms-primary); }
.form-check-input:focus { box-shadow: 0 0 0 3px var(--sms-primary-ring); border-color: var(--sms-primary); }
.form-check-label { font-size: 13.5px; }

/* ---------------------------------------------------------------- 表格 */
.table {
  --bs-table-bg: transparent;
  --bs-table-border-color: var(--sms-line-2);
  margin-bottom: 0;
  font-size: 13.5px;
  color: var(--sms-ink-2);
  font-variant-numeric: tabular-nums;
}
.table > :not(caption) > * > * { padding: 11px 14px; }
.table > thead > tr > th {
  background: var(--sms-surface-2);
  color: var(--sms-muted);
  font-weight: 600;
  font-size: 12.5px;
  letter-spacing: .02em;
  white-space: nowrap;
  border-bottom: 1px solid var(--sms-line);
  padding-top: 10px;
  padding-bottom: 10px;
}
.table > tbody > tr > td {
  vertical-align: middle;
  border-bottom: 1px solid var(--sms-line-2);
  background-color: transparent;
}
.table > tbody > tr:last-child > td { border-bottom: 0; }
.table-hover { --bs-table-hover-bg: #f7f9fe; }
.table > tbody > tr.row-empty > td { color: var(--sms-faint); }
.table .day-cell { white-space: nowrap; font-weight: 500; color: var(--sms-ink); }
.table .day-cell .sub { display: block; font-size: 11.5px; font-weight: 400; color: var(--sms-faint); }
.table td.wrap, .table th.wrap { white-space: normal; }
/* 长文本列（周报的「行动重点描述」）：
   1) 列上必须给 min-width —— 其它列都是 nowrap、各自占满声明宽度，只有这一列可换行，
      不给下限它就会被挤到几十像素宽，两行的话折成几十行、把整行撑到近千像素；
   2) 再长的内容在格子里内滚，行高封顶，别让一行占满整屏。 */
.table .cell-scroll { max-height: 119px; overflow-y: auto; }   /* 约 5 行 */

/* 工作台「项目总览」的表体：限高内滚。
   项目一多，这张表和「最近拜访」是并排等高的，整行会被它撑得很高
   （实测列 8 条时行高 918px，而最近拜访只有 278px），页面跟着变长。
   限高之后项目再多也不影响版式，而且一条都不会被藏起来。 */
.table-scroll { max-height: 302px; }   /* 约 4.5 行，露半行提示还能滚 */
/* 表头钉住：滚动的是表体，表头必须留在原地，否则滑下去就不知道哪列是哪列了。
   th 本身有底色（.table > thead > tr > th 里设了），所以压住内容也不会透出来。 */
.table-scroll > .table > thead > tr > th {
  position: sticky; top: 0; z-index: 1;
}

/* 名单类单元格（如周报的「被拜访人」）：一行到底，超出省略号，悬停出全文。
   这一列的人名一多就换行、把整行撑高，而它本来也不需要显示成多行。
   注意 max-width 要加在里层的 div 上 —— table-layout:auto 下 td 的 max-width 会被忽略。 */
.table .one-line {
  max-width: 150px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tbl-empty { padding: 52px 16px; text-align: center; color: var(--sms-muted); font-size: 13.5px; }
.tbl-empty .big {
  display: grid; place-items: center;
  width: 52px; height: 52px; margin: 0 auto 12px;
  border-radius: 50%;
  background: linear-gradient(160deg, #f3f6fd, #eaeffa);
  color: #b6c1d4;
  font-size: 22px;
  box-shadow: inset 0 0 0 1px rgba(13, 21, 36, .03);
}
.tbl-empty .big .ico { width: 23px; height: 23px; stroke-width: 1.6; }

/* ---------------------------------------------------------------- 徽标 */
.badge-soft {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2.5px 9px;
  border-radius: 999px;
  font-size: 12px; font-weight: 500;
  line-height: 1.65;
  white-space: nowrap;
  box-shadow: inset 0 0 0 1px rgba(13, 21, 36, .04);
}
.badge-soft.gray { background: #eef1f6; color: #57637a; }
.badge-soft.blue { background: var(--sms-info-soft); color: var(--sms-primary-hover); }
.badge-soft.green { background: var(--sms-ok-soft); color: #0a7a53; }
.badge-soft.amber { background: var(--sms-warn-soft); color: #96600a; }
.badge-soft.red { background: var(--sms-danger-soft); color: #b42318; }
.badge-soft.violet { background: var(--sms-violet-soft); color: #5c3fd6; }

/* ---------------------------------------------------------------- 统计块 */
.stat-tile .k .ico { color: var(--sms-faint); }

.stat-tile {
  position: relative;
  border: 1px solid var(--sms-line);
  border-radius: var(--r-lg);
  background: linear-gradient(180deg, #fff 0%, #fcfdff 100%);
  padding: 15px 17px;
  height: 100%;
  box-shadow: var(--sh-sm);
  overflow: hidden;
}
.stat-tile::after {
  content: "";
  position: absolute; right: -32px; top: -32px;
  width: 96px; height: 96px; border-radius: 50%;
  background: radial-gradient(circle, rgba(47, 84, 216, .07), transparent 70%);
  pointer-events: none;
}
.stat-tile .k {
  font-size: 12.5px; color: var(--sms-muted);
  margin-bottom: 7px; display: flex; align-items: center; gap: 6px;
}
.stat-tile .v {
  font-size: 24px; font-weight: 650; color: var(--sms-ink);
  line-height: 1.15; letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}
.stat-tile .v small { font-size: 13px; font-weight: 400; color: var(--sms-muted); margin-left: 4px; letter-spacing: 0; }

/* 报销管理：金额与摘要 */
.money {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.money.danger { color: var(--sms-danger); }
.money.ok { color: var(--sms-ok); }
.money.muted { color: var(--sms-muted); }
.stat-tile .v.danger { color: var(--sms-danger); }
.stat-tile .v.ok { color: var(--sms-ok); }
.stat-tile .v.info { color: var(--sms-primary); }
.stat-tile .foot {
  margin-top: 8px; font-size: 12px; color: var(--sms-muted); line-height: 1.45;
}
.reimb-summary {
  display: flex; flex-wrap: wrap; gap: 16px 30px;
  padding: 2px 0;
}
.reimb-summary .item .k { font-size: 12.5px; color: var(--sms-muted); margin-bottom: 3px; }
.reimb-summary .item .v {
  font-size: 17px; font-weight: 650; color: var(--sms-ink);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.reimb-summary .item .v small { font-size: 12px; font-weight: 400; color: var(--sms-muted); margin-left: 2px; }

/* 总览：账本摘要与区间发生额 */
.reimb-ledger {
  display: flex; align-items: center; gap: 32px; flex-wrap: wrap;
  padding: 4px 0 18px; margin-bottom: 18px;
  border-bottom: 1px dashed var(--sms-line);
}
.reimb-ledger-main .k { font-size: 12.5px; color: var(--sms-muted); }
.reimb-ledger-main .v {
  margin-top: 2px; font-size: 30px; font-weight: 650; line-height: 1.15;
  letter-spacing: -.02em; color: var(--sms-ink);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.reimb-ledger-main .v.danger { color: var(--sms-danger); }
.reimb-ledger-main .v small {
  margin-left: 4px; font-size: 13px; font-weight: 400;
  color: var(--sms-muted); letter-spacing: 0;
}
.reimb-ledger-main .foot { margin-top: 5px; font-size: 12px; color: var(--sms-muted); }
.reimb-ledger-bars { flex: 1 1 320px; min-width: 220px; }
.reimb-ledger-bars .bar {
  display: flex; height: 10px; border-radius: 999px;
  overflow: hidden; background: var(--sms-line-2);
}
.reimb-ledger-bars .seg { height: 100%; }
.reimb-ledger-bars .seg.offset { background: var(--sms-violet); }
.reimb-ledger-bars .seg.returned { background: var(--sms-primary); }
.reimb-ledger-bars .seg.debt { background: var(--sms-warn); }
.reimb-ledger-bars .legend {
  display: flex; flex-wrap: wrap; gap: 8px 18px;
  margin-top: 10px; font-size: 12px; color: var(--sms-muted);
}
.reimb-ledger-bars .legend .sw {
  display: inline-block; width: 8px; height: 8px; margin-right: 5px;
  border-radius: 2px; vertical-align: 1px;
}
.reimb-ledger-bars .legend .sw.offset { background: var(--sms-violet); }
.reimb-ledger-bars .legend .sw.returned { background: var(--sms-primary); }
.reimb-ledger-bars .legend .sw.debt { background: var(--sms-warn); }
.reimb-ledger-bars .legend b {
  color: var(--sms-ink-2); font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.reimb-metrics {
  display: grid; gap: 10px;
  grid-template-columns: repeat(auto-fit, minmax(128px, 1fr));
}
.reimb-metrics-6 { grid-template-columns: repeat(6, minmax(0, 1fr)); }
.reimb-metric {
  border: 1px solid var(--sms-line); border-radius: 10px;
  background: linear-gradient(180deg, #fff 0%, #fafbfe 100%);
  padding: 10px 12px; min-width: 0;
}
.reimb-metric .k {
  font-size: 12px; color: var(--sms-muted);
  margin-bottom: 4px; white-space: nowrap;
}
.reimb-metric .v {
  font-size: 18px; font-weight: 650; letter-spacing: -.01em;
  color: var(--sms-ink); font-variant-numeric: tabular-nums;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.reimb-metric .v small {
  margin-left: 3px; font-size: 11.5px; font-weight: 400;
  color: var(--sms-muted); letter-spacing: 0;
}
.reimb-metric .v.violet { color: var(--sms-violet); }
.reimb-metric .v.ok { color: var(--sms-ok); }
.reimb-metric .v.amber { color: var(--sms-warn); }
.reimb-metric .v.danger { color: var(--sms-danger); }
.reimb-metric.emphasis {
  background: var(--sms-ok-soft); border-color: #cdece0;
}

.reimb-groups {
  display: grid; gap: 14px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.reimb-group {
  border: 1px solid var(--sms-line); border-radius: 12px;
  background: #fcfdff; padding: 14px;
}
.reimb-group .reimb-metric { background: #fff; }
.reimb-group-head {
  display: flex; align-items: center; gap: 8px; margin-bottom: 12px;
}
.reimb-group-head .dot {
  flex: 0 0 8px; width: 8px; height: 8px; border-radius: 50%;
}
.reimb-group-head .dot.blue { background: var(--sms-primary); }
.reimb-group-head .dot.violet { background: var(--sms-violet); }
.reimb-group-head .dot.green { background: var(--sms-ok); }
.reimb-group-head .dot.amber { background: var(--sms-warn); }
.reimb-group-head h3 {
  margin: 0; font-size: 13.5px; font-weight: 600; color: var(--sms-ink);
}
.reimb-group-head .note {
  margin-left: auto; font-size: 11.5px; color: var(--sms-faint);
  white-space: nowrap;
}

@media (max-width: 1199.98px) {
  .reimb-metrics-6 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 991.98px) {
  .reimb-groups { grid-template-columns: 1fr; }
  .reimb-ledger { gap: 18px; }
}
@media (max-width: 575.98px) {
  .reimb-metrics-6 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .reimb-ledger-main .v { font-size: 25px; }
  .reimb-group-head .note { display: none; }
}

/* 工作台的两张状态卡 */
.status-card { display: flex; align-items: flex-start; gap: 13px; }
.status-card .ico-chip {
  width: 40px; height: 40px; flex: 0 0 40px;
  border-radius: 12px;
  display: grid; place-items: center;
  background: var(--sms-primary-soft);
  color: var(--sms-primary-hover);
  box-shadow: inset 0 0 0 1px rgba(47, 84, 216, .08);
}
.status-card .ico-chip .ico { width: 19px; height: 19px; }
.status-card .ico-chip.ok { background: var(--sms-ok-soft); color: #0a7a53; box-shadow: inset 0 0 0 1px rgba(14, 159, 110, .1); }
.status-card .ico-chip.wait { background: var(--sms-warn-soft); color: #96600a; box-shadow: inset 0 0 0 1px rgba(194, 116, 10, .1); }
.status-card .t { font-size: 13px; color: var(--sms-muted); margin-bottom: 3px; }
.status-card .v { font-size: 16.5px; font-weight: 650; color: var(--sms-ink); letter-spacing: -.012em; }

/* 快捷操作按钮：图标与文字对齐 */
.quick-actions .btn { display: flex; align-items: center; gap: 9px; padding: 9px 13px; }
.quick-actions .btn .ico { color: var(--sms-faint); transition: color var(--t); }
.quick-actions .btn:hover .ico { color: var(--sms-primary); }

/* ---------------------------------------------------------------- 工具栏 */
.toolbar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  padding: 12px 18px;
  border-bottom: 1px solid var(--sms-line);
  background: linear-gradient(180deg, #fcfdff, #fafbfe);
  border-radius: calc(var(--r-lg) - 1px) calc(var(--r-lg) - 1px) 0 0;
}
.toolbar .grow { flex: 1 1 auto; }
.filter-form { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.filter-form .form-control { font-size: 13px; padding: 6.5px 11px; }
.filter-form .form-select { font-size: 13px; padding: 6.5px 31px 6.5px 11px; }

/* ---------------------------------------------------------------- 可编辑周网格 */
.weekgrid {
  width: 100%;
  font-size: 13px;
  border-collapse: separate;
  border-spacing: 0;
  font-variant-numeric: tabular-nums;
}
.weekgrid th {
  background: var(--sms-surface-2);
  color: var(--sms-muted);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .02em;
  padding: 9px 8px;
  text-align: center;
  white-space: nowrap;
  border-bottom: 1px solid var(--sms-line);
  border-right: 1px solid var(--sms-line-2);
}
.weekgrid td {
  border-bottom: 1px solid var(--sms-line);
  border-right: 1px solid var(--sms-line-2);
  padding: 0;
  vertical-align: top;
  background: #fff;
  transition: background var(--t);
}
.weekgrid th:last-child, .weekgrid td:last-child { border-right: 0; }
.weekgrid tbody tr:hover td { background: #fbfcff; }
.weekgrid tbody tr:hover td.day { background: #f4f7fd; }

.weekgrid td.day {
  width: 74px;
  text-align: center;
  vertical-align: middle;
  background: var(--sms-surface-2);
  color: var(--sms-ink);
  font-weight: 500;
  padding: 8px 4px;
  white-space: nowrap;
}
.weekgrid td.day .d { display: block; font-size: 11px; color: var(--sms-faint); font-weight: 400; margin-top: 2px; }

/* 同一天的第 2 条：跟上一行连成一块，中间不画横线 —— 跟导出到 Excel 那条规则
   是同一个意思（那边靠不画边框，这边靠去掉上一行的下边框）。
   .merge-up 由模板里的 JS 打上：显示第 2 行时加给它的上一行，页面加载时如果
   第 2 条本来就有内容也一并加上。不用 CSS 相邻选择器是因为「第 2 行是 hidden 的
   时候」那条线必须留着。 */
.weekgrid tr.merge-up > td { border-bottom-color: transparent; }

/* 「＋ 同一天再加一个中心」：贴在「项目名称/拜访目的」那一格的下边 */
.weekgrid .add-slot {
  display: block; width: 100%; margin-top: 4px;
  padding: 4px 8px; text-align: left;
  border: 1px dashed var(--sms-line); border-radius: var(--r-sm);
  background: transparent; color: var(--sms-muted);
  font-size: 12px; font-family: inherit;
  transition: color var(--t), border-color var(--t), background var(--t);
}
.weekgrid .add-slot:hover {
  color: var(--sms-primary); border-color: var(--sms-primary);
  background: var(--sms-primary-soft);
}

.weekgrid input, .weekgrid textarea, .weekgrid select {
  width: 100%;
  border: 0;
  background: transparent;
  padding: 8px 9px;
  font-size: 13px;
  font-family: inherit;
  color: var(--sms-ink-2);
  resize: vertical;
  outline: none;
  border-radius: 0;
}
.weekgrid textarea { min-height: 66px; line-height: 1.55; }
.weekgrid input::placeholder, .weekgrid textarea::placeholder { color: #bcc5d4; }
.weekgrid input:focus, .weekgrid textarea:focus, .weekgrid select:focus {
  background-color: #f4f7ff;
  box-shadow: inset 0 0 0 1.5px var(--sms-primary);
  position: relative; z-index: 1;
}

/* 原生控件的默认外观在密表格里很吵，统一收一下 */
.weekgrid select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 20px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239aa4b6' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9.5l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 6px center;
  background-size: 11px 11px;
  cursor: pointer;
}
.weekgrid select[multiple] { background-image: none; padding-right: 4px; cursor: default; }
.weekgrid .is-invalid { border-color: var(--sms-danger) !important; }
.weekgrid .cell-error { display: block; margin-top: 4px; font-size: 11px; line-height: 1.45; color: var(--sms-danger); }
.weekgrid input[type="date"] { font-size: 12.5px; padding-left: 7px; padding-right: 1px; }
.weekgrid input[type="date"]::-webkit-calendar-picker-indicator { opacity: .42; cursor: pointer; }
.weekgrid input[type="date"]::-webkit-calendar-picker-indicator:hover { opacity: .85; }
.weekgrid input[type="number"] { appearance: textfield; -moz-appearance: textfield; }
.weekgrid input[type="number"]::-webkit-outer-spin-button,
.weekgrid input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.weekgrid tr.is-empty td.day { color: var(--sms-muted); font-weight: 400; }
.weekgrid tr.is-empty td.day .d { color: #c3cbd9; }
.weekgrid .grid-actions { width: 34px; text-align: center; vertical-align: middle; }
.weekgrid .grid-actions button {
  border: 0; background: transparent; color: #b6bfcf;
  font-size: 15px; line-height: 1; padding: 4px; border-radius: 6px;
}
.weekgrid .grid-actions button:hover { color: var(--sms-danger); background: var(--sms-danger-soft); }

/* ---------------------------------------------------------------- 提示 */
.msg {
  position: relative;
  border-radius: var(--r-md);
  padding: 11px 15px 11px 17px;
  font-size: 13.5px;
  margin-bottom: 12px;
  border: 1px solid transparent;
  box-shadow: var(--sh-xs);
}
.msg::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0;
  width: 3px; border-radius: var(--r-md) 0 0 var(--r-md);
}
.msg.success { background: var(--sms-ok-soft); color: #0a7a53; border-color: #c6ead9; }
.msg.success::before { background: var(--sms-ok); }
.msg.error { background: var(--sms-danger-soft); color: #b42318; border-color: #f7d2ce; }
.msg.error::before { background: var(--sms-danger); }
.msg.warning { background: var(--sms-warn-soft); color: #96600a; border-color: #f4e0be; }
.msg.warning::before { background: var(--sms-warn); }
.msg.info { background: var(--sms-info-soft); color: var(--sms-primary-hover); border-color: #d3defb; }
.msg.info::before { background: var(--sms-primary); }

.callout {
  border-radius: var(--r-md);
  padding: 13px 16px;
  font-size: 13.5px;
  border: 1px solid var(--sms-line);
  background: var(--sms-surface-2);
  line-height: 1.6;
}
.callout.warn { background: var(--sms-warn-soft); border-color: #f2ddb8; color: #8a5204; }

/* 页内切换（操作日志 / 登录日志） */
.tabs { display: flex; flex-wrap: wrap; gap: 4px; }
.tabs a {
  padding: 6px 14px; border-radius: var(--r-sm);
  font-size: 13.5px; color: var(--sms-muted); text-decoration: none;
  transition: background-color var(--t), color var(--t);
}
.tabs a:hover { background: var(--sms-line-2); color: var(--sms-ink-2); }
.tabs a.active {
  background: var(--sms-primary-soft); color: var(--sms-primary-hover); font-weight: 600;
}
.callout.info { background: var(--sms-info-soft); border-color: #d3defb; color: var(--sms-primary-hover); }

/* ---------------------------------------------------------------- 流程引导 */
.flow-steps { display: flex; flex-wrap: wrap; gap: 12px; align-items: stretch; }
.flow-step {
  flex: 1 1 260px;
  display: flex; gap: 12px; align-items: flex-start;
  padding: 15px 17px;
  border: 1px solid var(--sms-line);
  border-radius: var(--r-lg);
  background: linear-gradient(180deg, #fff, #fcfdff);
  box-shadow: var(--sh-xs);
  transition: box-shadow var(--t), border-color var(--t);
}
.flow-step:hover { box-shadow: var(--sh-sm); }
.flow-step.todo { border-color: #d3defb; background: linear-gradient(180deg, #fbfcff, #f7faff); }
.flow-step.done { border-color: #c6ead9; background: linear-gradient(180deg, #fbfefc, #f6fcf9); }
.flow-step .idx {
  width: 27px; height: 27px; flex: 0 0 27px;
  border-radius: 50%;
  display: grid; place-items: center;
  font-size: 13px; font-weight: 600;
  background: #eef1f6; color: var(--sms-muted);
  box-shadow: inset 0 0 0 1px rgba(13, 21, 36, .04);
}
.flow-step.done .idx { background: var(--sms-ok-soft); color: #0a7a53; }
.flow-step.todo .idx { background: var(--sms-primary-soft); color: var(--sms-primary-hover); }
.flow-step .idx .ico { width: 15px; height: 15px; stroke-width: 2.2; }
.flow-step .t { font-weight: 600; color: var(--sms-ink); margin-bottom: 3px; font-size: 13.5px; }
.flow-step .d { font-size: 12.5px; color: var(--sms-muted); margin-bottom: 10px; line-height: 1.5; }

/* ---------------------------------------------------------------- 详情页 */
.detail-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 16px 24px;
}
.detail-grid .k { font-size: 12.5px; color: var(--sms-muted); margin-bottom: 4px; }
.detail-grid .v { font-size: 13.5px; color: var(--sms-ink); word-break: break-word; line-height: 1.55; }
.detail-grid .v.blank { color: var(--sms-faint); }

/* 浅底胶囊链接：用在「单位信息」那种全是灰字字段的格子里，让能点的那一项自己站出来。
   底下没有别的链接，所以不做下划线，靠底色和悬停变深表示可点。 */
.link-pill {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 9px 3px;
  border-radius: 6px;
  background: var(--sms-primary-soft);
  color: var(--sms-primary);
  font-size: 12.5px; font-weight: 500;
  line-height: 1.6;
}
.link-pill:hover { background: #e2e9fd; color: var(--sms-primary-hover); }

.timeline { position: relative; padding-left: 24px; }
.timeline::before {
  content: ""; position: absolute; left: 5.5px; top: 8px; bottom: 8px;
  width: 1.5px; border-radius: 2px;
  background: linear-gradient(180deg, var(--sms-line), var(--sms-line-2));
}
.timeline-item { position: relative; padding: 0 0 20px; }
.timeline-item:last-child { padding-bottom: 0; }
.timeline-item::before {
  content: ""; position: absolute; left: -23px; top: 4px;
  width: 10px; height: 10px; border-radius: 50%;
  background: #fff;
  box-shadow: inset 0 0 0 2.5px var(--sms-primary), 0 0 0 4px var(--sms-primary-soft);
}
.timeline-item .meta {
  font-size: 12.5px; color: var(--sms-muted);
  margin-bottom: 5px; display: flex; flex-wrap: wrap; gap: 6px;
  align-items: center;
}
.timeline-item .meta span {
  display: inline-flex; align-items: center;
  padding: 1px 8px; border-radius: 999px;
  background: #f2f5fa; color: #5b6779;
  font-variant-numeric: tabular-nums;
}
.timeline-item .meta span:first-child { background: var(--sms-primary-soft); color: var(--sms-primary-hover); font-weight: 500; }
.timeline-item .body {
  font-size: 13.5px; color: var(--sms-ink-2);
  /* pre-wrap 会连模板自身的缩进一起保留 —— 在模板里 <div class="body"> 之后
     只要有一个换行或空格，正文第一行就会莫名缩进一截。内容必须紧贴着写。 */
  white-space: pre-wrap; line-height: 1.65;
}

/* ---------------------------------------------------------------- 覆盖 Bootstrap 细节 */
.text-muted { color: var(--sms-muted) !important; }
.text-danger { color: var(--sms-danger) !important; }
hr { border-color: var(--sms-line); opacity: 1; }
/* 表格滚动容器要裁到卡片圆角上，否则首/末行会顶出直角 */
.card > .table-responsive:first-child { border-radius: calc(var(--r-lg) - 1px) calc(var(--r-lg) - 1px) 0 0; }
.card > .table-responsive:last-child { border-radius: 0 0 calc(var(--r-lg) - 1px) calc(var(--r-lg) - 1px); }
.card > .table-responsive:only-child { border-radius: calc(var(--r-lg) - 1px); }

/* 列表分页条。接在表格后面，所以自己收一下卡片的底角 */
.pager {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  padding: 12px 16px;
  border-top: 1px solid var(--sms-line);
}
.card > .pager:last-child { border-radius: 0 0 calc(var(--r-lg) - 1px) calc(var(--r-lg) - 1px); }
.pager .hint { font-size: 12.5px; color: var(--sms-muted); }
.pager-links { display: flex; flex-wrap: wrap; gap: 4px; margin-left: auto; }
.pager .page-link {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 30px; height: 30px; padding: 0 9px;
  border: 1px solid var(--sms-line); border-radius: var(--r-xs);
  background: #fff; color: var(--sms-ink-2); font-size: 13px;
  text-decoration: none;
  transition: border-color var(--t), color var(--t), background-color var(--t);
}
.pager a.page-link:hover {
  border-color: var(--sms-primary); color: var(--sms-primary);
  background: var(--sms-primary-soft);
}
.pager .page-link.current {
  background: var(--sms-primary); border-color: var(--sms-primary);
  color: #fff; font-weight: 600;
}
.pager .page-link.disabled { color: var(--sms-faint); background: var(--sms-surface-2); }
.pager .page-gap {
  display: inline-flex; align-items: center; height: 30px;
  color: var(--sms-faint); font-size: 13px;
}

/* 可折叠的次要内容（如拜访提醒里「周期内」的那些中心） */
.fold { border-top: 1px solid var(--sms-line); }
.card > .fold:last-child {
  border-radius: 0 0 calc(var(--r-lg) - 1px) calc(var(--r-lg) - 1px);
  overflow: hidden;
}
.fold > summary {
  display: flex; align-items: center; gap: 7px;
  padding: 11px 16px;
  font-size: 12.5px; color: var(--sms-muted);
  cursor: pointer; user-select: none; list-style: none;
  transition: color var(--t), background-color var(--t);
}
.fold > summary::-webkit-details-marker { display: none; }
.fold > summary::before {
  content: ""; width: 6px; height: 6px; margin-left: 2px;
  border-right: 1.6px solid currentColor;
  border-bottom: 1.6px solid currentColor;
  transform: rotate(-45deg);
  transition: transform var(--t);
}
.fold[open] > summary::before { transform: rotate(45deg); }
.fold > summary:hover { color: var(--sms-primary); background: var(--sms-surface-2); }
/* header 自己已经有一条 border-bottom，紧跟其后的 .fold 再加一条就是 2px 粗线 */
.card > .card-header + .fold { border-top: 0; }
/* 折叠条上那一行预览：项目一多，靠它认得出哪条是哪条。
   min-width:0 必须有 —— flex item 默认 min-width:auto，不加就撑开不省略号。 */
.me-prev {
  flex: 1 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
code { color: var(--sms-primary-hover); background: var(--sms-primary-soft); padding: 1px 6px; border-radius: 5px; font-size: .92em; }
kbd { background: var(--sms-ink); border-radius: 5px; font-size: .85em; }

/* 表格里的链接：正文色更沉，hover 才亮起来，避免整列发蓝 */
.table a { color: var(--sms-ink); font-weight: 500; transition: color var(--t); }
.table a:hover { color: var(--sms-primary); text-decoration: underline; text-underline-offset: 2px; }
/* 表格里的实心主按钮要压过上面的链接色，否则蓝底黑字看不清。 */
.table a.btn-primary,
.table a.btn-primary:hover,
.table a.btn-primary:focus {
  color: #fff;
  text-decoration: none;
}
.table tbody tr td .form-text { margin-top: 2px; }

/* ---------------------------------------------------------------- AI 优化 */
/* AI 优化按钮：旁边一排都是灰按钮，不做出区分根本找不着它。
   用系统里已有的紫色（--sms-violet）配图标 —— 跟「保存」的实心蓝也不会打架。 */
.btn-ai {
  display: inline-flex; align-items: center; gap: 5px;
  border-color: #ddd6fb;
  background: linear-gradient(135deg, var(--sms-violet-soft), var(--sms-primary-soft));
  color: #5c3fd6; font-weight: 500;
}
.btn-ai:hover {
  border-color: #b9a9f7; color: #4b30c0;
  background: linear-gradient(135deg, #ece5ff, #e4ebfe);
  box-shadow: 0 3px 10px -4px rgba(122, 90, 248, .6);
}
.btn-ai:disabled, .btn-ai:disabled:hover {
  opacity: .6; box-shadow: none;
  border-color: #ddd6fb; color: #5c3fd6;
  background: linear-gradient(135deg, var(--sms-violet-soft), var(--sms-primary-soft));
}
.btn-ai .ico { width: 14px; height: 14px; stroke-width: 2.2; }

/* 按钮旁边的状态文字：优化中 / 已优化 N 处 / 具体报错 */
.ai-status { font-size: 12.5px; color: var(--sms-muted); }
.ai-status.ok { color: var(--sms-ok); }
.ai-status.bad { color: var(--sms-danger); }
.ai-status.warn { color: var(--sms-warn); }

/* AI 分析弹窗：先补信息再生成。跟按钮同一套紫色，让整块有辨识度。 */
#analyze-modal .modal-content {
  border: none;
  border-radius: 14px;
  overflow: hidden;
  box-shadow: 0 24px 60px -24px rgba(20, 30, 60, .4);
}
#analyze-modal .modal-header {
  background: linear-gradient(135deg, var(--sms-violet-soft), var(--sms-primary-soft));
  border-bottom: 1px solid var(--sms-violet-tint);
  padding: 16px 20px;
}
#analyze-modal .ai-title-ico {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px;
  background: #fff; border-radius: 9px;
  box-shadow: 0 2px 8px -2px rgba(122, 90, 248, .45);
}
#analyze-modal .ai-title-ico .ico { width: 18px; height: 18px; stroke-width: 2.2; color: #5c3fd6; }
#analyze-modal .modal-title { font-size: 16px; font-weight: 600; color: #3a2f6b; }
#analyze-modal .ai-title-sub { font-size: 12.5px; color: var(--sms-muted); line-height: 1.4; }
#analyze-modal .modal-body { padding: 20px; }

/* 通读/生成中的加载态：居中、软底、虚线框，跟普通内容区分开 */
.analyze-phase {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  padding: 36px 20px;
  background: var(--sms-surface-2);
  border: 1px dashed var(--sms-violet-tint);
  border-radius: 12px;
  color: #6b6594; font-size: 14px;
}

/* 多轮补信息的题目卡片 */
.analyze-q-hint { font-size: 12.5px; color: var(--sms-muted); margin-bottom: 12px; }
.analyze-question {
  padding: 14px 16px;
  background: var(--sms-surface-2);
  border: 1px solid var(--sms-line);
  border-radius: 12px;
  margin-bottom: 12px;
  transition: border-color .15s var(--ease), box-shadow .15s var(--ease);
}
.analyze-question:focus-within {
  border-color: var(--sms-violet);
  box-shadow: 0 0 0 3px var(--sms-violet-soft);
}
.analyze-q-label { display: flex; gap: 9px; align-items: flex-start; margin-bottom: 8px; }
.analyze-q-num {
  flex: 0 0 auto;
  width: 20px; height: 20px; border-radius: 50%;
  background: linear-gradient(135deg, #8a6cf6, var(--sms-violet));
  color: #fff; font-size: 12px; font-weight: 600;
  display: inline-flex; align-items: center; justify-content: center;
  margin-top: 1px;
}
.analyze-q-text { font-size: 13.5px; color: var(--sms-ink-2); line-height: 1.55; }
.analyze-question textarea { font-size: 13.5px; border-color: var(--sms-line); }
.analyze-question textarea:focus {
  border-color: var(--sms-violet);
  box-shadow: 0 0 0 3px var(--sms-violet-soft);
}

/* 提示词编辑框：等宽字体、行高松一些，长文本好读 */
.ai-prompt-box {
  font-family: ui-monospace, "Cascadia Mono", Consolas, "Microsoft YaHei", monospace;
  font-size: 12.5px;
  line-height: 1.75;
}

/* 被 AI 改过的输入框闪一下，让人一眼看到哪里动了 —— 不闪的话
   一屏几十个文本框，根本不知道它改了哪儿。 */
.ai-touched { border-color: var(--sms-primary) !important; animation: ai-flash 4.2s var(--ease); }
@keyframes ai-flash {
  0%, 55% { background-color: var(--sms-primary-soft); }
  100% { background-color: #fff; }
}

/* ============================================================ 双周会汇报 */
/* 展示页是开会投屏用的，不是录入表单 —— 排版准则是「一屏扫得完、后排看得清」，
   所以留白、字号、行高都比别处大一档。录入表单在编辑页，两边样式不复用。 */

.mt-head {
  display: flex; align-items: flex-start; gap: 16px;
  flex-wrap: wrap; margin-bottom: 18px;
}
.mt-head-main { flex: 1 1 auto; min-width: 0; }
.mt-title {
  font-size: 21px; font-weight: 650; color: var(--sms-ink);
  margin: 0 0 7px; letter-spacing: -.018em; line-height: 1.35;
}
.mt-meta {
  display: flex; flex-wrap: wrap; gap: 6px 14px; align-items: center;
  font-size: 12.5px; color: var(--sms-muted);
}
.mt-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }

/* ---------------------------------------------------------- 汇报计时 */
/* 讲的人用来自查「讲了多久」，只在汇报模式里出现。
   **固定在右下角**：一往下翻页头部就滚没了，计时跟着头部走等于没有。
   右下角不跟任何东西打架（头部那排按钮在右上角）。
   **刻意做得淡**（用户要求）：白底浅灰字、不用红也不用琥珀、数字等宽 ——
   等宽是为了跳秒时宽度不变，不会把旁边的字挤得动来动去（那比颜色更烦人）。
   **不做**倒计时、不变色、不闪烁：这些都在给人压力，而这一页是拿来汇报的，不是考试。
   跑着 = 实心小点，暂停 = 空心圈，**不靠颜色也能看出状态**。 */
.mt-timer-hud {
  position: fixed; right: 18px; bottom: 16px; z-index: 30;
  display: flex; align-items: center; gap: 2px;
  padding: 3px 7px 3px 3px; border-radius: 999px;
  background: rgba(255, 255, 255, .93);
  border: 1px solid var(--sms-line);
  box-shadow: var(--sh-sm);
  font-size: 13.5px;
}
.mt-timer {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 1px 7px; border-radius: 999px;
  color: var(--sms-muted);
  font-variant-numeric: tabular-nums;
  cursor: pointer; user-select: none;
}
.mt-timer:hover { background: var(--sms-line-2); }
.mt-timer .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--sms-faint); }
.mt-timer.paused { color: var(--sms-faint); }
.mt-timer.paused .dot { background: transparent; box-shadow: inset 0 0 0 1.5px var(--sms-faint); }
.mt-timer-reset {
  border: 0; background: transparent; padding: 1px 5px; border-radius: 5px;
  color: var(--sms-faint); font-size: 13px; line-height: 1; cursor: pointer;
}
.mt-timer-reset:hover { color: var(--sms-muted); background: var(--sms-line-2); }

/* ---------------------------------------------------------- 概览条 */
.mt-summary {
  /* 3 列：原先第 4 格是「风险分布」，需求说它不是重要展示项，移除了 */
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px; margin-bottom: 16px;
}
.mt-stat {
  background: #fff; border: 1px solid var(--sms-line);
  border-radius: var(--r-md); padding: 13px 15px; box-shadow: var(--sh-xs);
}
.mt-stat .k { font-size: 12px; color: var(--sms-muted); margin-bottom: 6px; }
.mt-stat .v {
  font-size: 22px; font-weight: 650; color: var(--sms-ink);
  line-height: 1.1; letter-spacing: -.02em;
}
.mt-stat .v small { font-size: 13px; font-weight: 500; color: var(--sms-muted); }
.mt-stat .sub { font-size: 12px; color: var(--sms-muted); margin-top: 6px; }
/* 净值合计是这一屏里最该被看见的数字之一（领导最关心"这摊子值多少钱"），
   淡灰小字太容易被忽略，给它一个带底色的标签。 */
.mt-stat .netamt {
  display: inline-block; margin-left: 2px; padding: 1px 8px;
  border-radius: 999px; font-weight: 650;
  font-variant-numeric: tabular-nums; letter-spacing: -.01em;
}
.mt-stat.opp .sub { color: var(--sms-primary-hover); }
.mt-stat.opp .netamt { color: var(--sms-primary-hover); background: var(--sms-primary-soft); }
.mt-stat.won .sub { color: #0a7a53; }
.mt-stat.won .netamt { color: #0a7a53; background: var(--sms-ok-soft); }
.mt-stat.opp .v { color: var(--sms-primary-hover); }
.mt-stat.won .v { color: #0a7a53; }

/* ---------------------------------------------------------- 风险标记 */
.mt-risk {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12.5px; color: var(--sms-ink-2); white-space: nowrap;
}
.mt-risk .dot {
  width: 9px; height: 9px; border-radius: 50%; flex: 0 0 9px;
}
.mt-risk-green .dot { background: var(--sms-ok); }
.mt-risk-amber .dot { background: var(--sms-warn); }
.mt-risk-red .dot { background: var(--sms-danger); }

/* 概览条里那三个数字要和色点一样大，才像一组 */
.mt-risks { display: flex; gap: 16px; align-items: baseline; }
.mt-risks .mt-risk {
  font-size: 22px; font-weight: 650; letter-spacing: -.02em; color: var(--sms-ink);
}
.mt-risks .mt-risk .dot { width: 8px; height: 8px; flex-basis: 8px; }

/* ---------------------------------------------------------- 总览表 */
.mt-table-wrap { overflow-x: auto; border-radius: var(--r-lg); }
.mt-table {
  width: 100%; border-collapse: separate; border-spacing: 0;
  background: #fff; border: 1px solid var(--sms-line);
  border-radius: var(--r-lg); overflow: hidden; box-shadow: var(--sh-sm);
}
.mt-table thead th {
  background: #f7f9fc; text-align: left; font-size: 12px; font-weight: 600;
  color: var(--sms-muted); padding: 11px 14px;
  border-bottom: 1px solid var(--sms-line); white-space: nowrap;
}
.mt-table .c-org, .mt-table .c-name { min-width: 130px; }
.mt-table .c-bid { min-width: 170px; }
/* 大类那一列：每行都有徽章，给个稳定宽度，免得每行宽窄不一 */
.mt-table .c-cat { white-space: nowrap; }
.mt-table .c-risk { white-space: nowrap; }

.mt-row { cursor: pointer; }
.mt-row > td {
  padding: 13px 14px; font-size: 13.5px; color: var(--sms-ink-2);
  border-bottom: 1px solid var(--sms-line-2); vertical-align: middle;
  transition: background var(--t);
}
.mt-row:hover > td { background: #fafcff; }
.mt-row.open > td {
  background: var(--sms-primary-soft); border-bottom-color: transparent;
}
.mt-row:focus-visible { outline: 0; }
.mt-row:focus-visible > td { box-shadow: inset 0 0 0 2px var(--sms-primary-ring); }
.mt-row > td.c-amount {
  font-variant-numeric: tabular-nums; white-space: nowrap; font-weight: 500;
  color: var(--sms-ink);
}
.mt-row > td.c-bid { color: var(--sms-muted); }

.mt-caret {
  display: inline-block; width: 0; height: 0;
  border-left: 5px solid currentColor;
  border-top: 4.5px solid transparent; border-bottom: 4.5px solid transparent;
  color: var(--sms-faint); transition: transform .18s var(--ease);
}
.mt-row.open .mt-caret { transform: rotate(90deg); }

/* ---------------------------------------------------------- 展开的详情 */
.mt-detail { display: none; }
.mt-detail.open { display: table-row; }
.mt-detail > td {
  padding: 0; background: #fbfdff;
  border-bottom: 1px solid var(--sms-line-2);
}
.mt-detail-inner { padding: 16px 18px 18px 46px; }
/* 「招标/上线」那一对年月下拉（项目表单和会议编辑页共用）。
   Bootstrap 的 .form-select 自带 width:100%，放进 flex 容器里如果不显式平分，
   两个都会想占满整行 —— 容器一允许换行（flex-wrap）就上下叠起来了。
   这里给个明确的 flex:1 1 0，两边各占一半，跟容器怎么配都稳。 */
.bid-pair > .form-select { flex: 1 1 0; width: auto; min-width: 0; }

/* 「当前阶段 / 涉及协作」这两条：项目的属性，跟下面三段汇报正文不是一回事。
   整行给一块淡蓝底 + 左边一道实色竖条，标签做成白底彩色药丸 ——
   原来只有一个小灰药丸、底色又是白的，汇报模式投屏时基本看不见。
   两块底色相同、上下只隔 8px，看着是一组。 */
.mt-coop {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px;
  padding: 7px 12px; margin-bottom: 8px;
  border-radius: var(--r-sm);
  background: var(--sms-primary-tint);
  border-left: 3px solid var(--sms-primary);
  font-size: 13px; color: var(--sms-ink);
}
.mt-coop .k {
  font-size: 11.5px; font-weight: 600; color: var(--sms-primary);
  padding: 1px 8px; border-radius: 999px; background: #fff;
}
.mt-blank { font-size: 13px; color: var(--sms-muted); }

/* 「当前阶段」那一行：整条阶段链（走过 → 当前 → 未来），见 _stage_ladder.html。
   **底色必须是白的**：上面 .mt-coop 那块淡蓝底会把「走过」的淡蓝药丸吞掉，
   三态就只剩一点点深浅差，投屏上分不出来。左边那道实色竖条保留 —— 它才是
   「这两行是一组」的线索。标签药丸也跟着反过来（深字淡底），白底白丸是看不见的。 */
.mt-stage-row {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px;
  padding: 7px 12px; margin-bottom: 8px;
  border-radius: var(--r-sm);
  background: #fff;
  border: 1px solid var(--sms-line);
  border-left: 3px solid var(--sms-primary);
  font-size: 13px; color: var(--sms-ink);
}
/* 标签用**中性灰**，不跟阶段药丸一样用淡蓝 —— 一样的底色让「当前阶段」看着
   就像进度里的第一环（用户原话）。灰底灰字 = 「这是个字段名」，蓝底 = 「这是个阶段」。
   字号跟阶段药丸一样大（用户要求整体缩小到跟标签齐平）。 */
.mt-stage-row .k {
  font-size: 11.5px; font-weight: 600; color: var(--sms-ink-2);
  padding: 1px 8px; border-radius: 999px; background: var(--sms-line);
}
/* 药丸链。flex-wrap：字典加到十几级也只是折行，不会把版面撑破。 */
.mt-stage { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 4px; }
.mt-stage-pill {
  padding: 2px 10px; border-radius: 999px;
  font-size: 11.5px; font-weight: 600; white-space: nowrap;
}
.mt-stage-passed { background: var(--sms-primary-tint); color: var(--sms-primary); }
/* 当前那一级：实心深蓝 + 白字 + 外圈一道光环。**不放大** —— 放大过一版，用户要求
   跟其它药丸一样大：实心深蓝本来就跟淡蓝/白底一眼分得开，不必再靠字号。 */
.mt-stage-current {
  background: var(--sms-primary); color: #fff;
  box-shadow: 0 0 0 2px #fff, 0 0 0 4px var(--sms-primary-ring);
}
.mt-stage-future {
  background: #fff; color: var(--sms-muted);
  border: 1px solid var(--sms-line);
}
.mt-stage-sep { color: var(--sms-faint); font-size: 12px; line-height: 1; }
/* 兜底：阶段没定，或者那个值已经不在字典里了 —— 这时不画阶梯，退回文字。 */
.mt-stage-fallback { color: var(--sms-ink); }
.mt-stage-fallback .hint { color: var(--sms-muted); font-size: 12px; margin-left: 6px; }

/* ------------------------------------------------- 汇报模式：「正在汇报」到哪一块 */
/* 鼠标点选的位置标记：**只剩一块紫色背景**。
   这一路是砍下来的，砍掉的每一版都在 README 和 spec 里记着原因，别再往回加：
     - 左边加一道 4px 紫条 → 这页每个块本来就有 2-3px 的左边条，只差 1px + 一个色相
     - 整个展开块外面再套一圈框 → 太丑，用户否掉
     - 块自己一圈细框 + 留白里一支 ▶ → 用户说只留背景色就行
   所以现在只有底色。**底色因此取得比 --sms-violet-soft 深一档**：原来那个 #f3f0ff
   几乎就是白的，单独用根本看不出来（它当初只是给描边打底的）。
   紫色是因为绿/黄/红被风险等级占了、蓝被阶段和链接占了，这页没用过紫色。
   初始什么都不加 —— 「当前」完全由浏览器端 JS 决定，服务端不写死（JS 没跑就还是原样）。 */
.mt-seg.is-live {
  border-radius: var(--r-sm);
  background: var(--sms-violet-tint);
}
/* 汇报模式里这些东西是可以点的，给个手型 —— 不给的话没人知道能点。
   （这不是标记的一部分，是「可点」的唯一提示，别跟着标记一起删。） */
html.reporting .mt-seg { cursor: pointer; }
/* 回款明细：**整行**可选中（讲到「这笔款」时点那一行）。底色铺在每个 td 上 ——
   `tr` 上的 background 在部分浏览器里不生效。行自己的底线别动：留着才跟别的行一致。 */
.mt-pay tr.mt-seg.is-live > td { background: var(--sms-violet-tint); }

.mt-block { border-left: 2px solid var(--sms-line); padding-left: 13px; margin-bottom: 14px; }
.mt-block:last-child { margin-bottom: 0; }
.mt-block .h {
  font-size: 11.5px; font-weight: 600; color: var(--sms-muted);
  margin-bottom: 5px; letter-spacing: .02em;
}
.mt-block .b {
  font-size: 13.5px; line-height: 1.75; color: var(--sms-ink-2);
  /* 跟 .timeline-item .body 同款：换行要原样保留。
     模板里内容必须紧贴 <div class="b">，否则模板缩进会被当正文渲染出来。 */
  white-space: pre-wrap;
}
.mt-block.b1 { border-left-color: var(--sms-primary); }
.mt-block.b2 { border-left-color: var(--sms-danger); }
.mt-block.b3 { border-left-color: var(--sms-ok); }

/* ---------------------------------------------------------- 其它 · 图例 */
.mt-other {
  margin-top: 18px; background: #fff; border: 1px solid var(--sms-line);
  border-radius: var(--r-lg); padding: 15px 18px; box-shadow: var(--sh-sm);
}
.mt-other-b {
  margin-top: 9px; font-size: 13.5px; line-height: 1.75;
  color: var(--sms-ink-2); white-space: pre-wrap;
}
.mt-legend {
  display: flex; flex-wrap: wrap; gap: 8px 22px;
  margin-top: 14px; font-size: 12px; color: var(--sms-muted);
}

/* ---------------------------------------------------------- 年度目标完成 */
/* 汇报模式里领导最先要看的一眼：签单和回款各完成多少。纯数字 + 百分比，不做进度条
   （需求明确否掉了进度条）。整块用蓝色调，跟上面灰色的概览条区分开 —— 这是 KPI，
   那只是计数。 */
.mt-audit {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 14px; margin-bottom: 18px; padding: 16px 20px;
  background: linear-gradient(180deg, #fbfcff, #f6f9ff);
  border: 1px solid #d3defb; border-radius: var(--r-lg);
}
.mt-audit .k { font-size: 12.5px; color: var(--sms-muted); }
.mt-audit .v {
  margin-top: 4px; font-size: 21px; font-weight: 600; color: var(--sms-ink);
  font-variant-numeric: tabular-nums;
}
.mt-audit .v small { font-size: 12.5px; font-weight: 400; color: var(--sms-muted); }
.mt-audit .p { margin-top: 3px; font-size: 12.5px; font-weight: 600; color: var(--sms-primary); }
.mt-audit .p.muted { font-weight: 400; color: var(--sms-faint); }
html.reporting .mt-audit { padding: 20px 24px; }
html.reporting .mt-audit .k,
html.reporting .mt-audit .p { font-size: 14px; }
html.reporting .mt-audit .v { font-size: 28px; }

/* ---------------------------------------------------------- 收款块 */
/* 展示页的收款明细，复用 .mt-table 的表格外观，但**行不是可点开的**，所以没有
   .mt-row。这里必须自己把单元格排版补上：.mt-table 的单元格样式全挂在 .mt-row > td
   上，收款明细没有那个类，就掉回浏览器默认样式（内边距只有 2px 上下、字号 16px），
   跟正上方那张项目表一比，两截表格的行高和左边缘全都对不齐。

   写成 `.mt-pay .mt-table td` 而不是去改 `.mt-row > td`：改动那条会牵连总览表
   里 .mt-row / .mt-detail 的一堆覆盖关系（金额列、展开行、归档态），风险大得多。 */
.mt-pay { margin-top: 22px; }
.mt-pay-sum { margin-bottom: 12px; }
.mt-pay .mt-table tbody > tr > td {
  padding: 13px 14px; font-size: 13.5px; color: var(--sms-ink-2);
  border-bottom: 1px solid var(--sms-line-2); vertical-align: middle;
}
/* 最后一行不画底线，让表格的圆角收干净（总览表那边是同一条规矩） */
.mt-pay .mt-table tbody > tr:last-child > td { border-bottom: 0; }
/* 金额是这一块要看的那个数：等宽数字（几行对得齐才扫得快）、加粗、近黑 */
.mt-pay .mt-table td.c-amount {
  font-variant-numeric: tabular-nums; white-space: nowrap;
  font-weight: 500; color: var(--sms-ink);
}
/* 「2026年12月第2周」是一个整体，断在中间没法读 */
.mt-pay .mt-table td.c-time { white-space: nowrap; }
/* 付款条件是自由文本（「预付30%、验收后付60%、质保金10%」这种一句话），不能 nowrap
   —— 它比别的列长得多。给个下限只是别让它挤成每行一个字的竖条；太长就正常折行。 */
.mt-pay .mt-table .c-terms { min-width: 150px; }

/* 汇报模式：这四个数是要给领导看的，放大一档。标签跟着抬一点，不然反差太大。 */
html.reporting .mt-pay .pay-sum .k { font-size: 14px; }
html.reporting .mt-pay .pay-sum b { font-size: 26px; }
html.reporting .mt-pay .mt-table tbody > tr > td { font-size: 16px; padding: 17px 16px; }

/* ---------------------------------------------------------- 汇报模式 */
/* 开会投屏：藏掉侧边栏和顶栏，字号整体放大一档，一次只展开一个项目。
   只切 html 上的一个类名，不另做页面 —— 散会后去掉 ?report=1 就回到只读页。 */
html.reporting .sidebar,
html.reporting .sidebar-backdrop,
html.reporting .topbar { display: none !important; }
html.reporting .page { padding: 30px 36px 64px; max-width: none; }
html.reporting .mt-head { margin-bottom: 22px; }
html.reporting .mt-title { font-size: 27px; }
html.reporting .mt-meta { font-size: 13.5px; }
/* 计时器在投屏上稍大一档（它是给讲的人看的，多半在电脑那头扫一眼，但投影上也得认得出） */
html.reporting .mt-timer-hud { font-size: 15px; bottom: 20px; right: 22px; }
html.reporting .mt-summary { gap: 16px; }
html.reporting .mt-stat .v,
html.reporting .mt-risks .mt-risk { font-size: 28px; }
html.reporting .mt-stat .sub { font-size: 13px; }
html.reporting .mt-table thead th { font-size: 13px; padding: 13px 16px; }
html.reporting .mt-row > td { font-size: 16px; padding: 17px 16px; }
html.reporting .mt-risk { font-size: 14.5px; }
html.reporting .mt-detail-inner { padding: 20px 22px 22px 50px; }
html.reporting .mt-block .h { font-size: 13px; }
html.reporting .mt-block .b,
html.reporting .mt-other-b { font-size: 16px; line-height: 1.9; }
/* 这两条也要跟着放大 —— 用户就是站在投屏前看这一块的，字号不动等于白改 */
html.reporting .mt-coop { font-size: 15px; padding: 9px 14px; }
html.reporting .mt-coop .k { font-size: 13px; }
/* 阶段链在投屏上要跟着放大 —— 这一页的用途就是投出来给一屋子人看 */
html.reporting .mt-stage-row { font-size: 15px; padding: 9px 14px; }
html.reporting .mt-stage-row .k { font-size: 13px; }
html.reporting .mt-stage-pill { font-size: 13px; padding: 3px 11px; }
/* 当前那级在投屏模式下也不放大 —— 跟普通模式一个规矩：只靠颜色和光环区分定位 */
html.reporting .mt-stage-sep { font-size: 13px; }
html.reporting .mt-stage-fallback .hint { font-size: 13px; }
/* 「正在汇报」只靠底色标记，没有描边和 ▶ 可放大 —— 投屏那档不需要额外规则。
   真觉得后排看不清，加的不是描边（用户否过），是把非当前块的对比降一档，见 README。 */

@media (max-width: 991.98px) {
  .mt-summary { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .mt-title { font-size: 18px; }
  .mt-detail-inner { padding: 14px 14px 16px 18px; }
}

/* ---------------------------------------------------------- 编辑页 */
/* 展示页是投屏用的、要好读；编辑页是录入用的、要好填。两边取向不同，样式不复用。 */
.me-item .card-header .section-title { min-width: 0; overflow-wrap: anywhere; }
/* 三段的标签右边挂着「AI 润色」，所以标签和按钮要排一行 */
.me-label-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; margin-bottom: 5px;
}
.me-label-row .form-label { margin-bottom: 0; }
.me-status { min-height: 17px; }

/* ============================================================ 门户首页 */
/* 域名根目录那一页。**个人站点** —— 不出现任何单位信息。

   审美方向：**大厂式的亮色简约**（Apple / 华为那种），靠留白、字号和克制的质感
   撑住，不靠堆装饰。这一版在原来「近白 + 大字」的基础上加了三样，都还很克制：

     ① 顶部一层柔和光斑（两个大半径模糊的径向渐变）—— 只做背景，不占内容位
     ② 卡片和胶囊换成半透明白 + 发丝描边 + 两级阴影，悬停轻轻上浮
     ③ 品牌色从苹果蓝换成偏靛的 #4f6df5，另配一个更深的号专门给文字用

   仍然**不做**的：网格纹理、彩色描边叠着堆、等宽字体、序号、装饰插图。
   干净本身就是高级感 —— 改这一页时守住这条。光斑是唯一的例外，因为它是背景层；
   再往内容上叠东西就散了。

   不继承 base.html：它是「系统外面」的入口，不该出现任何系统内部的导航。 */

.portal-body {
  /* 门户自己的令牌，不复用后台那套 --sms-*：门户要零售级的干净，
     跟后台那种带边框、带底色的表单不是一回事。 */
  --p-bg: #fbfbfd;                        /* 页面底：不纯白，纯白在屏幕上偏冷 */
  --p-bg-soft: #f5f6fa;                   /* 页脚底 */
  --p-surface: rgba(255, 255, 255, .78);  /* 卡片：半透明白，让下面的光斑透一点上来 */
  --p-ink: #1a1d29;                       /* 主文字：近黑偏蓝，不用纯黑 */
  --p-ink-2: #5b6172;                     /* 次文字 */
  --p-ink-3: #9aa0b4;                     /* 分组标题、页脚那种更弱的字 */
  --p-line: rgba(26, 29, 41, .08);
  --p-brand: #4f6df5;                     /* 全站唯一强调色 */
  --p-brand-ink: #3b55d9;                 /* 压深一档，给文字用（原色当正文偏浅） */
  --p-brand-soft: rgba(79, 109, 245, .09);
  --p-r: 18px;
  --p-sh: 0 1px 2px rgba(26, 29, 41, .04), 0 8px 24px rgba(26, 29, 41, .06);
  --p-sh-hover: 0 2px 4px rgba(26, 29, 41, .06),
                0 16px 40px rgba(79, 109, 245, .14);

  min-height: 100vh; display: flex; flex-direction: column;
  background: var(--p-bg);
  color: var(--p-ink-2);
}

/* 页内锚点（#apps / #links）平滑滚过去。
   **必须挂在 <html> 上** —— scroll-behavior 不是可继承属性，跟 overflow 不一样，
   写在 <body> 上一点作用都没有。用 .portal-html 钩子限定在门户这一页，
   免得把后台那些页面的滚动一起改掉。 */
.portal-html { scroll-behavior: smooth; }

/* 精灵图不该占位。模板里用 class 而不是内联 style，所以这条必须有。 */
.portal-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

/* ---------------------------------------------------------- 站头 */
/* 半透明 + 饱和度增强 + 14px 模糊：滚动时底下的内容会透上来一点，但字始终清楚。 */
.portal-top {
  position: sticky; top: 0; z-index: 10;
  background: rgba(251, 251, 253, .72);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid var(--p-line);
}
.portal-top-in {
  max-width: 1060px; margin: 0 auto; padding: 0 24px;
  height: 58px; display: flex; align-items: center; gap: 26px;
}
.portal-brand {
  display: inline-flex; align-items: center; gap: 10px;
  font-size: 17px; font-weight: 700; color: var(--p-ink); letter-spacing: .01em;
}
/* 品牌标记只用品牌色的线稿，不套色块 —— 色块在这个尺度上显得吵 */
/* 站标：浅蓝渐变圆角方块 + 白线稿符号。跟登录页 `.auth-brand .logo` 是同一套
   （同一个渐变、同一个圆角），两页的品牌位才算同一个站。
   阴影比登录页那版收一档 —— 那边压在深色面板上，这边是近白底，照搬会发灰。 */
.portal-logo {
  flex: 0 0 auto; width: 30px; height: 30px;
  display: inline-grid; place-items: center;
  border-radius: 9px; color: #fff;
  background: linear-gradient(140deg, #4a6ee8, #2f54d8 60%, #2444b6);
  box-shadow: 0 4px 11px -4px rgba(47, 84, 216, .55),
              inset 0 1px 0 rgba(255, 255, 255, .24);
}
.portal-logo svg { width: 17px; height: 17px; }
/* 站标走填充、不走描边 —— 细线在 17px 下会糊（跟后台 `.ico` 那条一个道理）。
   模板上的 svg 标签写的是 fill="none" stroke="currentColor"，但 CSS 优先级高于
   表现属性，这条能盖掉它，不用去改模板里的属性。 */
.portal-logo svg, .portal-foot-logo svg { fill: currentColor; stroke: none; }
/* 导航做成胶囊：悬停时底色淡淡浮出来，比只换字色更有「按得到」的感觉 */
.portal-nav { margin-left: auto; display: flex; gap: 4px; }
.portal-nav a {
  padding: 6px 14px; border-radius: 999px;
  font-size: 14px; color: var(--p-ink-2);
  transition: background .18s var(--ease), color .18s var(--ease);
}
.portal-nav a:hover { background: var(--p-brand-soft); color: var(--p-brand-ink); }

/* ---------------------------------------------------------- 主视觉 */
/* 「大气」出在两处：字放到最大、上下留足空，以及背后那层几乎看不见的光。
   标题上方的短横线、渐变文字、装饰图标之类一律不用 —— 大厂的 hero
   就是一行大字加一行小字。 */
.portal-hero {
  position: relative; overflow: hidden;
  padding: 96px 24px 72px; text-align: center;
}
/* 两团柔和的光斑。半径给得很大、模糊 70px，所以只看得出「这块亮一点」，
   看不出是个圆 —— 一旦看出形状就变成廉价装饰了。
   用 ::before/::after 而不是加 div：纯装饰，不该进 DOM。 */
.portal-hero::before, .portal-hero::after {
  content: ""; position: absolute; border-radius: 50%;
  filter: blur(70px); pointer-events: none;
}
.portal-hero::before {
  width: 480px; height: 480px; top: -220px; left: 50%;
  transform: translateX(-70%);
  background: radial-gradient(circle, rgba(79, 109, 245, .16), transparent 70%);
}
.portal-hero::after {
  width: 380px; height: 380px; top: -160px; left: 50%;
  transform: translateX(10%);
  background: radial-gradient(circle, rgba(120, 80, 220, .12), transparent 70%);
}
/* 内容压在光斑上面。overflow:hidden 是必须的 —— 光斑超出 hero 往下溢，
   不裁掉会在正文区留下一道淡淡的色带。 */
/* 三层叠起来，顺序写死：
   ① 两团光斑 z-index 0 ② 背后那幅画 z-index 1 ③ 文字 z-index 2。
   不靠 DOM 顺序碰运气 —— 光斑是用 ::before/::after 画的，紧跟其后的子元素和
   它俩谁盖谁得算一遍，不如直接标出来。 */
.portal-hero::before, .portal-hero::after { z-index: 0; }
.portal-hero-art {
  position: absolute; z-index: 1; pointer-events: none;
  /* 靠右出血，裁掉一截 —— 这样它是「排版元素」而不是「居中贴的水印」。
     居中版是 `top: 50%; left: 50%; transform: translate(-50%, -50%)`。 */
  top: 50%; right: -80px; transform: translateY(-50%);
  width: 520px; height: 520px;
  /* 这一档是量出来的，不是随手写的：画的线条色是 #b0c7fa（很浅），
     且只覆盖一成面积（线稿）。在近白底 #fbfbfd 上：
       .10 → 线条只比底色暗 7 级，基本看不见（第一版就是这个问题）
       .22 → 暗 17 级，明显看得出来，但仍是底纹、不抢字
     再往上（.28+）就开始像装饰而不是背景了，慎调。 */
  opacity: .22;
}
.portal-hero-art img { display: block; width: 100%; height: 100%; }
.portal-hero > :not(.portal-hero-art) { position: relative; z-index: 2; }
.portal-hero-eyebrow {
  display: inline-block; margin-bottom: 22px;
  padding: 4px 14px; border-radius: 999px;
  font-size: 13px; font-weight: 600; letter-spacing: .04em;
  color: var(--p-brand-ink);
  background: var(--p-brand-soft);
  border: 1px solid rgba(79, 109, 245, .18);
}
.portal-hero-title {
  font-size: clamp(34px, 6vw, 56px); font-weight: 800; color: var(--p-ink);
  letter-spacing: -.02em; line-height: 1.15; margin: 0;
}
.portal-hero-sub {
  margin: 14px 0 0;
  font-size: clamp(15px, 2.4vw, 18px); color: var(--p-ink-2);
}
/* 实时日期时间。tabular-nums 是必须的 —— 不锁数字宽度的话，秒数每跳一次
   整行都会轻轻抖一下。时间比日期大一号：这一行真正要看的那个数是它。 */
.portal-clock {
  margin: 34px 0 0;
  display: inline-flex; align-items: baseline; gap: 10px;
  font-size: 14px; color: var(--p-ink-2);
  font-variant-numeric: tabular-nums;
}
.portal-clock .sep { color: rgba(26, 29, 41, .2); }
.portal-clock .time {
  font-size: 22px; font-weight: 600; color: var(--p-ink); letter-spacing: .02em;
}

/* ---------------------------------------------------------- 应用区 */
.portal-main {
  flex: 1 1 auto; width: 100%; max-width: 1060px;
  margin: 0 auto; padding: 0 24px 80px;
}
.portal-main section { scroll-margin-top: 78px; }
/* 两块内容之间留一口气，别贴着 */
.portal-main section + section { margin-top: 56px; }

/* 分组标题：小号、全大写、拉开字距，右边接一条发丝线把空白收住。
   它不跟内容抢注意力 —— 这一页的主角是卡片。 */
.portal-section-title {
  display: flex; align-items: center; gap: 12px;
  margin: 0 0 20px;
  font-size: 13px; font-weight: 600; color: var(--p-ink-3);
  letter-spacing: .14em; text-transform: uppercase;
}
.portal-section-title::after {
  content: ""; flex: 1; height: 1px; background: var(--p-line);
}

.portal-grid {
  display: grid;
  /* **固定两栏**：现在就是两套系统。用 auto-fit 会铺成四列、只填两格，
     右边空一大片，看着像没排满。加到三套以上时这里要改回 auto-fit。 */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 20px;
}

/* 卡片 = 半透明白 + 发丝描边 + 两级阴影。静态就带一点浮起，悬停再抬 4px、
   阴影染成品牌色、描边也偏一点品牌色。
   里面只有三样：图标方块、系统名、「进入系统」。**不写系统介绍** ——
   名称本身已经说清了是哪个系统，多一行小字反而把版面拉散。 */
.portal-card {
  position: relative;
  display: flex; flex-direction: column;
  min-height: 208px;
  padding: 30px 26px 24px;
  border-radius: var(--p-r);
  background: var(--p-surface);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid var(--p-line);
  /* 第二层是**内阴影做出来的上沿高光** —— 半透明 + 模糊那套的标准搭配，
     一道 1px 的亮边就让卡片像块玻璃而不是一块白纸。极淡，只在侧面看得出。 */
  box-shadow: var(--p-sh), inset 0 1px 0 rgba(255, 255, 255, .7);
  /* 卡片里的内容整体居中：图标、名字、「进入系统」都对着一条中轴排 */
  text-align: center;
  transition: transform .22s cubic-bezier(.2, .7, .3, 1),
              box-shadow .22s var(--ease), border-color .22s var(--ease);
}
.portal-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--p-sh-hover), inset 0 1px 0 rgba(255, 255, 255, .9);
  border-color: rgba(79, 109, 245, .28);
}

/* 图标独立一行、名字在它下面（竖向排），比图标和名字挤一行更透气 */
.portal-card-top {
  display: flex; flex-direction: column; align-items: center; gap: 18px;
}
.portal-chip {
  flex: 0 0 auto; width: 46px; height: 46px; border-radius: 13px;
  display: grid; place-items: center;
  color: var(--p-brand);
  background: linear-gradient(135deg, rgba(79, 109, 245, .12),
                                      rgba(120, 80, 220, .12));
  border: 1px solid rgba(79, 109, 245, .16);
  transition: transform var(--t);
}
.portal-chip svg { width: 24px; height: 24px; }
.portal-card:hover .portal-chip { transform: scale(1.06); }

.portal-card-name {
  font-size: 17px; font-weight: 650; color: var(--p-ink);
  letter-spacing: -.01em; line-height: 1.3;
}
/* 系统名下面那一行小字。跟名字挨紧一点（8px）—— 它们是一组，
   跟上面的图标（18px）拉开档次，否则三样看着一样远，读起来散。 */
.portal-card-desc {
  margin-top: 8px;
  font-size: 13.5px; color: var(--p-ink-2); line-height: 1.5;
}

/* 「进入系统」是卡片里唯一的彩色元素。它靠 card 上的 text-align: center 居中，
   不需要自己再做 flex。不画分隔线 —— 白卡片上再来一条线就是多一道噪点；
   靠 margin-top: auto 让它沉到底部，两张卡自然对齐。 */
.portal-card-go {
  margin-top: auto; padding-top: 20px;
  font-size: 14px; font-weight: 600; color: var(--p-brand-ink);
}

/* 可选的右上角小标签。正常不填，填了也只是个更浅的灰胶囊，不抢眼。 */
.portal-tag {
  position: absolute; top: 18px; right: 20px;
  font-size: 11px; color: var(--p-ink-2);
  background: rgba(26, 29, 41, .05);
  padding: 2px 9px; border-radius: 999px;
}

/* ---------------------------------------------------------- 常用入口 */
/* 白底胶囊，压在卡片下面 —— 用白色跟卡片拉开层次。
   这一栏还有个作用是撑住页面的下半截：只有两张卡片时整页太短，
   看着不像站点、倒像登录页。

   **六等分铺满整行**，不是 flex 换行。用 flex 的话胶囊只有文字那么宽、
   挤在左边，右边空一大片，看着像没排完版。六等分之后每个胶囊一样宽、
   首尾正好顶到卡片区的左右边缘，跟上面的卡片对齐。 */
.portal-links {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 12px;
}
.portal-link {
  display: flex; align-items: center; justify-content: center;
  min-height: 46px; padding: 10px 12px;
  border-radius: 999px;
  background: #fff;
  border: 1px solid var(--p-line);
  box-shadow: 0 1px 2px rgba(26, 29, 41, .03);
  font-size: 14px; font-weight: 500; color: var(--p-ink-2);
  /* 等分之后每格宽度固定，文字一律不换行 —— 「客户管理」换行会把整行撑高 */
  white-space: nowrap;
  transition: transform .18s var(--ease), border-color .18s var(--ease),
              color .18s var(--ease), box-shadow .18s var(--ease);
}
.portal-link:hover {
  transform: translateY(-2px);
  border-color: rgba(79, 109, 245, .32);
  color: var(--p-brand-ink);
  box-shadow: 0 6px 16px rgba(79, 109, 245, .12);
}

/* ---------------------------------------------------------- 页脚 */
/* 备案号是硬要求，不是装饰：ICP 号必须链到工信部、公安备案号链到网安平台。 */
.portal-foot {
  border-top: 1px solid var(--p-line);
  background: var(--p-bg-soft);
  padding: 32px 24px 40px;
}
.portal-foot-in {
  max-width: 1060px; margin: 0 auto;
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  text-align: center;
  font-size: 13px; color: var(--p-ink-3);
}
.portal-foot-nav { display: flex; gap: 22px; }
.portal-foot-nav a { color: var(--p-ink-2); transition: color .18s var(--ease); }
.portal-foot-nav a:hover { color: var(--p-brand-ink); }
/* 页脚这枚站标跟顶栏同一个符号，只是压成灰的、更小 —— 页脚不该抢视线 */
.portal-copy {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 12px; color: var(--p-ink-3);
}
.portal-foot-logo {
  flex: 0 0 auto; display: inline-grid; place-items: center;
  width: 16px; height: 16px; color: var(--p-ink-3); opacity: .8;
}
.portal-foot-logo svg { width: 15px; height: 15px; }
.portal-beian {
  display: flex; flex-wrap: wrap; justify-content: center; align-items: center;
  gap: 6px 10px; font-size: 12px; color: var(--p-ink-3);
}
.portal-beian a { color: var(--p-ink-3); transition: color .18s var(--ease); }
.portal-beian a:hover { color: var(--p-ink-2); }
.portal-beian .sep { color: rgba(26, 29, 41, .18); }
/* 公安备案号配一枚盾牌小图标（网安备案的惯用标记），跟页脚站标同色同尺寸，不抢视线 */
.portal-police { display: inline-flex; align-items: center; gap: 5px; }
.portal-police-badge { width: 14px; height: 14px; flex: 0 0 auto; opacity: .85; }

/* 键盘焦点。链接默认没有焦点环，不给的话键盘用户根本看不出走到哪了 ——
   卡片和胶囊那种大块元素尤其明显。 */
.portal-body a:focus-visible {
  outline: 2px solid var(--p-brand); outline-offset: 3px; border-radius: 6px;
}

/* ---------------------------------------------------------- 入场 */
/* 一次编排好的入场：题头先落，卡片跟上，最后是常用入口。幅度很小（12px），
   大厂的动效都克制在这个量级 —— 动作大了就显廉价。
   延迟按区块给（#apps / #links），不按类给：两个区块的小标题是同一个类，
   按类给会让下面那块的小标题比上面的卡片还先出现。
   用 backwards 而不是 both —— both 会让动画终值一直挂在元素上，把悬停的
   translateY 压死（动画的优先级高于 transition，不是"后写的赢"）。 */
@keyframes p-rise {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}
.portal-hero-eyebrow, .portal-hero-title, .portal-hero-sub, .portal-clock,
.portal-section-title, .portal-card, .portal-links {
  animation: p-rise .7s var(--ease) backwards;
}
.portal-hero-title { animation-delay: .06s; }
.portal-hero-sub { animation-delay: .12s; }
.portal-clock { animation-delay: .18s; }
#apps .portal-section-title { animation-delay: .22s; }
#apps .portal-card:nth-child(1) { animation-delay: .26s; }
#apps .portal-card:nth-child(2) { animation-delay: .32s; }
#apps .portal-card:nth-child(3) { animation-delay: .38s; }
#links .portal-section-title { animation-delay: .38s; }
.portal-links { animation-delay: .42s; }

/* 系统里关掉动效的那一档，门户也得跟着关 —— 包括平滑滚动 */
@media (prefers-reduced-motion: reduce) {
  .portal-html { scroll-behavior: auto; }
  .portal-hero-eyebrow, .portal-hero-title, .portal-hero-sub, .portal-clock,
  .portal-section-title, .portal-card, .portal-links { animation: none; }
  .portal-card, .portal-chip, .portal-link, .portal-nav a { transition: none; }
}

/* 平板：六等分每格只剩一百来像素，折成三列两行 */
@media (max-width: 991.98px) {
  .portal-links { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  /* 窄屏上那幅画会横跨到居中的文字底下，两边都糊 —— 直接收掉，留光斑就够了 */
  .portal-hero-art { display: none; }
}

@media (max-width: 767.98px) {
  .portal-top-in { height: 54px; padding: 0 16px; gap: 14px; }
  .portal-nav { gap: 2px; }
  .portal-nav a { padding: 6px 10px; }
  .portal-hero { padding: 64px 20px 48px; }
  .portal-main { padding: 0 16px 56px; }
  .portal-grid { grid-template-columns: 1fr; }
  .portal-card { padding: 24px 22px 20px; min-height: 0; }
  .portal-link { font-size: 13.5px; }
}

/* 手机：两列三行 */
@media (max-width: 575.98px) {
  .portal-links { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.me-note {
  margin-top: 14px; padding: 9px 12px; font-size: 12.5px; line-height: 1.6;
  background: var(--sms-warn-soft); border: 1px solid #f2ddb8;
  border-radius: var(--r-sm); color: #8a5204;
}
.me-hr { margin: 18px 0 14px; border-color: var(--sms-line-2); opacity: 1; }


/* ------------------------------------------------ 项目报告时间线的行尾操作 */
/* .timeline-item .meta span 默认是灰底胶囊，这里要的是纯文字链接，得单独去掉底色 */
.timeline-item .meta .rpt-actions {
  margin-left: auto; gap: 10px; padding: 0; background: none;
}
.timeline-item .meta .rpt-actions a,
.timeline-item .meta .rpt-actions button {
  background: none; border: 0; padding: 0; font: inherit;
  color: var(--sms-primary); cursor: pointer;
}
.timeline-item .meta .rpt-actions a:hover,
.timeline-item .meta .rpt-actions button:hover { text-decoration: underline; }
.timeline-item .meta .rpt-actions .danger { color: var(--sms-danger); }

/* ------------------------------------------------------------ AI 分析历史 */
/* 一条一个 <details>，最新的由模板加 open 默认展开 —— 分析正文动辄上千字，
   十条全摊开会把页面拉得没法看。 */
.ai-analysis {
  border: 1px solid var(--sms-line); border-radius: var(--r-md);
  margin-bottom: 10px; overflow: hidden; background: #fff;
}
.ai-analysis:last-child { margin-bottom: 0; }
.ai-analysis > summary {
  cursor: pointer; padding: 9px 13px; background: #f7f9fc;
  font-size: 12.5px; color: var(--sms-muted);
  display: flex; flex-wrap: wrap; gap: 6px; align-items: center;
  list-style: none;
}
.ai-analysis > summary::-webkit-details-marker { display: none; }
.ai-analysis > summary::before {
  content: "▸"; color: var(--sms-primary); font-size: 11px;
  transition: transform .15s var(--ease);
}
.ai-analysis[open] > summary::before { content: "▾"; }
.ai-analysis > summary span {
  padding: 1px 8px; border-radius: 999px; background: #eef1f6; color: #5b6779;
  font-variant-numeric: tabular-nums;
}
.ai-body {
  padding: 13px 15px; border-top: 1px solid var(--sms-line);
  font-size: 13.5px; line-height: 1.75; color: var(--sms-ink-2);
  /* 与 .timeline-item .body 同款：分析正文的换行要原样保留。
     模板里内容必须紧贴 <div class="ai-body">，否则缩进会被当正文渲染。 */
  white-space: pre-wrap;
}
/* 展开后的操作行（目前只有「删除这次分析」）。放在正文下面而不是塞进 <summary>：
   <summary> 只允许短语内容，塞 <form> 非法；用 form="..." 指过来也会因为
   summary 的默认行为把 details 顺带折叠掉。 */
.ai-actions {
  padding: 10px 15px 12px; border-top: 1px solid var(--sms-line);
  text-align: right; background: var(--sms-surface-2);
}
/* 顶栏左边那几个按钮的显示/隐藏。**这三条要放一起看**，很容易漏一条 ——
   `.mobile-toggle`（三条横杠）当初就漏了默认的 `display:none`，于是桌面端也露出来了，
   而它绑的是手机端抽屉的开关，在桌面上点了完全没反应（是个死按钮）。
   加新的顶栏按钮时，记得这里补一条默认隐藏 + 在下面某个断点里放出来。 */
.mobile-toggle { display: none; }   /* 手机端抽屉开关：桌面端用不着 */
.back-btn { display: none; }        /* 返回上一页：只给桌面端（手机上系统返回手势更顺手） */
.sb-toggle { display: none; }       /* 侧栏收窄：手机端本来就是抽屉，没有"收窄"这回事 */

@media (min-width: 992px) {
  .back-btn { display: inline-flex; }
  .sb-toggle { display: inline-flex; }
}

/* ------------------------------------------------------------ 回款台账 */
/* 项目详情页与双周会编辑页共用的那一块：上面一行合计，下面明细表。
   合计是领导扫一眼要看的东西，所以数字用 near-black + tabular-nums（几行数字
   对齐才读得快），标签反而是灰的小字。 */
/* 回款/目标的合计条：**并排的数字块**，不是「一行文字 + ·号分隔」。
   原来那种 flex 横排 + 分隔符的写法，在汇报模式（字号放大一档）会折行，
   结果是 `·` 被甩到行尾**单独占一行**，投屏上很难看。改成一格一个数之后，
   屏幕再窄也只是格子变窄，永远不会出现孤零零的分隔符。 */
.pay-sum {
  display: grid;
  /* 自适应列数：宽的时候四个一排，窄了自动折成两排，不用写断点 */
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 10px 20px;
  padding: 13px 16px; margin-bottom: 14px;
  background: var(--sms-surface-2); border: 1px solid var(--sms-line);
  border-radius: var(--r-md);
}
.pay-sum .item { min-width: 0; }
.pay-sum .k { font-size: 12px; color: var(--sms-muted); }
.pay-sum b {
  display: block; margin-top: 3px;
  font-size: 18px; font-weight: 600; color: var(--sms-ink);
  font-variant-numeric: tabular-nums;
}
.pay-sum b small { font-size: 12px; font-weight: 400; color: var(--sms-muted); }
/* 「正式名称」是公司内部立项名，没人记得住 —— 压成次要文字，不跟金额抢视线 */
.pay-official { color: var(--sms-faint); font-size: 12.5px; }

/* 侧边栏收起：只留图标。桌面端专用 —— 手机上它本来就是抽屉，没有"收窄"这回事。
   收没收起由 <html> 上的 .sb-collapsed 决定，那个类在首屏绘制前就由 base.html
   里的内联脚本按 localStorage 打好，所以翻页不会闪。 */
@media (min-width: 992px) {
  /* .sb-toggle 的显示开关在上面那组「顶栏按钮显隐」里，这里不重复写 */
  .sidebar { transition: width .18s var(--ease), flex-basis .18s var(--ease); }

  html.sb-collapsed .sidebar { width: 68px; flex: 0 0 68px; }
  html.sb-collapsed .sidebar .brand { justify-content: center; padding-left: 0; padding-right: 0; }
  html.sb-collapsed .sidebar .brand-name,
  html.sb-collapsed .sidebar .nav-label,
  html.sb-collapsed .sidebar .sidebar-foot { display: none; }
  html.sb-collapsed .sidebar .nav-item { justify-content: center; padding: 9.5px 0; }
  html.sb-collapsed .sidebar .nav-item .ico { margin: 0; }
  /* 分组标题在收起态变成一条细分隔线，比整段消失更能保住层次 */
  html.sb-collapsed .sidebar .nav-group {
    height: 1px; margin: 12px 17px; padding: 0;
    overflow: hidden; font-size: 0;
    background: rgba(255, 255, 255, .08);
  }
}

@media (max-width: 991.98px) {
  .sidebar {
    position: fixed; z-index: 1045; left: 0; top: 0;
    transform: translateX(-100%);
    transition: transform .24s var(--ease);
    box-shadow: 0 0 60px rgba(8, 14, 26, .5);
  }
  .sidebar.open { transform: none; }
  .sidebar-backdrop {
    display: none; position: fixed; inset: 0; z-index: 1040;
    background: rgba(9, 15, 27, .48);
    backdrop-filter: blur(2px);
  }
  .sidebar-backdrop.show { display: block; }
  .mobile-toggle { display: inline-flex; }
  .page { padding: 14px 13px 44px; }
  .topbar { padding: 9px 13px; min-height: 54px; }
  .topbar .crumb { font-size: 14px; gap: 6px; }
  /* 手机顶栏太窄，塞不下"头像 + 名字 + 角色·大区"三样：
     头像会被压成椭圆，角色那行会把胶囊顶成两行高。
     只留名字 —— 认得出是谁、点得开菜单就够了，其余在「个人资料」里看。
     !important 是必须的：模板里这两个 span 带 Bootstrap 的 .d-block，
     那条工具类本身带 !important，不加就压不住（试过一次，角色行照旧显示）。 */
  .topbar .user-chip { padding: 5px 13px; }
  .topbar .user-chip .avatar,
  .topbar .user-chip .meta .r { display: none !important; }
  .topbar .user-chip .meta .n { font-size: 13.5px; }
  .card > .card-body { padding: 14px; }
  .card > .card-header { padding: 12px 14px; min-height: 48px; }
  .toolbar { padding: 11px 13px; }
  .table > :not(caption) > * > * { padding: 10px 12px; }
  .weekgrid { font-size: 12.5px; }
  .weekgrid td.day { width: 56px; font-size: 12px; }
  .stat-tile .v { font-size: 21px; }
}

/* ============================================================ 错误页 */
/* 404 / 403 / 400 / 500。**一页两形态**：站外走 templates/errors/public.html
   （独立页，跟门户同一套亮色简约风），站内走 templates/errors/app.html
   （继承 base.html，带侧边栏）。哪一档用哪个形态见 apps/portal/errors.py。

   这一版**刻意没做**的事：给 500 配红色、给每一档配一张插画、给错误码加描边或
   渐变、堆一串「你可以试试」的建议。门户那一节的审美方向（大厂式亮色简约，
   干净本身就是高级感）在这里同样适用，而且错误页要比首页更克制 —— 人已经撞上
   问题了，页面上再堆情绪是添乱。唯一的装饰是背后那两团光斑，跟 hero 同源。 */

/* ---------------------------------------------------------- 公开形态 */
/* 竖排 flex 而不是 `grid` + `place-items: center`。**grid 那版在手机上会横向溢出**：
   不给 grid-template-columns 时那一列的宽度按 max-content 算，而 .err-lead 的
   max-width:30em（≈465px）比 350px 的视口还宽，整列就撑出去了 —— 标题文字被切掉、
   按钮跑到屏幕外。flex 列的交叉轴尺寸直接取容器宽度，没有这个坑。 */
.err-stage {
  position: relative; overflow: hidden;
  flex: 1 1 auto;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  padding: 72px 24px 88px; text-align: center;
}
/* 光斑跟 hero 是同一套做法（大半径 + 70px 模糊，只看得出「这块亮一点」）。
   一个在上、一个在下，把中间那块内容兜住。 */
.err-stage::before, .err-stage::after {
  content: ""; position: absolute; border-radius: 50%;
  filter: blur(70px); pointer-events: none; z-index: 0;
}
.err-stage::before {
  width: 460px; height: 460px; top: -210px; left: 50%;
  transform: translateX(-72%);
  background: radial-gradient(circle, rgba(79, 109, 245, .15), transparent 70%);
}
.err-stage::after {
  width: 360px; height: 360px; bottom: -180px; left: 50%;
  transform: translateX(4%);
  background: radial-gradient(circle, rgba(120, 80, 220, .11), transparent 70%);
}
/* min-width:0 跟上面同一个原因：flex 项目默认 min-width:auto，
   内容里只要有个不肯缩的东西（长链接、宽表格），它就把容器顶宽。 */
.err-inner { position: relative; z-index: 1; width: 100%; max-width: 560px; min-width: 0; }

/* 错误码是这一页的主角，字号给到比门户主标题还大 —— 门户的主标题是「欢迎」，
   这一页的标题要让人一眼看出是第几号错误。
   tabular-nums 在这里不是防抖：400 / 403 / 404 / 500 四个数字宽度一致，
   改文案、换档位时版面不会跟着跳。 */
.err-code {
  margin: 0;
  font-size: clamp(64px, 13vw, 108px); font-weight: 800;
  line-height: 1; letter-spacing: -.05em;
  color: var(--p-ink); font-variant-numeric: tabular-nums;
}
.err-title {
  margin: 18px 0 0;
  font-size: clamp(21px, 3.4vw, 27px); font-weight: 700;
  color: var(--p-ink); letter-spacing: -.015em; line-height: 1.35;
}
.err-lead {
  margin: 12px auto 0; max-width: 30em;
  font-size: clamp(14px, 2.2vw, 15.5px); color: var(--p-ink-2);
}

.err-actions {
  margin-top: 30px;
  display: flex; gap: 12px; justify-content: center; flex-wrap: wrap;
}
/* 按钮是门户上唯一需要自己做的一种控件（原来只有卡片和文字链）。
   胶囊形跟 .portal-hero-eyebrow 那个胶囊对得上；min-height 44px 是触屏的
   最小可点面积，手机上两个按钮折行之后也不至于太挤。 */
.err-btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; padding: 0 24px; border-radius: 999px;
  font-size: 14.5px; font-weight: 600; letter-spacing: .01em;
  background: var(--p-surface); color: var(--p-ink);
  border: 1px solid var(--p-line);
  box-shadow: 0 1px 2px rgba(26, 29, 41, .04);
  transition: transform .18s var(--ease), box-shadow .18s var(--ease),
              border-color .18s var(--ease), background .18s var(--ease);
}
.err-btn:hover {
  transform: translateY(-1px); color: var(--p-ink);
  border-color: rgba(79, 109, 245, .35); box-shadow: var(--p-sh);
}
.err-btn.is-primary {
  background: var(--p-brand); border-color: transparent; color: #fff;
  box-shadow: 0 1px 2px rgba(26, 29, 41, .06),
              0 8px 20px -8px rgba(79, 109, 245, .6);
}
.err-btn.is-primary:hover {
  background: var(--p-brand-ink); color: #fff;
  box-shadow: 0 2px 4px rgba(26, 29, 41, .08),
              0 14px 30px -10px rgba(79, 109, 245, .72);
}

/* 只有 404 会渲染这一块（视图那边控制，见 apps/portal/errors.py）。
   复用门户的「常用入口」：名片式错误页到这里就断头了，给几条路比一句「抱歉」有用。 */
.err-links { margin-top: 56px; text-align: left; }

/* 入场：跟门户同一条编排（p-rise，12px 位移），延迟按元素从上到下递增。 */
.err-code, .err-title, .err-lead, .err-actions { animation: p-rise .7s var(--ease) backwards; }
.err-code { animation-delay: .04s; }
.err-title { animation-delay: .1s; }
.err-lead { animation-delay: .16s; }
.err-actions { animation-delay: .22s; }

/* ---------------------------------------------------------- 站内形态 */
/* 系统里别的页面都是「顶栏 + 一张卡片」的骨架，这一页也照办，只是卡片里没有
   表格和按钮组，只有一段话加两个出口。min-height 让它落在页面中间偏上的位置，
   跟别的内容页保持同一种呼吸感。
   跟 .err-stage 一样用 flex 列，不用 grid —— 理由见上面 .err-stage 那段注释。 */
.err-app {
  min-height: calc(100vh - 220px);
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  padding: 24px 0 56px;
}
.err-app-panel {
  width: 100%; max-width: 520px; min-width: 0; text-align: center;
  padding: 44px 36px 40px;
  background: var(--sms-surface);
  border: 1px solid var(--sms-line);
  border-radius: var(--r-xl);
  box-shadow: var(--sh-sm);
}
/* 错误码做成一块方章。跟公开形态的大号数字是同一个意思，只是系统里的字号体系
   本来就小一号，直接搬那个 108px 会把这张卡片撑爆。 */
.err-app-code {
  display: inline-grid; place-items: center;
  min-width: 84px; height: 84px; padding: 0 18px; margin: 0 0 20px;
  border-radius: var(--r-lg);
  background: var(--sms-primary-soft); color: var(--sms-primary);
  font-size: 34px; font-weight: 800; letter-spacing: -.03em;
  font-variant-numeric: tabular-nums;
}
.err-app-title { font-size: 21px; font-weight: 700; margin: 0 0 8px; }
.err-app-lead {
  margin: 0 auto; max-width: 30em;
  font-size: 13.5px; line-height: 1.7; color: var(--sms-muted);
}
.err-app-actions {
  margin-top: 26px;
  display: flex; gap: 10px; justify-content: center; flex-wrap: wrap;
}

@media (prefers-reduced-motion: reduce) {
  .err-code, .err-title, .err-lead, .err-actions { animation: none; }
  .err-btn { transition: none; }
  .err-btn:hover { transform: none; }
}

@media (max-width: 767.98px) {
  .err-stage { padding: 52px 20px 64px; }
  .err-actions { gap: 10px; }
  /* 手机上一行放不下两个胶囊，各占一行、都拉满宽 —— 挨着排会一个宽一个窄 */
  .err-btn { width: 100%; }
  .err-links { margin-top: 44px; }
  .err-app { min-height: 0; padding: 8px 0 40px; }
  .err-app-panel { padding: 34px 22px 30px; }
}

/* ==========================================================================
   智能体接入（MCP 令牌页）
   这一页的读者是「要照着抄配置的人」，所以代码块是主角：
   等宽字体、可横向滚动、复制按钮紧贴在角上。配置文本一律走 <pre> 的
   textContent，不塞进 HTML 属性 —— 多行加引号，塞属性里迟早被转义转坏。
   ========================================================================== */

/* 刚签发那一枚：整页只有这一处是一次性的东西，给它最重的视觉重量 */
.mcp-fresh {
  border-color: #f0c98a;
  box-shadow: var(--sh-sm);
}
.mcp-fresh > .card-header { background: var(--sms-warn-soft); }
.mcp-fresh > .card-header .section-title { color: #7a4a06; }

.mcp-token-line {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
.mcp-token-line code {
  flex: 1 1 320px;
  min-width: 0;
  padding: 10px 12px;
  background: var(--sms-surface-2);
  border: 1px solid var(--sms-line);
  border-radius: var(--r-sm);
  font-size: 13.5px;
  word-break: break-all;
  color: var(--sms-ink);
}

.mcp-lede { font-size: 13.5px; color: var(--sms-ink-2); line-height: 1.75; }
.mcp-lede code, .hint code, .mcp-points code {
  padding: 1px 5px;
  background: var(--sms-primary-soft);
  border-radius: var(--r-xs);
  font-size: 12.5px;
  color: var(--sms-primary-hover);
}

.mcp-points {
  margin: 0; padding-left: 18px;
  font-size: 13px; color: var(--sms-ink-2); line-height: 1.9;
}
.mcp-points li::marker { color: var(--sms-faint); }

/* 列表里的令牌标识：只要看得出「这几枚不是同一枚」就够了 */
.mcp-prefix {
  padding: 1px 6px; background: var(--sms-surface-2);
  border: 1px solid var(--sms-line); border-radius: var(--r-xs);
  font-size: 12.5px; color: var(--sms-muted);
}

.mcp-issue { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.mcp-issue-label { font-size: 13px; color: var(--sms-muted); margin: 0; white-space: nowrap; }
.mcp-issue .form-control { flex: 1 1 260px; min-width: 0; }

.mcp-guide + .mcp-guide { margin-top: 22px; }
.mcp-guide-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 8px; }
.mcp-guide-name { font-size: 13.5px; font-weight: 600; color: var(--sms-ink); }
.mcp-guide-where {
  font-size: 12.5px; color: var(--sms-muted);
  font-family: var(--sms-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
}

.mcp-code {
  margin: 0 0 8px;
  padding: 13px 15px;
  background: #0d1524;             /* 深底：配置文本里的空格与换行才看得清 */
  color: #d7e0f0;
  border-radius: var(--r-md);
  font-size: 12.8px;
  line-height: 1.75;
  overflow-x: auto;
  white-space: pre;
}

.mcp-people { display: flex; flex-wrap: wrap; gap: 8px; }
.mcp-person {
  padding: 4px 11px;
  border: 1px solid var(--sms-line);
  border-radius: 999px;
  font-size: 13px; text-decoration: none; color: var(--sms-ink-2);
  transition: background var(--t), border-color var(--t);
}
.mcp-person:hover { background: var(--sms-primary-soft); border-color: var(--sms-primary-ring); }

.btn.copied { background: var(--sms-ok-soft); border-color: var(--sms-ok); color: #0a7a53; }

/* 「怎么接上」整卡折叠：原生 <details>，summary 复用 card-header 的外观，
   省掉一段 JS。收起时 summary 是卡片里唯一可见的元素，四个角都该圆、底边去掉。 */
.mcp-fold-card > summary.card-header {
  cursor: pointer;
  list-style: none;
  user-select: none;
}
.mcp-fold-card > summary.card-header::-webkit-details-marker { display: none; }
.mcp-fold-caret {
  flex: 0 0 auto;
  width: 6px; height: 6px; margin-left: 2px;
  border-right: 1.6px solid currentColor;
  border-bottom: 1.6px solid currentColor;
  transform: rotate(-45deg);
  transition: transform var(--t);
}
.mcp-fold-card[open] > summary.card-header .mcp-fold-caret { transform: rotate(45deg); }
.mcp-fold-card:not([open]) > summary.card-header {
  border-bottom: 0;
  border-radius: calc(var(--r-lg) - 1px);
}

/* ---------------------------------------------------------------- 行程日历 */
.cal-card { overflow: hidden; }
.cal-head { display:flex; align-items:center; justify-content:space-between; gap:16px; padding:18px 20px 14px; border-bottom:1px solid var(--sms-line-2); }
.cal-month { display:flex; align-items:center; gap:10px; }
.cal-month-title { min-width:130px; text-align:center; }
.cal-month-title strong { display:block; font-size:17px; color:var(--sms-ink); letter-spacing:-.01em; }
.cal-today-link { display:inline-block; margin-left:6px; font-size:12px; color:var(--sms-primary); white-space:nowrap; }
.cal-today-link:hover { color:var(--sms-primary-hover); text-decoration:underline; }
.cal-nav { width:30px; height:30px; display:inline-flex; align-items:center; justify-content:center; border-radius:50%; color:var(--sms-muted); border:1px solid transparent; transition:background var(--t), color var(--t); }
.cal-nav:hover { background:var(--sms-primary-soft); color:var(--sms-primary); }
.cal-nav-next .ico { transform:rotate(180deg); }
.cal-head-right { display:flex; align-items:center; gap:8px; }
.cal-owner-form { display:flex; align-items:center; gap:8px; }
.cal-owner-label { font-size:12.5px; color:var(--sms-muted); white-space:nowrap; }
.cal-owner-form .form-select { width:140px; }
.cal-overview { display:flex; flex-direction:column; gap:7px; padding:12px 20px; background:var(--sms-surface-2); border-bottom:1px solid var(--sms-line-2); }
.cal-kpis { display:flex; align-items:baseline; flex-wrap:wrap; gap:4px 20px; }
.cal-kpi { display:inline-flex; align-items:baseline; gap:4px; font-size:12.5px; color:var(--sms-muted); white-space:nowrap; }
.cal-kpi strong { font-size:17px; font-weight:700; color:var(--sms-ink); letter-spacing:-.02em; }
.cal-cityline { display:flex; align-items:center; gap:8px; min-width:0; }
.cal-cityline-label { flex:0 0 auto; font-size:12px; color:var(--sms-faint); }
.cal-cityline-scroll { display:flex; align-items:center; gap:6px; min-width:0; overflow-x:auto; padding-bottom:2px; scrollbar-width:thin; scrollbar-color:var(--sms-line) transparent; }
.cal-cityline-scroll::-webkit-scrollbar { height:5px; }
.cal-cityline-scroll::-webkit-scrollbar-thumb { background:var(--sms-line); border-radius:99px; }
.cal-citychip { flex:0 0 auto; display:inline-flex; align-items:baseline; gap:3px; padding:2px 9px; border-radius:99px; background:var(--sms-primary-tint); font-size:12px; color:var(--sms-ink-2); white-space:nowrap; }
.cal-citychip b { color:var(--sms-primary); font-weight:700; }
.cal-grid { display:grid; grid-template-columns:repeat(7, minmax(0, 1fr)); gap:8px; padding:0 20px; }
.cal-grid-head { padding-top:14px; padding-bottom:8px; }
.cal-grid-head span { text-align:center; font-size:12px; font-weight:600; color:var(--sms-faint); letter-spacing:.04em; }
.cal-grid-body { padding-bottom:20px; }
.cal-day { position:relative; min-height:78px; display:flex; flex-direction:column; align-items:flex-start; gap:6px; padding:9px 10px; border:1px solid var(--sms-line); border-radius:var(--r-md); background:var(--sms-surface); color:var(--sms-ink-2); font:inherit; text-align:left; }
.cal-day.is-outside { background:transparent; border-color:transparent; opacity:.34; color:var(--sms-muted); text-decoration:none; cursor:pointer; transition:opacity var(--t), border-color var(--t), background var(--t); }
.cal-day.is-outside:hover { opacity:.85; border-color:var(--sms-line); background:var(--sms-surface); text-decoration:none; }
.cal-day.is-selected { border-color:var(--sms-primary); box-shadow:0 0 0 3px var(--sms-primary-ring); }
.cal-day-no { font-size:13px; font-weight:600; line-height:1.2; color:var(--sms-ink-2); }
.cal-day.has-data { cursor:pointer; box-shadow:var(--sh-xs); transition:border-color var(--t), box-shadow var(--t), transform var(--t); }
.cal-day.has-data:hover { border-color:var(--sms-primary-ring); box-shadow:var(--sh-md); transform:translateY(-1px); }
.cal-day.has-data:focus-visible { outline:none; border-color:var(--sms-primary); box-shadow:0 0 0 3px var(--sms-primary-ring); }
.cal-day.today .cal-day-no { width:22px; height:22px; display:inline-flex; align-items:center; justify-content:center; border-radius:50%; background:var(--sms-primary); color:#fff; margin:-3px 0 -1px -3px; }
.cal-day.today:not(.has-data) { border-color:var(--sms-primary-ring); }
.cal-cities { display:flex; flex-direction:column; gap:5px; max-width:100%; }
.cal-city { display:inline-block; max-width:100%; padding:2px 10px; border-radius:99px; background:var(--sms-primary-soft); color:var(--sms-primary); font-size:14px; font-weight:600; line-height:1.45; letter-spacing:-.01em; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.cal-city::before { content:""; display:inline-block; width:5px; height:5px; margin-right:5px; border-radius:50%; background:currentColor; opacity:.5; vertical-align:1px; }
.cal-city.is-muted { background:var(--sms-line-2); color:var(--sms-faint); font-weight:500; }
.cal-city.is-muted::before { display:none; }
.cal-count { position:absolute; right:9px; bottom:7px; font-size:11px; color:var(--sms-faint); }
.cal-empty { display:flex; flex-direction:column; align-items:center; gap:6px; padding:28px 20px 34px; color:var(--sms-muted); }
.cal-empty .ico { color:var(--sms-faint); }
.cal-empty strong { font-size:14px; color:var(--sms-ink-2); }
.cal-empty span { font-size:12.5px; }
.cal-detail { border-top:1px solid var(--sms-line-2); background:var(--sms-surface-2); }
.cal-detail-placeholder { padding:22px 20px; font-size:13px; color:var(--sms-muted); text-align:center; }
.cal-detail-head { display:flex; align-items:flex-start; justify-content:space-between; flex-wrap:wrap; gap:10px 14px; padding:16px 20px 12px; }
.cal-detail-title { display:flex; align-items:center; flex-wrap:wrap; gap:8px; }
.cal-detail-title strong { font-size:18px; color:var(--sms-ink); letter-spacing:-.01em; }
.cal-detail-weekday { font-size:12.5px; color:var(--sms-muted); }
.cal-detail-title .cal-cities { flex-direction:row; flex-wrap:wrap; }
.cal-detail-actions { display:flex; align-items:center; flex-wrap:wrap; gap:10px; }
.cal-detail-source { font-size:12px; color:var(--sms-faint); }
.cal-detail-body { display:grid; grid-template-columns:repeat(2, minmax(0, 1fr)); align-items:start; gap:12px; padding:0 20px 20px; }
.cal-detail-body > .cal-visit:only-child,
.cal-detail-body > .cal-visit:last-child:nth-child(odd) { grid-column:1 / -1; }
.cal-visit { padding:14px 16px; border:1px solid var(--sms-line); border-radius:var(--r-md); background:var(--sms-surface); box-shadow:var(--sh-xs); }
.cal-visit-head { display:flex; align-items:baseline; justify-content:space-between; flex-wrap:wrap; gap:4px 12px; }
.cal-visit-title { display:flex; align-items:center; gap:8px; min-width:0; }
.cal-visit-name { font-size:15px; color:var(--sms-ink); }
a.cal-visit-name { text-decoration:none; }
a.cal-visit-name::after { content:"›"; margin-left:4px; color:var(--sms-faint); font-weight:400; }
a.cal-visit-name:hover { color:var(--sms-primary); text-decoration:underline; }
.cal-inline-link { color:var(--sms-primary); text-decoration:none; }
.cal-inline-link:hover { text-decoration:underline; }
.cal-visit-no { flex:0 0 auto; padding:1px 8px; border-radius:99px; background:var(--sms-ok-soft); color:var(--sms-ok); font-size:11.5px; font-weight:600; }
.cal-visit-place { font-size:12px; color:var(--sms-muted); }
.cal-visit-fullname { margin-top:2px; font-size:12.5px; color:var(--sms-muted); word-break:break-word; }
.cal-visit-fullname a { color:inherit; text-decoration:none; }
.cal-visit-fullname a:hover { color:var(--sms-primary); text-decoration:underline; }
.cal-visit-facts { display:flex; flex-wrap:wrap; gap:4px 16px; margin-top:6px; font-size:12.5px; }
.cal-visit-fact { display:inline-flex; gap:6px; min-width:0; color:var(--sms-ink-2); word-break:break-word; }
.cal-visit-fact b { flex:0 0 auto; font-weight:400; color:var(--sms-faint); }
.cal-visit-fact.is-warn { color:var(--sms-warn); }
.cal-visit-action { margin-top:10px; padding:9px 11px; border-radius:var(--r-sm); background:var(--sms-primary-tint); }
.cal-visit-action-label { display:block; margin-bottom:3px; font-size:11.5px; font-weight:600; color:var(--sms-primary); }
.cal-visit-action-text { margin:0; font-size:13px; line-height:1.65; color:var(--sms-ink-2); white-space:pre-wrap; word-break:break-word; }
.cal-visit-meta { display:grid; grid-template-columns:repeat(2, minmax(0, 1fr)); gap:4px 16px; margin:10px 0 0; }
.cal-visit-meta-row { display:grid; grid-template-columns:72px minmax(0, 1fr); gap:6px; font-size:12.5px; }
.cal-visit-meta-row.is-wide { grid-column:1 / -1; }
.cal-visit-meta-row dt { color:var(--sms-faint); font-weight:400; }
.cal-visit-meta-row dd { margin:0; color:var(--sms-ink-2); word-break:break-word; }
@media (max-width:720px) {
  .cal-head { flex-direction:column; align-items:stretch; gap:10px; padding:14px 14px 12px; }
  .cal-month { justify-content:flex-start; gap:4px; }
  .cal-month-title { flex:1; min-width:0; }
  .cal-today-link { margin-left:2px; font-size:11.5px; }
  .cal-overview { gap:5px; padding:10px 14px; }
  .cal-kpis { gap:3px 14px; }
  .cal-kpi { font-size:11.5px; }
  .cal-kpi strong { font-size:15px; }
  .cal-cityline { gap:6px; }
  .cal-cityline-label { font-size:11px; }
  .cal-citychip { font-size:11px; padding:2px 8px; }
  .cal-grid { gap:3px; padding:0 8px; }
  .cal-grid-head { padding-top:10px; padding-bottom:6px; }
  .cal-grid-head span { font-size:11px; letter-spacing:0; }
  .cal-grid-body { padding-bottom:14px; }
  .cal-day { min-height:64px; padding:4px; gap:3px; border-radius:var(--r-sm); }
  .cal-day-no { font-size:12px; }
  .cal-city { font-size:10.5px; padding:1px 2px; letter-spacing:-.01em; }
  .cal-city::before { display:none; }
  .cal-day.today .cal-day-no { width:19px; height:19px; }
  .cal-count { top:6px; right:5px; bottom:auto; font-size:10px; }
  .cal-detail-head { padding:14px 14px 10px; }
  .cal-detail-title strong { font-size:16px; }
  .cal-detail-body { grid-template-columns:minmax(0, 1fr); padding:0 14px 14px; }
  .cal-visit { padding:12px; }
  .cal-visit-meta { grid-template-columns:minmax(0, 1fr); }
  .cal-detail-placeholder { padding:18px 14px; }
}
