/* Tipsy AB-Config Console — minimal, dependency-free styling. */

:root {
  --fg: #1f2937;
  --muted: #6b7280;
  --bg: #ffffff;
  --card: #ffffff;
  --border: #e5e7eb;
  --primary: #2563eb;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  color: var(--fg);
  background: var(--bg);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

/* Geometry mirrors the React ConsoleChrome header (minHeight 68px, px 24px,
   toolbar gap 16px, nav item px 12px / py 6px, gap 4px) so the pre-mount
   fallback paint does not visibly re-flow when React replaces it. Colors
   mirror the light antd-style header: white surface, #f0f0f0 bottom border. */
.topbar {
  display: flex;
  align-items: center;
  gap: 16px;
  min-height: 68px;
  padding: 0 24px;
  background: #ffffff;
  color: rgba(0,0,0,.88);
  border-bottom: 1px solid #f0f0f0;
  position: sticky;
  top: 0;
  z-index: 10;
}

.brand {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 700;
  font-size: 14px;
  color: inherit;
  text-decoration: none;
}

.brand::before { content: ""; width: 10px; height: 10px; border-radius: 50%; background: #1677ff; flex-shrink: 0; }

.nav-main { display: flex; gap: 4px; flex: 1; }

.nav-item {
  text-decoration: none;
  color: rgba(0,0,0,.65);
  font-weight: 500;
  /* Nav pills: fixed 6-character width (Chinese "灰度白名单" = 6 chars is the
     widest label; English long labels extend past it rather than clipping),
     centered text, smaller than the page title. */
  font-size: 14px;
  min-width: 6em;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 6px 12px;
  /* Match the React chrome's antd default borderRadius (6px) so the server
     fallback pill does not flash a different shape before React replaces it
     on tab navigation. */
  border-radius: 6px;
}

.nav-item:hover { background: rgba(0,0,0,.04); color: rgba(0,0,0,.88); }

.nav-item.is-active { background: #e6f4ff; color: #1677ff; font-weight: 600; }

.user-box { display: flex; gap: 12px; align-items: center; }

/* Fallback user-box controls (pre-mount paint): mirror the React chrome's
   LanguageToggle (two-segment pill), #user-sub chip slot, and logout button so
   the no-JS / pre-mount paint has the same silhouette as the mounted chrome —
   no shape or color jump on MPA navigation. The user id is unknown
   server-side, so #user-sub is a transparent width placeholder; the lang
   segment highlight is set by the translate script from localStorage. */
.lang-toggle-skeleton {
  display: flex;
  height: 38px;
  width: 112px;
  flex-shrink: 0;
  border: 1px solid #d9d9d9;
  border-radius: 6px;
  overflow: hidden;
}
.lang-seg {
  flex: 1 1 50%;
  min-width: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: rgba(0,0,0,.88);
  font-size: 14px;
  font-weight: 600;
  line-height: 1;
  padding: 0 10px;
  white-space: nowrap;
  cursor: pointer;
}
.lang-seg.is-on { color: #fff; background: #1677ff; }
.lang-seg.is-on:hover { background: #4096ff; }

.user-sub {
  /* Reserve the width of a 19-digit uid (matches the React Chip's min/max) so
     the user box does not reflow — but keep it an invisible slot (transparent
     color, no pill background) so it never flashes as a stray "—" or pill
     before React replaces it with the real id chip. */
  display: inline-block;
  min-width: 144px;
  max-width: 180px;
  height: 24px;
  color: transparent;
  overflow: hidden;
  white-space: nowrap;
}

.logout-skeleton {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: none;
  background: transparent;
  color: rgba(0,0,0,.65);
  font: inherit;
  font-size: 0.9rem;
  padding: 4px 8px;
  cursor: pointer;
}
.logout-skeleton:hover { color: rgba(0,0,0,.88); }

/* Fallback nav pill (server-rendered) keeps the exact radius of the React
   chrome's antd button — see the radius comment next to .nav-item above. */


.language-toggle { font-weight: 600; min-width: 3.5em; }

/* Full-bleed content: only the 24px side padding separates the page from the
   viewport edges (no centered max-width column). */
.content { padding: 32px 24px 48px; }

.react-page-active .content { padding-top: 32px; }

.react-page-root { min-height: 420px; }

/* Modal 一级内容区块间距统一 32px；不要命中表单内部的嵌套 Flex，组件
   自己声明的 12/16px 字段间距必须保留。 */
.ant-modal-container .ant-modal-body > .ant-flex-vertical { gap: 32px !important; }

/* 所有 Modal 的标题与内容保持清晰分区，避免首个表单项紧贴标题。 */
.ant-modal-container .ant-modal-header { margin-bottom: 32px; }

/* 所有 Modal 弹窗（用户约定 2026-08-23）：垂直居中；整个 Dialog 限高
   80vh（上下各留约 10%）；内容过高时 body 出纵向滚动条。 */
/* 中心化走 ConfigProvider modal.centered（main.jsx）；此处只做限高 +
   内部纵向滚动——上下各留约 10%，过高时弹窗 body 出滚动条。 */
.ant-modal-container {
  max-height: 80vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.ant-modal-container .ant-modal-header,
.ant-modal-container .ant-modal-footer {
  flex: 0 0 auto;
}

.ant-modal-container .ant-modal-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  /* AntD leaves 24px at the container edge. Extend only the scroll box into
     that gutter, then return the same width as inner padding: content keeps
     its original alignment while the scrollbar sits 8px from the Dialog edge. */
  margin-right: -16px;
  padding-right: 16px;
  scrollbar-gutter: stable;
}

/* Dialog sections use dividers for hierarchy instead of nested outlined cards.
   Keep this scoped to modal content so standalone pages can still use cards as
   visual containers where that context is useful. */
.ant-modal-container .ant-modal-body .ant-card {
  border: 0 !important;
  box-shadow: none !important;
  background: transparent !important;
}
.ant-modal-container .ant-modal-body .ant-card + .ant-card {
  border-top: 1px solid #f0f0f0 !important;
  padding-top: 24px;
}

/* A few legacy detail previews still carry an inline outline.  They are
   sections inside a Dialog, so treat them the same as the borderless cards. */
.ant-modal-container .ant-modal-body #domain-parent-preview {
  border: 0 !important;
  border-radius: 0 !important;
  padding-inline: 0 !important;
}

/* Keep the section rhythm visible after removing card outlines. */
.ant-modal-container .ant-modal-body .dialog-section-divider {
  margin: 0;
}

/* Detail metadata uses the same horizontal KV rhythm as Dialog forms instead
   of a paragraph of bold labels followed by values. */
.info-fields-card .ant-card-body { padding: 0 0 0 28px; }
.info-field-row {
  display: grid;
  grid-template-columns: 96px minmax(0, 1fr);
  gap: 16px;
  align-items: baseline;
}
.info-field-label {
  text-align: left;
  white-space: nowrap;
}
.info-field-value {
  min-width: 0;
  overflow-wrap: anywhere;
}
.ant-modal-container .ant-modal-body .table-slot-card .ant-card-body {
  padding: 0;
}

/* Dialog 表单统一左右 KV：标签列固定，控件列吃满剩余宽度。复杂表格/分组
   编辑器仍保留自身行结构，只给明确标记的字段行套用。 */
.ant-modal-body .dialog-form-row {
  display: grid !important;
  grid-template-columns: 120px minmax(0, 1fr);
  align-items: start !important;
  gap: 16px !important;
  width: 100%;
}
.ant-modal-body .dialog-form-label {
  padding-top: 8px;
  text-align: right;
}
.ant-modal-body .dialog-form-control { min-width: 0; }

/* 管理员创建弹层字段较少：使用紧凑标签列，控件向右吃满 Dialog 内容区。 */
#admin-create-form .dialog-form-row {
  grid-template-columns: 32px minmax(0, 1fr);
  gap: 16px !important;
}
#admin-create-form .dialog-form-label {
  align-self: center;
  padding-top: 0;
  justify-self: end;
  white-space: nowrap;
}
#admin-create-form .dialog-form-control { grid-column: 2; }
#admin-create-form .admin-create-input {
  width: 100%;
  height: 32px;
  box-sizing: border-box;
}

/* 白名单创建表单使用短标签，避免继承 120px 标签列后整体右移；同时恢复
   组件声明的表单行/键列表间距，避免 Modal 的 32px 区块间距误伤嵌套 Flex。 */
#wl-create-form > .ant-flex-vertical { gap: 16px !important; }
#wl-create-form .dialog-form-row {
  grid-template-columns: 40px minmax(0, 1fr);
}
#wl-create-form .dialog-form-label {
  justify-self: end;
  white-space: nowrap;
}
#wl-create-form .dialog-form-control.ant-flex-vertical { gap: 12px !important; }

