/* 根字号维持浏览器默认16px(v32.5曾短暂调到18px,按要求改回来)。 */
html {
  font-size: 16px;
  /* 侧栏顶部到视口顶部的实际距离(导航栏高度+容器上边距),这个值因浏览器/系统的字体渲染、
     导航栏是否换行等因素会有细微差异,不能硬编码猜一个rem数——真实数值由 sidebarMenu.js 在
     页面加载和窗口尺寸变化时实测后写回这个变量,这里给的只是JS跑之前的兜底默认值。 */
  --admin-shell-top: 4.75rem;
}
body {
  background-color: #f4f6f9;
}
/* 内容较短时,底部不再留大片空白——用flex布局把主体内容撑到至少占满一屏,
   底部固定跟一条窄边的版权信息栏,无论屏幕多高,空白都只有这一条窄边,不会随屏幕变化。 */
html, body { height: 100%; }
body { display: flex; flex-direction: column; }
body > .container-fluid { flex: 1 0 auto; }
.app-footer {
  flex-shrink: 0;
  padding: .5rem 1rem;
  text-align: center;
  font-size: .72rem;
  color: #8a94a3;
  background: #eef1f5;
  border-top: 1px solid #dee2e6;
}
@media print {
  .app-footer { display: none !important; }
}
.login-body {
  min-height: 100vh;
}
.login-card {
  width: 100%;
  max-width: 380px;
}

/* v34新增:登录页改成参考设计的左右分屏样式——左边品牌+表单(白底),右边公司业务闭环标语+
   渐变背景(呼应新LOGO的蓝绿配色,带一点"科技感"),小屏幕(手机/窄浏览器)下右边直接隐藏,
   只保留左边表单,不强行挤压成两栏挤在一起。 */