/* 实验创建 Dialog：表单居中，使用能容纳中英文标签的紧凑 KV 列；字段
   间距走组件声明的 16px，不被 Modal 一级区块间距放大。 */
#exp-create-form {
  width: 100%;
  margin-inline: auto;
}
#exp-create-form > .ant-flex-vertical { gap: 16px !important; }
#exp-create-form > .ant-flex-vertical > .dialog-form-row {
  grid-template-columns: 48px minmax(0, 1fr);
}
#exp-create-form > .ant-flex-vertical > .dialog-form-row > .dialog-form-label {
  justify-self: end;
  white-space: nowrap;
}
#exp-create-form .experiment-section-row > .dialog-form-label { padding-top: 8px; }

/* 资源创建弹层不再套一层窄的内部 max-width；标签列按各表单实际文案
   收紧，让控件区在弹层左右获得对称、紧凑的留白。 */
#namespace-create-form,
#config-create-form,
#domain-create-form,
#layer-create-form {
  width: 100%;
  margin-inline: auto;
}

#namespace-create-form .dialog-form-row,
#layer-create-form .dialog-form-row {
  grid-template-columns: 56px minmax(0, 1fr);
}

#domain-create-form .dialog-form-row {
  grid-template-columns: 72px minmax(0, 1fr);
  padding-right: 16px;
}