.login-body-split { min-height: 100vh; background: #fff; }
.login-split-shell { display: flex; min-height: 100vh; }
.login-split-left {
  flex: 1 1 480px;
  max-width: 560px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 3rem clamp(1.5rem, 6vw, 5rem);
}
/* v34.1:品牌区直接放公司logo原图(图标+文字已经是一张图,不用再拼六边形图标+文字两段了) */
.login-brand-row { margin-bottom: 1.6rem; }
.login-company-logo { height: 42px; width: auto; }
/* v34.3:产品标题区改成"图标在上、文字在下"竖排居中(公司logo那一行仍然保持左上角不动,
   只有这一块单独居中,不影响下面表单还是左对齐)——图标也适当放大一些,更像一个独立的
   产品标志区,而不是紧挨着文字的小图标 */
.login-product-row {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: .9rem;
  margin: 2.75rem 0 2rem;
}
.login-product-icon { height: 84px; width: auto; }
.login-product-title {
  font-size: clamp(1.6rem, 3vw, 2.1rem);
  font-weight: 500;
  letter-spacing: .01em;
  margin: 0;
  background: linear-gradient(90deg, #1f2d3d 0%, #0d6efd 60%, #17c3b2 100%);
  -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
}
.login-form-split .form-label { font-size: .8rem; color: #495057; }
.login-submit-btn { background: #1f2d3d; border-color: #1f2d3d; padding: .55rem 0; font-weight: 600; }
.login-submit-btn:hover { background: #14202c; border-color: #14202c; }
.login-split-right {
  flex: 1 1 50%;
  background:
    radial-gradient(1200px 600px at 15% 20%, rgba(23,195,178,.12), transparent 60%),
    radial-gradient(1200px 700px at 85% 80%, rgba(13,110,253,.12), transparent 60%),
    #f4f6f8;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 3rem;
  text-align: center;
  gap: 2.5rem;
}
/* v34.2:标语去掉粗体,改跟产品标题(.login-product-title)同一套"渐变字+轻字重+拉开字距"的
   科技感处理,不用死板的纯黑粗体标语 */
.login-tagline {
  font-size: clamp(1.3rem, 2.3vw, 1.7rem);
  font-weight: 400;
  letter-spacing: .06em; /* 全大写之后字距比原来再拉开一点,大写字母挤在一起容易糊成一团 */
  line-height: 1.35;
  text-transform: uppercase; /* 只影响英文/西语这种有大小写的文字,中文本身没有大小写区分,不受影响 */
  white-space: pre-line; /* i18n.js用textContent渲染,标语里如果有换行用\n而不是<br>,这里配合显示出来 */
  background: linear-gradient(90deg, #1f2d3d 0%, #0d6efd 55%, #17c3b2 100%);
  -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
}
.login-flow-row { display: flex; gap: clamp(1rem, 3vw, 2.5rem); flex-wrap: wrap; justify-content: center; }
.login-flow-step {
  display: flex; flex-direction: column; align-items: center; gap: .5rem;
  font-size: .72rem; font-weight: 700; letter-spacing: .05em; color: #1f2d3d;
}
.login-flow-step i.bi {
  font-size: 1.6rem;
  width: 3.2rem; height: 3.2rem;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: #fff;
  color: #0d6efd;
  box-shadow: 0 .3rem .8rem rgba(31,45,61,.08);
}
.app-navbar {
  background: #1f2d3d;
  padding-top: .2rem;
  padding-bottom: .2rem;
}
.app-navbar .navbar-brand, .app-navbar .nav-link, .app-navbar .navbar-text {
  color: #fff !important;
}
.table-responsive {
  /* 之前固定75vh封顶,在侧栏改成精确贴合视口高度之后反而成了短板——侧栏和右边内容区已经被
     撑到同一个精确高度了,表格自己却在75vh就不再长高,表格下面多出一截空当,状态栏没法紧贴
     到跟侧栏底部按钮对齐的位置。改成跟侧栏同一个视口高度上限,表格才能把flex分给它的空间用完,
     状态栏才能顶到最底。 */
  max-height: calc(100vh - var(--admin-shell-top) - .75rem);
}
/* 表格区域按可用空间自适应高度,内容少时不再在表格下方/页面底部留大片空白;
   多出来的高度改成撑高表格可视区域(能多看几行/多滚动一点),而不是变成看不见用途的空白margin。
   :not(.d-none) 是为了只对"当前正显示的那个标签页"生效——每个标签页div平时用d-none切换显示/隐藏。
   光靠 flex:1 1 auto 不够——它只在"父级有明确的高度、且比内容自然高度更高"时才有多余空间可撑;
   之前 .admin-content 及外层容器都没有明确高度(只是 auto),table-responsive 实际上没有可撑的空间,
   所以在有些浏览器/分辨率组合下表格下方仍然留白。这里补上 flex:1 1 auto + min-height:0,让"当前
   标签页"这一层也参与撑高链条(.admin-shell靠min-height兜底一个基准高度 → admin-content通过下面
   的 display:flex 把高度传给它 → 当前标签页flex:1撑满admin-content → 再传给table-responsive)。 */
.admin-content > div:not(.d-none),
.container-fluid > div:not(.d-none):not(.admin-shell) {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}
/* 这里必须是明确的 height,不能只给 min-height——之前只给下限的话,.admin-content 的实际高度是
   "内容自己撑出来多高就是多高"(表格哪怕设了max-height,只要它上面还有工具栏、下面还有状态栏,
   三者加起来就会比视口还高一截),行高统计出来的这个"内容自然高度"又会顺着 align-items:stretch
   反过来把 admin-shell 撑得比侧栏还高,状态栏就被推到视口外面,得滚动浏览器才能看到——这正是
   实测到的bug。改成明确的 height 之后,admin-shell 自己就是这么高、不再"看内容多高决定多高",
   表格被留出的空间只会是"总高度减去工具栏和状态栏用掉的",flex会正确地把表格挤到这个空间里、
   多出来的行数靠表格自己的滚动条看,状态栏就能稳稳地停在跟侧栏按钮同一行。 */
.admin-shell { height: calc(100vh - var(--admin-shell-top) - .75rem); }
.admin-content {
  display: flex;
  flex-direction: column;
}
.table-responsive {
  flex: 1 1 auto;
  min-height: 220px;
}
/* 表格空白区域可以直接按住拖动平移(上下左右),不用非得精准对准滚动条 */
.table-responsive { cursor: grab; }
.table-responsive.drag-scrolling { cursor: grabbing; user-select: none; }
.table-responsive th, .table-responsive td { cursor: default; }
.table-responsive th[data-col-key] { cursor: grab; } /* 表头本身还保留原来"可拖拽调整列顺序"的手势提示 */

/* 表格下方的状态栏:显示当前共几条/筛选后几条/最后刷新时间,把原来表格下面那块空白利用起来 */
.table-status-bar {
  flex: 0 0 auto;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: .5rem;
  padding: .35rem .6rem;
  margin-top: .35rem;
  background: #eef1f5;
  border: 1px solid #dee2e6;
  border-radius: .4rem;
  font-size: .74rem;
  color: #6c757d;
  flex-wrap: wrap;
}
/* 包装状态色标:New Original=绿, Used Original=黄, Blank Box=红,历史"-"数据用中性灰 */
.pkg-badge { display:inline-block; padding:.25em .6em; border-radius:.35rem; font-size:.8rem; font-weight:600; color:#fff; white-space:nowrap; }
.pkg-New_Original { background:#198754; }
.pkg-Used_Original { background:#e0a800; color:#3a2e00; }
.pkg-Blank_Box { background:#dc3545; }
.pkg-unknown { background:#adb5bd; color:#212529; }

.badge-cart { background:#0d6efd; }
.badge-confirmed { background:#198754; }
.badge-shipped { background:#6f42c1; }
.badge-cancelled { background:#6c757d; }
.badge-pending { background:#fd7e14; }
.badge-backorder-pending { background:#dc3545; }
.badge-backorder-approved { background:#0dcaf0; color:#000; }

/* 统一状态配色(v32.3):物料/需求单/采购单三张表里凡是"审核状态/审批状态"这一类徽章,一律按
   同一套规则上色——驳回/作废/取消=红,通过/生效=绿,待审/待批准/评估中这类"还没定案"的中间状态=
   深黄色背景(区别于Bootstrap自带的bg-warning那种偏浅的亮黄,深色背景配白字更醒目、也跟"预警"
   的视觉分量对得上),不管在哪张表看到的都是同一种颜色语言,不用来回猜这个黄跟那个黄是不是一个意思。 */
.badge-status-approved { background:#198754; color:#fff; }
.badge-status-cancelled { background:#dc3545; color:#fff; }
.badge-status-pending { background:#a86500; color:#fff; }
.col-toggle-panel {
  max-height: 220px;
  overflow-y: auto;
}
th.sortable { cursor: pointer; }
th.sortable:hover { background-color: #e9ecef; }

/* 表头一律单行显示,超出用省略号+鼠标悬浮显示完整文字(title属性),不再允许自动换行——
   之前允许换行在列比较窄时会导致英文长词/"QTY/PP"这类词被一个字符一行地拆开,表头变得很高很占地方。
   列宽不够就用省略号,需要看全称就拖宽这一列,或者用表头小菜单里的"自动调整列宽"。 */
.table-responsive th {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: middle;
  line-height: 1.25;
  max-width: 260px;
  font-size: .78rem;
  padding-top: .4rem;
  padding-bottom: .4rem;
}
.table-responsive td {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 260px;
  font-size: .82rem;
}
th[data-col-key] {
  position: relative;
  cursor: grab;
  padding-right: 28px !important;
}
th[data-col-key]:active { cursor: grabbing; }
.col-resize-handle {
  position: absolute;
  right: 0;
  top: 0;
  bottom: 0;
  width: 8px;
  cursor: col-resize;
  z-index: 6;
}
.col-resize-handle:hover, .col-resize-handle:active {
  background: rgba(13, 110, 253, .35);
}

/* 表头小菜单触发按钮(三点图标):排序/按列筛选/自动调宽/隐藏本列都从这个菜单进 */
.col-menu-trigger {
  position: absolute;
  right: 11px;
  top: 2px;
  cursor: pointer;
  font-size: .8rem;
  color: #8a94a3;
  z-index: 6;
  padding: 2px;
}
.col-menu-trigger:hover { color: #0d6efd; }
.col-menu-trigger-active { color: #0d6efd; }
.col-header-menu {
  position: fixed;
  background: #fff;
  border: 1px solid #dee2e6;
  border-radius: .5rem;
  box-shadow: 0 .5rem 1.5rem rgba(0,0,0,.18);
  padding: .4rem;
  width: 230px;
  z-index: 2000;
  font-size: .85rem;
  cursor: default;
}
.col-header-menu hr { margin: .35rem 0; }
.col-menu-item {
  padding: .4rem .5rem;
  border-radius: .35rem;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: .5rem;
}
.col-menu-item:hover { background: #f1f3f5; }
.col-menu-filter { padding: 0 .1rem; }

/* "自定义显示列"改成弹出面板(点按钮才出现,不占表格上方常驻空间),面板内每一行可以直接拖拽
   调整列顺序,跟表头拖拽是同一套逻辑,两边操作会互相同步。 */
.col-picker-popup {
  position: fixed;
  background: #fff;
  border: 1px solid #dee2e6;
  border-radius: .5rem;
  box-shadow: 0 .5rem 1.5rem rgba(0,0,0,.18);
  padding: .5rem;
  width: 260px;
  z-index: 2000;
  font-size: .85rem;
}
.col-picker-hint { padding: 0 .3rem .4rem; }

/* 物料库存"操作"下拉菜单:所在单元格/表格容器都设了 overflow:hidden/auto 做省略号截断和滚动条,
   菜单打开(JS里popperConfig已强制strategy:fixed跳出裁切)后再把层级提到跟其它"跳出表格"的浮层
   (列筛选菜单/自定义列面板)一样高,避免被吸顶表头或别的浮层盖住。 */
.mat-ops-icon + .dropdown-menu { z-index: 2000; }
.col-picker-list { max-height: 320px; overflow-y: auto; margin-bottom: .4rem; }
.col-picker-row {
  display: flex;
  align-items: center;
  gap: .4rem;
  padding: .3rem .3rem;
  border-radius: .35rem;
  cursor: grab;
}
.col-picker-row:hover { background: #f1f3f5; }
.col-picker-row.dragging { opacity: .4; }
.col-picker-row .form-check-label { flex: 1 1 auto; cursor: pointer; }
.col-picker-drag-handle { color: #adb5bd; }
.col-picker-reset { width: 100%; }

/* 需求#6:toolbar顶部自定义按钮跟搜索框保持一栏布局——按钮改成"只显示图标",原文字/计数挪进
   .toolbar-label-wrap(JS里common.js的enableCompactToolbarButtons做的)并用这条规则隐藏,
   完整文字改成悬浮提示(title)显示,这样一排能塞下更多按钮,不容易挤到换行。 */
.toolbar-row .btn .toolbar-label-wrap { display: none; }
.toolbar-row .btn > i { margin-right: 0 !important; }

/* 冻结表头:表格容器可滚动,表头始终吸顶 */
.table-responsive {
  overflow-y: auto;
  position: relative;
}
.table-responsive thead th {
  position: sticky;
  top: 0;
  z-index: 5;
  background-color: #eef1f5;
  box-shadow: 0 1px 0 #dee2e6;
}
.table-responsive thead th.sortable:hover { background-color: #dfe4ea; }

/* 每托盘数量(栈板包装数量):跟包装状态列一样用彩色徽章统一风格,而不是整格铺色 */
.table-responsive thead th.col-pallet-highlight {
  background-color: #ffe69c !important;
}
.qty-badge {
  display: inline-block;
  padding: .25em .6em;
  border-radius: .35rem;
  font-size: .8rem;
  font-weight: 600;
  color: #fff;
  background: #6f42c1;
  white-space: nowrap;
}

/* Pallet QTY(整栈板数+散货数量) 突出显示,引导以整栈板为单位销售 */
.col-full-pallet {
  color: #084298;
  font-weight: 700;
  background-color: #e7f1ff;
}
.table-responsive thead th.col-full-pallet {
  background-color: #cfe2ff !important;
}

/* 实时库存徽章:有货=蓝底白字,库存<=0=红底白字 */
.stock-badge {
  display: inline-block;
  padding: .25em .65em;
  border-radius: .35rem;
  font-size: .85rem;
  font-weight: 700;
  color: #fff;
  white-space: nowrap;
}
.stock-badge-pos { background: #0d6efd; }
.stock-badge-neg { background: #dc3545; }

/* 汇总栏 */
.summary-bar {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  margin-bottom: .75rem;
}
.summary-chip {
  background: #fff;
  border: 1px solid #dee2e6;
  border-radius: .5rem;
  padding: .4rem .8rem;
  font-size: .82rem;
  color: #495057;
}
.summary-chip strong {
  display: block;
  font-size: 1.05rem;
  color: #1f2d3d;
}

/* 工具栏(搜索+自定义列+导出+打印+汇总)合并成一行,给表格腾出更多显示空间 */
.toolbar-row { margin-bottom: .5rem; }
.toolbar-search { width: 200px; flex: 0 1 200px; }
.toolbar-select { width: 150px; flex: 0 1 150px; }
.summary-bar-inline {
  margin-bottom: 0;
  gap: .4rem;
}
.summary-bar-inline .summary-chip {
  padding: .25rem .55rem;
  font-size: .72rem;
  white-space: nowrap;
}
.summary-bar-inline .summary-chip strong {
  display: inline;
  font-size: .82rem;
  margin-left: .25rem;
}
@media (max-width: 991px) {
  .toolbar-search, .toolbar-select { width: 100%; flex: 1 1 100%; }
  .summary-bar-inline { width: 100%; }
}

/* 语言切换与LOGO */
.lang-switcher .dropdown-menu { min-width: 9rem; }
.lang-switcher .dropdown-item.active { background-color: #e7f1ff; color: #084298; }

/* 右上角"系统设置"齿轮菜单:语言/时区/账号管理/备份/退出登录都收在这一个下拉里 */
.settings-dropdown-menu { min-width: 240px; }
.lang-switcher-inline .lang-inline-btn { font-size: .78rem; }
.app-logo { height: 35px; width: auto; }
.login-logo { height: 60px; width: auto; margin-bottom: .5rem; }
.brand-block { line-height: 1.1; flex: 0 0 auto; width: max-content; }
.brand-company { font-size: .85rem; font-weight: 700; color: #fff; white-space: nowrap; }
.brand-system { font-size: .65rem; color: #c7d0da; letter-spacing: .02em; white-space: nowrap; }
@media (max-width: 576px) {
  .app-logo { height: 28px; }
  .brand-company { font-size: .78rem; }
  .brand-system { font-size: .58rem; }
}

/* v34新增:系统主页(卡片式入口)。风格跟顶部导航栏(深蓝#1f2d3d)+登录页新LOGO呼应,
   用一点渐变/发光的"科技感"细节(卡片顶部渐变条+悬浮时的光晕),但整体还是克制的浅色卡片网格,
   不喧宾夺主——毕竟每天真正干活是在点进去之后的二级操作界面,主页只是个入口。 */
.home-hero { padding: .25rem .1rem .5rem; }
.home-hero-title {
  font-size: 1.25rem; font-weight: 700; color: #1f2d3d;
  background: linear-gradient(90deg, #1f2d3d 0%, #0d6efd 55%, #17c3b2 100%);
  -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
}
.home-hero-sub { font-size: .8rem; color: #6c757d; margin-top: .15rem; }
.home-card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: .9rem;
}
.home-card {
  position: relative;
  background: #fff;
  border: 1px solid #e9ecef;
  border-radius: .7rem;
  padding: 1.1rem .9rem .9rem;
  cursor: pointer;
  overflow: hidden;
  transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease;
}
.home-card::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px;
  background: linear-gradient(90deg, #0d6efd, #17c3b2);
  opacity: 0; transition: opacity .15s ease;
}
.home-card:hover { transform: translateY(-3px); box-shadow: 0 .5rem 1.2rem rgba(13,110,253,.12); border-color: #cfe2ff; }
.home-card:hover::before { opacity: 1; }
.home-card-icon {
  width: 42px; height: 42px; border-radius: .55rem;
  background: linear-gradient(135deg, #0d6efd15, #17c3b220);
  display: flex; align-items: center; justify-content: center;
  font-size: 1.25rem; color: #0d6efd; margin-bottom: .6rem;
}
.home-card-title { font-size: .92rem; font-weight: 600; color: #1f2d3d; margin-bottom: .2rem; }
.home-card-desc { font-size: .74rem; color: #868e96; line-height: 1.35; }
/* 没有任何操作权限的模块:整卡置灰,只留模块名+一个锁形图标+"访问权限受限"提示,不显示原来的说明文字，
   也不会因为hover/点击露出任何子目录信息——点击后走跟侧栏一样的toast提示,不会真正导航进去 */
.home-card-restricted { cursor: not-allowed; background: #f8f9fa; }
.home-card-restricted:hover { transform: none; box-shadow: none; border-color: #e9ecef; }
.home-card-restricted .home-card-icon { background: #e9ecef; color: #adb5bd; }
.home-card-restricted .home-card-title,
.home-card-restricted .home-card-desc { color: #adb5bd; }
.home-card-lock { position: absolute; top: .6rem; right: .7rem; color: #adb5bd; font-size: .85rem; }

/* 打印 / 导出PDF:加logo水印背景+机密声明,防止打印件外流后被冒用来源不明或商业盗用 */
.print-header { color: #1f2d3d; }
@media print {
  @page { margin: 12mm; }
  body {
    position: relative;
  }
  body::before {
    content: '';
    position: fixed;
    inset: 0;
    background-image: url('assets/logo.png');
    background-repeat: repeat;
    background-size: 260px auto;
    background-position: center;
    opacity: .06;
    z-index: 9999;
    pointer-events: none;
  }
  .table-responsive {
    max-height: none !important;
    overflow: visible !important;
  }
  table { font-size: 10px; }
  .summary-bar-inline { margin-bottom: .5rem !important; }
  .col-resize-handle { display: none !important; }
}

@media (max-width: 576px) {
  .table-responsive { max-height: 55vh; }
  h4 { font-size: 1.1rem; }
  .summary-chip { flex: 1 1 45%; }
}

/* 左侧菜单栏(管理端):功能标签页按分组显示在左边,内容区在右边;
   分组可折叠、分组和组内标签页都可拖拽调整顺序,给以后新增模块留出结构空间。*/
.admin-shell {
  display: flex;
  align-items: stretch; /* 这条现在只影响右边 .admin-content(撑满可用高度那条撑高链条要靠它);
                            侧栏自己不再吃这条规则,单独用 align-self:flex-start 跳出来,见下面 .admin-sidebar */
  gap: 1rem;
}
/* 侧栏改成"始终按视口高度固定",不再靠 align-items:stretch 去跟 .admin-content 比高低——
   之前侧栏的高度是被"拉伸成跟右边内容区一样高"这件事决定的,内容区一旦比一屏还高(数据多的表、
   长表单),整个侧栏(包括它自己的sticky定位)也跟着被拉到那么高,导致sticky失去意义:
   跟着页面一起往上滚,菜单顶部的项目滚出屏幕,底部按钮和页脚一起出现在视口底部——效果等于完全没固定住
   (在Safari上尤其明显,stretch+sticky同时用是已知的兼容性坑)。
   现在改成 align-self:flex-start 让侧栏彻底跳出"跟内容区比高"这件事,自己的高度就锁定视口高度
   (height,不只是max-height上限),不管右边内容多高多矮,侧栏永远是这么大一块、贴着视口顶部固定住,
   菜单项多的话就靠自己内部 .admin-sidebar-groups 的滚动条来看,不会带着整个侧栏跟页面一起跑。
   top/height 都用 --admin-shell-top 这个变量(sidebarMenu.js实测写入,见该文件),而不是随便猜一个
   rem数——猜的数字只要跟导航栏实际高度差一点,侧栏底部就会超出或不够到视口底边,"表单管理/恢复布局"
   这两个按钮就会跟右边表格下面的状态栏对不齐,或者被顶到屏幕外面。
   纵向仍然是三段式flex布局:菜单分组区/中间空白/底部固定栏位。 */
.admin-sidebar {
  width: 230px;
  flex: 0 0 230px;
  background: #fff;
  border: 1px solid #dee2e6;
  border-radius: .6rem;
  padding: .6rem;
  position: sticky;
  top: var(--admin-shell-top);
  align-self: flex-start;
  height: calc(100vh - var(--admin-shell-top) - .75rem);
  max-height: calc(100vh - var(--admin-shell-top) - .75rem);
  display: flex;
  flex-direction: column;
  transition: width .18s ease, flex-basis .18s ease;
}
.admin-sidebar #adminNavSource { display: none; } /* 只是给sidebarMenu.js取数据用的原始来源,渲染出来的分组结构会把这些li挪走 */
/* min-height:0 是必须的,不是可写可不写——flex子项默认的自动最小高度是"按内容撑出来的高度",
   不加这条的话,即使 admin-shell 已经给了明确高度,.admin-content 仍然会因为"内容天生就想比这个
   高度高"而拒绝被压缩到那个高度以内,等于上面 admin-shell 定死高度的效果被这里悄悄抵消掉。 */
.admin-content { flex: 1 1 auto; min-width: 0; min-height: 0; }

/* 菜单分组区:占满侧栏里除了底部固定栏位以外的所有剩余空间(flex:1),自身内容超出时单独滚动;
   内容没那么多的时候,底下就是自然留白——这块留白就是给以后新模块预留的位置。 */
.admin-sidebar-groups {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
}
.admin-sidebar-groups::-webkit-scrollbar { width: 5px; }
.admin-sidebar-groups::-webkit-scrollbar-thumb { background: #dee2e6; border-radius: 3px; }

.sidebar-group + .sidebar-group { margin-top: .3rem; }
.sidebar-group.dragging { opacity: .4; }
/* 主功能模块(分组标题):比子项字号大一点、加粗、颜色更深,图标用主题蓝色区分出"这是一级"。 */
.sidebar-group-header {
  display: flex;
  align-items: center;
  gap: .35rem;
  padding: .5rem .5rem;
  border-radius: .4rem;
  cursor: grab;
  font-weight: 700;
  font-size: .8rem;
  letter-spacing: .02em;
  color: #1f2d3d;
  text-transform: uppercase;
  user-select: none;
}
.sidebar-group-header:hover { background: #f1f3f5; }
.sidebar-group-header:active { cursor: grabbing; }
.sidebar-group-header > i.bi { color: #0d6efd; font-size: .95rem; }
.sidebar-drag-handle { color: #ced4da !important; cursor: grab; }
.sidebar-group-chevron { transition: transform .15s ease; font-size: .7rem; color: #adb5bd !important; }
.sidebar-group.collapsed .sidebar-group-chevron { transform: rotate(-90deg); }
.sidebar-group.collapsed .sidebar-group-body { display: none; }
/* 子模块(组内标签页):在分组标题基础上明显缩进,左边加一条细竖线当"从属于上面这组"的视觉引导线;
   字号比标题小一号、颜色更浅、图标用中性灰(选中态另外变白),跟主功能模块拉开主次差异。 */
.sidebar-group-body {
  padding-left: .65rem;
  margin: .1rem 0 0 .95rem;
  border-left: 2px solid #eef1f5;
}
.sidebar-group-body li.dragging { opacity: .35; }
.sidebar-group-body .nav-link {
  display: flex;
  align-items: center;
  border-radius: .4rem;
  padding: .38rem .6rem;
  font-size: .8rem;
  font-weight: 400;
  color: #6c757d;
  cursor: grab;
}
.sidebar-group-body .nav-link i.bi { color: #adb5bd; }
.sidebar-group-body .nav-link:active { cursor: grabbing; }
.sidebar-group-body .nav-link:hover { background: #f1f3f5; }
.sidebar-group-body .nav-link.active { background: #0d6efd; color: #fff; font-weight: 500; }
.sidebar-group-body .nav-link.active i.bi { color: #fff; }
/* v34:细粒度权限置灰——账号所属角色能看到这个标签页,但这个具体账号在权限组里对该模块一个操作
   权限都没有,置灰显示模块名(证明功能存在)但不能真正点进去,点击时改成弹提示而不是跳转,
   见 admin.js 的 hasModuleAccess/applyModuleAccessGating */
.nav-link.tab-restricted { opacity: .45; cursor: not-allowed; }
.nav-link.tab-restricted:hover { background: transparent; }
.nav-link.tab-restricted.active { background: transparent; color: inherit; }
.nav-link.tab-restricted i.bi { color: #adb5bd; }

/* 底部固定栏位:表单管理/恢复默认菜单顺序固定钉在侧栏最下方,不随分组内容多少而上下浮动。 */
.admin-sidebar-footer {
  flex: 0 0 auto;
  margin-top: .5rem;
  padding-top: .5rem;
  border-top: 1px solid #eef1f5;
  font-size: .75rem;
}
/* 表单管理/恢复布局这两个按钮并排显示,各占一半宽度,文字居中对齐;不强制单行,
   英文/西语翻译比中文长,允许换行避免被裁掉 */
.admin-sidebar-footer button { flex: 1 1 0; text-align: center; line-height: 1.3; }

/* 悬浮显示的侧栏整体折叠/展开按钮:平时半透明贴在侧栏右边缘,鼠标移到侧栏范围内才完全显示;
   折叠状态下始终显示(不然折叠成一条窄栏后不好精准hover到它,用户会打不开)。 */
.sidebar-collapse-toggle {
  position: absolute;
  top: .6rem;
  right: -11px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: #fff;
  border: 1px solid #dee2e6;
  color: #6c757d;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: .7rem;
  cursor: pointer;
  opacity: 0;
  transition: opacity .15s ease, background .15s ease, color .15s ease;
  z-index: 5;
}
.admin-sidebar:hover .sidebar-collapse-toggle,
.admin-sidebar.collapsed .sidebar-collapse-toggle {
  opacity: 1;
}
.sidebar-collapse-toggle:hover { background: #0d6efd; color: #fff; border-color: #0d6efd; }

/* 折叠成仅图标模式:侧栏收窄,分组标题文字/子项文字/表单管理按钮文字都隐藏,只留图标居中显示,
   把腾出来的宽度让给右边的数据表格区域;鼠标悬浮到图标上用title原生提示显示完整名称。 */
.admin-sidebar.collapsed { width: 56px; flex: 0 0 56px; padding: .6rem .3rem; }
/* 折叠成仅图标模式时,56px宽度并排放不下两个按钮,改回上下堆叠(跟组内标签页图标一样只显示图标) */
.admin-sidebar.collapsed .admin-sidebar-footer { flex-direction: column; gap: .25rem; }
.admin-sidebar.collapsed .sidebar-group-label,
.admin-sidebar.collapsed .sidebar-group-chevron,
.admin-sidebar.collapsed .sidebar-drag-handle,
.admin-sidebar.collapsed .nav-link span,
.admin-sidebar.collapsed .admin-sidebar-footer span {
  display: none;
}
.admin-sidebar.collapsed .sidebar-group-header { justify-content: center; padding: .5rem .2rem; cursor: default; }
.admin-sidebar.collapsed .sidebar-group-body { margin-left: 0; padding-left: 0; border-left: none; }
.admin-sidebar.collapsed .sidebar-group-body .nav-link,
.admin-sidebar.collapsed .admin-sidebar-footer button {
  justify-content: center;
  padding: .45rem .2rem;
}
.admin-sidebar.collapsed .sidebar-group-body .nav-link i.bi,
.admin-sidebar.collapsed .admin-sidebar-footer button i.bi { margin-right: 0 !important; }

@media (max-width: 767px) {
  /* 手机上侧栏跟内容区改成上下堆叠(不是左右并排),两块加起来自然会比一屏高,
     不能再用桌面那套"精确卡视口高度"的算法,不然内容会被硬生生截掉一截看不见——
     改回 auto,让页面按内容自然高度长,该多长滚多少页面。 */
  .admin-shell { flex-direction: column; height: auto; min-height: 0; }
  .admin-sidebar {
    width: 100%;
    flex: 1 1 100%;
    position: static;
    height: auto;
    max-height: none;
  }
  /* 手机上侧栏本来就是上下堆叠、不占表格宽度,折叠功能没意义,强制按展开态显示,悬浮按钮也不显示 */
  .admin-sidebar.collapsed { width: 100%; flex: 1 1 100%; padding: .6rem; }
  .admin-sidebar.collapsed .sidebar-group-label,
  .admin-sidebar.collapsed .sidebar-group-chevron,
  .admin-sidebar.collapsed .nav-link span,
  .admin-sidebar.collapsed .admin-sidebar-footer span { display: inline; }
  .admin-sidebar.collapsed .sidebar-group-header { justify-content: flex-start; padding: .5rem .5rem; }
  .admin-sidebar.collapsed .sidebar-group-body { margin-left: .95rem; padding-left: .65rem; border-left: 2px solid #eef1f5; }
  .admin-sidebar.collapsed .sidebar-group-body .nav-link,
  .admin-sidebar.collapsed .admin-sidebar-footer button { justify-content: flex-start; padding: .38rem .6rem; }
  .sidebar-collapse-toggle { display: none; }
}