#namespace-create-form .dialog-form-label,
#domain-create-form .dialog-form-label,
#layer-create-form .dialog-form-label {
  justify-self: end;
  white-space: nowrap;
}
#domain-create-form .dialog-form-control { grid-column: 2; }

/* ConfigCreateForm 为兼容独立 /configs/new 页面仍保留 120px inline 标签列；
   仅在 Modal 内覆盖为紧凑列，并统一使用 antd secondary 文本色。 */
.ant-modal-body #config-create-form .config-form-label { width: 56px !important; }
.ant-modal-body #config-create-form .config-form-control { min-width: 0; }

#domain-create-form > .ant-flex-vertical,
#layer-create-form > .ant-flex-vertical,
#namespace-create-form > .ant-flex-vertical { gap: 16px !important; }

#domain-create-form #parent-form-select .ant-radio-group {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 24px;
  padding-top: 6px;
}

#domain-create-form .domain-slot-fields {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* Combo 流量占比的重复项统一成逐行 KV。避免字段标题浮在输入框上方，
   并让重复项、新增按钮和 simple 余量使用同一套 antd 尺寸。 */
.combo-share-groups {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.combo-create-section { padding: 24px 40px 24px 24px; }
.combo-create-section + .combo-create-section { border-top: 1px solid #f0f0f0; }
.combo-modal-form .dialog-form-row { grid-template-columns: 88px minmax(0, 1fr); }
.combo-share-item {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 0 0 24px;
  background: transparent;
}
.combo-share-item + .combo-share-item {
  padding-top: 24px;
  border-top: 1px solid #f0f0f0;
}
.combo-share-item-header { min-height: 32px; }
.combo-share-item .combo-share-field,
.combo-simple-pct {
  display: grid !important;
  grid-template-columns: 88px minmax(0, 1fr);
  align-items: center !important;
  gap: 16px !important;
  width: 100%;
}
.combo-share-item .combo-share-field .dialog-form-label,
.combo-simple-pct .dialog-form-label {
  justify-self: end;
  padding-top: 0;
  white-space: nowrap;
}
.combo-share-item .combo-share-field .dialog-form-control,
.combo-simple-pct .dialog-form-control { min-width: 0; }
.combo-share-add-actions { padding-left: 104px; }
.combo-simple-pct .ant-flex > .ant-input { flex: 1; }

.react-page-root { min-width: 0; }

/* 列表页固定视口布局（模板约定 2026-08-23）：带 .console-list-page 的列表
   页里 body/整个页面不滚，表格内容在 .console-table-host 内部滚动，分页器
   （ListTable 内 sticky bottom）始终钉在页面底部可见。非列表页（详情/表单）
   不受影响：仍旧整页滚动。 */
body:has(.console-list-page) {
  display: flex;
  flex-direction: column;
  height: 100vh;
  overflow: hidden;
}
/* React 挂载点 #root 是 body 的直接子级，必须进 flex 链，否则 .content 的
   flex:1 无从继承（实测表格区只撑到内容高度、页面底部留白）。 */
#console-root:has(.console-list-page) {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}
body:has(.console-list-page) .content { padding-bottom: 24px; }
.content:has(.console-list-page) {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}
#console-page-root:has(.console-list-page) {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
}
.react-page-root.console-list-page {
  flex: 1;
  min-height: 0;
  /* 区块间距（用户约定 2026-08-23）：头部行/表格/分页之间的留白加大，
     antd Flex 的 gap 以 inline style 注入，此处用 !important 强制。 */
  gap: 32px !important;
}
.react-page-root.console-list-page > * { flex-shrink: 0; }
.react-page-root.console-list-page > .console-table-host {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
.react-page-root.console-list-page .console-list-table {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
.react-page-root.console-list-page .console-list-table-scroll {
  flex: 1;
  min-height: 0;
}
/* 边框已移到表格滚动区（ListTable 内），外层 host 不再画边框，
   分页器就不会贴着带边框盒子的底边。 */
.console-list-page .console-table-host { border: none !important; }

@media (max-width: 900px) {
  .content { padding: 24px 16px 36px; }
  .topbar { min-height: 60px; padding: 0 16px; }
  .nav-main { overflow-x: auto; padding-bottom: 2px; }
  .nav-item { white-space: nowrap; }
  .user-box { gap: 8px; }
}

@media (max-width: 640px) {
  .content { padding: 20px 12px 28px; }
  .user-sub { max-width: 132px; overflow: hidden; text-overflow: ellipsis; }
}

.btn {
  display: inline-block;
  border: 1px solid var(--border);
  background: var(--card);
  color: var(--fg);
  padding: 6px 12px;
  border-radius: 9px;
  cursor: pointer;
  text-decoration: none;
  font-size: 0.95em;
  transition: background .16s ease, border-color .16s ease, transform .16s ease;
}

.btn:hover { background: #f3f4f6; transform: translateY(-1px); }

.btn-link { border: none; background: transparent; color: var(--primary); padding: 4px 8px; }

.btn-link:hover { text-decoration: underline; background: transparent; }

/* List identifiers remain clickable, but read as links rather than boxed
   controls. The detail action remains a regular button in the Actions column. */
.resource-id-link.ant-btn {
  height: auto;
  padding: 0;
  font-weight: 500;
}

.group-row.ant-card {
  margin-bottom: 0;
  padding: 0;
  border-color: var(--border);
  background: #fafafa;
}
.group-row.ant-card .ant-card-body { padding: 20px; }
.g-no-preview { display: block; font-size: 16px; }
#groups-container .group-kv-row {
  display: grid;
  grid-template-columns: 88px minmax(0, 1fr);
  align-items: start;
  gap: 16px;
}
#groups-container .group-kv-label {
  padding-top: 5px;
  color: var(--muted);
  text-align: right;
  white-space: nowrap;
}
#groups-container .group-kv-control { min-width: 0; }
#groups-container .group-id-control .ant-input { flex: 1 1 auto; min-width: 0; }

code { background: #f3f4f6; padding: 1px 6px; border-radius: 3px; font-family: ui-monospace, monospace; font-size: 0.9em; }

/* config_version key/version selector rows in the experiment group editor:
   each key + version antd Select plus its Remove button sit on ONE line
   (create page and detail edit mode render identically). Scoped strictly to
   .kv-* so other pages' selects are untouched. */

.kv-selector { display: flex; flex-direction: column; gap: 6px; min-width: 240px; }

#groups-container .kv-row {
  display: grid !important;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto;
  gap: 12px !important;
  align-items: center;
}

.kv-row .ant-select { flex: 1 1 auto; min-width: 0; }
#groups-container .kv-row .kv-version { width: 100%; }
#groups-container .kv-row-actions {
  min-width: 168px;
  white-space: nowrap;
}
#groups-container .kv-selector > .kv-add { align-self: flex-end; }

/* One key-select + version-select + remove button per row (like .kv-row). */

.wl-key-row { display: flex; gap: 8px; align-items: center; }

/* key/version 选择器全部定宽（用户约定 2026-08-23：点「添加」后下拉宽度
   必须稳定——包括首次加行时 Remove 按钮出现引起的重排）。 */
.wl-key-row .ant-select { min-width: 0; }
.wl-key-row .wl-version { flex: 0 0 220px; }

/* Stacked (key → version) lines inside the list table's Keys cell. */

.wl-key-line { white-space: nowrap; line-height: 1.6; }

/* Version status-label badges (configs detail page). kind from
   /api/v1/configs/key-status drives the class: full/whitelist/ab/ab-paused/
   ab-draft. (The blue "published" fall-through badge was removed 2026-08-26 —
   it carried no signal and misread as "live".) */
.status {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 12px;
  font-size: 0.85em;
  background: #e5e7eb;
  color: var(--fg);
}
.status-full { background: #dcfce7; color: #166534; }
.status-whitelist { background: #ffedd5; color: #9a3412; }
.status-ab { background: #ede9fe; color: #5b21b6; }
.status-ab-paused { background: #fef9c3; color: #854d0e; }
.status-ab-draft { background: #e5e7eb; color: #4b5563; }

/* Per-version value cell in the Versions table: monospace, capped width,
   wrap long/multi-line values without blowing out the table layout. */
.value-cell {
  margin: 0;
  max-width: 360px;
  max-height: 6em;
  overflow: auto;
  white-space: pre-wrap;
  word-break: break-word;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.85em;
}

/* Compact inline row (experiments/new #slot-range, G5): controls sit on one
   row at content width; wraps on narrow viewports. */
.form.form-inline {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 12px;
}

/* ================= AB Dashboard (experiment dashboard page) =================
   Card-shell styles ported from ab_design_new/dashboard_universal.html.
   Light-only (no dark scheme by design); fixed colors approximate the
   prototype's CSS variables; MUI covers the sidebar toggle/alerts. */

.dash-layout { display: flex; align-items: flex-start; gap: 0; width: 100%; min-width: 0; }
.dash-side {
  width: 320px; flex: 0 0 320px; background: #fbfcfd;
  border: 1px solid #e8eaed; border-radius: 12px; padding: 20px 18px;
  position: sticky; top: 76px; max-height: calc(100vh - 96px); overflow-y: auto;
}
.dash-main { flex: 1 1 auto; min-width: 0; max-width: 100%; padding: 0 0 60px 28px; }

.dash-side .dash-side-toolbar {
  display: flex;
  justify-content: flex-end;
  margin: -8px -8px 10px 0;
}
.dash-side.dash-side-collapsed {
  width: 52px;
  flex-basis: 52px;
  padding: 10px 8px;
  overflow: hidden;
  display: flex;
  justify-content: center;
}
.dash-side-collapsed #dashboard-sidebar-expand {
  width: 34px;
  height: 34px;
}

.dash-side .side-h {
  font-size: 12px; font-weight: 700; letter-spacing: .6px; color: #9ca3af;
  text-transform: uppercase; margin: 0 0 6px;
}
.dash-side .side-sub { font-size: 11.5px; color: #9ca3af; margin: 0 0 18px; }
.dash-side .dim { margin-bottom: 20px; }
.dash-side .dim-title {
  font-size: 13.5px; font-weight: 700; color: #111827; margin: 0 0 9px;
  display: flex; align-items: center; gap: 6px;
}
.dash-side .dim-title .ic { width: 4px; height: 13px; border-radius: 2px; background: #2f6fed; opacity: .85; }
.dash-side .opts { display: flex; flex-wrap: wrap; gap: 5px; }
.dash-side .opt {
  display: flex; align-items: center; justify-content: space-between;
  padding: 6px 11px; border-radius: 8px; font-size: 13px; color: #374151;
  cursor: pointer; border: 1px solid transparent; transition: .12s; user-select: none;
}
.dash-side .opt:hover { background: #eef1f5; }
.dash-side .opt.on { background: #2f6fed; color: #fff; font-weight: 600; }
.dash-side .side-foot {
  margin-top: 8px; padding-top: 14px; border-top: 1px dashed #e8eaed;
  font-size: 11px; color: #9ca3af; line-height: 1.5;
}

.dash-side .daterange { display: flex; flex-direction: column; gap: 8px; margin: 2px 0 16px; }
.dash-side .dr-quick { display: flex; gap: 6px; margin-top: 2px; }
.dash-side .dr-quick .qk {
  flex: 1; text-align: center; font-size: 11px; color: #374151;
  border: 1px solid #e8eaed; border-radius: 7px; padding: 4px 0; cursor: pointer; transition: .12s;
}
.dash-side .dr-quick .qk:hover { border-color: #2f6fed; color: #2f6fed; }
.dash-side .dr-quick .qk.on { background: #2f6fed; color: #fff; border-color: #2f6fed; }
.dash-side .dr-err { font-size: 11px; color: #e5484d; min-height: 14px; }

.dash-side .csvbar { margin-bottom: 18px; }
.dash-side .csv-btn {
  width: 100%; padding: 8px 0; border: 1px solid #2f6fed; border-radius: 9px;
  background: #2f6fed; color: #fff; font-size: 13px; font-weight: 600; cursor: pointer;
  font-family: inherit; transition: .12s;
}
.dash-side .csv-btn:hover { opacity: .88; }
.dash-side .csv-hint { font-size: 10.5px; color: #9ca3af; margin-top: 5px; text-align: center; }

.dash-main .pagehead {
  display: flex; align-items: baseline; justify-content: space-between;
  margin: 2px 2px 18px; flex-wrap: wrap; gap: 8px;
}
.dash-main .pagehead > * { min-width: 0; }
.dash-main .pagehead h1 { font-size: 19px; margin: 0; font-weight: 700; overflow-wrap: anywhere; }
.dash-main .pagehead .sub { font-size: 12.5px; color: #6b7280; overflow-wrap: anywhere; }
.dash-main .legend { display: flex; gap: 16px; font-size: 12.5px; align-items: center; flex-wrap: wrap; }
.dash-main .legend i {
  display: inline-block; width: 11px; height: 11px; border-radius: 3px;
  margin: 0 5px 0 4px; vertical-align: -1px;
}
.dash-main .legend-item { display: inline-flex; align-items: center; cursor: pointer; user-select: none; }
.dash-main .legend-item input.ctrl-checkbox { cursor: pointer; margin: 0; }
.dash-main .legend-hint { font-size: 11px; color: #9ca3af; }

.dash-main .warnbar {
  border-radius: 9px; padding: 8px 13px; font-size: 12px; margin: 0 0 16px; font-weight: 600;
}
.dash-main .warnbar.monitor { background: #fff8e6; border: 1px solid #f0c869; color: #8a6d1a; }

.mgroup-card { margin-top: 34px; min-width: 0; }
.mgroup-card:first-child { margin-top: 6px; }
.mgroup-h {
  display: flex; align-items: center; gap: 11px; margin-bottom: 14px;
  padding: 9px 14px; border-radius: 10px;
  background: linear-gradient(90deg, var(--grp-tint), transparent 70%);
  border-left: 5px solid var(--grp-c, #2f6fed);
  min-width: 0;
}
.mgroup-h .bar { width: 8px; height: 22px; border-radius: 3px; background: var(--grp-c, #2f6fed); }
.mgroup-h .name { font-size: 19px; font-weight: 900; letter-spacing: .3px; color: #111827; min-width: 0; overflow-wrap: anywhere; }
.mgroup-h .desc { font-size: 11.5px; color: #6b7280; font-weight: 500; min-width: 0; overflow-wrap: anywhere; }
.mcard-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; min-width: 0; }
.mcard { border: 1px solid #e8eaed; border-radius: 11px; padding: 13px 15px 15px; background: #fff; min-width: 0; }
.mcard-h {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; margin-bottom: 6px; flex-wrap: wrap;
  min-width: 0;
}
.mcard-t { font-size: 13.5px; font-weight: 700; color: #111827; min-width: 0; overflow-wrap: anywhere; }
.fam {
  display: inline-block; font-size: 9px; font-weight: 700; padding: 1px 6px;
  border-radius: 8px; margin-left: 6px; vertical-align: middle; letter-spacing: .2px;
}
.fam.sameday { background: #f3effe; color: #8b5cf6; border: 1px solid #e3d9fb; }
.fam.rolling { background: #e9f9f0; color: #128a5a; border: 1px solid #c8ecd7; }
.daybtns { display: flex; gap: 4px; }
.db {
  font-size: 10.5px; color: #374151; border: 1px solid #e8eaed; border-radius: 6px;
  padding: 2px 8px; cursor: pointer; transition: .12s; user-select: none;
}
.db:hover { border-color: #128a5a; color: #128a5a; }
.db.on { background: #128a5a; color: #fff; border-color: #128a5a; }
.db.dis { opacity: .35; cursor: not-allowed; pointer-events: none; }
.mcard .chart { width: 100%; position: relative; margin-top: 4px; }
.mcard .chart-with-data { height: 230px; }
.mcard .chart svg text { font-size: 11px; fill: #9ca3af; }
.mcard .chart { cursor: crosshair; }
.mcard .dashboard-tooltip {
  position: absolute; top: 6px; transform: translateX(-50%); pointer-events: none;
  background: #fff; border: 1px solid #e8eaed; border-radius: 9px; padding: 7px 10px;
  box-shadow: 0 4px 16px rgba(16,24,40,.13); font-size: 12px; min-width: 130px;
  transition: opacity .1s; z-index: 5; display: none;
}
.mcard .dashboard-tooltip .tip-x { font-weight: 700; color: #111827; margin-bottom: 4px; font-size: 11.5px; }
.mcard .dashboard-tooltip .tip-r { display: flex; justify-content: space-between; gap: 14px; margin: 2px 0; }
.mcard .dashboard-tooltip .tip-r span { font-size: 11.5px; }
.mcard .dashboard-tooltip .tip-r b { font-variant-numeric: tabular-nums; font-size: 11.5px; }
.mcard .dashboard-tooltip .tip-r b em {
  font-style: normal; color: #6b7280; font-weight: 600; margin-left: 3px; font-size: 10.5px;
}
.cmp-wrap { margin-top: 9px; padding-top: 9px; border-top: 1px dashed #f0f1f3; }
.cmp { display: flex; flex-wrap: wrap; gap: 14px; align-items: center; }
.cmp-i { font-size: 11.5px; display: inline-flex; align-items: center; gap: 5px; min-width: 0; overflow-wrap: anywhere; }
.cmp-i b { font-weight: 600; }
.cmp-i .p { color: #1fa971; font-weight: 700; font-variant-numeric: tabular-nums; }
.cmp-i .n { color: #e5484d; font-weight: 700; font-variant-numeric: tabular-nums; }
.cmp-i .z { color: #6b7280; font-weight: 700; font-variant-numeric: tabular-nums; }
.cmp-i .dz { color: #9ca3af; font-style: italic; }
.cmp-na { font-size: 11.5px; color: #9ca3af; font-style: italic; }
.axis-note { font-size: 11px; color: #9ca3af; margin-top: 8px; }
.dash-note { font-size: 11px; color: #9ca3af; margin-top: 24px; text-align: center; }

@media (max-width: 1100px) { .mcard-grid { grid-template-columns: 1fr; } }
@media (max-width: 900px) {
  .dash-layout { flex-direction: column; }
  .dash-side { position: static; width: 100%; flex: none; max-height: none; margin-bottom: 16px; }
  .dash-side.dash-side-collapsed {
    width: 100%;
    justify-content: flex-end;
  }
  .dash-main { padding-left: 0; }
}

.namespace-detail-modal .dialog-form-row {
  grid-template-columns: 56px minmax(0, 1fr);
}
.namespace-detail-modal .dialog-form-label {
  justify-self: end;
  white-space: nowrap;
}
.namespace-detail-modal .desc-input {
  flex: 1;
  min-width: 0;
}

/* Refetch feedback: the page keeps the previous payload visible but dims the
   charts area while the new slice is in flight (hook sets data-loading). The
   sidebar stays interactive — filter changes abort the in-flight request. */
.dash-layout[data-loading] .dash-main { opacity: .55; pointer-events: none; transition: opacity .15s; }

/* 全项目 textarea 固定尺寸: 禁止拖拽缩放; 高度由 rows 决定; 文案超出走内部滚动条. */
textarea,
textarea.ant-input {
  resize: none;
  overflow-y: auto;
}
